[Devlog] 로컬 프로토타이핑 - 브롤스타즈 모작 #1

2026. 6. 1. 18:13·Devlog/Crawl-Stars

먼저 서버를 붙이지 않은 채로 클라이언트 프로토타이핑을 진행 한 뒤,

 

서버로 시뮬레이션 로직을 이주시키고,

 

레이턴시를 최소한으로 할 수 있는 예측, 보간 시스템을 구현하는 순서로 가기로 했다.

 

접근을 어떻게 해야 할 지 막막했지만 AI를 통해서 갈피를 잡을 수 있었다.

 

직접 서버쪽에 들어갈 로직까지 구현해 보면서 프로젝트를 파악하는 것은 좋은 접근인 것 같다.

 

 

우선, 구현해야 할 부분은 맵 랜더링, 유저 인풋 처리, 플레이어 이동 및 충돌, 투사체 이동 및 충돌이다.

 

그리고 이 것들을 서버 권위 구조로 만들어야 한다.

 

 

매 프레임 클라이언트 인풋을 저장하고,

 

클라이언트 Tick에 맞춰 저장된 인풋을 서버로 보낸다.

 

서버는 Tick에 맞춰 모든 클라이언트에게 인풋을 받은 뒤, 이동과 충돌, 투사체 생성 처리를 한다.

 

모든 움직임에 대해 시뮬레이팅을 1Tick 진행시킨 뒤, 게임이 끝났는지 판별 한 뒤

 

현재의 스냅샷을 각 클라이언트에게 전송시킨다.

 

마지막으로 스냅샷을 전송 받은 클라이언트는 그 스냅샷에 대해 그려주기만 하면 끝이다.

 

실질적으로 클라이언트에서 하나의 코어 로직도 돌지 않는다.

 

 

하나씩 진행해 보자.

 

먼저, 플레이어를 이동시키고 충돌시키려면 맵이 있어야 한다.

 

시뮬레이팅과 랜더링을 동시에 하려면 맵 데이터는 클라와 서버 둘 다 동일한 데이터를 가져야 할 것이다.

 

{
  "width": 20,
  "height": 20,
  "index": 0,
  "maxPlayers": 6,
  "map": [
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1],
    [1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
    [1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1],
    [1,0,0,1,0,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
    [1,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1],
    [1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,0,0,0,0,0,0,1,0,0,0,1],
    [1,0,0,0,0,0,0,1,1,0,0,1,1,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
    [1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,1,0,0,1,0,0,0,0,0,0,0,1],
    [1,0,0,0,0,0,0,0,1,1,1,1,0,0,0,0,0,0,0,1],
    [1,0,0,0,1,0,0,0,0,0,0,0,0,0,1,0,0,0,0,1],
    [1,0,0,0,1,0,0,1,0,0,0,0,1,0,0,1,0,0,0,1],
    [1,0,0,0,0,0,0,1,0,0,0,0,1,0,0,0,0,0,0,1],
    [1,0,0,1,1,0,0,0,0,0,0,0,0,0,1,1,0,0,0,1],
    [1,2,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,2,1],
    [1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1],
    [1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1]
  ]
}

 

데이터는 json으로 세팅했고, 우선은 이정도면 충분할 것 같다.

 

map의 요소 중 0은 Ground, 1은 Wall, 2 는 SpawnPoint로 대입시킨다.

 

타일맵은 굳이 필요 없을 것 같아서 채택하지 않았다.

 

 

map 데이터를 순회하면서 각 인덱스 * 타일 사이즈에 맞게 움직이면서 타일 프리팹을 로드 및 세팅한다.

 

각 타일은 오브젝트 풀링과 스프라이트 동적 로드 및 캐싱 기능을 적용해 두었다.

 

맵 데이터와 스프라이트는 임시로 AI로 뽑아냈다.

 

 

다음으로 플레이어와 인풋 차례다.

 

각 플레이어 프리팹 또한 오브젝트 풀링을 적용시켜 두고, 각 프리팹에 리스너 스크립트를 부착시킨다.

 

게임에 들어온 모든 플레이어를 그려줘야 하므로 이 리스너들을 매니저로 관리한다.

 

 

W, A, S, D 를 받아서 하나의 방향 벡터로 만들고, 이를 현재 포지션과 같이 시뮬레이터로 전달한다.

 

시뮬레이터는 30ms 간격의 Tick마다 이를 가져와 현재 포지션에서 방향 벡터만큼 전진시킨다.

 

이후 도착한 포지션을 플레이어 매니저에게 전달해서 각 플레이어를 다음 포지션으로 이동시킨다.

 

 

다음으로 충돌 처리다. 벽은 사각형, 플레이어는 원으로 취급해서 원과 사각형의 충돌을 검증한다.


포지션을 맵 인덱스로 변환해가며 캐릭터의 맵 인덱스 추론하고 근처의 벽들을 충돌 후보군으로 추린다.


원의 중심에서 사각형에 가장 가까운 점을 찾은 뒤,


그 거리가 캐릭터의 반지름(사이즈) 보다 작거나 같으면 충돌했다고 판정한다.

 

캐릭터를 임시로 캡슐 모양으로 했는데, 탑뷰이기도 하니깐 원으로 바꿔야겠다.

 

 

공격 시스템을 구현하기 전에, 카메라를 고정시켜야 한다.

 

마우스 포인터를 향해 발사해야 하므로, 결국은 카메라를 거쳐야하기 때문이다.

 

카메라는 본인 플레이어 Transform을 필드로 저장해서 LateUpdate에서 따라가도록 고정시켰다.

 

 

공격은 중요한 판정이므로 Tick마다 인풋을 받으면 간혹 놓치는 때가 발생할 것 같다는 생각이 들었다.

 

그래서 Update에서 매 프레임 공격 방향 벡터를 필드에 저장시켜 놓고,

 

Tick에서는 저장한 필드를 가져오는 방식으로 이를 방지했다.

 

 

시뮬레이터로 공격 방향까지 전달시키면,

 

시뮬레이터는 해당 플레이어가 공격했다는 것으로 판단하고 투사체를 생성하도록 한다.

 

생성된 투사체는 플레이어의 움직임처럼 시뮬레이팅한다.

 

충돌 처리 또한 투사체를 원으로 취급해서 플레이어와 동일하게 처리한다.

 

투사체는 수시로 생성되고 파괴되는 오브젝트라 오브젝트 풀링에 적합해 적용시켜준다.

 

 

플레이어는 공격 방향을 쳐다보고 공격해야 하므로,

 

방향 벡터로 각도를 구해서 플레이어를 회전시켜야 한다.

 

      y
      ↑
      │     ● (x, y)
      │    /
      │   /  ← direction
      │  /
      │ /
──────┼────────→ x
    0 │

 

(x, y) 라는 벡터가 있을 때, 


x축으로 선을 그어 직각 삼각형을 만들고 원점에 닿아있는 각 (θ) 을 구해야 한다.

 

원점을 플레이어라고 봤을 때, 점 (x, y) 를 바라봐야 하기 때문이다.

 

tan(θ) = y / x

-> arctan(tan(θ)) = arctan(y / x)

-> θ = arctan(y / x)


아크탄젠트는 탄젠트의 역함수라서 좌변은 θ 가 된다.

2차원 평면에서 이를 활용하려면 x와 y가 음수인지, 양수인지가 중요하기 하기 때문에 유의해야 한다.


유니티의 경우 Atan() 대신 Atan2() 사용하여 정확한 사분면까지 계산할 수 있다.

 

아크탄젠트의 결과는 ‘라디안’이기 때문에, ‘도’ 로 변경해줘야한다. 

 

degree = radian * 180 / π

 

위의 식으로 구할 수 있지만, 유니티의 경우 이를 지원하는 상수가 있어서 아래와 같이 사용 가능하다.

 

degree = radian * Mathf.Rad2Deg

 

 

마지막으로 플레이어를 여럿 스폰시켜서 이들을 동기화시킬 수 있도록 list로 관리하게 두면 끝이다.

 

플레이어와 플레이어가 충돌할 수 있고, 투사체가 플레이어와 충돌할 수 있게 처리해둔다.

 

원과 원의 충돌은 두 원의 중심의 거리가 두 원의 반지름의 합보다 작을 때 충돌했다고 판정할 수 있다.

 

 

이제 어느정도 감을 잡고, 서버와 이을 준비가 되었다.

 

코어 로직을 직접 만들어 보면서 자꾸 잊어버리는 수학과 물리 공식들을 리마인드할 수 있어서 유익했다.

 

다음엔 어느정도 게임 기본 루프를 구현해두고, REST API 요청과 WebSocket 소통 기반을 세팅할 예정이다.

 

 

 

'Devlog > Crawl-Stars' 카테고리의 다른 글

[Devlog] 게임 기본 루프 개발 - 브롤스타즈 모작 #2  (0) 2026.06.07
[Devlog] 또 다른 시작 - 브롤스타즈 모작 #0  (0) 2026.05.29
'Devlog/Crawl-Stars' 카테고리의 다른 글
  • [Devlog] 게임 기본 루프 개발 - 브롤스타즈 모작 #2
  • [Devlog] 또 다른 시작 - 브롤스타즈 모작 #0
SikPang
SikPang
게임 개발 공부 블로그
  • Second Step : 공부 블로그
    SikPang
    • 분류 전체보기 (51) N
      • Low-level (20)
        • Computer System (11)
        • Operating System (1)
        • System Programming (8)
      • Programming (12) N
        • C# .NET (12) N
        • C++ (0)
      • Data Structure (5)
      • Algorithm (3)
      • Artificial Intelligence (0)
      • Computer Graphics (1)
      • Game Engine (5)
        • Unity (5)
        • Unreal (0)
      • Memoir (2)
      • Devlog (3) N
        • Crawl-Stars (3) N
  • 최근 글

  • 전체
    오늘
    어제


  • hELLO· Designed By정상우.v4.10.6
SikPang
[Devlog] 로컬 프로토타이핑 - 브롤스타즈 모작 #1
상단으로

티스토리툴바