1. 슈퍼 클래스 :
단순함을 유지하여 개발생산성 향상시키기
Super-class based game object system
㈜소프트네트
김성익
noerror@softnette.com
2. 목차
01. 지향점
02. 개발 관점의 변화 추적
03. 슈퍼클래스 소개
04. 슈퍼클래스 기반 게임 오브젝트 시스템
05. 기타 사례 및 아이디어
06. 결론
07. Q&A
3. 지향점
1. 납득 가능한 기획적 명세는 가능한 모두 구현하자
생산성, 확장성, 재사용성
2. 빠르게 개발하되 코드의 품질을 일정 수준 이상으로 유지
하자
단순화, 표준화
성능, 우아함
솔루션 : 개발 역량을 높이거나 더 오래 일을 하거나 ??
4. 개발 관점의 변화 추적
절차적에서 객체지향적으로
일반화된 효과적이고 유연하며, 확장성 높은 개발
추상화, 상속
fread(header, 1, len, fp);
f->Read(&header, len);
CreateTextureFromMemory(ptr, size, pTexture);
CreateTextureFromFile(fp, &pTexture);
CFileIO* f1 = CMemIO::Create(ptr, size);
CreateTextureFromFile(f1, &pTexture);
CFileIO* f2 = CStdIO::Open(L"checkbox.dds");
CreateTextureFromFile(f2, &pTexture);
CFileIO* f3 = CHttpIO::Open(L“http://softnette.com/checkbox.dds");
CreateTextureFromFile(f3, &pTexture);
#Object Oriented Programming
5. 개발 관점의 변화 추적
절차적에서 객체지향적으로
재 사용성 확장성을 위한 적절한 설계가 요구됨
구성 및 설계에 따른 시행 착오와 정비로 인한 개발 지연
복잡도가 높아질수록 개발 효율이 떨어짐
6. 개발 관점의 변화 추적
자료주도적으로
프로그래머 의존도 축소
개발 관점에서 생산성 향상
확장성
7. 개발 관점의 변화 추적
자료주도적으로
스크립트
손쉽게 프로그램과 연동 가능
높은 유연성
8. 개발 관점의 변화 추적
자료주도적으로
스크립트
스크립트 자체의 접근성
비 프로그래머가 작성한 스크립트의 품질과 오류
디버깅 이슈
활용도가 높을 수록 성능 이슈
9. 개발 관점의 변화 추적
자료주도적으로
그래프 편집
비 프로그래머들도 쉽게 접근
훌륭하게 시각화된 작업 환경
손쉬운 바인딩 Quartz composer
확장성
NDC2008: 차세대 게임 개발을 위한 실시간 비주얼 그래프 편집 vvvv
http://dev.ntreev.com/publications
10. 개발 관점의 변화 추적
자료주도적으로
그래프 편집
개발 도구의 높은 편의성이 요구됨
기반 시스템 구축의 난이도 접근성
복잡도와 무관하게 양이 증가할수록 직관성 감소
vvvv video visualizer
11. 개발 관점의 변화 추적
컴포넌트기반 시스템
객체를 컴포넌트 조합으로 구성
누구나 기능 조합 가능
다양한 구성이 가능
NDC2010:M2아키텍처리뷰
GDC Canada 2009:Theory and Practice of Game Object Component
Architecture
12. 개발 관점의 변화 추적
컴포넌트기반 시스템
비의존적, 독립적인 컴포넌트 제작의 난이도
개발 효율 저하
덜(?) 직관적인 코드
간접 참조의 스트레스
내부 통신으로 인한 성능 문제
GDC Canada 2009:Theory and practi…
13. 개발 요구사항
일반화도 잘 되면서 구조가 단순하고, 자료주
도적이며, 다양한 기능 조합도 가능하고,
개발 스트레스 없이 빠르게 개발 가능한 방법
이 목표입니다
17. 셰이더로 보는 사례
슈퍼 셰이더 사례 (aka. UberShader)
실시간 렌더링의 패러다임 변화
고정 파이프라인에서 프로그래머블 셰이더 시대로
셰이더 시스템
렌더링에 최적화된 프로세스 유닛
단순한 구성, 대량의 프로세스 유닛 추구
동적 분기 취약
하나의 머트리얼은 하나의 셰이더 (혹은 technique)
19. 셰이더로 보는 사례
조합이슈
그림자 기능 추가시 (두 배 증가)
float4 ps(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy);
}
float4 ps_light(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy) * Lambert(In.Normal.xyz);
}
float4 ps_lightmap(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy) * tex2D(LightMapSampler, In.Texcoord1.xy);
}
float4 ps_withshadow(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy) * ProjShadow(In.ShadowPos);
}
float4 ps_light_withshadow(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy) * Lambert(In.Normal.xyz) * ProjShadow(In.ShadowPos);
}
float4 ps_lightmap_withshadow(PS_IN In) : COLOR
{
return tex2D(DiffuseMapSampler, In.Texcoord0.xy) * tex2D(LightMapSampler, In.Texcoord1.xy) * ProjShadow(In.ShadowPos);
}
20. 셰이더로 보는 사례
조합이슈
그림자 기능 추가
스키닝 추가 (4배)
스펙큘러 마스크 (8배)
리플렉션 매핑 (16배)
UV스크롤 (32배)
노멀맵 (64배)
대단한 의지가 아니면 관리가 불가능
일정 시점 이상이 되면 셰이더의 난이도와 상관없이 작업 불가능한 상태
대부분 정책적으로 기능을 제한하는 방향으로
21. 셰이더로 보는 사례
조합해법
코드 생성
그래프 편집
셰이더 조합 스크립트
UberShader
Unreal3 Material Editor
22. 셰이더로 보는 사례
UberShader
소스 코드는 유지하고 전처리를 통해서 기능 선택
float4 ps(PS_IN In) : COLOR
{
float4 out = tex2D(DiffuseMapSampler, In.Texcoord0.xy);
#ifdef _LIGHT
out *= Lambert(In.Normal.xyz);
#endif
#ifdef _LIGHTMAP
out *= tex2D(LightMapSampler, In.Texcoord1.xy);
#endif
#ifdef _SHADOW
out *= ProjShadow(In.ShadowPos);
#endif
return out;
}
technique complete {
pass p0 {
VertexShader = compile vs_3_0 vs();
PixelShader = compile ps_3_0 ps();
}
}
23. 슈퍼클래스 특징
1. 요구사항을 구현함에 있어서 구조적인 부
담이 굉장히 적다
2. 쉽게 자료화 할 수 있다
3. 도구화할 수 있다
4. 표준화하여 다양한 파트에 적용할 수 있다
24. 슈퍼클래스 적용 시나리오
이동하는 코드를 이용해 가상의 사례를 하나
구성해봅니다
void CActor::Move(const Vec &dv, int lv)
{
float t = SweepTest(m_Pos, m_Radius, dv, NULL);
m_Pos += dv * t;
}
25. 슈퍼클래스 적용 시나리오
요구 사항 : 어떤 객체는 충돌이 없습니다
void CActor::Move(const Vec &dv)
{
if (m_flags&_NOCOLLISION)
{
m_Pos += dv;
return ;
}
float t = SweepTest(m_Pos, m_Radius, dv, NULL);
m_Pos += dv * t;
}
26. 슈퍼클래스 적용 시나리오
요구 사항 : 슬라이딩이 되어야 합니다
void CActor::Move(const Vec &dv, int lv)
{
if (m_flags&_NOCOLLISION)
{
m_Pos += dv;
return ;
}
Plane p;
float t = CMomo::GetInstance().SweepTest(m_Pos, m_Radius, dv, &p);
if (t < 1.0f)
{
if (t > 0)
m_Pos += dv * t;
if (lv < 3)
{
Vec vr = dv * (1-t);
float d1 = p.Distance(m_Pos) - m_Radius;
Move(vr - p.Normal() * Vec::dot(vr, p.Normal()), lv+1);
}
return;
}
m_Pos += dv;
}
27. 슈퍼클래스 적용 시나리오
요구 사항 : 슬라이딩 안 하는 객체도 있어요
void CActor::Move(const Vec &dv, int lv)
{
if (m_flags&_NOCOLLISION)
{
m_Pos += dv;
return ;
}
Plane p;
float t = CMomo::GetInstance().SweepTest(m_Pos, m_Radius, dv, &p);
if (t < 1.0f)
{
if (t > 0)
m_Pos += dv * t;
if ((m_flags&_SLIDING) && lv < 3)
{
Vec vr = dv * (1-t);
float d1 = p.Distance(m_Pos) - m_Radius;
Move(vr - p.Normal() * Vec::dot(vr, p.Normal()), lv+1);
}
return;
}
m_Pos += dv;
}
28. 슈퍼클래스 적용 시나리오
요구 사항 : 바닥에만 슬라이딩하게 해주세요.
바운딩도 필요합니다. 슬라이딩시 마찰력을
적용해주세요. 단, 무적상태가 아닐 때 만.
충돌할 때 이벤트를 발생시켜 주세요. 대미지
중 이동할 때는 충돌 지점에 파티클 좀 뿌려
주세요. 단,캐릭터가 HP10%이하인 경우만….
29. 슈퍼클래스 적용 시나리오
요구 사항 : 바닥에만 슬라이딩하게 해주세요.
바운딩도 필요합니다. 슬라이딩시 마찰력을
적용해주세요. 단, 무적상태가 아닐 때 만.
충돌할 때 이벤트를 발생시켜 주세요. 대미지
중 이동할 때는 충돌 지점에 파티클 좀 뿌려
주세요. 단,캐릭터가 HP10%이하인 경우만….
NO PROBLEM
30. 슈퍼 클래스기반 프로그래밍의 장단점
장점
1. 구조적으로 큰 부담 없이 기존코드를 유지하면
서 새로운 기능 추가 가능
기능 코드만 추가하고 객체를 생성할 때 선택적으로 해당
기능을 set 시켜주기만 하면 끝
2. 쉽고 단순하고 직관적
단점
1. 코드가 조잡해지는 느낌
2. 하드코딩 하는 느낌
31. 인식의 전환
코드가 조잡해 보인다
어떤 식으로 구성하더라도 결과적으로 핵심 구현
로직이 다른 코드일까
현재 코드의 쾌적함을 위해 기획자와 명세를 협상
하고 있는 건 아닌지 성찰해봅니다
“우리는 바닥에서만 슬라이딩은 불가능합니다. 끝”
약간 심적인 부담이 있겠지만 쿨~하게 구현해 주
는 것이 완벽한 프로그래머보다 멋진 프로그래머
솔루션 : 심리적인 해결. “조잡해 보이는 건 명세 탓!! 하지만 로직을
단순하고 멋지게 유지하는 것보다는 명세가 우선이다”라고 암시 암시 암시
32. 인식의 전환
하드 코딩 같아 보인다
일관성을 유지하면서도 관리가 가능하게 작성된
코드라면 하드코딩과 다르다 (암시)
if (m_Type == _WOLF && m_HP <= 10)
PlaySound(L"아파");
if ((m_Flags&_HURT_SOUND) && m_HP <= m_HurtsAlramHP)
PlaySound(L"아파");
void CGameRoom::LeaveRoom(CSession* session, bool graceful)
{
if (!IsLobby())
NotifyLeaveMessage(session);
if (IsDungeon() && graceful == false)
session->ExitPenalty();
}
void CGameRoom::LeaveRoom(CSession* session, bool graceful)
{
if (m_flags&_NOTIFY_LEAVE)
NotifyLeaveMessage(session);
if ((m_flags&_EXIT_PENALTY) && graceful == false)
session->ExitPenalty();
}
38. 도구화
기능간의 관계
기능 간의 관계를 데이터 상에서 검증
<shading text="셰이딩">
<_NO_LIGHTING text="셰이딩안함" type="bool">false</_NO_LIGHTING>
<_USE_SPECULAR text="스펙큘러채널" type="bool" enabletest="!_NO_LIGHTING">false</_USE_SPECULAR>
<_RIMLIGHTING text="림라이트" type="bool" enabletest="!_NO_LIGHTING">false</_RIMLIGHTING>
</shading>
40. 슈퍼클래스기반 게임오브젝트
극단적인 사용 안을 제안합니다
게임 객체를 하나의 오브젝트 타입으로 구성
모든 게임 객체는 Actor
데이터에 의해서만 기능 set / reset
단순한 구조
신입도 알 수 있어요
Note) 현재 이 구조로 프로젝트 진행하고 있습니다 (소프트네트)
41. 슈퍼클래스기반 게임오브젝트
Actor(SuperActor)
모든 기능을 구현
모든 객체는 길 찾기, Head tag, 동기화 등을 맘대로 활용 가능
기능 추가하고 데이터 연동만 해주면 OK (신입도 할 수 있어요)
재사용성 Better (vs ObjectOrientDesign)
“아이템 사라질 때 mob 사라질 때처럼 해주시면 안되나요 ?”
“Re:그렇게 하세요”
직접 참조 OK (vs Component-based, OOD)
메시 모델의 손의 위치를 사운드 출력 부에서 직접 참조
카메라 객체에서 플레이어의 머리본의 TM을 직접 참조
빠르고 직관적
42. 슈퍼클래스기반 게임오브젝트
SuperActor
“구현부가 너무 비대해지지 않을까요 ?”
구현부를 적절히 컴포넌트화한다면 납득할 수준으로 관
리가 가능
각 컴포넌트는 계층적이지 않기 때문에 단순하게 구성가
능 (일반화의 제약도 없음)
다만 과도하게 컴포넌트화하고 정리를 하는 것보다는 관리 가능한 수준에서
느슨하게 처리하는 것을 추천
43. 슈퍼클래스기반 게임오브젝트
SuperActor
“일일이 비트 검사하면 속도상 불이익이 있지 않을
까요 ?”
구현은 트리 구조를 이루게 됨
그룹단위로 체크해서 최 상위에서 skip 하도록
점프는 이동 하위에, 2단 점프는 점프 하위
좀 더 도전적으로 DOD로 확장가능(슈퍼클래스기
반 파티클 참조)
#Data Oriented Design
http://www.lameproof.com/zboard/zboard.php?id=bbs2&no=790
44. 슈퍼클래스기반 게임오브젝트
SuperActor
“그럼 모든 속성도 가지고 있다는 건데, 메모리 낭
비가 심하지 않을지요” (필요한 경우 heap으로)
template <class _Tx> class CValuePtr {
public :
CValuePtr() { m_Ptr = NULL; }
~CValuePtr() { if (m_Ptr != NULL) delete m_Ptr; }
_Tx* operator-> () { if (m_Ptr == NULL) m_Ptr = new _Tx; return m_Ptr; }
private :
_Tx* m_Ptr;
} ;
class CActor {
private:
struct _DIALOG {
_DIALOG() { Shape = NULL; }
~_DIALOG() { if (Shape) delete Shape; }
float OpenStep;
Sphere* Shape; value = *node.FindChild(L"dialog_offset");
float Offset[2]; swscanf_s(value, L"%f %f", &m_Dialog->Offset[0], &m_Dialog->Offset[1])
}; value = *node.FindChild(L"dialog_size");
CValuePtr<_DIALOG> m_Dialog; m_Dialog->Shape = new Sphere(Vec(0, 0), unicode::atof(value), 14);
} ;
45. 슈퍼클래스기반 게임오브젝트
슈퍼 클래스 기반 게임 오브젝트 장점
단순한 구조로 빠른 개발이 가능
기능 추가가 용이하며 많은 코드 공유 가능
소규모 여러 프로젝트에서 공유 가능
사이드뷰 액션 게임과 TPS, 스포츠 게임이 같은 프로젝트
코드 관리 용이성
데이터 검증으로 dead code 판단 가능
Legacy code를 유지하면서 방어적으로 기능 교체
46. 슈퍼클래스기반 게임오브젝트
슈퍼 클래스 기반 게임 오브젝트 장점
툴 연동 가능
툴에서 필요한 기능만 활성하면 툴 상에서 인터렉션 가능
툴 내 게임 플레이도 가능
생산성 향상
52. 슈퍼클래스 기반 파티클
성능 문제
분기문(if)로 인한 제어 해저드
대량의 연산이 이루어지기 때문에 성능을 고려할
때는 민감한 사안
#control hazard
53. 슈퍼클래스 기반 파티클
성능 문제
해법#1 자료지향설계(DOD)
bool CParticle::Update(float dt, void * data, int count, unsigned long updatemask) const
{
_PARTICLE* p = (_PARTICLE*)((char*)data + off);
int i;
for(i=0; i<count; i++)
p->curTime[i] += dt;
if (updatemask&_COLOR_BLEND)
{
unsigned long* blendcolor = (unsigned long*) GetData(p, _U_BLENDCOLOR);
for(i=0; i<count; i++)
p->color[i] += LerpColor(blendcolor[i*2+0], blendcolor[i*2+1], p->curTime / p->lifeTime);
}
if (updatemask&_GRAVITY)
{
Vec3* vel = (Vec3*) GetData(p, _U_VELOCITY);
for(i=0; i<count; i++)
vel[i] += += Vec3(0, -5.0f, 0) * dt;
}
if (updatemask&_VELOCITY)
{
const Vec3* vel = (const Vec3*) GetData(p, _U_VELOCITY);
for(i=0; i<count; i++)
p->pos[i] += vel[i] * dt;
}
54. 슈퍼클래스 기반 파티클
성능 문제
해법#2 컴파일러 마법
도달하지 않는 코드, 상수연산은 제거됨
#include <windows.h>
inline void update(unsigned long mask)
{
if (mask&1)
::Sleep(0xFF);
if (mask&2)
::Sleep(0xF);
}
void main()
{
unsigned long mask = rand();
update(mask);
update(2);
}
55. 슈퍼클래스 기반 파티클
성능 문제
해법#2 컴파일러 마법
많이 호출되는 타입은 아래처럼
bool CParticle::Update(float dt, void * data, int count) const
{
if (m_UpdateMask == (_COLOR_BLEND|_GRAVITY|_VELOCITY))
return Update(dt, data, count, _COLOR_BLEND|_GRAVITY|_VELOCITY);
else if (m_UpdateMask == _COLOR_BLEND)
return Update(dt, data, count, _COLOR_BLEND);
return Update(dt, data, count, m_UpdateMask);
}
해당 기능만 실행하는 코드 생성
BEST
56. 슈퍼클래스 기반 파티클
슈퍼클래스 기반 파티클의 장점
다양한 조합의 파티클
손쉬운 도구 연동
이펙트 디자이너의 요구를 빠르게 반영 가능
바로 구현 후 데이터 연동만 해 놓으면 OK
“호혹시 파티클이 회오리 모양의 경로로 움질일 수 있을
까요?” “네 만들어 드리겠습니다”
빠른 연산
시스템이 단순하여 병렬화나 메모리 친화적인 다양한 최
적화가 가능
57. 슈퍼클래스 기반
유저 인터페이스 시스템
컴포넌트의 예외적인 명세
포스트 프로세싱 / 모션 컨트롤
자료 주도적인 구성
셰이더 , 셰이더 변수, 블룸, 렌더 타겟,… 조합으로 패스
정의
여러 패스
58. 슈퍼클래스 기반 서버
일반적인 서버 구성
게임의 성격에 맞도록 서버를
구성
기본 모듈을 공유하는 서로
다른 프로젝트로 예상
게임테크 2010:Extracting MMORPG Server Engine from TERA
60. 슈퍼클래스 기반 서버
슈퍼 클래스 기반의 서버 구성
서버는 한 종류
데이터에 의해서 기능 명세
다기능 서버
게임 성격과 서비스 상황에
맞도록 세팅
인증+필드, 인증+던전, 던전
인증+던정,필드 …
기능 단위보다는 지역, 처리
유저 수 기준
61. 슈퍼클래스 기반 서버
클라이언트와 코드 공유
클라이언트의 게임 객체를 서버에서도 사용
필요한 기능만 활성 가능하도록 제한
“길 찾기 따로 만들어야 되나요 ?”
“클라이언트에서 구현한 AI캐릭터를 서버에서 사용할 수
없나요 ?”
“서버용 데이터 안 만들고 애니메이션 이벤트 검증할 수
있나요?”
NO PROBLEM
정책적으로 클라이언트, 툴, 서버에서 게임 객체 공유 (소프트네트)
63. 결론
슈퍼 클래스를 이용하면
구조적인 복잡도가 낮아 직관적으로 구성 가능
단순한 구조로 설계 및 관리의 스트레스에서 해방
구현에 좀 더 집중 가능하며 개발 속도 개선
도구를 이용하여 프로젝트 개발 속도 개선
코멘트 : 우아하고 섹시하진 않지만 합리적으로 검토해 볼만
한 아이디어
슈퍼클래스, 상태 머신, 스크립트 3S 삼총사면 무서울 게 없습니다