개요
- 서버를 처음 접해보다 보니 서버의 로직을 짜는데 고민을 많이 했었다. 클라이언트만 구현하면 되었을 땐, 최대한 "클래스가 필요한 데이터를 가지고 '동작'만 할 수 있도록 짜는 것이 가장 좋다" 라고 생각하고 있었다.
- 온라인이 나타나기 시작하면서 다양한 사람들과 함께 플레이하는 게임이 만들어지기 시작했는데, 이때 플레이어들의 정보를 취합해서 전달하는 기능이 필요했다고 한다.

- 여러 사용자와 상호 작용
- 클라이언트에서 해킹당하면 안 되는 처리
- 플레이어의 상태 보관
이 세 가지가 서버가 하는 일이라고 " 게임 서버 프로그래밍 교과서 " 라는 책의 일부분에서 주워 들었다.
핵심 처리
- 그래서 나는 몬스터의 로직은 이렇게 나누기로 결정했다.
- 1. 서버 : 몬스터의 정보, 판정, 변경되는 로직을 처리
- 2. 클라이언트 : 서버가 전달하는 정보를 토대로 보여지는 렌더링 (ex. 애니메이션, 이펙트 등) 처리
- 그 이유는 이러한 구분 자체가 몬스터를 구현하는데 굉장히 깔끔하다는 느낌을 받았다. 또한, 정보 자체를 서버에서 관리하면 권위적으로 서버가 결정하여 전달하기 때문에 정보 꼬임으로부터 크게 신경 쓰지 않아도 되고, 보안적으로도 안전하기 때문이었다.
- 추가적으로 플레이어와의 상호작용이 많은 몬스터의 경우는 플레이어의 정보를 즉각 받아 처리하여 반응 속도에 대해서 클라이언트에서 관리하는 것보다 좋을 것이라고 생각했다.
서버 : FSM 패턴
- 서버는 핵심 정보를 변경하는 역할을 한다.
- 때문에 몬스터의 상태 정보를 정확히 변경하고, 그 상태에 따른 행동을 명확히 해야 한다. 그 부분에 대해서는 FSM 패턴이 좋은 디자인 패턴이 되겠다고 생각했다.



- FSM 패턴 자체가 한 행동에 대한 부분을 클래스마다 분리하니까 문제가 되는 행동을 명확히 파악할 수 있다.
- 특히 유용한 점은 입장 함수에서 변경된 정보의 패킷을 한 번만 보냈기 때문에(Moving 시에는 제외) 패킷을 언제 보내는 지 정확히 파악하기가 쉬웠다.
로직
// FSM Interface
public interface ISkillBehavior
{
void OnStart(Monster caster, MonsterSkillData skillData);
void OnUpdate(Monster caster);
void OnHit(Monster caster, Creature target);
void OnEnd(Monster caster);
}
// FSM 변경
public override void Update()
{
_currentState?.Execute(this);
}
#region State
public void ChangeState(IMonsterState newState)
{
_currentState?.Exit(this);
State = DetermineMonsterState(newState);
_currentState = newState;
_currentState?.Enter(this);
}
-
문제점 : AI의 문제인가, 로직의 문제인가
- 사실 충돌 시점에서 Monster의 Position에 대해서 문제가 발생했다.
- 몬스터와 플레이어 사이의 장애물이 존재할 때 몬스터가 스킬을 사용하면 서버와 클라이언트 상의 위치가 현저히 꼬이는 것이다.
- 물론 서버의 정보가 정확했다. Astar와 Funnel 알고리즘을 구현할 때 데이터 쪼가리인 폴리곤을 보간하는 사이에 장애물에 대한 충돌 처리까지 포함시키지 않아 서버 상에서는 그대로 직선 거리를 장애물을 통과 시켜 만들어냈던 것이다..... (이걸 찾고자 작업 시간 내내 고민했다)
- 현업에 대한 문제이지만, 충돌 담당에게 벽 충돌 처리를 요청했더니 ~하면 될 것 같으니 네가 만들어보라는 대답을 들은 후,
그렇게 되면 충돌 에디터까지 만들어 프로젝트 시간이 오래 걸릴 것이라 생각해 어차피 몬스터는 거의 장애물이 없는 공간에서만 움직이기에 타협하고 그 시간에 필요한 기능을 만들자는 생각을 했다.
(결국 장애물을 마주치면 장애물에 걸려 움직임이 틀어지면, 서버 위치로 몬스터가 순간이동 하여 재정비 시켰다.)
- 물론 현업 시에 불편한 부분, 충돌이라고 볼 수 있지만 팀원들이 수동적인 편이었다고 생각하기에 그날 팀 플레이를 이렇게 하는 것이 맞을까라는 생각을 했다. (협업 시 리더십 있는 사람이 되고 싶다고 생각했다 ㅠ.ㅠ)
결론
- 문제로 머리를 좀 싸매긴 했지만, 원인을 찾았고 정보 변경 상 FSM이 유용하다고 생각했다. DirectX로 프로젝트를 제작했을 땐 급하게 프로젝트를 마무리 해야 했기도 하고, 렌더링 변경 부분까지 상태 변경 코드에 집에 넣어 로직이 더럽다는 생각도 하고, 꼬일 때도 있었지만, 이번 프로젝트에서 동작 방시게 대해서 FSM을 사용했을 때는 이 패턴에 대한 장점을 직접 느낄 수 있었고, 조금이라는 꼬이는 부분이 있다면 금방 고칠 수 있었다.
- 살짝 아쉬웠던 부분은 사용되는 스킬에 따라서 처리 부분이 달랐기에 skill FSM을 추가로 만들었는데 이 부분에 대해서는
기존 스킬 데이터 json 파일에 호출할 스킬 클래스 이름을 데이터화 시켜 전략 패턴을 사용해도 좋았을 것이라고 생각한다.
클라이언트 : Behavior Tree
- 클라이언트는 Material을 변경하거나 Effect/Animation을 알맞게 호출을 해야 한다.
- Behavior Tree를 선택한 이유는 AND/OR 표현이 가능해서 이펙트 호출 조건의 다양성과 애니메이션 및 머티리얼 변경 같은 복합적인 상황을 순서 및 분기 흐름으로 효율적으로 조율할 수 있으며, 특히 요구 사항을 JSON 데이터로 관리하여 유연성을 높일 수 있다고 판단했기 때문에 선택했다.
- 또한, 한 번 만들어 놓은 후부터 데이터화만 시키면 쉽게 원하는 표현이 가능했고, 같은 부분에 대해서 여러 번 사용되기 때문에 재사용하는 데 있어서 좋을 것이라고 생각했다.

로직
Behavior Tree 참고 영상
'이론 > 프로젝트 분석' 카테고리의 다른 글
| [Undead Survivor] 창작 게임 소개 (0) | 2025.11.04 |
|---|---|
| [Server] 클라이언트 - 서버 연결 (0) | 2025.10.28 |
| [Unity ER] A Star*, Funnel Algorithm (0) | 2025.10.26 |