담당 프로젝트 2014. 10. 22. 18:15

Blood Summoner

 


초기 버전 동영상 ~ 개발 도중 촬영한거라 GStar2008 출품 버전보다 완성도가 떨어진다.






아카데미 졸업 프로젝트 지스타 2008 출품!! 

온라인 AOS 게임

개발 기간 : 08년 4월 ~ 08년 11월 11일

개발 인원 : 클라이언트 2명, 서버 1명 외 기획 1명, 디자인 4명

사용 언어 및 라이브러리 : C++, STL, DirectX9, HLSL

개발 툴: VS2005, 렌더몽키, PerfHUD, 소스세이프


담당 업무 : 리드 프로그래머

-  엔진 Architecture 설계 및 구현

-  게임 전반에 걸친 시스템 및 로직 구현

-  프레임워크 구현

스택 기반의 Scene 전환 시스템, 게임 중 투명한 지도를 별도의 장면으로 출력하거나

특정 위치에서 이벤트를 출력 후 기존 상태로 그대로 돌아갈 수 있는 구조

-  수학 및 물리 함수 구현

   라인 충돌, 원 충돌, 평면과 라인 충돌, 점의 포함 여부, 선분 길이

-  엔진 프로파일링 시스템 구현

   초기에 엔진 프레임이 나오지 않는 문제의 원인을 찾기 위해서 사용

   자체 개발한 스마트 포인터와 스키닝 계산에 문제가 있다는 사실을 발견 후 해결

-  스크립트 시스템 구현

   엔진 빌드 정보(시스템, 리소스)

   게임 데이터 (스테이지 정보, 캐릭터 상태)

-  풀 시스템 구현 :  메모리 단편화 방지와 참조 국소성을 좋게하기 위해 사용

   리소스 풀 (참조 카운팅 방식)

   동적 객체 풀 (영웅, 소환수, NPC, 배경, 이펙트, 발사체)

-  멀티스레드 구현

   네트웍 동기화를 위한 로직 갱신 스레드와 렌더링 스레드

-  Scene Factory  : Factory Method 패턴을 이용해서 Scene을 찍어낸다.

-  Actor AI 구현

   주변 정황 탐색 시스템 – 가장 가까운 동료, 적 인식, 동시에 인지하는 적들과 동료들

   가까운 적을 공격하는 공격 상태, 지역을 지키는 홀드 상태, 대기 상태, 강제 이동,

   타겟 공격 등 전반적인 AI와 상태전이

-  FSM 제작

   플레이어 조작 UI, World, Actor

-  길 찾기 & 지형 충돌 맵 시스템 구현

Navigation Mesh 를 이용한 길 찾기 & 지형 충돌맵 시스템

-  Flocking 시스템

   주변 정황 탐색 무리 이동, 가까운 동료나 적과 일정거리 유지하기, 타겟에 접근하기

-  게임 플레이 UI 구현

   커스텀 UI와는 별도의 독자적인 UI 클래스 구현 및 적용

-  네트워크 동기화 FSM 구현

 소환, 각종 스킬 사용, 쿨타임, 업그레이드 시점 등  멀티 플레이시 동기화에 관련된 모든

 이벤트 기반 FSM 구현 적용

-  네트워크 동기화 확인용 로그 출력 시스템

World FSM 입출력 이벤트, 이벤트 발생 객체 ID, 받는 객체 ID, 발생 프레임 등 동기화에 필요한 모든 사항을 텍스트로 출력하는 시스템

담당 프로젝트 2014. 10. 22. 18:15

Renderware Project Fry




렌더웨어 미니 프로젝트

온라인 TPS 게임

개발 기간 : 2008년 3월 한달간 작업

인원 : 클라 1명 서버 1명 외 기획 1명, 디자인 2명

언어 및 라이브러리 : C++, STL, 렌더웨어 그래픽 라이브러리

개발 툴 : VS2005, 소스세이프

담당 업무 : 리드 프로그래머

-      렌더웨어 렌더링 라이브러리 클래스화 작업

-      프로젝트 엔진 Architecture 설계 및 제작

-      게임 전반에 걸친 시스템 및 로직 구현

-      프레임워크 제작

-      수학 및 물리 함수 제작 (포탄의 포물선 이동, 충돌 처리)

-      최적화를 위한 자체 풀 시스템 (객체)

-      랜더웨어 기반 기술사용 목록 - 멀티 카메라, 프로젝션 그림자 구현

최종 인터페이스 레이어 객체 (Actor)


프로그래밍 일반 2014. 9. 15. 11:56

통합 빌드 시스템 구축하기 (Unity Build)


코드 종속성를 해결하기 위한 작업들은 너무 기본적인 부분이기 때문에 여기에선 논외로 치고..


프로젝트 규모가 커지면 왜 이렇게 빌드 시간이 오래 걸리는가..


빌드 명령어를 실행했을 때 컴파일러가 하는 일을 한번 들여다보자..


VS C/C++ 명령어에 /E 옵션을 추가하면 컴파일러의 전처리 과정을 확인 할 수 있는데 해당 옵션을 적용 시킨 후 간단한 hello world 프로그램을 빌드해보자.


결과를 보면..



3444 라인..


???


여기에 간단한 클래스를 추가해보자..

#pragma once


struct SBossSkill;

class CBoardProcess;


namespace skill

{

class CSkill

{

public :

CSkill(const SBossSkill* pSkillInfo = NULL) : m_pReferSkillBaseInfo(pSkillInfo) 

{

}

virtual ~CSkill() {}


// 스킬을 실행 합니다.

BOOL IsExecuteSkill(int iTargetPnum, CBoardProcess* pBoardPorcess) const;


protected:

virtual BOOL IsExecute(int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList) const = 0;


protected:

const SBossSkill* m_pReferSkillBaseInfo;

};

}


스킬을 상속받는 모래폭풍을 추가하고..

#pragma once


#include <vector>


#include "skill.h"



namespace skill

{


class CSandStorm

: public CSkill

{

public :

CSandStorm(const  SBossSkill* pSkillInfo = NULL) : CSkill(pSkillInfo) {}

virtual ~CSandStorm() {}


protected:

virtual BOOL IsExecute(int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList) const override;

};

}


심플하게 관리자를 하나 둔 후..

#pragma once


#include <map>

#include "noncopyable.h"


namespace EBossSkill { enum Type; }

struct SBossSkill;



class CSkillManager

: private virtual INonCopyable

{

private:

CSkillManager() {}

virtual ~CSkillManager();


public :

static CSkillManager& GetInstance() {


static CSkillManager instance;

return instance;

}


BOOL LoadData();


const SBossSkill* GetSkillInfo(EBossSkill::Type find);

const skill::CSkill* GetSkillObject(EBossSkill::Type find);


private:

typedef std::map<EBossSkill::Type, SBossSkill*> SKILL_INFO;

typedef std::map<EBossSkill::Type, skill::CSkill*> SKILL_EXE;


SKILL_INFO m_bossSkillInfo; // 스킬 정보

SKILL_EXE m_bossSkillExe; // 스킬 실행

};


빌드를 하면..



151039 라인...


스킬을 하나 더 추가하면..


164116 라인...


모듈이 헤더파일을 포함하면 포함한 만큼 컴파일러에 전처리 과정이 추가된다..


이러한 이유 때문에 프로젝트의 크기가 커져서 모듈의 수가 많아지면 많아질 수록 빌드시간은 늘어나게 되는 것이다..


모듈에서 호출되는 기본 라이브러리들은 미리컴파일된 헤더에 포함하고.. 코드 종속성도 고려해서 작업하면 어느정도 효과를 볼 수 있다.


담당중인 프로젝트에서도 기본 라이브러리들은 미리컴파일된 헤더에 포함시키고 실타래처럼 역인 코드 종속성을 완화시키는 작업을 통해 재컴파일되는 파일 수량은 기존대비 90%감소하고 리빌드 시간은 22% 감소했다.


대단히 빨라진 것 같지만.. 아직도 3분 40초대.. 프로젝트가 작아서 이정도지 만약 규모가 큰 프로젝트라면 10분에서 30분도 걸리는게 빌드 시간이라는걸 생각하면 부족한게 사실..


그러면 이쯤에서 Unity Build에 대해 알아보도록 하자..


유니티 빌드의 개념은 단순하다.


위에서 모듈이 늘어날 수록 동일한 전처리 과정이 늘어나서 빌드 시간이 길어진다는 사실을 알았기 때문에 소스가 하나라면 불필요한 전처리 과정은 사라지지 않을까..


테스트..

cpp 파일을 빌드에서 제외 시킨 후


cpp를 하나로 통합하고..

// HelloWorld.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"


#include "SandHell.h"


BOOL skill::CSandHell::IsExecute( int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList ) const 

{

return TRUE;

}



#include "SandStorm.h"


BOOL skill::CSandStorm::IsExecute( int iTargetPnum, CBoardProcess* pBoardPorcess, const std::vector<int>& myBlockList ) const 

{

return TRUE;

}



#include "skillManager.h"

#include "SandStorm.h"

#include "SandHell.h"


BOOL CSkillManager::LoadData()

{

return TRUE;

}


const SBossSkill* CSkillManager::GetSkillInfo( EBossSkill::Type find )

{

return NULL;

}


const skill::CSkill* CSkillManager::GetSkillObject( EBossSkill::Type find )

{

return NULL;

}



int _tmain(int argc, _TCHAR* argv[])

{

printf("hello world\n");

return 0;

}

빌드하면..

70838 라인...

불필요한 전처리가 많이 줄었다..


그럼 작업 중인 프로젝트의 소스파일을 어떻게 통합하느냐...


// HelloWorld.cpp : Defines the entry point for the console application.

//

#include "stdafx.h"


#include "SandHell.cpp"

#include "SandStorm.cpp"

#include "skillManager.cpp"



int _tmain(int argc, _TCHAR* argv[])

{

printf("hello world\n");

return 0;

}

cpp를 직접 포함하면 된다.


이쯤에서 중간 정리를 해보면..


수작업

  • 기존 솔루션에 통합 cpp 추가
  • 통합 cpp 빌드에서 제외
  • 통합 빌드 환경 추가 
  • 통합 빌드 환경은 미리 컴파일된 헤더를 사용하지 않는다.

 자동화

  •  stdafx.cpp 와 통합 cpp 파일을 제외한 cpp 파일들을 빌드에서 제외
  • 빌드에서 제외한 cpp 파일을 포함한 통합 cpp 파일 갱신
  • 빌드 전 이벤트로 실행



이제 vc 프로젝트 파일을 메모장으로 열어보면..


.vcproj 파일이 xml 문서라는 사실을 알 수 있는데..

자동화하는 방법은 프로젝트 파일을 파싱해서 대상 파일들을 빌드에서 제외시키고 제외된 목록을 알아오는 것으로 간단히 처리할 수 있다.


xml 파서 오픈 소스 라이브러리 : 

Markup115.zip



파일을 두개로 통합하는 예)

1. 기존 솔루션에 통합빌드 소스 추가 후 빌드에서 제외 시키기.




2. 통합 빌드 환경 추가




3. 통합 빌드 환경은 미리 컴파일된 헤더를 사용하지 않는다.




4. 대상 파일들을 빌드에서 제외하고 통합 빌드 파일에서 include 한다.






5. 자동화 프로그램 UnityMaker


// stdafx.h 

// CMakeUp 클래스가 stl을 사용하도록 셋팅

#define MARKUP_STL


메인 로직




함수들..



UnityMaker.exe 실행 배치 파일 Base.bat

@rem 경로 단순화

CD..


@rem 실행파일, 프로젝트 xml, 솔루션, 통합작업에서 제외할 필터 이름, 통합 소스 수

%CD%\externals\UnityMaker\UnityMaker.exe %CD%\src\Client.vcproj Base UnityBuild 2


pause



6. 빌드 이벤트 등록




7. 적용 결과

디버그 빌드 37초

릴리즈 빌드 30초


대략 80 ~ 90% 정도 감소를 보인다.


빌드 시간 확인하는 방법

툴 -> 옵션 -> 프로젝트 & 솔루션 -> 프로젝트 셋팅 -> Build Timing (Yes)



8. 참고 사항

- define, 지역 변수, enum 등 이름 충돌 문제는 이번 기회에 수정하자.

- 디버깅도 가능 잘된다.

- include 누락은 기존 빌드환경에서 테스트 가능하다.


9. 링크 결과물




10. 소스


UnityMaker.zip




11. 통합 빌드 시스템 허드슨에 구축하기

링크 : 통합 빌드 허드슨에 구축하기


프로그래밍 일반 2014. 7. 22. 16:23

Visual studio 에서 find가 되지 않을 때..

Ctrl + ScroolLock 을 정상적으로 검색이 된다.

Project BloodSummoner 2014. 7. 7. 12:04

cube 텍스쳐 생성 방법

ndivia dds 포토샵 플러그인을 다운 받아 설치 후 포토샵에서 제작


directx texture tool 에서 작성 (directx sdk)

Project BloodSummoner 2014. 7. 7. 11:51

행렬의 곱이 성립하려면

A 행렬은 l행 m열

B 행렬을 m행 n열이라고 정의할 때 두행렬의 곱셈이 성립하려면

A의 열과 B의 행이 같아야한다.

두 행렬 곱의 결과는 l행 n열의 행렬이 나온다.



행기준 변환 행렬일 때 벡터와의 곱셈 연산


mul(벡터, 행렬)



열기준 변환 행렬 예





열기준 변환 행렬일 때는 


mul(행렬, 벡터)


3D 이팩트를 공부하려면 기본적인 내용..

담당 프로젝트 2014. 7. 5. 02:46

DirectDraw HELL






Direct Draw 미니 프로젝트

작업 기간 : 2007년 8월 한달간 작업

사용 언어 및 라이브러리 : C++, STL, Direct Draw, MFC

개발 툴 : VS2005


작업 내용 :

- Direct Draw를 이용한 엔진 설계 및 구현

- MFC를 이용한 애니메이션 툴 개발

- 게임 프레임워크, 시스템 및 로직 구현



프로그래밍 일반 2014. 6. 24. 17:11

디스어셈블리

컴파일러 최적화 기술은 나날이 발전하고 있고 예전에 공부했던 최적화 내용은 지금과 다를 수도 있다.


최근 서버 코드를 리팩토링하면서 당연히 문제가 있을거라 생각했던 코드들[각주:1]도 아주 훌륭하게 컴파일러는 처리를 해주고 있었다.


아... 내가 알고 있는 지식이 지금도 유효한가 묘한 경험.. 뭐 어셈을 알면 코드를 검증하기도 쉽고 디버깅에도 도움이되고 무엇보다 


프로그램을 이해하는데 도움이 되니 기회가 될때마다 학습을해두는게 좋다.


VS2008 에서 Disassembly 단축키 Alt + 8


어셈코드를 읽기위해 숙지하고 있어야할 정보들..



- 범용 레지스터 (32비트)

EAX : 확장 누산기, 대부분 입출력과 산술, 논리 연산 수행


EAX 32비트, AX 16비트, AH 상위 영역(SUB 16bit), AL 하위 영역  -> 범용 레지스터 공통적으로 적용가능


EBX : 주소 지정을 확장하기 위해 인덱스로 사용, 특정 주소 지정을 위한 베이스 레지스터로서 DI, SI와 결합될 수 있다.

         산술 연산에도 사용한다.

ECX : 반복 수행할 때 횟수를 지정하는데 사용, 루프나 시프트 등에서 사용

EDX : 64비트 값들의 상위 32비트를 담는 용도로 사용

ESP : 스택포인터, 스택의 꼭대기를 가리킨다(offset, x86은 스택이 아래로 자라므로 실제로는 바닥이다).

   스택에 데이터가 push 되거나 pop 될 때마다 증감된다.

EBP : 스택프레임, 프로시저나 함수에 대한 스택프레임을 담는 용도로 사용된다. 스택의 데이터에 접근하기 위해 사용

ESI : 메모리 이동이나 비교시에 원본주소를 담는 용도로 사용

EDI : 메모리 이동이나 비교시에 대상의 주소를 담는 용도로 사용




- EIP 레지스터

EIP : 인스트럭션[각주:2] 포인트, 다음에 실행할 코드의 주소를 가지고 있다.



- 세그먼트 레지스터

cs : 코드세그먼트, 코드영역의 세그먼트를 지닌다. 32비트 어드레싱에서는 별의미가 없다.

ds : 데이터세그먼트, 데이터영역의 세그먼트를 지닌다. 역시 32비트 어드레싱에서는 별의미가 없다.

ss : 스택 세그먼트, 

es : extra segment, 데이터 전송을 받은 수신측에서의 시작 부분을 가리키고 있다.

스트링 명령에 보조적으로 사용되기도 한다.

fs, gs - es 처럼 데이터를 가리키는 역활을 하지만 보조적으로 쓰이는 역활



- 명령어

LEA - 메모리 주소값 (세그먼트 + 오프셋) 을 추출

CMP - 두 값을 비교 (깊은 의미 - 두 값의 차이 계산, 둘다 같은 값이면 차가 0 이므로 ZF가 1로 셋팅)

JMP : 비교 점프

above 크다, below 작다, greater (부호 포함 크다), less 부호 포함 작다

JNE - Jump not equal

JE - (Jump if equal) : ZF = 1

JA - Jump if (unsigned) above) : CF = 0 and ZF = 0

      앞에서 수행한 비교문 (cmp) 에서 앞에 것이 클때만 점프

JB - (Jump if below) : CF = 1 앞에서 수행한 비교문에서 앞에 것이 작을 때만 점프

JG - (Jump if greater) : ZF = 0 and SF = 0 , 앞의 것이 크거나 같다면 점프한다.

JL - (Jump if less)

LOOP - 반복

LOOPE - 같다면 반복

LOOPNE - 같지 않다면 반복

CALL - 서브루틴으로 분기

RET - 원래의 루틴으로 복귀

IRET - 인터럽트 복귀

JE/JZ - 결과가 0이면 분기

JNE/JNZ - 결과가 0이 아니면 분기

JL/JNGE  - 결과가 작으면 분기

JO - 오버플로우가 발생하면 분기

JNLE/JG  - 결과가 크면 분기


movzx - (move zero extend : 무브 제로 확장) 

movsx - (Move sign extend : 무브 부호 확장)


mov -  


int iValue = 10;

00F724FE  mov         dword ptr [iValue],0Ah    // 의미 iValue 라는 dword형 변수에 16진수 0A (10진수 10) 를 넣는다. 



- 스트링 명령어

LODS - 메모리로부터 레지스터에 데이터를 로드, ESI(포인터)의 내용을 EAX로 로드

STOP - 메모리에 데이터를 저장, EAX의 값을 EDI가 가리키는 곳에 저장

MOVS - ESI가 가리키는 곳의 값을 EDI가 가리키는 곳에 복사

CMPS - 메모리와 메모리를 비교, ESI와 EDI의 내용을 비교한 결과에 따라 플래그 설정

SCAS - 레지스터와 메모리의 내용을 비교, EDI

REP - REP 뒤에 오는 스트링 명령을 ECX가 0이 될 때까지 반복

   REP 명령과 조합하여 사용하면 ECX 레지스터가 지정하는 횟수만큼 반복하여 데이터를 전송한다.

   이때 주소는 자동으로 갱신되지 때문에 한 명령으로 연속된 여러 데이터를 전송할 수 있다.

REPE, PEPNE - 일치하는 데이터가 얻어질 때까지, 일치하지 않는 데이터가 얻어질 때까지 메모리상의 데이터를 탐색할 수 있다


우선 이정도만 알아도 왠만한 코드는 볼 수 있다..


계속 업데이트 예정..

  1. switch case 문이 점프테이블을 만드는 룰 위반 [본문으로]
  2. CPU를 동작 시키는 명령어. 인스트럭션 집한은 인스트럭션 셋이라한다. [본문으로]
SQL 2014. 6. 13. 10:57

데이터 베이스 생성 후 게임서버에서 접근이 되지 않을 때

데이터 베이스를 신규로 생성한 후 게임 서버나 웹에서 접근이 되지 않는 경우 


해당 DB의 보안 -> 로그인 메뉴를 이용하여 접근 권한 설정을 해줄 필요가 있다.


신규 데이터 베이스에 로그인 되는 것을 확인한 후에는 


데이터 베이스 소유자와 접근해서 사용하는 계정이 다른경우 SP에 대한 실행 권한을 별도로 설정해주면 된다.

프로그래밍 일반 2014. 4. 14. 16:39

C++ 11 컴파일 타임 assertion ( static_assert )

웹 컴파일러 링크 : http://melpon.org/wandbox/


컴파일 타임 assertion 은 상수식과 문자열 리터럴로 구성되어 있다.


static_assert(constant-expression, string literal);


컴파일러는 식을 계산한 다음 expression 이 거짓이라면 오류 메시지에 string 을 적는다.


static_assert 는 컴파일시에 처리되기 때문에 런타임에 의존하는 값들을 확인할 때는 사용할 수 없다.


int Func(int* p, int n) 

{

static_assert( NULL != p, "p is not null");  // 오류

}