그냥

People who are really serious about software should make their own hardware.

– Alan Kay

스티브 잡스의 아이폰 최초 소개 동영상 을 보다가 위 인용 문구가 굉장히 인상깊었다.

UI/UX에 관한 이야기지만

소프트웨어 만으로 가치를 창출하는 것과

하드웨어와 함께 가치를 창출하는 것 둘 중에

좀 더 파워풀하게 세상을 변화 시킬 수 있는 것은

후자 일 수 밖에 없는건 변함이 없는 것 같다.

Advertisements
그냥

ConcurrentDictionary in Unity

.Net 쪽에서는 Concurrent Collection이라고해서

lock-free Collection을 제공한다. 작업하다보면, 한쪽 스레드에서는

컬렉션에 입력만 하고, 한쪽 스레드에서는 해당 컬렉션을 읽기만 할때가 있는데

lock을 걸거나 readwritelock (slim)을 쓰기에는 뭔가 아까운 느낌인데

Concurrent 시리즈가 lock 없이 접근 가능하다는 구현방법이 마음에 들어서

서버쪽에서 자주 애용했는데

유니티는 .net 3.5라서 해당 collection이 제공이 안되어서 아쉬워하다가

검색해보니 다음 두가지 방법으로 사용 가능한 것으로 보인다.

(사실 모든 폰에서 테스트를 해봐야 정확할 것 같다.)

 

  1. https://www.nuget.org/packages/TaskParallelLibrary/
  2. https://github.com/SaladLab/NetLegacySupport

 

1번은 꽤 오래된(2012.04.14.) TPL이고,

2번은 공개된 .net 코드가 3.5환경에서 컴파일이 되도록 수정된 버전이다. 대규모 사용 등으로 안정성은 아직 검증이 많이 된것은 아니지 않나 싶다. Saladbowl이라는 게임 벤쳐회사에 재직중인 TD님이 올려주신 코드. Concurrent 시리즈 중에 Dictionary만 있다.

가볍고 최신 코드기반으로 적용된 2번을 사용하려고 하다가 한번 더 검색을 해봤는데

http://blogs.msdn.com/b/pfxteam/archive/2011/11/08/10235147.aspx

위와같은 공개된 테스트가 있어서

.Net 3.5 에서 컴파일한 1번과 2번 ConcurrentDictionary와

.Net 4.5.2에서 컴파일한 ConcurrentDictionary의 테스트를 위 테스트 코드로 해보았다.

example1은 인풋을 0으로 했고 결과는 다음과 같다.

result

.Net 4.5쪽이 당연히 가장 빨랐고

example1과 example2는 TPL이, example3은 netlegacysupport쪽이 빨랐다.

즉, 한쪽 키에 몰릴경우에는 tpl이 빠른데, 평균적으로 분산될 경우 게다가, 멀티스레딩환경

이라면 유니티에서 사용하기에는 netlegacysupport쪽이 낫다는 결론이 나왔다.

 

추가. Net 3.5에서 Parallel.Invoke는 TPL에 있는 버전을 사용하여

4.5에서의 성능과 3.5에서의 성능을 실질적으로 비교하긴 힘듬

ConcurrentDictionary in Unity

시뮬레이션

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

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

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

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

그래도 일단은 대략적으로 최대 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을 사용하기로했다.

 

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

시뮬레이션

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