๊ธฐ์ˆ  ์ž๋ฃŒ

์ง€์›

Cloud Code

Cloud Code

Lobby ์‚ฌ์šฉ

Use Cloud Code and Lobby service together to trigger server-side actions when players join lobbies.
์ฝ๋Š” ์‹œ๊ฐ„ 2๋ถ„์ตœ๊ทผ ์—…๋ฐ์ดํŠธ: 12์‹œ๊ฐ„ ์ „

๊ฒŒ์ž„์—์„œ Lobby์™€ Cloud Code๋ฅผ ํ•จ๊ป˜ ์‚ฌ์šฉํ•˜์—ฌ ์ผ๋ฐ˜์ ์ธ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ์ •์˜ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๊ฒŒ์ž„ ๋กœ๋น„๊ฐ€ ๋งค์น˜๋ฅผ ์‹œ์ž‘ํ•  ์ค€๋น„๊ฐ€ ๋œ ์ƒํ™ฉ์ด๋ผ๊ณ  ๊ฐ€์ •ํ•ฉ๋‹ˆ๋‹ค. ์ค€๋น„๋œ ๊ฒŒ์ž„ ๋กœ๋น„์— ๋Œ€ํ•œ ์‘๋‹ต์œผ๋กœ ๋‹ค์Œ ์Šคํฌ๋ฆฝํŠธ๋ฅผ ํŠธ๋ฆฌ๊ฑฐํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์ด ์˜ˆ์‹œ์—์„œ๋Š” Remote Config์™€์˜ ์—ฐ๋™๋„ ํ•จ๊ป˜ ๋‹ค๋ฃน๋‹ˆ๋‹ค. ์ด ์˜ˆ์‹œ๋ฅผ ๋”ฐ๋ฅด๊ธฐ ์ „์— Unity Cloud Dashboard๋ฅผ ํ†ตํ•ด Lobby์™€ Cloud Code ๋ชจ๋‘๋ฅผ ํ™œ์„ฑํ™”ํ•˜๊ณ  ๋‹จ๊ณ„์— ๋”ฐ๋ผ ํ•„์ˆ˜ SDK๋ฅผ ์„ค์น˜ํ•˜์‹œ๊ธฐ ๋ฐ”๋ž๋‹ˆ๋‹ค. ์„œ๋น„์Šค ์ธ์ฆ์„ ์‚ฌ์šฉํ•˜์—ฌ ํ”„๋กœ์ ํŠธ์—์„œ ๋กœ๋น„๋ฅผ ์ƒ์„ฑํ•˜๊ณ  ID๋ฅผ ๊ธฐ๋กํ•ด ๋‘ก๋‹ˆ๋‹ค. Cloud Code๋ฅผ ํ†ตํ•ด ์ด๋ฅผ ์ˆ˜ํ–‰ํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค(Unity Lobby ์˜ˆ์‹œ ํ™•์ธ). ๋‹ค์Œ ์˜ˆ์‹œ๋Š” ๋กœ๋น„ ID, ๊ทธ๋ฆฌ๊ณ  ๋กœ๋น„ ์ƒ์„ฑ์— ์‚ฌ์šฉ๋˜๋Š” ์„œ๋น„์Šค ID๋ผ๋Š” ๋‘ ํŒŒ๋ผ๋ฏธํ„ฐ๋ฅผ ์Šคํฌ๋ฆฝํŠธ์— ์ œ๊ณตํ•ฉ๋‹ˆ๋‹ค. ๋กœ๋น„๋ฅผ ๋งŒ๋“  ๋‹ค์Œ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ์— ์—ฌ๋Ÿฌ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์„ ์ ์šฉํ•˜์—ฌ ์—…๋ฐ์ดํŠธํ•ฉ๋‹ˆ๋‹ค. ์ด๋Ÿฌํ•œ ๋ณ€๊ฒฝ ์‚ฌํ•ญ์€ ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ์‹ ๊ทœ ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ๋” ์ด์ƒ ์ฐธ์—ฌํ•  ์ˆ˜ ์—†๋„๋ก ๋กœ๋น„๋ฅผ ์ž ๊ธ‰๋‹ˆ๋‹ค.
  • ๋กœ๋น„์˜ ํ”Œ๋ ˆ์ด์–ด ID๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒŒ์ž„์˜ ๋ฌด์ž‘์œ„ โ€˜ํ”Œ๋ ˆ์ด์–ด ์ˆœ์„œโ€™๋ฅผ ๊ตฌ์„ฑํ•ฉ๋‹ˆ๋‹ค.
  • Remote Config์— ์ •์˜๋œ
    LOBBY_CURRENT_MAPS
    ์„ค์ •์„ ์‚ฌ์šฉํ•˜์—ฌ ๊ฒŒ์ž„์˜ ๋งต์„ ๋ฌด์ž‘์œ„๋กœ ์„ ํƒํ•ฉ๋‹ˆ๋‹ค. ์Šคํฌ๋ฆฝํŠธ๊ฐ€ ์„ฑ๊ณต ๋˜๋Š” ์‹คํŒจ๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๊ธฐ๋ณธ ๋ถ€์šธ์„ ๋ฐ˜ํ™˜ํ•ฉ๋‹ˆ๋‹ค.
JavaScript
const _ = require("lodash-4.17");const { LobbyApi, DataObjectVisibilityEnum } = require("@unity-services/lobby-1.2");const { SettingsApi } = require("@unity-services/remote-config-1.1");module.exports = async ({params,context,logger}) => { const { serviceId, lobbyId } = params; const { projectId, environmentId, playerId } = context; const lobbyApi = new LobbyApi(context); const remoteConfig = new SettingsApi(context); try { const {data: lobby} = await lobbyApi.getLobby(lobbyId, serviceId); return setUpGame(remoteConfig, projectId, environmentId, serviceId, lobbyApi, logger, lobby); } catch (err) { logger.error(`Failed to set up lobby: ${err.response.data.detail}`); return false; }};// Updates a lobby to set up for the start of a game, including reading a value from Remote Config to randomly assign to the lobby.async function setUpGame(remoteConfig, projectId, environmentId, serviceId, lobbyApi, logger, lobby) { let playerIds = []; for (let i = 0; i < lobby.players.length; i++) { playerIds.push(lobby.players[i].id); } // Generate a turn order for the game by shuffling the player IDs. _.shuffle(playerIds); // Load all of the maps that are currently configured as available in Remote Config and pick a random one for this game. let maps = getCurrentMapsFromRemoteConfig(remoteConfig, projectId, environmentId) if (maps == null) { throw "Maps loaded from Remote Config are null."; } let gameMap = _.sample(maps); try { const updateResponse = await lobbyApi.updateLobby( lobby.id, serviceId, null, { isLocked: true, // Lock the lobby so that new players cannot join. hostId: playerIds[0], // Transfer host ownership from the service to one of the players. data: { "playerOrder": { value: playerIds.toString(), visibility: DataObjectVisibilityEnum.Member // Set the visibility of the player order so that only lobby members can access it. }, "map": { value: gameMap, visibility: DataObjectVisibilityEnum.Public // Set the visibility of the game map so that anyone who views the lobby can access it. } } } ); return true; } catch (err) { throw err; } return lobbyUpdateSuccess;}// Loads the Remote Config JSON value for LOBBY_CURRENT_MAPS and returns the array of maps inside of it.// If the JSON is invalid, the return value is null.async function getCurrentMapsFromRemoteConfig(remoteConfig, projectId, environmentId) { const currentMapsId = 'LOBBY_CURRENT_MAPS'; const remoteConfigResponse = await remoteConfig.assignSettingsGet( projectId, environmentId, 'settings', [currentMapsId] ); let mapsJSON = remoteConfigResponse?.data?.configs?.settings?.[currentMapsId]; if (!mapsJSON || !mapsJSON.maps || mapsJSON.maps.length == 0) { return null; } return mapsJSON.maps;}// Uncomment the code below to enable the inline parameter definition// - Requires Cloud Code JS dev environment setup with NodeJS (https://docs.unity3d.com/Packages/com.unity.services.cloudcode@latest/index.html?subfolder=/manual/Authoring/javascript_project.html)//// module.exports.params = {// serviceId: { type: "String", required: true },// lobbyId: { type: "String", required: true },// };
์ด Remote Config JSON์˜ ๊ตฌ์กฐ๋Š” ๋‹จ์ˆœํ•˜์ง€๋งŒ ์ด ์˜ˆ์‹œ ๊ตฌ์„ฑ์„ ํ™•์žฅํ•˜์—ฌ ๊ฒŒ์ž„์„ ๋” ์„ธ๋ฐ€ํ•˜๊ฒŒ ์ œ์–ดํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ๋งˆ์ฐฌ๊ฐ€์ง€๋กœ ๊ฒŒ์ž„ ์„ค์ • ์™ธ์— Cloud Code์—์„œ ๋‹ค์–‘ํ•œ ํŠธ๋ฆฌ๊ฑฐ๋ฅผ ๊ณ ๋ คํ•  ์ˆ˜ ์žˆ์œผ๋ฉฐ, ๊ทธ ์˜ˆ์‹œ๋Š” ๋‹ค์Œ๊ณผ ๊ฐ™์Šต๋‹ˆ๋‹ค.
  • ๋กœ๋น„ ์ƒ์„ฑ์— ๋”ฐ๋ผ ํŠธ๋ฆฌ๊ฑฐํ•˜์—ฌ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ ๊ฐ’์„ ์กฐ์ •ํ•˜๊ณ  ๋ฌธ์ž์—ด์„ ๋‹ค๋“ฌ์Šต๋‹ˆ๋‹ค.
  • Cloud Save ํ”Œ๋ ˆ์ด์–ด ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋ ˆ์ด์–ด์˜ ์ฟผ๋ฆฌ ๊ฒฐ๊ณผ๋ฅผ ํ•„ํ„ฐ๋งํ•˜๊ณ , ์ฐธ์—ฌ๋ฅผ ์Šน์ธ/๊ฑฐ๋ถ€ํ•˜๊ณ , ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.
  • Economy ๋ฐ์ดํ„ฐ๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ํ”Œ๋ ˆ์ด์–ด ๋˜๋Š” ๋กœ๋น„ ๋ฉ”ํƒ€๋ฐ์ดํ„ฐ๋ฅผ ์„ค์ •ํ•ฉ๋‹ˆ๋‹ค.

Lobby ์‚ฌ์šฉ โ€ข Cloud Code โ€ข Unity Docs