Education_Tutorial Series/Coding Project: Battle Royale
Coding Project: Battle Royale - [7] Cleanup and Reset (정리와 리셋)
Roblox_개발자
2021. 4. 21. 00:38
이번글은 게임 종료후 다음 게임을 시작하기 전까지 처리해줘야 하는 일들에 대해 설명하고 있다.
큰 틀은
1. 경기종료 후, 플레이어들에게 경기 결과 보여주기.
2. 다음 경기를 위한 준비하기
a. 무기 제거하기
b. 플레이어를 로비로 이동시키고, 변수 초기화하기
이렇게 구성되어 있다.
배틀로얄 튜토리얼의 마지막 단계다. 이번 글에서는 이전에 작성했던 코드들을 완성시켜 게임 루프가 원활하게 진행되는 것을 중점으로 한다.
Updating the GUI (GUI 업데이트)
게임을 정리하고 리셋하기 전에 플레이어들에게 경기 결과를 알려줘야 한다. 이번 섹션은 어떤식으로 플레이어들에게 경기결과를 보여주는지 설명하는 섹션이다.
Get the Winning Player Name (최종 승리자 이름 얻기)
경기 결과를 보여주기 위해선 우승자의 이름을 가져온다.
우승자의 결과를 가져오는 방법은 activePlayer의 첫번째 인덱스에 있는 값을 가져오면 된다.
PlayerManager에 getWinnerName 함수를 추가한다.
플레이어의 연결이 끊겼을 수 있으니 다시 한번 activePlayers의 첫번째 인덱스 값이 존재하는지 확인해준다.
activePlayers의 첫번째 인덱스 값이 존재한다면 해당 플레이어의 이름을 변수에 담아주고, 존재하지 않는다면 에러 메세지를 리턴해준다.
if activePlayers[1] then
local winningPlayer = activePlayers[1]
return winningPlayer.Name
else
return "Error: No winning player found"
end
Get the End Status (엔딩 상태 얻기)
모듈 함수를 사용하여 TimerUp이든 FoundWinner이든 엔딩 상태에서 정보를 가져옵니다. 그런 다음 해당 상태 변수를 DisplayManager로 보내 적절한 메시지로 상태 GUI를 업데이트한다.
MatchManager에 getEndStatus 모듈함수를 추가해준다. 파라미터는 endState를 받는다.
게임 엔딩이 타임오버일 경우와 최후의 1인일 경우에 따른 분기점을 나눠준다.
각 조건에 따라 statusToReturn 변수에 다른 값을 할당해준다.
return을 통해 statusToReturn 값을 반환해준다.
Display and Test (경기 결과 보여주기 및 테스트)
GameManager의 statusToReturn에서 업데이트 된 공지 사항을 가져와 DisplayManager를 사용하여 플레이어에게 표시한다.
GameManager 안에 위의 코드를 추가해준다.
GUI에 리턴받은 메세지를 보여주기 위해, disPlayManager의 updateStatus함수를 호출하고 endStatus를 파라미터 값으로 넘겨준다.
플레이어들이 메세지(경기결과)를 볼 수 있도록 wait를 사용하여 루프를 잠시 멈춘다.
잘 작동하는지 테스트해보자.
타임오버로 게임이 종료된 경우 Time ran out! 메세지가,
최후의 1인이 남아서 게임이 종료된 경우 Winner is playerName 메세지가 보여야 한다.
원하는 결과가 나오지 않는다면 위에 Troubleshooting Tips를 참고하자.
Starting New Matches (새 매치 시작하기)
새 매치를 시작하기 전에, 약간의 transition이 주어진다. 이 transition
을 통해 유저는 경기 결과를 확인할 수 있고, 로비로 순간 이동되는 것이 덜 급작스럽게 느껴지게 된다.
transition이 끝나면 경기장에 남아 있는 플레이어는 모두 제거되고, 모든 코드가 재설정 된다. 이렇게 하면 플레이어들은 초기화된 경기를 다시 시작할 수 있게 된다.
Transition to Intermission (transition에서 intermission)
transition동안에 유저들이 싸우지 못하도록 무기를 모두 제거해준다.
위의 코드전부를 PlayerManager안에 추가하도록 하자. 이 함수는 플레이어가 무기를 장착중이던, 백팩안에 무기를 가지고 있던 그들의 무기를 모두 제거한다.
local function removePlayerWeapon(whichPlayer)
-- Check to see if a player exist in case they disconnected or left.
if whichPlayer then
local character = whichPlayer.Character
-- If the player has it currently on their character
if character:FindFirstChild("Weapon") then
character.Weapon:Destroy()
end
-- If theplayer has the weapon in their backpack
if whichPlayer.Backpack:FindFirstChild("Weapon") then
whichPlayer.Backpack.Weapon:Destroy()
end
else
print("No player to remove weapon")
end
end
removeAllWeapons 모듈 함수를 생성한다.
removeAllWeapons 함수 안에, pairs를 이용한 for문을 실행시켜 모든 플레이어의 무기를 제거해주도록 한다.
for playerKey, whichPlayer in pairs(activePlayers) do
removePlayerWeapon(whichPlayer)
end
Cleanup (정리)
MatchManager 안에 cleanupMatch 함수를 생성하고 그 안에 playerManager의 removeAllWeapons 함수를 호출한다.
function MatchManager.cleanupMatch()
playerManager.removeAllWeapons()
end
그 다음, GameManager의 whil 루프 안에 matchManager의 cleanupMatch함수를 호출한다.
매치가종료된 후 플레이어의 무기가 제거되는지 테스트해보자.
테스트할 때 플레이어 연결이 끊기니까 에러가 났음. 실제 게임을 만들때는 플레이어가 접속이 끊기는 것에 대한 예외처리도 필수적으로 해줘야 할 듯
Reset (리셋)
경기가 끝난 후에도 플레이어가 경기장에 남아있는 것과 같이 아직 해결해야 할 몇가지 부분이 남아있다. 경기가 정리되면 다음 게임을 리셋해줘야 한다. 리셋에는 경기장의 플레이어를 다시 로비로 보내고 active players table
을 지우는 것이 포함된다. 리셋을하면 게임 루프가 무한정 실행될 수 있다.
첫째로, 플레이어들을 로비 돌려보내는 작업을 해보자.
1. PlayerManager안에 resetPlayers 함수를 생성한다.
2. 생성한 함수 안에 activePlayers 테이블을 순회하는 for문을 추가한다.
3. for문 안에 respawnPlayerInLobby 함수를 호출하고 player를 파라미터로 건네준다.
function PlayerManager.resetPlayers()
for playerKey, whichPlayer in pairs(activePlayers) do
respawnPlayerInLobby(whichPlayer)
end
end
activePlayers 테이블을 빈 테이블로 초기화시켜준다.
MatchManager 안에 resetMatch 함수를 생성한 후, playerManager의 resetPlayer함수를 호출한다.
function MatchManager.resetMatch()
playerManager.resetPlayers()
end
GameManager의 while 루프문 안에 matchManager의 resetMatch 함수를 호출한다.
테스트를 진행하여 적어도 2번 이상 게임이 에러없이 진행되는 것을 확인하자.
이제 게임 루프가 완료되었으므로 다음 강의에서는 소품 팩을 사용하여 게임 세계를 개선하는 등 게임 플레이를 개선하고 다듬는 방법을 배울 예정이다.
테스트 시 에러가 난다면 Troubleshooting Tips를 참고하자.
Scripts (전체 스크립트)
GameManager
-- Services
local ServerStorage = game:GetService("ServerStorage")
local Players = game:GetService("Players")
-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local matchManager = require(moduleScripts:WaitForChild("MatchManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))
-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
while true do
displayManager.updateStatus("Waiting for Players")
repeat
wait(gameSettings.intermissionDuration)
until Players.NumPlayers >= gameSettings.minimumPlayers
displayManager.updateStatus("Get ready!")
wait(gameSettings.transitionTime)
matchManager.prepareGame()
local endState = matchEnd.Event:Wait()
local endStatus = matchManager.getEndStatus(endState)
displayManager.updateStatus(endStatus)
matchManager.cleanupMatch()
wait(gameSettings.transitionTime)
matchManager.resetMatch()
end
MatchManager
local MatchManager = {}
-- Services
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Module Scripts
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local playerManager = require(moduleScripts:WaitForChild("PlayerManager"))
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
local displayManager = require(moduleScripts:WaitForChild("DisplayManager"))
local timer = require(moduleScripts:WaitForChild("Timer"))
-- Events
local events = ServerStorage:WaitForChild("Events")
local matchStart = events:WaitForChild("MatchStart")
local matchEnd = events:WaitForChild("MatchEnd")
-- Values
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local timeLeft = displayValues:WaitForChild("TimeLeft")
-- Creates a new timer object to be used to keep track of match time.
local myTimer = timer.new()
-- Local Functions
local function stopTimer()
myTimer:stop()
end
local function timeUp()
matchEnd:Fire(gameSettings.endStates.TimerUp)
end
local function startTimer()
myTimer:start(gameSettings.matchDuration)
myTimer.finished:Connect(timeUp)
while myTimer:isRunning() do
-- Adding +1 makes sure the timer display ends at 1 instead of 0.
timeLeft.Value = (math.floor(myTimer:getTimeLeft() + 1))
-- By not setting the time for wait, it offers more accurate looping
wait()
end
end
-- Module Functions
function MatchManager.prepareGame()
playerManager.sendPlayersToMatch()
matchStart:Fire()
end
function MatchManager.getEndStatus(endState)
local messageToReturn
if endState == gameSettings.endStates.FoundWinner then
local winnerName = playerManager.getWinnerName()
messageToReturn = "Winner is : " .. winnerName
elseif endState == gameSettings.endStates.TimerUp then
messageToReturn = "Time ran out!"
else
messageToReturn = "Error found"
end
return messageToReturn
end
function MatchManager.cleanupMatch()
playerManager.removeAllWeapons()
end
function MatchManager.resetMatch()
playerManager.resetPlayers()
end
matchStart.Event:Connect(startTimer)
matchEnd.Event:Connect(stopTimer)
return MatchManager
PlayerManager
local PlayerManager = {}
-- Services
local Players = game:GetService("Players")
local ServerStorage = game:GetService("ServerStorage")
local ReplicatedStorage = game:GetService("ReplicatedStorage")
-- Modules
local moduleScripts = ServerStorage:WaitForChild("ModuleScripts")
local gameSettings = require(moduleScripts:WaitForChild("GameSettings"))
-- Events
local events = ServerStorage:WaitForChild("Events")
local matchEnd = events:WaitForChild("MatchEnd")
-- Map Variables
local lobbySpawn = workspace.Lobby.StartSpawn
local arenaMap = workspace.Arena
local spawnLocations = arenaMap.SpawnLocations
-- Values
local displayValues = ReplicatedStorage:WaitForChild("DisplayValues")
local playersLeft = displayValues:WaitForChild("PlayersLeft")
-- Player Variables
local activePlayers = {}
local playerWeapon = ServerStorage.Weapon
local function checkPlayerCount()
if #activePlayers == 1 then
matchEnd:Fire(gameSettings.endStates.FoundWinner)
print("Found winner")
end
end
local function removeActivePlayer(player)
print("removing player")
for playerKey, whichPlayer in pairs(activePlayers) do
if whichPlayer == player then
table.remove(activePlayers, playerKey)
playersLeft.Value = #activePlayers
checkPlayerCount()
end
end
end
local function respawnPlayerInLobby(player)
player.RespawnLocation = lobbySpawn
player:LoadCharacter()
end
local function preparePlayer(player, whichSpawn)
player.RespawnLocation = whichSpawn
player:LoadCharacter()
local character = player.Character or player.CharacterAdded:Wait()
-- Give the player a tool
local sword = playerWeapon:Clone()
sword.Parent = character
local humanoid = character:WaitForChild("Humanoid")
humanoid.Died:Connect(function()
respawnPlayerInLobby(player)
removeActivePlayer(player)
end)
end
local function onPlayerJoin(player)
player.RespawnLocation = lobbySpawn
end
local function removePlayerWeapon(whichPlayer)
-- Check to see if a player exist in case they disconnected or left.
if whichPlayer then
local character = whichPlayer.Character
-- If the player has it currently on their character
if character:FindFirstChild("Weapon") then
character.Weapon:Destroy()
end
-- If the player has the weapon in their backpack
if whichPlayer.Backpack:FindFirstChild("Weapon") then
whichPlayer.Backpack.Weapon:Destroy()
end
else
print("No player to remove weapon")
end
end
function PlayerManager.sendPlayersToMatch()
local availableSpawnPoints = spawnLocations:GetChildren()
for playerKey, whichPlayer in pairs(Players:GetPlayers()) do
table.insert(activePlayers,whichPlayer)
-- Gets a spawn location and then removes it from the table so the next player gets the next spawn
local spawnLocation = availableSpawnPoints[1]
table.remove(availableSpawnPoints, 1)
preparePlayer(whichPlayer, spawnLocation)
end
playersLeft.Value = #activePlayers
end
function PlayerManager.getWinnerName()
if activePlayers[1] then
local winningPlayer = activePlayers[1]
return winningPlayer.Name
else
return "Error: No player found"
end
end
function PlayerManager.removeAllWeapons()
for playerKey, whichPlayer in pairs(activePlayers) do
removePlayerWeapon(whichPlayer)
end
end
function PlayerManager.resetPlayers()
for playerKey, whichPlayer in pairs(activePlayers) do
respawnPlayerInLobby(whichPlayer)
end
activePlayers = {}
end
-- Events
Players.PlayerAdded:Connect(onPlayerJoin)
return PlayerManager
참고 링크
education.roblox.com/en-us/resources/battle-royale/cleanup-and-reset
정리 및 초기화
이 플랫폼은 사용자 경험을 개선하고, 콘텐츠를 맞춤 설정하고, 소셜 미디어 기능을 제공하고, 트래픽을 분석하기 위해 쿠키를 사용합니다. 이 플랫폼의 쿠키 사용을 중지 또는 관리하는 방법
education.roblox.com