시뮬레이션

게임을 제작할때 모든 스펙을 아는 것이

그에 맞는 서비스를 구축하는  가장 좋은 설계를 할 수 있는 시작점이지만,

일반적으로 기획과 개발을 동시에하는 국내 게임 개발 프로세스 상에서는

그런 상황이 웬만해서는 있지 않다.

그래도 일단은 대략적으로 최대 8명 이내의 유저가 다이나믹하게

플레이하는 멀티플레이 상황을 생각했기에 그에 맞는

서버 – 클라이언트 설계에 대한 생각을 이어갔다.

 

클라이언트 해킹으로 게임 플레이에 지장을 주지않으려면

결국 서버베이스로 가야하고,

현재까지 나온 게임 중에 이것을 가장 잘 구현한 것은 LOL이라고

생각했다. 누군가에게 서버에서 모든 처리를 한다고 들은것같다. 실제로

헬퍼 이외의 핵은 듣지 못했기에, 거의 확실하다고 본다.

LOL은 굉장히 빠른 반응속도가 요구되는 게임이다.

 

당연한 이야기지만, 서버에서 처리한다고

클라이언트가 서버에서 응답올 때까지 기다리는 상황은 절대로 용납되지 않는다.

클라이언트와 서버가 동시에 프로세싱하면서, 서버는

한 클라이언트로 부터 요청을 받아서 검증하고 문제 없다면

브로드캐스팅하고 문제가 있다면, 예외처리를 해준다.

(얼마전 포프님의 동영상 을 보고 동감했지만,  해당 클라이언트를 강제 종료 시키는게 좋은 처리인것같다. 물론 검증이 100% 신용할 수 있어야한다.)

여기서 동시에 프로세싱 한다는 의미는 결국

서버, 클라이언트에서 동일 input에 동일 output을 내는 시뮬레이션을 해야하고,

그게 가능하면 자동적으로 replay도 가능해야한다고 생각했다.

 

replay system 관련글들을 검색하다가 얻은 결론은 (그중 하나)

floating-point data types는 사용하면 안된다는 것이 가장 핵심이라고

생각됐다. 처음에는 float, double들을 int, long형으로 소숫점 몇째자리까지

표현하도록 (예를들면 1.3을 소숫점 세째자리까지 표현할때 1300으로) 해보려고했는데

오차도 생각보다 크고 조잡하고, 에러가 생겼을 경우 찾기 힘들것같았다.

(x, y point를 표현할때 normalized vector를 구하면 그 차이가 무시할수없었다.)

결국 게임에서 이렇게까지 정확해야 하나? 하는 생각이 들었다.

그 시점에서 C#으로 같은 고민을 하던 사람의 코드를 찾았다. (링크)

역시나 조잡하다.

다른 글에서는 컴파일러는 어짜피 같은 것을 쓸테고  cpu architecture가 달라도 크게 결과가 다르지는 않았다고 한다. (링크)

그냥 floating-point를 사용하는 것의 이득이 더 크다고 판단하고

floating-point data type을 쓰기로 했다.

오차는 거의 없을것이지만 반드시 발생할것을 감안해야한다.

서버에서의 예외처리로 오차가 적다면 강제 종료가 아니라

국소적인 데이터에 대해서 서버의 상태를 해당 클라이언트로 싱크하는

로직이 추가되어야 할것이다.

모든 데이터를 싱크하기에는 데이터량이 너무 많고

예외처리 상황은 많지 않을 것이라고 가정한다.

오차를 조금이라도 줄이기 위해서 double을 사용해볼까 했지만,

Unity에서도 float을 쓰고 있고

요즘시대에 FPU가 없는 CPU가 있을까 생각이 들긴 하지만 이 글(링크)에서

FPU가 없으면 float이 더 빠를 수 있고 적은 메모리를 사용한다는 장점을 보고

그냥 float을 사용하기로했다.

 

=== 이 글은 미완성. 계속 추가!

Advertisements
시뮬레이션

Unity ForceMode

출처 : Rigidbody에 사용하는 ForceMode에 대한 설명

If you have objects that use Unity’s physics system, via a rigidbody component, you can add forces to them to get them to move. Forces can be added using one of four different ‘modes’. The names of these modes aren’t very enlightening and I don’t think the Unity3D documentation is very clear about their differences. For my own reference, and in case it helps others, this is what I’ve figured out.

  • ForceMode.Force. If the AddForce call occurs in a FixedUpdate loop, the full force supplied to the AddForce call will only have been exerted on the rigidbody after one second. Think of it as ‘Force exerted per second’
  • ForceMode.Acceleration. Like ForceMode.Force, except the object’s mass is ignored. The resulting movement will be as though the object has a mass of 1. The following lines will give the same result
    rigidbody.AddForce((Vector3.forward * 10),ForceMode.Force);
    rigidbody.AddForce((Vector3.forward * 10)/rigidbody.mass,ForceMode.Acceleration);
  • ForceMode.Impulse. The entirety of the force vector supplied to the AddForce call will be applied all at once, immediately.
  • ForceMode.VelocityChange. Like ForceMode.Impulse except the object’s mass is ignored. So the following lines give the same result:
    rigidbody.AddForce((Vector3.forward * 10),ForceMode.Impulse);
    rigidbody.AddForce((Vector3.forward * 10)/rigidbody.mass,ForceMode.VelocityChange);

I made a little test to verify this. The script demonstrates how the ForceModes work by canceling out their differences through modifying the values passed to the AddForce call.

Unity ForceMode