대다수의 경험에서 시간 경과에 따른 플레이어 경험 데이터를 추적하기 위해 플레이어 통계를 사용합니다. 고득점, 총 게임 승리 횟수, 총 플레이 시간, 수집된 아이템 등의 통계는 플레이어에게 게임 진행 상황을 알려주며, 이는 모두 플레이어가 경험으로 다시 돌아오도록 유도하는 좋은 방법입니다.
Verse 퍼시스턴스는 Verse 스크립트에 퍼시스턴스 데이터를 추가할 수 있도록 해주는 강력한 도구입니다. 퍼시스턴스 데이터는 플레이어 단위, 섬 단위로 저장되며, 게임플레이 세션 사이에 동일하게 유지됩니다. 퍼시스턴스 데이터를 통해 플레이 세션 사이의 플레이어 진행 상황을 추적할 수 있으며, UEFN에서 이전에는 할 수 없었던 독특하고 흥미로운 플레이 경험을 다양하게 제작해 볼 수 있습니다.
이 튜토리얼은 Verse를 사용하여 플레이어 통계의 커스텀 테이블을 생성하고, 경험을 여러 차례에 걸쳐 플레이하면서 통계가 유지되도록 구성하는 방법을 보여줍니다. 이 튜토리얼을 마친 후에는 Verse에서 나만의 게임 내 순위표 만들기를 확인해 보고 퍼시스턴스를 사용해 게임 내 순위표를 제작하는 방법을 알아보세요.
사용된 Verse 언어 기능
클래스: 이 예시에서는 싱글 플레이어용 통계 그룹을 추적하는 퍼시스턴스 클래스뿐만 아니라 단일 통계를 관리하는 Verse 클래스를 생성합니다.
생성자: 생성자는 관련 클래스의 인스턴스를 생성하는 특수 함수입니다.
Weak_map: weak_map은 반복작업할 수 없는 단순한 맵입니다. Verse 퍼시스턴스 데이터는 weak_map에 저장되어야 합니다.
레벨 구성하기
이 예시에서는 다음과 같은 사물과 장치를 사용합니다.
2 x 버튼 장치: 플레이어가 장치와 상호작용할 때, 현재 점수에 포인트를 추가합니다. 게임 종료를 시뮬레이션하는 또 다른 버튼 장치를 사용하여 현재 점수에 따라 플레이어의 승리 또는 패배에 추가합니다.
1 x 게시판 장치: 플레이어에게 퍼시스턴스 데이터를 표시하는 것이 중요한 경우가 있습니다. 테스트 목적으로 표시하기도 하고, 플레이어의 참여도를 높이거나 진행 상황을 보여주기 위해 표시하기도 합니다. 언제, 어떤 데이터를 표시해야 하는지는 경험에 따라 다르지만, 이 예시에서는 게시판 장치에 점수, 고득점, 승리, 패배 통계 데이터를 표시할 것입니다.
퍼시스턴스 플레이어 통계 트래킹하기
먼저, 플레이어별로 어떤 통계를 추적하고 싶은지 정의하는 것이 중요합니다. 예를 들어 플레이어의 올타임 점수, 현재 순위 또는 최고 랩타임을 트래킹하고자 할 수 있습니다. 이 예시에서는 각 플레이어의 통계 값 테이블에서 점수, 승리, 패배를 추적합니다. 이 작업은 새 클래스이자 메인 퍼시스턴스 클래스가 될 player_stats_table에서 이루어집니다.
player_stats_table 클래스를 생성하려면 다음 단계를 따릅니다.
Verse 익스플로러를 사용하여
player_stats_table.verse로 명명된 새 Verse 파일을 생성합니다.새 Verse 파일에서
player_stats_table로 명명된 새 클래스를 생성합니다. 클래스에<persistable>및<final>모디파이어를 둘 다 추가합니다.<persistable>모디파이어는 클래스 내 데이터가 퍼시스턴스가 되도록 하며, 퍼시스턴스 데이터는 오버라이드되거나 서브클래스화할 수 없으므로<final>모디파이어가 필요합니다.Verseusing { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Tracks different persistable stats for each player. player_stats_table := class<final><persistable>:player_stats_table에 세 가지int값을 추가합니다. 이름을Score,Wins,Losses로 지정합니다. 이러한 값들은 각각 플레이어별로 수명 점수, 승리, 패배를 추적합니다. 또한player_stats_table의 현재 버전을 트래킹하기 위해Version으로 명명된int를 추가합니다.Verse# Tracks different persistable stats for each player. player_stats_table := class<final><persistable>: # The version of the current stats table. Version<public>:int = 0 # The score of a player. Score<public>:int = 0 # The number of wins for a player. Wins<public>:int = 0player_stats_table class의 인스턴스를 생성하려면<constructor>함수를 사용합니다. 이 생성자는 Verse 퍼시스턴스에서 변수 필드를 포함하는 클래스가 퍼시스턴스가 되도록 허용하지 않기 때문에 필요합니다. 생성자를 사용하면 변수인 기존 퍼시스턴스 통계의 사본을 생성하여 퍼시스턴스 클래스 값을 업데이트하고, 사본을 업데이트한 다음, 클래스의 원본 인스턴스를 변경된 값으로 교체할 수 있습니다. 파일에 새 생성자 함수MakePlayerStatsTable()을 추가합니다. 이 생성자는player_stats_table클래스의 원본(이전) 인스턴스를 받아 주어진 원래 값에서 새 값을 생성합니다.Verse# Creates a new player_stats_table with the same values as the previous player_stats_table. MakePlayerStatsTable<constructor>(OldTable:player_stats_table)<transacts> := player_stats_table: Version := OldTable.Version Score := OldTable.Score Wins := OldTable.Wins Losses := OldTable.Losses모든
player_stats_tables를 트래킹하기 위해,player의 퍼시스턴스weak_map을player_stats_table인스턴스에 사용합니다. 이 weak 맵을 파일에 추가합니다.Verse# Maps players to a table of their player stats. var PlayerStatsMap:weak_map(player, player_stats_table) = map{}완성된
player_stats_table클래스는 다음과 같습니다.Verseusing { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Tracks different persistable stats for each player. player_stats_table := class<final><persistable>: # The version of the current stats table. Version<public>:int = 0 # The score of a player.
모든 플레이어의 플레이어 통계 관리하기
player_stats_table 클래스로 개별 플레이어의 통계를 트래킹할 수 있지만, 아직 이를 관리할 방법이 없습니다. 각 플레이어가 득점할 때마다 해당 플레이어의 통계 테이블을 업데이트해야 하며, 경험 디자인에 따라 한 번에 여러 플레이어의 통계 테이블을 업데이트해야 할 수도 있습니다.
이를 해결하기 위해 모든 플레이어의 통계를 관리할 또 다른 클래스를 사용하게 되며, 기록 통계는 플레이어가 승리, 패배 또는 득점할 때마다 변경됩니다. 매니저 클래스를 구성하려면 아래 단계를 따릅니다.
Verse Explorer를 사용하여
player_stats_manager로 명명된 새 Verse 파일을 생성합니다. 이 파일에서 새 클래스player_stats_manager를 생성합니다.Verseusing { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Manages and updates player_stat_tables for each player. player_stats_manager := class():player_stats_manager는 몇 가지 작업을 수행해야 합니다. 플레이어의player_stats_table을 구성하고, 플레이어별로Score,Wins,Losses를 업데이트하고, 플레이어의player_stats_table을 반환해야 합니다. 이러한 각 작업은 별도의 함수에서 처리합니다. 새 함수InitializePlayer()를player_stats_manager클래스 정의에 추가합니다. 이 함수는 주어진 플레이어의 통계를 초기화합니다.Verse# Initialize stats for the given player. InitializePlayer(Player:player):void=InitializePlayer()에서 주어진 플레이어가 이미PlayerStatsMap에 존재하는지 확인합니다. 존재하지 않는 경우 맵 내 해당 플레이어의 값을 새player_stats_table로 설정합니다. 완성된InitializePlayer()함수는 다음과 같습니다.Verse# Initialize stats for the given player. InitializePlayer(Player:player):void= if: not PlayerStatsMap[Player] set PlayerStatsMap[Player] = player_stats_table{} else: Print("Unable to initialize player stats")새 함수
InitializeAllPlayers()를player_stats_manager클래스 정의에 추가합니다. 이 함수는 플레이어의 배열을 받고 모든 플레이어의InitializePlayer()를 호출합니다. 완성된InitializeAllPlayers()함수는 다음과 같습니다.Verse# Initialize stats for all current players. InitializeAllPlayers(Players:[]player):void = for (Player : Players): InitializePlayer(Player)특정 플레이어의 통계를 반환하려면 해당 플레이어의
player_stats_table을 반환하는 함수가 필요합니다. 새 함수GetPlayerStats()를 에이전트를 받는player_stats_manager클래스 정의에 추가합니다. 플레이어의 통계 테이블이 존재하지 않는 경우 이 함수가 실패하고 롤백하도록<decides><transacts>모디파이어를 추가합니다.GetPlayerStats()에서 새player_stats_table변수PlayerStats를 생성합니다.Verse# Return the player_stats_table for the provided Agent. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{}if표현식에서 이 함수에 전달된에이전트를플레이어로 형변환한 다음PlayerStatsMap에서 해당 플레이어의player_stats_table을 얻습니다. 그런 다음MakePlayerStatsTable()을 호출하여PlayerStats를 해당 테이블에 설정합니다. 마지막으로PlayerStats를 반환합니다. 완성된GetPlayerStats()함수는 다음과 같습니다.Verse# Return the player_stats_table for the provided Agent. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{} if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] set PlayerStats = MakePlayerStatsTable(PlayerStatsTable) PlayerStats각각의 Score, Wins, Losses 통계를 업데이트하려면, 각 통계마다 함수를 생성합니다.
player_stats_manager파일에AddScore()로 명명된 새 함수를 추가합니다. 이 함수는 점수를 부여할 에이전트와 에이전트에게 부여할 점수의int수를 받습니다.Verse# Adds to the given Agent's score and updates both their stats table # in PlayerStatsManager and the billboard in the level. AddScore<public>(Agent:agent, NewScore:int):void=데이터는 먼저 플레이어가 퍼시스턴스
weak_map에 유효한 데이터를 보유하고 있다는 것을 유효성 검사한 다음, 해당 데이터를 클래스의 업데이트된 사본으로 교체하는 방식으로 업데이트됩니다. 점수를 업데이트하려면PlayerStatsTable에서 플레이어의 점수를 얻은 다음PlayerStatsMap의 테이블을MakePlayerStatsTable()을 사용하여 생성하는 새player_stats_table의 결과로 설정하여 현재 점수 + 새 점수를 전달합니다. 몇 가지 필드가 포함된 클래스로 작업하는 경우, 클래스 생성자를 사용하면 업데이트하고 싶을 때마다 모든 필드를 명시적으로 복사하지 않아도 손쉽게 단일 필드를 업데이트할 수 있습니다.AddScore()함수는 다음과 같습니다.Verse# Adds to the given Agent's score and updates both their stats table # in PlayerStatsManager and the billboard in the level. AddScore<public>(Agent:agent, NewScore:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentScore := PlayerStatsTable.Score set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Score := CurrentScore + NewScoreMakePlayerStatsTable()호출 시 플레이어의 승리 또는 패배에 각각NewWins및NewLosses를 추가하여 승리 및 패배에 대해 이 프로세스를 반복합니다.Verse# Adds to the given Agent's wins and updates both their stats table # in PlayerStatsManager and the billboard in the level. AddWin<public>(Agent:agent, NewWins:int):void= if: Player := player[Agent] PlayerStatsTable := PlayerStatsMap[Player] CurrentWins := PlayerStatsTable.Wins set PlayerStatsMap[Player] = player_stats_table: MakePlayerStatsTable<constructor>(PlayerStatsTable) Wins := CurrentWins + NewWins최종
player_stats_manager파일은 다음과 같습니다.Verseusing { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /UnrealEngine.com/Temporary/Diagnostics } # Manages and updates player_stat_tables for each player. player_stats_manager := class(): # Return the player_stats_table for the provided Agent. GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table= var PlayerStats:player_stats_table = player_stats_table{}
장치로 퍼시스턴스 테스트하기
퍼시스턴스 클래스를 구성했으니, 이제 레벨에서 테스트할 차례입니다.
player_stats_example이라는 이름의 새 Verse 장치를 생성합니다. 방법은 Verse를 사용하여 나만의 장치 만들기를 참고하세요.
player_stats_example클래스 정의 상단에 다음 필드를 추가합니다.ScorePointsButton으로 명명된 편집 가능button_device입니다. 이 버튼은 활성화될 때마다 플레이어의 점수에 추가됩니다.Verse# Adds to the activating player's score. @editable ScorePointsButton:button_device = button_device{}StatsBillboard로 명명된 편집 가능billboard_device입니다. 플레이어의 점수, 고득점, 승리, 패배를 표시합니다.Verse# Displays the player's Score, High Score, Wins, and Losses @editable StatsBillboard:billboard_device = billboard_device{}CheckWinButton으로 명명된 편집 가능 button_device입니다. 이 버튼은 각 플레이어의 점수를 리셋하고, 플레이어의 점수에 따라 승리 또는 패배를 부여합니다.Verse# Resets the player's score and award them a win or a loss # depending if their current score is greater than WinScore. @editable CheckWinButton:button_device = button_device{}WinScore로 명명된 편집 가능int입니다.CheckWinButton이 활성화된 후 플레이어가 승리를 부여받기 위해 달성해야 하는 점수입니다.Verse# The score players need to reach to be awarded a win after # the CheckWinButton is activated. @editable WinScore:int = 10AwardScore로 명명된 편집 가능int입니다. 버튼과 상호작용할 때 플레이어에게 부여되는 점수입니다.Verse# The amount of score to award per button press. @editable AwardScore:int = 1PlayerStatsManager로 명명된player_stats_manager입니다. 모든 플레이어의 통계를 관리하고 업데이트합니다.Verse# Manages and updates stats for each player. PlayerStatsManager:player_stats_manager = player_stats_manager{}StatsMessage로 명명된 메시지로, 에이전트와 4개의 인티저 Score, MaxScore, Wins, Losses를 받습니다. 이 메시지를 사용하여 게시판에 플레이어의 통계를 표시합니다.
Verse# Displays a player's stats on a billboard. StatsMessage<localizes>(Player:agent, Score:int, Wins:int, Losses:int):message = "{Player}, Stats:\n Score: {Score}\n Wins: {Wins}\n Losses: {Losses}"
코드를 컴파일한 뒤 Verse로 작성한 장치를 섬에 끌어 놓습니다. 레벨에 Verse 장치 추가하기에서 단계를 확인하세요.
장치의 디테일(Details) 패널에서 레벨의 버튼 장치를 ScorePointsButton에 할당하고 게시판 장치를 StatsBillboard에 할당합니다.
주어진 플레이어의 통계를 StatsBillboard에 표시하려면, 새 함수
UpdateStatsBillboard()를player_stats_example클래스 정의에 추가합니다. 이 함수는 통계를 표시할 에이전트를 받습니다.Verse# Retrieves the stats of the given player and displays their stats # on the StatsBillboard. UpdateStatsBillboard(Agent:agent):void=UpdateStatsBillboard()에서 통계 매니저의GetPlayerStats[]함수를 호출하여 주어진 에이전트의 현재 통계를 얻습니다. 그런 다음 StatsBillboard에서SetText()를 호출하여 새StatsMessage()를 전달합니다. 이StatsMessage()를 생성하기 위해 에이전트의 현재 통계에서 에이전트의 Score, Wins, Losses에 액세스하여 얻습니다. 완성된UpdateStatsBillboard()함수는 다음과 같습니다.Verse# Retrieves the stats of the given player and displays their stats # on the StatsBillboard. UpdateStatsBillboard(Agent:agent):void= if: # Get the current stats of the given agent. CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent] then: StatsBillboard.SetText( StatsMessage( Player := Agent,새 함수
AddScore()를player_stats_example클래스 정의에 추가합니다. 이 함수는 에이전트를 받고 ScorePointsButton과 상호작용할 때마다 에이전트의 점수에 추가됩니다.Verse# Adds to the given player's score and updates both their stats table # in PlayerStatsManager and the billboard in the level. AddScore(Agent:agent):void=AddScore()에서 현재 점수뿐만 아니라 주어진 에이전트의 현재 통계도 얻습니다. 그런 다음PlayerStatsManager에서AddScore()를 호출하여 에이전트, 그리고 새 점수를 부여하기 위한AwardScore를 전달합니다. 마지막으로UpdateStatsBillboard()를 호출하여 주어진 에이전트를 전달합니다. 완성된AddScore()함수는 다음과 같습니다.Verse# Adds to the given player's score and updates both their stats table # in PlayerStatsManager and the billboard in the level. AddScore(Agent:agent):void= if: CurrentPlayerStats := PlayerStatsManager.GetPlayerStats[Agent] CurrentScore := CurrentPlayerStats.Score then: Print("Current Score is: {CurrentScore}") PlayerStatsManager.AddScore(Agent, AwardScore) UpdateStatsBillboard(Agent)CheckWin 버튼과 상호작용할 때 플레이어에게 승리 또는 패배를 부여하려면, 새 함수
CheckWin()을 player_stats_manager 클래스 정의에 추가합니다.Verse# Awards a player a win or a loss when they interact # with the CheckWinButton. CheckWin(Agent:agent):void=먼저 변수
CurrentScore를 정의하여 에이전트의 현재 점수를 트래킹합니다. 그런 다음AddScore()함수와 마찬가지로 플레이어 통계 테이블에서 현재 점수를 얻습니다.Verse# Awards a player a win or a loss when they interact # with the CheckWinButton. CheckWin(Agent:agent):void= var CurrentScore:int = 0 if: PlayerStats := PlayerStatsManager.GetPlayerStats[Agent] set CurrentScore = PlayerStats.Score에이전트의 현재 점수가
WinScore보다 큰 경우PlayerStatsManager에서 승리를 기록해야 합니다. 그렇지 않은 경우 패배를 기록합니다. 마지막으로 음수인CurrentScore로AddScore()를 호출하여 에이전트의 점수를 리셋한 다음 통계 게시판에 에이전트의 통계를 표시합니다. 완성된CheckWin()함수는 다음과 같습니다.Verse# Awards a player a win or a loss when they interact # with the CheckWinButton. CheckWin(Agent:agent):void= var CurrentScore:int = 0 if: PlayerStats := PlayerStatsManager.GetPlayerStats[Agent] set CurrentScore = PlayerStats.Score then: Print("Current Score is: {CurrentScore}") if:OnBegin()에서ScorePointsButton.InteractedWithEvent를AddScore()에 등록하고,CheckWinButton.InteractedWithEvent를CheckWin()에 등록합니다. 그런 다음GetPlayers()를 호출하여 게임 내 각 플레이어의 배열을 얻고, 통계 매니저의InitializeAllPlayers()함수를 사용하여 모두 초기화합니다.Verse# Runs when the device is started in a running game OnBegin<override>()<suspends>:void= # Register Button Events ScorePointsButton.InteractedWithEvent.Subscribe(AddScore) CheckWinButton.InteractedWithEvent.Subscribe(CheckWin) Players := GetPlayspace().GetPlayers() # Initialize player stats PlayerStatsManager.InitializeAllPlayers(Players)코드를 저장하고 컴파일합니다.
레벨에서 퍼시스턴스 테스트하기
편집 세션에서 퍼시스턴스 데이터를 테스트할 수 있지만, 이 데이터는 세션을 종료하고 재시작하면 리셋됩니다. 데이터가 여러 세션에 걸쳐 유지되도록 하려면 플레이테스트 세션을 시작하고 섬 설정에서 특정 설정을 변경해야 합니다. 편집 세션과 플레이테스트 세션 둘 모두에서 퍼시스턴스 데이터를 테스트하도록 섬을 구성하는 방법은 퍼시스턴스 데이터 페이지의 퍼시스턴스 데이터로 테스트하기를 참조하세요.
세션을 구성한 후 레벨을 플레이테스트하는 경우, ScorePoints 버튼과의 상호작용이 플레이어의 점수에 추가되고 게시판에 해당 업데이트가 표시되어야 합니다. CheckWin 버튼과의 상호작용은 플레이어의 점수에 따라 플레이어의 승리 또는 패배에 추가되어야 합니다. 대기실로 돌아와 섬에 다시 들어간 후, 플레이어의 통계가 유지되어야 하며 플레이어의 총 승리/패배 횟수 및 고득점이 업데이트될 때마다 게시판에 표시되어야 합니다.
직접 해보기
이 가이드를 마침으로써 Verse를 사용하여 여러 게임플레이 세션에 걸쳐 유지되고 플레이어별로 추적되는 퍼시스턴스 데이터를 생성하는 방법을 배웠습니다. 이제 자신만의 경험을 한층 발전시키는 데 퍼시스턴스를 활용하는 방법을 살펴보세요.
플레이어가 도달한 마지막 체크포인트를 기억하는 저장 파일 시스템을 만들 수 있는가?
대화를 나눈 캐릭터 및 캐릭터와의 현재 관계를 기억하는 시스템을 만들 수 있는가?
목표 달성을 위해 플레이어에게 여러 세션의 총 제한 시간만 부여하고, 플레이어가 목표 달성에 실패하는 경우 진행 상황을 리셋하는 시스템을 만들 수 있는가?
완성된 코드
이 섹션 튜토리얼에서 빌드한 완성된 코드는 다음과 같습니다.
player_stats_table.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Tracks different persistable stats for each player.
player_stats_table := class<final><persistable>:
# The version of the current stats table.
Version<public>:int = 0
# The score of a player.
player_stats_manager.verse
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Manages and updates player_stat_tables for each player.
player_stats_manager := class():
# Return the player_stats_table for the provided Agent.
GetPlayerStats(Agent:agent)<decides><transacts>:player_stats_table=
var PlayerStats:player_stats_table = player_stats_table{}
player_stats_example.verse
using { /Fortnite.com/Devices }
using { /Fortnite.com/Game }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# A Verse-authored creative device that can be placed in a level
player_stats_example := class(creative_device):
# Adds to the activating player's score.
@editable