플레이어의 유휴 여부 결정하기
이 섹션에서는 플레이어가 마지막 시뮬레이션 업데이트 이후 특정 거리만큼 이동했는지 여부를 확인하는 방법을 알아봅니다. 플레이어가 이동했다면, 플레이어의 현재 위치가 저장되고 다시 한번 확인됩니다. 플레이어가 이동하지 않았다면, 루프가 중단되고 메서드가 완료됩니다. 이 메서드는 GetFortCharacter[], GetTransform() 및 Translation을 사용하여 플레이어의 위치를 얻습니다. 해당 API 레퍼런스 페이지에서 자세한 내용을 알아볼 수 있습니다.
이 페이지에는 이 게임플레이에 필요한 게임플레이 메커니즘의 실행법을 보여주는 Verse 스니펫이 포함되어 있습니다. 아래 단계를 따라 이 튜토리얼의 6단계에 있는 전체 스크립트를 복사합니다.
플레이어의 유휴 여부를 결정하려면 다음 단계를 따릅니다.
-
AwaitStopMoving()으로 명명된 에이전트 클래스를 위한 확장 메서드를 생성합니다. 즉, 이미 정의된 클래스에 커스텀 메서드를 추가하는 것입니다.(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent has moved less than the minimum distance.") -
플레이어의 초기 위치를 얻습니다.
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent has moved less than the minimum distance.") # 씬 내 에이전트의 캐릭터로부터 에이전트의 초기 위치를 얻습니다. if (Tracked := PropAgent.GetFortCharacter[]): var StartPosition:vector3 = Tracked.GetTransform().Translation -
다음 시뮬레이션 업데이트에서 플레이어의 다음 위치를 얻습니다.
(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent has moved less than the minimum distance.") # 씬 내 에이전트의 캐릭터로부터 에이전트의 초기 위치를 얻습니다. if (Tracked := PropAgent.GetFortCharacter[]): var StartPosition:vector3 = Tracked.GetTransform().Translation Sleep(0.0) # 다음 게임 틱에서 에이전트의 위치를 얻습니다. NewPosition := Tracked.GetTransform().Translation - 초기 위치와 최근 위치 간의 거리가 허용되는 한계치 내에 있는지 확인하여,
MinimumDistance파라미터로 함수에 전달됩니다.(PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent has moved less than the minimum distance.") # 씬 내 에이전트의 캐릭터로부터 에이전트의 초기 위치를 얻습니다. if (Tracked := PropAgent.GetFortCharacter[]): var StartPosition:vector3 = Tracked.GetTransform().Translation Sleep(0.0) # 다음 게임 틱에서 에이전트의 위치를 얻습니다. NewPosition := Tracked.GetTransform().Translation # 시작 위치에서 새 위치까지의 거리가 MinimumDistance보다 짧은 경우, 에이전트는 이동하지 않은 것이며 루프가 중단됩니다. if (Distance(StartPosition, NewPosition) < MinimumDistance): Logger.Print("Agent has moved less than the minimum distance.") # 그렇지 않으면 플레이어가 새 위치에서 이동하도록 하기 위해 StartPosition을 리셋합니다. else: set StartPosition = NewPosition -
이제 초기 위치와 최근 위치 간의 확인을 루프하고, 위치 간 거리가
MinimumDistance한계치를 초과하면 루프를 중단합니다.# 에이전트가 MinimumDistance보다 적게 이동할 때까지 루프합니다. (PropAgent:agent).AwaitStopMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent has moved less than the minimum distance.") # 씬 내 에이전트의 캐릭터로부터 에이전트의 초기 위치를 얻습니다. if (Tracked := PropAgent.GetFortCharacter[]): var StartPosition:vector3 = Tracked.GetTransform().Translation loop: Sleep(0.0) # 다음 게임 틱에서 에이전트의 위치를 얻습니다. NewPosition := Tracked.GetTransform().Translation # 시작 위치에서 새 위치까지의 거리가 MinimumDistance보다 짧은 경우, 에이전트는 이동하지 않은 것이며 루프가 중단됩니다. if (Distance(StartPosition, NewPosition) < MinimumDistance): Logger.Print("Agent has moved less than the minimum distance.") break # 그렇지 않으면 플레이어가 새 위치에서 이동하도록 하기 위해 StartPosition을 리셋합니다. else: set StartPosition = NewPosition
심장 박동 전 카운트다운
HeartBeat.MoveTime - HeartBeat.WarningTime 과 동일한 시간 동안 대기한 후 카운트다운 시간이 다 될 때까지 경고 및 카운트다운 타이머를 표시한 다음, 경고 및 카운트다운 텍스트를 지우려면 다음 단계를 따릅니다.
- CountdownTimer()로 명명된 함수를 생성합니다.
# HeartBeatWarningTime이 시작될 때까지 딜레이합니다. 그런 다음 HeartBeatWarningTime으로 카운트다운하고 카운트다운 텍스트를 설정합니다. 지연되면 텍스트를 지웁니다. CountdownTimer(PropAgent:agent)<suspends>:void = Logger.Print("Starting heartbeat countdown.") -
먼저 heartbeat.verse 에 구성한 맵에서 연결된 플레이어의
heartbeat_warning_ui를 얻으려고 시도해야 합니다. 시도에 성공하면, 다음으로는 플레이어 중지와 카운트다운 타이머 표시 사이의 딜레이를 시작해야 합니다.# HeartBeatWarningTime이 시작될 때까지 딜레이합니다. 그런 다음 HeartBeatWarningTime으로 카운트다운하고 카운트다운 텍스트를 설정합니다. 지연되면 텍스트를 지웁니다. CountdownTimer(PropAgent:agent)<suspends>:void= Logger.Print("Starting heart beat countdown.") if (UIData := HeartBeat.WarningUI[PropAgent]): Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # 경고가 표시되기 전 해당 시간 동안 슬립합니다. Logger.Print("Starting heartbeat warning.") else: Logger.Print("UIData not found.") -
이제 화면에 표시되며 1초마다 감소하는 변수를 생성하겠습니다.
WarningTimeRemaining으로 명명합니다. heartbeat.verse 에서WarningTime으로 설정합니다.WarningTimeRemaining은int이고WarningTime은float이기 때문에Ceil[]함수를 사용하여int를 생성해야 합니다.# HeartBeatWarningTime이 시작될 때까지 딜레이합니다. 그런 다음 HeartBeatWarningTime으로 카운트다운하고 카운트다운 텍스트를 설정합니다. 지연되면 텍스트를 지웁니다. CountdownTimer(PropAgent:agent)<suspends>:void= Logger.Print("Starting heart beat countdown.") if (UIData := HeartBeat.WarningUI[PropAgent]): Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # 경고가 표시되기 전 해당 시간 동안 슬립합니다. Logger.Print("Starting heartbeat warning.") var WarningTimeRemaining:int = 0 if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {} else: Logger.Print("UIData not found.") -
카운트다운 루프를 시작하기 전에,
defer표현식을 사용하여CountdownTimer()함수가 완료될 때마다 플레이어의 UI에서 카운트다운 타이머를 지웁니다. 해당 함수는 타이머가 다 되거나 플레이어가 다시 이동하기 시작할 때만 완료됩니다. 자세한 내용은 defer를 참조하세요.# HeartBeatWarningTime이 시작될 때까지 딜레이합니다. 그런 다음 HeartBeatWarningTime으로 카운트다운하고 카운트다운 텍스트를 설정합니다. 지연되면 텍스트를 지웁니다. CountdownTimer(PropAgent:agent)<suspends>:void= Logger.Print("Starting heart beat countdown.") if (UIData := HeartBeat.WarningUI[PropAgent]): Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # 경고가 표시되기 전 해당 시간 동안 슬립합니다. Logger.Print("Starting heart beat warning.") var WarningTimeRemaining:int = 0 if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {} # defer는 함수가 완료될 때나, 경합 패배와 같이 취소될 때 일어납니다. # 따라서 이 경우에는 카운트다운이 완료되거나 사물 에이전트가 카운트다운 완료 전에 이동하면 경고 텍스트가 지워집니다. defer: UIData.Text.SetText(HeartBeatWarningClear) # 경고 텍스트를 남은 시간으로 설정하고, 1초 대기한 다음, 남은 시간을 감소시킵니다. 카운트다운이 완료되면 루프가 중단됩니다. UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining)) else: Logger.Print("UIData not found.") -
마지막으로, 카운트다운 타이머를 감소시키는 루프를 생성합니다.
SetText()함수를 사용하여WarningTimeRemaining이 포함된HeartBeatWarningMessage를 표시합니다. 그런 다음Sleep()으로 1초 대기한 후 남은 시간을 감소시킵니다.WarningTimeRemaining이 0 이하이면 카운트다운이 완료되고 루프가 중단됩니다.# HeartBeatWarningTime이 시작될 때까지 딜레이합니다. 그런 다음 HeartBeatWarningTime으로 카운트다운하고 카운트다운 텍스트를 설정합니다. 지연되면 텍스트를 지웁니다. CountdownTimer(PropAgent:agent)<suspends>:void= Logger.Print("Starting heart beat countdown.") if (UIData := HeartBeat.WarningUI[PropAgent]): Sleep(HeartBeat.MoveTime - HeartBeat.WarningTime) # 경고가 표시되기 전 해당 시간 동안 슬립합니다. Logger.Print("Starting heart beat warning.") var WarningTimeRemaining:int = 0 if (set WarningTimeRemaining = Ceil[HeartBeat.WarningTime]) {} # defer는 함수가 완료될 때나, 경합 패배와 같이 취소될 때 일어납니다. # 따라서 이 경우에는 카운트다운이 완료되거나 사물 에이전트가 카운트다운 완료 전에 이동하면 경고 텍스트가 지워집니다. defer: UIData.Text.SetText(HeartBeatWarningClear) # 경고 텍스트를 남은 시간으로 설정하고, 1초 대기한 다음, 남은 시간을 감소시킵니다. 카운트다운이 완료되면 루프가 중단됩니다. loop: Logger.Print("Heart beat in {WarningTimeRemaining} seconds.") UIData.Text.SetText(HeartBeatWarningMessage(WarningTimeRemaining)) Sleep(1.0) set WarningTimeRemaining -= 1 if (WarningTimeRemaining <= 0): break else: Logger.Print("UIData not found.")
유휴 플레이어에 이펙트 재생하기
AwaitStopMoving() 이 완료되면 플레이어의 카운트다운 타이머가 시작될 시간이라는 것을 알게 되며, 그런 다음 심장 박동 이펙트가 재생됩니다. 하지만 플레이어가 다시 이동하기 시작하는 즉시, 현재 실행 중인 타이머나 심장 박동을 취소해야 합니다. 이렇게 하려면 race 표현식을 사용해야 합니다. 두 가지 race 표현식은 다음과 같습니다.
-
PropAgent.AwaitStartMoving(MinimumMoveDistance) -
심장 박동 이펙트 재생 전에 카운트다운이 있는 경우의
block
AwaitStartMoving() 은 경합에서 승리하고 카운트다운 타이머나 심장 박동 이펙트를 중지하는 데 필요합니다.
block 표현식은 그 안에 있는 CountdownTimer() 및 StartHeartbeart() 라는 두 함수가 순차적으로 실행되고 서로 경합하지 않도록 하는 데 사용됩니다. 카운트다운 타이머는 사물 플레이어에게 심장 박동 이펙트가 타이머 완료 이후 시작될 것이라고 알리게 되어 있으므로, 타이머와 심장 박동을 동시에 시작하는 것은 이치에 맞지 않습니다.
플레이어가 너무 오랫동안 이동하지 않았을 때 이펙트를 재생하려면 다음 단계를 따릅니다.
AwaitStartMoving()으로 명명된 확장 메서드를 생성합니다. 구현은 다음 사항을 제외하고 동일합니다.
-
플레이어가 마지막 시뮬레이션 업데이트 이후
MinimumDistance또는 그 이상 이동했는지 확인합니다. 이와 달리,AwaitStopMoving()에서는MinimumDistance보다 적게 이동했는지 확인합니다. -
매 루프 이후
StartPosition을 리셋하지 않습니다.StartPosition이 매 루프 끝에서 리셋된 경우 플레이어는 시뮬레이션 업데이트에 걸린 시간에 전체MinimumDistance또는 그 이상 이동해야 하는데, 이는 불가능합니다.# 에이전트가 MinimumDistance보다 많이 이동할 때까지 루프합니다. (PropAgent:agent).AwaitStartMoving(MinimumDistance:float)<suspends>:void= Logger.Print("Checking if the agent moves further than the minimum distance.") # 씬 내 에이전트의 캐릭터로부터 에이전트의 초기 위치를 얻습니다. if (Tracked := PropAgent.GetFortCharacter[]): StartPosition:vector3 = Tracked.GetTransform().Translation loop: Sleep(0.0) # 다음 게임 틱에서 에이전트의 위치를 얻습니다. NewPosition := Tracked.GetTransform().Translation # 시작 위치에서 새 위치까지의 거리가 MinimumDistance보다 길거나 같은 경우, 에이전트는 이동한 것이며 루프가 중단됩니다. if (Distance(StartPosition, NewPosition) >= MinimumDistance): Logger.Print("Agent has moved more than or equal to the minimum distance.") break
-
RunPropGameLoop()로 명명된 함수를 생성합니다. 이 함수는 플레이어가 너무 오랫동안 유휴 상태일 때 심장 박동 이펙트 재생을 관리합니다.# 사물 에이전트가 이동을 멈추면, 사물 에이전트가 MinimumMoveDistance를 초과하여 이동하는지, 심장 박동 타이머가 완료되는지 또는 사물 에이전트가 처치되는지 확인하기 위해 경합합니다. RunPropGameLoop(PropAgent:agent)<suspends>:void = Logger.Print("Starting prop agent game loop.") -
사물 에이전트가 최소 거리보다 적게 이동할 때까지 대기한 다음 진행합니다.
# 사물 에이전트가 이동을 멈추면, 사물 에이전트가 MinimumMoveDistance를 초과하여 이동하는지, 심장 박동 타이머가 완료되는지 또는 사물 에이전트가 처치되는지 확인하기 위해 경합합니다. RunPropGameLoop(PropAgent:agent)<suspends>:void = Logger.Print("Starting prop agent game loop.") # 사물 에이전트가 처치되거나 플레이어가 세션에서 나갈 때까지 사물 비헤이비어를 무한 루프합니다. loop: # 사물 에이전트가 최소 거리 미만으로 이동할 때까지 대기한 다음 진행합니다. PropAgent.AwaitStopMoving(MinimumMoveDistance) Sleep(0.0) -
race표현식을 추가하여AwaitStartMoving()완료 간에 경합합니다. 플레이어가 이동하기 시작했으며 처음에는CountdownTimer()가, 그런 다음에는StartHeartbeat()이 있는block표현식이 실행된다는 뜻입니다.# 사물 에이전트가 이동을 멈추면, 사물 에이전트가 MinimumMoveDistance를 초과하여 이동하는지, 심장 박동 타이머가 완료되는지 또는 사물 에이전트가 처치되는지 확인하기 위해 경합합니다. RunPropGameLoop(PropAgent:agent)<suspends>:void = Logger.Print("Starting prop agent game loop.") # 사물 에이전트가 처치되거나 플레이어가 세션에서 나갈 때까지 사물 비헤이비어를 무한 루프합니다. loop: # 사물 에이전트가 최소 거리 미만으로 이동할 때까지 대기한 다음 진행합니다. PropAgent.AwaitStopMoving(MinimumMoveDistance) # 사물 에이전트가 최소 거리를 초과하여 이동할 때까지 심장 박동을 카운트다운한 다음 심장 박동을 무한 재생합니다. race: PropAgent.AwaitStartMoving(MinimumMoveDistance) block: CountdownTimer(PropAgent) PropAgent.StartHeartbeat() Sleep(0.0) # race가 완료되면(사물 에이전트가 이동하면) 루프가 다시 시작됩니다.