NDC 2016 발표 자료입니다
아래는 공개된 세션의 소개입니다
------------------------------------------------------------
게임을 개발하다 보면 액션, RPG를 비롯한 많은 장르에서 캐릭터의 스킬을 만들어야 하는 때가 옵니다.
다양한 스킬의 기획을 제한하지 않으면서도, 빠르게 개발한다는 두 가지 목표를 달성하기 위해서는 어떻게 개발해야 하는지 고민하고 프로젝트에 적용한 결과를 공유하는 세션입니다.
다음과 같은 세 가지 개발 방법을 보여드리고, 각각의 장단점을 비교하는 형태로 진행됩니다.
1. 고전적인 하드 코딩 방식
2. 한 달에 하나씩의 캐릭터 업데이트를 단행했던 <최강의>에서 사용했던 스킬 툴 방식 (온라인 액션RPG, 언리얼3 기반)
3. 현재 개발 중인 모바일 프로젝트 <건파이트>에서 시도하고 있는 언리얼4 블루프린트 기반 방식
12. 이후 진행 방식
하나의 예제 스킬을 세가지 방법으로 구현하며 비교
하드코딩
<최강의 군단> 방식
(온라인 액션RPG, 언리얼3, 자체 스킬 툴)
<건파이트 맨션> 방식
(모바일 게임, 언리얼4, 블루프린트 기반)
13. 예제 스킬 기획
반격기 <Guard & Attack>
스킬을 사용하면 방어 모션을 취한다.
방어 모션에서 1초가 지나면 스킬이 종료된다.
방어 모션 중 피격 당했는데, 공격자가 3미터 이내에 있다면 그쪽으로 공격한다.
공격 모션이 끝나면 스킬이 종료된다.
14. 예제 스킬 영상
유투브에서 보기
https://www.youtube.com/watch?v=VJnZDRbY3lA
15. 예제 스킬의 순서도
스킬 종료
공격자가
3미터 이내에
있는가?
스킬 사용 방어
피격
공격
Y
모션
종료
1초 지남
N
16. 스테이트 머신
캐릭터의 상태는 여러 스테이트들의 전환으로 구성됨
대기
피격
피격경직
경직 끝
대기
반격기사용
반격기
반격기종료
대기
17. 스테이트 머신
캐릭터의 상태는 여러 스테이트들의 전환으로 구성됨
대기
피격
피격경직
경직 끝
대기
반격기사용
반격기
반격기종료
대기
스테이트 머신은
어느 방법에서든
동일하다 가정
(하드코딩)
이 스테이트(스킬)
하나를 어떻게
만들지가 논의의 핵심
18. 반격기
스테이트 머신
캐릭터의 상태는 여러 스테이트들의 전환으로 구성됨
대기
피격
피격경직
경직 끝
대기
반격기사용
반격기
반격기종료
대기
확대
19. 반격기 스킬 종료
공격자가
3미터 이내에
있는가?
스킬 사용 방어
피격
공격
Y
모션
종료
1초 지남
N
스테이트 머신
캐릭터의 상태는 여러 스테이트들의 전환으로 구성됨
대기
피격
피격경직
경직 끝
대기
반격기사용
반격기
반격기종료
대기
확대
20. 반격기 스킬 종료
공격자가
3미터 이내에
있는가?
스킬 사용 방어
피격
공격
Y
모션
종료
1초 지남
N
스테이트 머신
캐릭터의 상태는 여러 스테이트들의 전환으로 구성됨
대기
피격
피격경직
경직 끝
대기
반격기사용
반격기
반격기종료
대기
스테이트
서브 스테이트
[반격기] 스테이트 안에 [방어]와 [공격]이라는 작은 스테이트
24. 예시 스킬 구현 (대충 느낌만 보세요!)
void GuardNAttack::OnStartSubState(SubStateType SubState)
{
if (SubState == GUARD)
{ // 방어 시작 : 방어 애니 재생하고 1초 뒤 이벤트 실행 설정
PlayAnim(GuardAnim);
SetTimerEvent(1.f);
}
else if (SubState == ATTACK)
{
PlayAnim(AttackAnim);
}
}
void GuardNAttack::OnTimerEvent()
{
if (CurrentSubState == GUARD)
{
EndState();
}
}
void GuardNAttack::OnDamage(Actor Hitter, Damage DM)
{
if (CurrentSubState == GUARD)
{
if (GetDistanceTo(Hitter) <= 300.f)
{
RotateTo(Hitter);
ChangeSubState(ATTACK);
}
}
}
void GuardNAttack::OnAnimEnd()
{
if (CurrentSubState == ATTACK)
{
EndState();
}
}
25. 예시 스킬 구현 (대충 느낌만 보세요!)
void GuardNAttack::OnStartSubState(SubStateType SubState)
{
if (SubState == GUARD)
{
PlayAnim(GuardAnim);
SetTimerEvent(1.f);
}
else if (SubState == ATTACK)
{
PlayAnim(AttackAnim);
}
}
void GuardNAttack::OnTimerEvent()
{
if (CurrentSubState == GUARD)
{ // 1초 뒤 아무 일도 안 일어나면 스킬 종료
EndState();
}
}
void GuardNAttack::OnDamage(Actor Hitter, Damage DM)
{
if (CurrentSubState == GUARD)
{
if (GetDistanceTo(Hitter) <= 300.f)
{
RotateTo(Hitter);
ChangeSubState(ATTACK);
}
}
}
void GuardNAttack::OnAnimEnd()
{
if (CurrentSubState == ATTACK)
{
EndState();
}
}
26. 예시 스킬 구현 (대충 느낌만 보세요!)
void GuardNAttack::OnStartSubState(SubStateType SubState)
{
if (SubState == GUARD)
{
PlayAnim(GuardAnim);
SetTimerEvent(1.f);
}
else if (SubState == ATTACK)
{
PlayAnim(AttackAnim);
}
}
void GuardNAttack::OnTimerEvent()
{
if (CurrentSubState == GUARD)
{
EndState();
}
}
void GuardNAttack::OnDamage(Actor Hitter, Damage DM)
{
if (CurrentSubState == GUARD)
{ // 피격당하면
if (GetDistanceTo(Hitter) <= 300.f)
{ // 공격자와의 거리가 300cm 이내인지 검사해서
RotateTo(Hitter); // 공격자를 바라보고
ChangeSubState(ATTACK); // 공격 상태로
}
}
}
void GuardNAttack::OnAnimEnd()
{
if (CurrentSubState == ATTACK)
{
EndState();
}
}
27. 예시 스킬 구현 (대충 느낌만 보세요!)
void GuardNAttack::OnStartSubState(SubStateType SubState)
{
if (SubState == GUARD)
{
PlayAnim(GuardAnim);
SetTimerEvent(1.f);
}
else if (SubState == ATTACK)
{ // 공격 시작 : 공격 애니 재생
PlayAnim(AttackAnim);
}
}
void GuardNAttack::OnTimerEvent()
{
if (CurrentSubState == GUARD)
{
EndState();
}
}
void GuardNAttack::OnDamage(Actor Hitter, Damage DM)
{
if (CurrentSubState == GUARD)
{
if (GetDistanceTo(Hitter) <= 300.f)
{
RotateTo(Hitter);
ChangeSubState(ATTACK);
}
}
}
void GuardNAttack::OnAnimEnd()
{
if (CurrentSubState == ATTACK)
{
EndState();
}
}
28. 예시 스킬 구현 (대충 느낌만 보세요!)
void GuardNAttack::OnStartSubState(SubStateType SubState)
{
if (SubState == GUARD)
{
PlayAnim(GuardAnim);
SetTimerEvent(1.f);
}
else if (SubState == ATTACK)
{
PlayAnim(AttackAnim);
}
}
void GuardNAttack::OnTimerEvent()
{
if (CurrentSubState == GUARD)
{
EndState();
}
}
void GuardNAttack::OnDamage(Actor Hitter, Damage DM)
{
if (CurrentSubState == GUARD)
{
if (GetDistanceTo(Hitter) <= 300.f)
{
RotateTo(Hitter);
ChangeSubState(ATTACK);
}
}
}
void GuardNAttack::OnAnimEnd()
{
if (CurrentSubState == ATTACK)
{ // 공격 애니 종료시 스킬 종료
EndState();
}
}
29. 하드코딩 방식의 장점
시스템 / 툴 구축 비용이 저렴하다
기간이 짧은 프로젝트나 프로토타입에 적합
구현할 수 있는 기획의 제한이 (거의) 없다
30. 하드코딩 방식의 단점
기능 수정 = 코드 수정
잦은 수정으로 코드 안정성 저하
[기능 수정]과 [구현 확인] 사이에 컴파일 시간이 들어가서 시간 낭비가 큼
많은 컨텐츠 = 많은 코드
빠른 업데이트를 위해서 프로그래머가 많이 필요함
코드량이 빠르게 증가하므로 코드 유지 보수 비용이 커짐
기획자 ≠ 구현자
기획을 정확하게 전달하기 힘든 데서 오는 비효율
35. <최강의 군단> 소개
PC 플랫폼의 온라인 액션 RPG. 약칭 <최군>
엔진 : 언리얼3
개발 시작 : 2011년
국내 서비스 시작 : 2014년
현재 해외 계약 후 서비스 준비중
소개 영상 : https://www.youtube.com/watch?v=n5zQHw2GXNI
36. <최강의 군단> 폭풍 업데이트 시기
2014년 말~2015년 중순
출처 : 2015년 3월 23일 INVEN 기사
(http://www.inven.co.kr/webzine/news/?news=129222)
37. 업데이트 시점 캐릭터 추가 캐릭터 개편
2014년 11월
12월
2015년 1월
2월
3월
4월
5월
7월
8월
+사이드킥 32종
캐릭터 관련 업데이트
38. 캐릭터 하나의 스킬 볼륨 (액티브+패시브)
캐릭터 <오드리>의 스킬 소개 페이지
(www.herowarz.com/gameguide/skill.hero?contentID=952)
끝까지 스크롤해서
옆으로 돌리면…
57. 도입 결과 : 기획자의 경우 - 장점
커뮤니케이션 문제가 없는 자유로운 구현
개발자에게 전달하기 어려운 미세한 감각까지 욕심나는 만큼 구현 가능
직접 구현한다는 부담에도 불구, 자유로움을 더 선호함
구현과 자체 피드백의 빠른 사이클
스스로 만들고 테스트하는 과정에서 기획을 개선하고
실제 구현 과정에서 새로운 스킬 아이디어를 얻기도
58. 도입 결과 : 기획자의 경우 - 단점
시스템 구축까지 시간이 걸림
초기에는 기능이 없어서 기획이 제약되는 일이 있었음
기획자의 숙련도 요구
신규 기획자에게 툴 교육 필요, 숙련자가 퇴사시 타격이 큼
툴 숙련도가 구현에 걸림돌이 되기도
때로 다른 기획자가 만든 스킬을 분석해야 하는 어려움
관리 룰을 잘 만들고 지키지 못하면 관리 문제 발생
무분별한 리소스 복제로 인한 용량 낭비 등
59. 도입 결과 : 프로젝트의 경우
(많은 기획자 분들이 고통 받았지만)
어떻게든 원하는 속도로 업데이트를 해냄
64. 툴 매뉴얼 잘 제공하기
에디터에서 노드에 우클릭 후 위키 열기 메뉴를 선택하면 해당 노드의 위키 페이지가 열리게 구현
위키는 구글 사이트 도구 (sites.google.com)으로 제작
65. 사용자의 실수로 인한 시간낭비 줄이기
기획자가 알기 힘든 실수는 시스템적으로 발견해서 경고해야 함
ex)
무한루프 발생
패킷이 과도하게 발생하는 스킬
기능 자동화로 실수 방지하기
ex)
손으로 입력하던 애니메이션 이름을 콤보박스로 선택
이벤트를 연결하면 실행 조건을 물어보는 콤보박스 띄우기
68. <건파이트 맨션> 소개
모바일 플랫폼 신규 프로젝트
엔진 : 언리얼4
아직 스킬 개발 툴은 기획자 사용 단계 이전으로, 발표자만 사용 중
<미공개 프로젝트라 게임의 자세한 내용을 공개하지 못하는 점 양해 부탁 드립니다>
69. 독자적 스킬 툴 시스템 구축?
<최강의 군단> 시스템을 그대로 이식하기는 쉽지 않다
언리얼3의 그래프 편집 시스템을 이식해서 썼는데, 엔진이 바뀌었기 때문
새로 만들까?
<최강의 군단>에 비해 전체 개발 기간이 짧음
시스템 구축에 긴 시간 쓰기 힘듬
70. 블루 프린트
언리얼 4의 스크립트 언어 (그래프형 코딩 툴)
void UIU_State::OnDamage(CActor* Other, CDamage* DM, CDamageInstance* DamageInstance)
{
this->RotateToActor(Other, -1.0);
this->ChangeSubstate(Attack);
}
동일한 내용
노드 = 순수한
함수일 뿐, 별도의
프로퍼티가 없음
캐스팅, 상속,
오버라이드 등
높은 수준의
코딩기능도 지원
71. 블루 프린트가 일종의 코딩 툴이므로, 전체 구조는 하드코딩과 비슷함
블루 프린트를 활용한 스킬 시스템
스킬A
코드
스킬B
코드
스킬C
코드
스킬 공통
코드
공통 시스템 부분은
C++을 이용해 구현
각 스킬별 개별 코드는
블루프린트로 구현
72. 기본 시스템 제작 : 박인호님 (에이스톰)
블루 프린트로 구현한 예시 스킬
이걸 블루프린트로
번역한 것이라고
보시면 편합니다
73.
74. ↑
방어
서브 스테이트
시작
1초 뒤
호출되게 →
설정된 이벤트
피격시
호출되게 →
설정된 이벤트
적과의 거리에 따라
분기 처리
↓
공격 서브 스테이트로
↓
↑
공격
서브 스테이트
시작
← 애니메이션 종료시
스킬 종료
75. 전체 하드코딩 < 블루프린트 병행
컴파일 시간 없이 스킬 로직 테스트
코드 안정성 증가
블루 프린트는 null pointer에 접근해도 튕기지 않음
C++과 블루프린트의 장점을 잘 살릴 수 있음
블루프린트는 간단한 로직 구현은 더 편하지만, 복잡성이 높아지면 난해해짐
76. 최군 스킬 툴 vs 블루프린트
시스템 구축 비용?
블루프린트는 언리얼4를 쓴다면 시스템 구축 비용 매우 저렴
그러나 다른 엔진을 쓴다면 사용할 수 없음
77. 최군 스킬 툴 < 블루프린트
프로퍼티 기반 vs 함수 기반
블루 프린트의 확장성과 자유도가 높음
ex) 최군의 경우
“이펙트를 타겟 좌표에 생성하고 싶어요”
→ Play Particle 이벤트에 SpawnLocBase라는 프로퍼티 추가했어요.
이걸 SPAWN_LOC_Target 으로 설정하시면 되요.
“이펙트를 타겟 뒤쪽 100uu 좌표에 생성하고 싶어요”
→ Play Particle 이벤트에 Offset 프로퍼티 추가했어요. X값을 100으로 설정하시면 되요.
78. 최군 스킬 툴 < 블루프린트
프로퍼티 기반 vs 함수 기반
블루 프린트의 확장성과 자유도가 높음
ex) 블루프린트의 경우
“이펙트를 타겟 좌표에
생성하고 싶어요”
“이펙트를 타겟 뒤쪽 100uu
좌표에 생성하고 싶어요”
79. 구조적 vs 절차적
최군쪽이 전체 구조가 눈에 잘 들어옴
같은 내용을 비교하면…
최군 스킬 툴 > 블루프린트
80. 최군 스킬 툴 > 블루프린트
구조적 vs 절차적
최군쪽이 전체 구조가 눈에 잘 들어옴
vs
81. 최군 스킬 툴 > 블루프린트
구조적 vs 절차적
최군쪽이 전체 구조가 눈에 잘 들어옴
다행히 최군에 비해 건파이트 맨션의 스킬이 단순해서 큰 문제는 안되는 상태
vs
(실제 최군 스킬의 스크린샷입니다)
?
82. 최군 스킬 툴 > 블루프린트
블루프린트는 코딩 기반이므로, 사용 난이도가 더 높음
알아야 할 개념들 : 변수 선언, 함수 선언, 캐스팅, 멤버 함수 호출, 상속…
기획자가 잘 사용할 수 있을까?
앞으로 검증해야 할 부분