WIN32 AI, 렌더링 최적화, 물리처리, 강체
카테고리: WINAPI
21일차(공부 끝)
- Default
- Client.h
- framework.h
- Resouce.h
- targetver.h
- Engine
1. Header
**1. define.h**
**2. struct.h**
**3. func.h
4. func.cpp**
5. global.h
2. Core
**1. CCore.cpp
2. CCore.h**
3. Manager
1. KeyMgr
1. CkeyMgr.cpp
2. CkeyMgr.h
2. TimeMgr
1. CTimeMgr.cpp
2. CTimeMgr.h
3. SceneMgr
1. CSceneMgr.cpp
2. CSceneMgr.h
4. PathMgr
1. CPathMgr.cpp
2. CPathMgr.h
5. ResMgr
1. CResMgr.cpp
2. CResMgr.h
**** 6. CollisionMgr
1. CCollisionMgr.cpp
2. CCollisionMgr.h
7. EventMgr
1. CEventMgr.cpp
**** 2. CEventMgr.h
8. Camera
1. CCamera.cpp
**** 2. CCamera.h
9. CUIMgr.
1. CUIMgr.cpp
2. CUIMgr.h
4. Object
1. Monser
1. CMonster.cpp
2. CMonster.h
2. Missle
1. Missile.cpp
2. Missile.h
**3. Player
1. CPlayer.cpp
2. Cplayer.h**
4. Tile
1. CTile.cpp
2. CTile.h
**** 5. UI
1. BtnUI
1. CBtnUI.cpp
2. CBtnUI.h
**** 2. PanelUI
1. CPanelUI.cpp
2. CPanelUI.h
**** 1. CUI.cpp
2. CUI.h
**6. Ground
1. CGround.cpp
2. CGround.h
1. CObject.cpp
2. CObject.h**
5. Scene
**1. Scene_Start
1. CScene_Start.cpp
2. CScene_Start.h**
**2. Scene_Tool
1. CScene_Tool.cpp
2. CScene_Tool.h**
**3. CScene.cpp
4. CScene.h**
6. Resource
1. Sound
2. Texture
1. CTexture.cpp
2. CTexture.h
3. CRes.cpp
4. CRes.h
7. Component
1. Collider
1. CCollider.cpp
2. CCollider.h
2. Animator
**1. Animation
1. CAnimation.cpp
2. CAnimation.h**
1. CAnimator.cpp
2. CAnimation.h
**3. Gravity
1. CGravity.cpp
2. CGravity.h
4. Rigidbody
1. CRigidbody.cpp
2. CRigidbody.h**
8. Module
**1.AI
2. State
1. ATTACK
2. DEAD
3. IDLE
1. CIdleState.cpp
2. CIdleState.h
4. TRACE
1. CTraceState.cpp
2. CTraceState.h
3. AI.cpp
4. AI.h
9. Factory
1. MonFactory
1. CMonFactory.cpp
2. CMonFactory.h**
1. SelectGDI.cpp
2. SelectGDI.h
- 리소스 파일
- main.cpp
- pch.h.
- 며칠동안 엄청 버스트를 해서 추가된 게 많다.
- Sound는 Direct Sound를 쓰는 코드가 있긴 하지만, 여러 기능적인 이슈로 FMOD로 교체할 예정이라 추가하진 않겠다.
- define.h
enum class GROUP_TYPE {
DEFAULT,
TILE,
GROUND,
MONSTER,
PLAYER,
PROJ_PLAYER,
PROJ_MONSTER,
UI = 31,
END = 32,
};
enum class EVENT_TYPE {
CREATE_OBJECT,
DELETE_OBJECT,
SCENE_CHANGE,
CHANGE_AI_STATE,
END,
};
enum class MON_STATE {
IDLE,
PATROL,
TRACE,
ATTACK,
RUNAWAY,
DEAD,
END,
};
- 위 3부분이 변경, 추가되었다.
- func.h
void Fscanf(char* _pOutBuff, FILE* _pFile);
void SaveWstring(const wstring& _str, FILE* _pFile);
void LoadWstring(wstring& _str, FILE* _pFile);
- func.cpp
void Fscanf(char* _pOutBuff, FILE* _pFile)
{
int idx = 0;
while (true) {
char c = (char)getc(_pFile);
if (c == '\n') {
_pOutBuff[idx++] = '\0';
break;
}
_pOutBuff[idx++] = c;
}
}
void SaveWstring(const wstring& _str, FILE* _pFile)
{
//Animation의 이름을 저장한다.(데이터 직렬화)
const wchar_t* pStrName = _str.c_str();
size_t iLen = _str.length();
//문장 길이 저장.
fwrite(&iLen, sizeof(size_t), 1, _pFile);
//문자열 저장.
fwrite(pStrName, sizeof(wchar_t), iLen, _pFile);
}
void LoadWstring(wstring& _str, FILE* _pFile)
{
size_t iLen = 0;
wchar_t szBuffer[256] = {};
//문장 길이 저장.
fread(&iLen, sizeof(size_t), 1, _pFile);
//문자열 저장.
fread(szBuffer, sizeof(wchar_t), iLen, _pFile);
_str = szBuffer;
}
- 문자를 가져오기 위한 커스텀 scanf, 그리고 문자를 파일시스템으로 저장 로드하기 위한 함수들을 구현했다.
- struct.h
public:
float Length() {
return sqrt(x * x + y * y);
}
bool IsZero() {
if (x == 0.f && y == 0.f) return true;
else return false;
}
Vec2& Normalize() {
float fLen = Length();
if (fLen == 0.f) {
return *this;
}
x /= fLen;
y /= fLen;
return *this;
}
public:
Vec2 operator - () {
return Vec2(-x, -y);
}
Vec2& operator = (POINT _pt) {
x = (float)_pt.x;
y = (float)_pt.y;
return *this;
}
Vec2 operator + (Vec2 _vOther) {
return Vec2(x + _vOther.x, y + _vOther.y);
}
void operator += (Vec2 _vOther) {
x += _vOther.x;
y += _vOther.y;
}
Vec2 operator - (Vec2 _vOther) {
return Vec2(x - _vOther.x, y - _vOther.y);
}
void operator -= (Vec2 _vOther) {
x -= _vOther.x;
y -= _vOther.y;
}
void operator -= (float _f) {
x -= _f;
y -= _f;
}
Vec2 operator * (Vec2 _vOther) {
return Vec2(x * _vOther.x, y * _vOther.y);
}
Vec2 operator * (int _ivalue) {
return Vec2(x * (float)_ivalue, y * (float)_ivalue);
}
Vec2 operator * (float _fvalue) {
return Vec2(x * _fvalue, y * _fvalue);
}
void operator *= (float _fvalue) {
x *= _fvalue;
y *= _fvalue;
}
Vec2 operator / (Vec2 _vOther) {
assert(!(0.f == _vOther.x || 0.f == _vOther.y));
return Vec2(x / _vOther.x, y / _vOther.y);
}
Vec2 operator / (float _fValue) {
assert(!(_fValue == 0.f));
return Vec2(x / _fValue, y / _fValue);
}
- 늘 그렇듯이 연산자 오버로딩이다.
- CCore.h
public:
int init(HWND _hWnd, POINT _ptResolution);
void progress();
void DockMenu();
void DivideMenu();
void ChangeWindowSize(Vec2 _vResolution, bool _bMenu);
- CCore.cpp
void CCore::progress() {
// ============
// Manager Update
// ============
CTimeMgr::GetInstance()->update();
CkeyMgr::GetInstance()->update();
CCamera::GetInstance()->update();
// ============
// Scene Update
// ============
CSceneMgr::GetInstance()->update();
// 충돌 체크.
CCollisionMgr::GetInstance()->update();
//UI 이벤트 체크
CUIMgr::GetInstance()->update();
// ============
// Rendering
// ============
//화면 초기화(흰 사각형으로 덧 씌움)
Clear();
//어디다가 그려야하는지 알려주기.
CSceneMgr::GetInstance()->render(m_pMemTex->GetDC());
CCamera::GetInstance()->render(m_pMemTex->GetDC());
BitBlt(m_hDC, 0, 0, m_ptResolution.x, m_ptResolution.y, m_pMemTex->GetDC(), 0, 0, SRCCOPY);
CTimeMgr::GetInstance()->render();
//렌더링까지 전부 다 끝난 후에 EventMgr에서 이벤트 지연 처리.
// ============
// 이벤트 지연 처리.
// ============
CEventMgr::GetInstance()->update();
}
void CCore::DockMenu()
{
//툴 Scene에서 사용할 메뉴를 붙인다.
SetMenu(m_hWnd, m_hMenu);
ChangeWindowSize(GetResolution(), true);
}
void CCore::DivideMenu()
{
SetMenu(m_hWnd, nullptr);
ChangeWindowSize(GetResolution(), false);
}
void CCore::ChangeWindowSize(Vec2 _vResolution, bool _bMenu)
{
RECT rt = { 0,0,(long)_vResolution.x, (long)_vResolution.y };
AdjustWindowRect(&rt, WS_OVERLAPPEDWINDOW, _bMenu);
SetWindowPos(m_hWnd, nullptr, 100, 100, rt.right - rt.left, rt.bottom - rt.top, 0);
}
void CCore::Clear()
{
SelectGDI gdi(m_pMemTex->GetDC(), BRUSH_TYPE::BLACK);
Rectangle(m_pMemTex->GetDC(), -1, -1, m_ptResolution.x + 1, m_ptResolution.y + 1);
}
void CCore::CreateBrushPen()
{
// Hollow Brush
m_arrBrush[(UINT)BRUSH_TYPE::HOLLOW] = (HBRUSH)GetStockObject(HOLLOW_BRUSH);
m_arrBrush[(UINT)BRUSH_TYPE::BLACK] = (HBRUSH)GetStockObject(BLACK_BRUSH);
//R,G,B Pen
m_arrPen[(UINT)PEN_TYPE::RED] = (HPEN)CreatePen(PS_SOLID, 1, RGB(255, 0, 0));
m_arrPen[(UINT)PEN_TYPE::GREEN] = (HPEN)CreatePen(PS_SOLID, 1, RGB(0, 255, 0));
m_arrPen[(UINT)PEN_TYPE::BLUE] = (HPEN)CreatePen(PS_SOLID, 1, RGB(0, 0, 255));
}
- 툴 씬을 제외한 다른 씬에서 메뉴바가 없도록 세팅해주었다.
- CGround.h
#pragma once
#include "CObject.h"
class CGround :
public CObject
{
private:
virtual void start();
virtual void update();
public:
virtual void OnCollisionEnter(CCollider* _pOther);
virtual void OnCollision(CCollider* _pOther);
virtual void OnCollisionExit(CCollider* _pOther);
CLONE(CGround);
public:
CGround();
~CGround();
};
- CGround.cpp
#include "pch.h"
#include "CGround.h"
#include "CCollider.h"
#include "CGravity.h"
CGround::CGround()
{
CreateCollider();
}
CGround::~CGround()
{
}
void CGround::start()
{
GetCollider()->SetScale(GetScale());
}
void CGround::update()
{
}
void CGround::OnCollisionEnter(CCollider* _pOther)
{
CObject* pOtherObj = _pOther->GetObj();
if (pOtherObj->GetName() == L"Player") {
//pOtherObj->GetGravity()->SetGround(true);
//파고 들었을 경우에 중심 위치에서.
Vec2 vObjPos = _pOther->GetFinalPos();
Vec2 vObjScale = _pOther->GetScale();
Vec2 vPos = GetCollider()->GetFinalPos();
Vec2 vScale = GetCollider()->GetScale();
float fLen = abs(vObjPos.y - vPos.y);
float fInterpolValue = (vObjScale.y / 2.f + vScale.y / 2.f) - fLen;
vObjPos = pOtherObj->GetPos();
vObjPos.y -= fInterpolValue;
pOtherObj->SetPos(vObjPos);
}
}
void CGround::OnCollision(CCollider* _pOther)
{
CObject* pOtherObj = _pOther->GetObj();
if (pOtherObj->GetName() == L"Player") {
//pOtherObj->GetGravity()->SetGround(true);
//파고 들었을 경우에 중심 위치에서.
Vec2 vObjPos = _pOther->GetFinalPos();
Vec2 vObjScale = _pOther->GetScale();
Vec2 vPos = GetCollider()->GetFinalPos();
Vec2 vScale = GetCollider()->GetScale();
float fLen = abs(vObjPos.y - vPos.y);
float fInterpolValue = (vObjScale.y / 2.f + vScale.y / 2.f) - fLen;
vObjPos = pOtherObj->GetPos();
vObjPos.y -= (fInterpolValue);
//갔던 방향으로의 Velocity값 초기화 해줘야함.
pOtherObj->SetPos(vObjPos);
}
}
void CGround::OnCollisionExit(CCollider* _pOther)
{
CObject* pOtherObj = _pOther->GetObj();
if (pOtherObj->GetName() == L"Player") {
//그라운드 체크 해체. 우리 게임은 중력 설정 없어서..
//pOtherObj->GetGravity()->SetGround(false);
}
}
- 말이 Ground지만, 내 프로젝트에선 벽으로 사용할 것이다. 따라서, 지금은 위에서 내려앉는 것만 체크하지만, 이후에는 4방향에서 오는 것도 처리할 것이다.
- CPlayer.h
#pragma once
#include "CObject.h"
enum class PLAYER_STATE {
IDLE,
WALK,
ATTACK,
JUMP,
DEAD,
};
class CTexture;
class CPlayer :
public CObject
{
private:
vector<CObject*> m_vecColObj;
PLAYER_STATE m_eCurState;
PLAYER_STATE m_ePrevState;
int m_iDir;
int m_iPrevDir;
public:
virtual void update();
virtual void render(HDC _dc);
private:
void CreateMissile();
void update_move();
void update_state();
void update_animation();
virtual void OnCollisionEnter(CCollider* _pOther);
//플레이어 오브젝트는 복사되면
CLONE(CPlayer)
public:
CPlayer();
~CPlayer();
};
- CPlayer.cpp
#include "pch.h"
#include "CPlayer.h"
#include "CSceneMgr.h"
#include "CScene.h"
#include "CkeyMgr.h"
#include "CTimeMgr.h"
#include "CResMgr.h"
#include "CMissile.h"
#include "CTexture.h"
#include "CCollider.h"
#include "CAnimator.h"
#include "CAnimation.h"
#include "CRigidbody.h"
#include "CGravity.h"
CPlayer::CPlayer()
: m_eCurState(PLAYER_STATE::IDLE)
, m_ePrevState(PLAYER_STATE::IDLE)
, m_iDir(1)
, m_iPrevDir(1)
{
//m_pTex = CResMgr::GetInstance()->LoadTexture(L"PlayerTex", L"texture\\Tenshi.bmp");
CreateCollider();
GetCollider()->SetOffsetPos(Vec2(0.f, 15.f));
GetCollider()->SetScale(Vec2(25.f, 25.f));
CreateRigidBody();
CTexture* m_pTex = CResMgr::GetInstance()->LoadTexture(L"PlayerTex", L"texture\\link_0.bmp");
//우리 텍스쳐가 완벽하게 편집이 되어 있어서 저렇게 수월하게 할 수 있었음...
//근데 그렇지 않은 경우가 정말 많음.
CreateAnimator();
GetAnimator()->LoadAnimation(L"animation\\player_idle_down.anim");
GetAnimator()->LoadAnimation(L"animation\\player_idle_left.anim");
GetAnimator()->LoadAnimation(L"animation\\player_idle_up.anim");
GetAnimator()->LoadAnimation(L"animation\\player_idle_right.anim");
GetAnimator()->LoadAnimation(L"animation\\player_walk_down.anim");
GetAnimator()->LoadAnimation(L"animation\\player_walk_left.anim");
GetAnimator()->LoadAnimation(L"animation\\player_walk_up.anim");
GetAnimator()->LoadAnimation(L"animation\\player_walk_right.anim");
//IDLE 애니메이션 추가
/*
GetAnimator()->CreateAnimation(L"IDLE_DOWN", m_pTex, Vec2(0.f, 0.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 3);
GetAnimator()->CreateAnimation(L"IDLE_LEFT", m_pTex, Vec2(0.f, 65.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 3);
GetAnimator()->CreateAnimation(L"IDLE_UP", m_pTex, Vec2(0.f, 130.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 1);
GetAnimator()->CreateAnimation(L"IDLE_RIGHT", m_pTex, Vec2(0.f, 195.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 3);
//걷기 애니메이션 추가
GetAnimator()->CreateAnimation(L"WALK_DOWN", m_pTex, Vec2(0.f, 260.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 10);
GetAnimator()->CreateAnimation(L"WALK_LEFT", m_pTex, Vec2(0.f, 325.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 10);
GetAnimator()->CreateAnimation(L"WALK_UP", m_pTex, Vec2(0.f, 390.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 10);
GetAnimator()->CreateAnimation(L"WALK_RIGHT", m_pTex, Vec2(0.f, 455.f), Vec2(60.f, 65.f), Vec2(60.f, 0.f), 0.1f, 10);
*/
//Animation 저장해보기
GetAnimator()->Play(L"IDLE_DOWN", true);
GetAnimator()->FindAnimation(L"IDLE_DOWN")->Save(L"animation\\player_idle_down.anim");
GetAnimator()->FindAnimation(L"IDLE_LEFT")->Save(L"animation\\player_idle_left.anim");
GetAnimator()->FindAnimation(L"IDLE_UP")->Save(L"animation\\player_idle_up.anim");
GetAnimator()->FindAnimation(L"IDLE_RIGHT")->Save(L"animation\\player_idle_right.anim");
GetAnimator()->FindAnimation(L"WALK_DOWN")->Save(L"animation\\player_walk_down.anim");
GetAnimator()->FindAnimation(L"WALK_LEFT")->Save(L"animation\\player_walk_left.anim");
GetAnimator()->FindAnimation(L"WALK_UP")->Save(L"animation\\player_walk_up.anim");
GetAnimator()->FindAnimation(L"WALK_RIGHT")->Save(L"animation\\player_walk_right.anim");
//CreateGravity();
}
CPlayer::~CPlayer()
{
}
void CPlayer::update()
{
update_move();
update_state();
update_animation();
Vec2 vPos = GetPos();
if (KEY_TAP(KEY::Z)) {
CreateMissile();
}
if (KEY_TAP(KEY::ENTER)) {
SetPos(Vec2(640.f, 384.f));
}
GetAnimator()->update();
//맨 뒤에 놔줘야 이전과 현재를 맞춰놔야 이후에 발생하는 상태가 적용가능.
//EX) 충돌, 콜라이더 등등.
m_ePrevState = m_eCurState;
m_iPrevDir = m_iDir;
}
void CPlayer::render(HDC _dc)
{
//컴포넌트(충돌체, etc...) 가 있는 경우 렌더.
component_render(_dc);
}
void CPlayer::CreateMissile()
{
Vec2 vMissilePos = GetPos();
vMissilePos.y -= GetScale().y / 2.f;
CMissile* pMissile = new CMissile;
pMissile->SetName(L"Missile_Player");
pMissile->SetPos(vMissilePos);
pMissile->SetScale(Vec2(25.f, 25.f));
pMissile->SetDir(Vec2(0.f, -1.f));
//이걸 바로 추가하는 게 아니라, 이벤트를 등록하는 것.
CreateObject(pMissile, GROUP_TYPE::PROJ_PLAYER);
}
void CPlayer::update_move()
{
CRigidbody* pRigid = GetRigidbody();
if (KEY_HOLD(KEY::W)) {
pRigid->AddForce(Vec2(0.f, -200.f));
}
if (KEY_HOLD(KEY::S)) {
pRigid->AddForce(Vec2(0.f, 200.f));
}
if (KEY_HOLD(KEY::A)) {
pRigid->AddForce(Vec2(-200.f, 0.f));
}
if (KEY_HOLD(KEY::D)) {
pRigid->AddForce(Vec2(200.f, 0.f));
}
if (KEY_TAP(KEY::W)) {
pRigid->AddVelocity(Vec2(pRigid->GetVelocity().x, -100.f));
}
if (KEY_TAP(KEY::S)) {
pRigid->AddVelocity(Vec2(pRigid->GetVelocity().x, 100.f));
}
if (KEY_TAP(KEY::A)) {
pRigid->AddVelocity(Vec2(-100.f, pRigid->GetVelocity().y));
}
if (KEY_TAP(KEY::D)) {
pRigid->AddVelocity(Vec2(100.f, pRigid->GetVelocity().y));
}
}
void CPlayer::update_state()
{
CRigidbody* pRigid = GetRigidbody();
if (KEY_TAP(KEY::W)) {
m_iDir = 0;
m_eCurState = PLAYER_STATE::WALK;
}
if (KEY_TAP(KEY::D)) {
m_iDir = 1;
m_eCurState = PLAYER_STATE::WALK;
}
if (KEY_TAP(KEY::S)) {
m_iDir = 2;
m_eCurState = PLAYER_STATE::WALK;
}
if (KEY_TAP(KEY::A)) {
m_iDir = 3;
m_eCurState = PLAYER_STATE::WALK;
}
if (0.f == pRigid->GetSpeed()) {
m_eCurState = PLAYER_STATE::IDLE;
}
}
void CPlayer::update_animation()
{
if (m_ePrevState == m_eCurState && m_iPrevDir == m_iDir) return;
switch (m_eCurState) {
case PLAYER_STATE::IDLE:
{
if (m_iDir == 0) {
GetAnimator()->Play(L"IDLE_UP", true);
}
else if (m_iDir == 1) {
GetAnimator()->Play(L"IDLE_RIGHT", true);
}
else if (m_iDir == 2) {
GetAnimator()->Play(L"IDLE_DOWN", true);
}
else if (m_iDir == 3) {
GetAnimator()->Play(L"IDLE_LEFT", true);
}
}
break;
case PLAYER_STATE::WALK:
if (m_iDir == 0) {
GetAnimator()->Play(L"WALK_UP", true);
}
else if (m_iDir == 1) {
GetAnimator()->Play(L"WALK_RIGHT", true);
}
else if (m_iDir == 2) {
GetAnimator()->Play(L"WALK_DOWN", true);
}
else if (m_iDir == 3) {
GetAnimator()->Play(L"WALK_LEFT", true);
}
break;
case PLAYER_STATE::ATTACK:
break;
case PLAYER_STATE::DEAD:
break;
}
}
void CPlayer::OnCollisionEnter(CCollider* _pOther)
{
CObject* pOtherObj = _pOther->GetObj();
if (L"Ground" == pOtherObj->GetName()) {
Vec2 vPos = GetPos();
if (vPos.y < pOtherObj->GetPos().y) {//내가 땅보다 위에 있을 때.
}
}
}
- Rigidbody로 힘 기반 이동, 그리고 애니메이션 추가(이동 방향, 상태별)
- CObject.h
#pragma once
#include "global.h"
//오브젝트라는 건, 가장 부모격인 녀석. 오브젝트마다 성향이 있음
//어떤 건 UI, 어떤 건 캐릭터...
//오브젝트를 일괄적으로 관리할 녀석이 필요함. -> Scene
//이 오브젝트는 부모 클래스. -> 상속받은 오브젝트도 Scene의 업데이트 시점에
//부모쪽에 구현되어있는 update뿐만 아니라, 각 오브젝트(자식)들 마다의 update를 하고 싶음
//충돌할 오브젝트, 충돌하지 않을 오브젝트(UI, 배경등등...) <- 이런 식으로 계속 트리처럼 나누는 건 예외가 있을수도 있음.
// 따라서 부품기반의 구조가 필요함(컴포넌트 구조)
#include "CCamera.h"
class CCollider;
class CAnimator;
class CRigidbody;
class CGravity;
class CObject
{
private:
wstring m_ObjName;
Vec2 m_vPos;
Vec2 m_vScale;
//Component
CCollider* m_pCollider;
CAnimator* m_pAnimator;
CRigidbody* m_pRigidBody;
CGravity* m_pGravity;
bool m_bAlive; //자기 자신이 활성화 or 비활성화. (삭제 전용)
bool m_bEnable; //일시적인 활성화 or 비활성화.
public:
void SetPos(Vec2 _vPos) { m_vPos = _vPos; }
void SetScale(Vec2 _vScale) { m_vScale = _vScale; }
Vec2 GetPos() { return m_vPos; }
Vec2 GetScale() { return m_vScale; }
void SetName(const wstring& _strName) { m_ObjName = _strName; }
const wstring& GetName() { return m_ObjName; }
CCollider* GetCollider() { return m_pCollider; }
CAnimator* GetAnimator() { return m_pAnimator; }
CRigidbody* GetRigidbody() { return m_pRigidBody; }
CGravity* GetGravity() { return m_pGravity; }
void CreateCollider();
void CreateAnimator();
void CreateRigidBody();
void CreateGravity();
virtual void OnCollision(CCollider* _pOther) {};
virtual void OnCollisionEnter(CCollider* _pOther) {};
virtual void OnCollisionExit(CCollider* _pOther) {};
bool IsDead() {
return !m_bAlive;
}
private:
void SetDead() { m_bAlive = false; }
public:
virtual void start() {};
virtual void update() = 0;
virtual void finalupdate();
virtual void render(HDC _dc);
void component_render(HDC _dc);
virtual CObject* Clone() = 0;
public:
CObject();
CObject(const CObject& _origin);
virtual ~CObject();
//복사 생성자의 문제. -> 단순히 복사만 하면 m_pCollider의 주소가 그대로 복사됨.
//따라서 m_pCollider을 따로 만들어줘야 함.
//m_bAlive도 가져올 필요는 없음.
friend class CEventMgr;
};
- CObject.cpp
#include "pch.h"
#include "CObject.h"
#include "CCollider.h"
#include "CAnimator.h"
#include "CResMgr.h"
#include "CTexture.h"
#include "CRigidbody.h"
#include "CGravity.h"
CObject::CObject()
: m_vPos{}
, m_vScale{}
, m_pCollider(nullptr)
, m_pAnimator(nullptr)
, m_pRigidBody(nullptr)
, m_pGravity(nullptr)
, m_bAlive(true)
, m_bEnable(true)
{
}
CObject::CObject(const CObject& _origin)
: m_ObjName(_origin.m_ObjName)
, m_vPos(_origin.m_vPos)
, m_vScale(_origin.m_vScale)
, m_pCollider(nullptr)
, m_bAlive(true)
, m_bEnable(true)
, m_pAnimator(nullptr)
, m_pRigidBody(nullptr)
, m_pGravity(nullptr)
{
if (_origin.m_pCollider != nullptr) {
m_pCollider = new CCollider(*_origin.m_pCollider);
m_pCollider->m_pOwner = this;
}
if (_origin.m_pAnimator != nullptr) {
m_pAnimator = new CAnimator(*_origin.m_pAnimator);
m_pAnimator->m_pOwner = this;
}
if (_origin.m_pGravity != nullptr) {
m_pGravity = new CGravity(*_origin.m_pGravity);
m_pGravity->m_pOwner = this;
}
if (_origin.m_pRigidBody != nullptr) {
m_pRigidBody = new CRigidbody(*_origin.m_pRigidBody);
m_pRigidBody->m_pOwner = this;
}
}
CObject::~CObject() {
if (m_pCollider != nullptr) delete m_pCollider;
if (m_pAnimator != nullptr) delete m_pAnimator;
if (m_pGravity != nullptr) delete m_pGravity;
if (m_pRigidBody != nullptr)delete m_pRigidBody;
}
void CObject::finalupdate()
{
if (m_pAnimator) m_pAnimator->finalupdate();
if (m_pGravity) m_pGravity->finalupdate();
if (m_pRigidBody)m_pRigidBody->finalupdate();
if (m_pCollider) m_pCollider->finalupdate();
}
void CObject::render(HDC _dc)
{
//진짜 좌표.
Vec2 vRenderPos = CCamera::GetInstance()->GetRenderPos(m_vPos);
Rectangle(_dc, (int)(vRenderPos.x - m_vScale.x / 2.f), (int)(vRenderPos.y - m_vScale.y / 2.f),
(int)(vRenderPos.x + m_vScale.x / 2.f), (int)(vRenderPos.y + m_vScale.y / 2.f));
component_render(_dc);
}
void CObject::component_render(HDC _dc)
{
if (m_pAnimator != nullptr) m_pAnimator->render(_dc);
if (m_pCollider != nullptr) m_pCollider->render(_dc);
}
void CObject::CreateCollider()
{
m_pCollider = new CCollider;
m_pCollider->m_pOwner = this;
}
void CObject::CreateAnimator()
{
m_pAnimator = new CAnimator;
m_pAnimator->m_pOwner = this;
}
void CObject::CreateRigidBody()
{
m_pRigidBody = new CRigidbody;
m_pRigidBody->m_pOwner = this;
}
void CObject::CreateGravity()
{
m_pGravity = new CGravity;
m_pGravity->m_pOwner = this;
}
- 리지드 바디관련 기능 추가. 뭐 없다.
- CScene.h
#pragma once
#include "global.h"
#include "CMonFactory.h"
//전방선언하는 이유는 컴파일 속도에 영향을 주지 않기 위해.
class CObject;
class CScene
{
private:
vector<CObject*> m_arrObj[(UINT)GROUP_TYPE::END]; //벡터 안에 모든 오브젝트 집어 넣겠다. 이런 특성(요소)를 가진만큼 나눠주기.
//달리말하면 그룹 갯수만큼 나눠주기.
wstring m_strName; //Scene 이름
UINT m_iTileX; //타일 가로 개수
UINT m_iTileY; //타일 세로 개수.
CObject* m_pPlayer; //Player
public:
void SetName(const wstring& _strName) { m_strName = _strName; }
const wstring& GetName() { return m_strName; }
UINT GetTileX() { return m_iTileX; }
UINT GetTileY() { return m_iTileY; }
CObject* GetPlayer() { return m_pPlayer; }
virtual void start();
virtual void update();
virtual void finalupdate();
virtual void render(HDC _dc);
void render_tile(HDC _dc);
void render_monster(HDC _dc);
virtual void Enter() = 0; //해당 Scene에 진입 시 호출.
virtual void Exit() = 0; //해당 Scene에 탈출 시 호출.
public:
//클래스는 헤더에 구현하면 인라인 처리가 됨.
//따라서 함수 호출 비용이 사라짐.
void AddObject(CObject* _pObj, GROUP_TYPE _eType)
{
m_arrObj[(UINT)_eType].push_back(_pObj);
}
void RegisterPlayer(CObject* _pPlayer) { m_pPlayer = _pPlayer; }
const vector<CObject*>& GetGroupObject(GROUP_TYPE _eType)
{
return m_arrObj[(UINT)_eType];
}
vector<CObject*>& GetUIGroup() { return m_arrObj[(UINT)GROUP_TYPE::UI]; }
void DeleteGroup(GROUP_TYPE _eGroup);
void DeleteAll();
void CreateTile(UINT _IXCount, UINT _IYCount);
void LoadTile(const wstring& _strRelativePath);
public:
CScene();
//소멸자의 가상함수 해줘야함. 씬 매니저가 모든 Scene을 부모 포인터로 관리함.
// CSceneMgr에서 씬을 소멸시킬때, 소멸자는 부모인 CScene만 호출됨.
virtual ~CScene();
};
- CScene.cpp
#include "pch.h"
#include "CScene.h"
#include "CObject.h"
#include "func.h"
#include "CTile.h"
#include "CTimeMgr.h"
#include "CResMgr.h"
#include "CPathMgr.h"
#include "CCamera.h"
#include "CCore.h"
CScene::CScene()
: m_iTileX(0)
, m_iTileY(0)
, m_pPlayer(nullptr)
{
}
CScene::~CScene()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
//m_arrObj[그룹][물체] 삭제.
delete m_arrObj[typeIDX][objIDX];
}
//씬이 사라지면, 그 씬의 벡터들도 다 사라짐.
//STL의 RAII가 알아서 삭제하기 때문.
}
}
void CScene::start()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
m_arrObj[typeIDX][objIDX]->start();
}
}
}
void CScene::update()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
if (!m_arrObj[typeIDX][objIDX]->IsDead()) {
m_arrObj[typeIDX][objIDX]->update();
}
}
}
}
//움직이고 했던 걸, 마지막으로 업데이트 함.
//충돌체가 플레이어 따라가게 함, 충돌 처리.
void CScene::finalupdate()
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
for (size_t objIDX = 0; objIDX < m_arrObj[typeIDX].size(); objIDX++) {
//Final Update는 돌려줌. 내부적으로 Component들의 마무리 단계 업데이트(충돌처리나, 참조관계등)
m_arrObj[typeIDX][objIDX]->finalupdate();
}
}
}
void CScene::render(HDC _dc)
{
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
if ((UINT)GROUP_TYPE::TILE == typeIDX) {
render_tile(_dc);
continue;
}
/*
if ((UINT)GROUP_TYPE::MONSTER == typeIDX) {
render_monster(_dc);
continue;
}
*/
auto ObjVecIter = m_arrObj[typeIDX].begin();
for (; ObjVecIter != m_arrObj[typeIDX].end();) {
if (!(*ObjVecIter)->IsDead()) {
(*ObjVecIter)->render(_dc);
ObjVecIter++;
}
else {
//Dead상태일 경우엔 렌더링에서 삭제하기.
ObjVecIter = m_arrObj[typeIDX].erase(ObjVecIter);
}
}
}
}
void CScene::render_tile(HDC _dc)
{
const vector<CObject*> vecTile = GetGroupObject(GROUP_TYPE::TILE);
//화면 안에 들어오는 애들의 범위를 잡아내어, 들어오는 애들만 렌더링 해준다.
Vec2 vCamLook = CCamera::GetInstance()->GetLookAt();
Vec2 vResolution = CCore::GetInstance()->GetResolution();
Vec2 vLeftTop = vCamLook - vResolution / 2.f;
Vec2 vRightDown = vCamLook + vResolution / 2.f;
int iTileSize = TILE_SIZE;
//Width, HEIGHT로 대체 가능할지도?
int iLTCol = (int)vLeftTop.x / iTileSize;
int iLTRow = (int)vLeftTop.y / iTileSize;
int iLTIdx = (m_iTileX * iLTRow) + iLTCol;
int iClientWidth = (int)vResolution.x / iTileSize + 1;
int iClientHeight = (int)vResolution.y / iTileSize + 1;
for (int iCurRow = iLTRow; iCurRow < (iLTRow + iClientHeight); iCurRow++) {
for (int iCurcol = iLTCol; iCurcol < (iLTCol + iClientWidth); iCurcol++) {
if (iCurcol < 0 || m_iTileX <= iCurcol ||
iCurRow < 0 || m_iTileY <= iCurRow) continue;
int iIdx = (m_iTileX * iCurRow) + iCurcol;
vecTile[iIdx]->render(_dc);
}
}
}
//추가적인 최적화 방법 -> 커다란 이미지 하나를 두고 그 부분의 일부분만 잘라서 오면 되지 않나?
void CScene::render_monster(HDC _dc)
{
}
void CScene::DeleteGroup(GROUP_TYPE _eGroup)
{
Safe_Delete_Vec<CObject*>(m_arrObj[(UINT)_eGroup]);
}
void CScene::DeleteAll()
{
for (UINT GroupIdx = 0; GroupIdx < (UINT)GROUP_TYPE::END; GroupIdx++) {
DeleteGroup((GROUP_TYPE)GroupIdx);
}
}
void CScene::CreateTile(UINT _IXCount, UINT _IYCount)
{
DeleteGroup(GROUP_TYPE::TILE);
m_iTileX = _IXCount;
m_iTileY = _IYCount;
//타일 생성
CTexture* pTileTex = CResMgr::GetInstance()->LoadTexture(L"Tile", L"texture\\tera2.bmp");
for (UINT tileIDX = 0; tileIDX < _IYCount; tileIDX++) {
for (UINT tileJDX = 0; tileJDX < _IXCount; tileJDX++) {
CTile* pTile = new CTile();
pTile->SetPos(Vec2((float)(tileJDX * TILE_SIZE), (float)(tileIDX * TILE_SIZE)));
pTile->SetTexture(pTileTex);
AddObject(pTile, GROUP_TYPE::TILE);
}
}
}
void CScene::LoadTile(const wstring& _strRelativePath)
{
wstring strFilePath = CPathMgr::GetInstance()->GetContentPath();
strFilePath += _strRelativePath;
//커널 오브젝트
FILE* pFile = nullptr;
_wfopen_s(&pFile, strFilePath.c_str(), L"rb");
assert(pFile);
//데이터 로드.
UINT xCount = 0;
UINT yCount = 0;
fread(&xCount, sizeof(UINT), 1, pFile);
fread(&yCount, sizeof(UINT), 1, pFile);
CreateTile(xCount, yCount);
//모든 타일들을 개별적으로 저장할 데이터를 로드하게 함.
const vector<CObject*>& vecTile = GetGroupObject(GROUP_TYPE::TILE);
for (size_t tileIdx = 0; tileIdx < vecTile.size(); tileIdx++) {
((CTile*)vecTile[tileIdx])->Load(pFile);
}
fclose(pFile);
}
- 중요한 점은 화면에 표시되는 타일만 렌더링 해준다. 그래서 최적화를 이루어낼 수 있다.
- start함수를 넣어서, Enter이후에 세팅되게 start라는 생명 주기로 돌아간다.
- CScene_Start.h
#pragma once
#include "CScene.h"
class CScene_Start :
public CScene
{
private:
Vec2 m_vForcePos;
float m_fForceRadius;
float m_fCurRadius;
float m_fForce;
bool m_bUseForce;
public:
virtual void update();
//부모로부터 상속받은 가상함수인지, 일반 함수인지 보통은 구별 안되서 virtual씀.
//virtual을 안적어도 가상함수이긴 함... 그래도 명시적으로.
virtual void render(HDC _dc);
virtual void Enter();
virtual void Exit();
public:
void CreateForce();
public:
CScene_Start();
~CScene_Start();
};
- CScene_Start.cpp
#include "pch.h"
#include "CScene_start.h"
#include "CObject.h"
#include "CCore.h"
#include "CTexture.h"
#include "CPlayer.h"
#include "CMonster.h"
#include "CCollisionMgr.h"
#include "CPathMgr.h"
#include "CkeyMgr.h"
#include "CSceneMgr.h"
#include "CCamera.h"
#include "AI.h"
#include "CIdleState.h"
#include "CTraceState.h"
#include "CRigidbody.h"
#include "SelectGDI.h"
#include "CTimeMgr.h"
#include "CGround.h"
CScene_Start::CScene_Start()
: m_bUseForce(false)
, m_fForceRadius(500.f)
, m_fCurRadius(0.f)
, m_fForce(500.f)
{
}
CScene_Start::~CScene_Start()
{
}
void CScene_Start::update()
{
if (KEY_HOLD(KEY::LBTN)) {
m_bUseForce = true;
CreateForce();
}
else {
m_bUseForce = false;
}
for (UINT typeIDX = 0; typeIDX < (UINT)GROUP_TYPE::END; typeIDX++) {
const vector<CObject*>& vecObj = GetGroupObject((GROUP_TYPE)typeIDX);
for (size_t objIDX = 0; objIDX < vecObj.size(); objIDX++) {
if (!vecObj[objIDX]->IsDead()) {
if (m_bUseForce && vecObj[objIDX]->GetRigidbody()) {
Vec2 vDiff = vecObj[objIDX]->GetPos() - m_vForcePos;
float fLen = vDiff.Length();
if (fLen < m_fForceRadius) {
float fRatio = 1.f - (fLen / m_fForceRadius);
float fForce = m_fForce * fRatio;
vecObj[objIDX]->GetRigidbody()->AddForce(vDiff.Normalize() * fForce);
}
}
vecObj[objIDX]->update();
}
}
}
/*
if (KEY_TAP(KEY::ENTER) ) {
ChangeScene(SCENE_TYPE::TOOL);
}
*/
}
void CScene_Start::render(HDC _dc)
{
CScene::render(_dc);
if (!m_bUseForce) return;
SelectGDI gdi1(_dc, BRUSH_TYPE::HOLLOW);
SelectGDI gdi2(_dc, PEN_TYPE::GREEN);
m_fCurRadius += m_fForceRadius * 3.f * fDT;
if (m_fCurRadius > m_fForceRadius) {
m_fCurRadius = 0.f;
}
Vec2 vRenderPos = CCamera::GetInstance()->GetRenderPos(m_vForcePos);
Ellipse(_dc,
(int)(vRenderPos.x - m_fCurRadius),
(int)(vRenderPos.y - m_fCurRadius),
(int)(vRenderPos.x + m_fCurRadius),
(int)(vRenderPos.y + m_fCurRadius)
);
}
void CScene_Start::Enter()
{
Vec2 vResolution = CCore::GetInstance()->GetResolution();
//Object 추가.
//실제 생성된 객체는 플레이어, 주소를 받은 건 부모 클래스.
CObject* pObj = new CPlayer;
pObj->SetPos(Vec2(640.f, 384.f));
pObj->SetScale(Vec2(100.f, 100.f));
pObj->SetName(L"Player");
AddObject(pObj, GROUP_TYPE::PLAYER);
RegisterPlayer(pObj);
CMonster* pMon = CMonFactory::CreateMonster(MON_TYPE::NORMAL, vResolution / 2.f - Vec2(0.f, 300.f));
AddObject(pMon, GROUP_TYPE::MONSTER);
//Enter은 몰라고 update에서는 CreateObject로 해야함...
//CreateObject(pMon, Object_type::MONSTER):
//땅 물체 배치
CObject* pGround = new CGround;
pGround->SetName(L"Ground");
pGround->SetPos(Vec2(640.f, 584.f));
pGround->SetScale(Vec2(200.f, 60.f));
AddObject(pGround, GROUP_TYPE::GROUND);
//충돌 지점.
//Player 그룹과 Monster그룹간의 충돌 체크
//update에서 로직, finalupdate에서 최종 움직임 완료
//이제 새로운 충돌이 발생할수도 있음.
CCollisionMgr::GetInstance()->CheckGroup(GROUP_TYPE::PLAYER, GROUP_TYPE::MONSTER);
CCollisionMgr::GetInstance()->CheckGroup(GROUP_TYPE::PROJ_PLAYER, GROUP_TYPE::MONSTER);
CCollisionMgr::GetInstance()->CheckGroup(GROUP_TYPE::GROUND, GROUP_TYPE::PLAYER);
//Camera Look 지점.
CCamera::GetInstance()->SetLookAt(vResolution / 2.f);
//Camera 효과 지점.
CCamera::GetInstance()->FadeOut(1.f);
CCamera::GetInstance()->FadeIn(1.f);
start();
}
void CScene_Start::Exit()
{
//나갈때 전부 삭제해줘야함.
DeleteAll();
//충돌도 전부 초기화 해주기.
CCollisionMgr::GetInstance()->Reset();
}
void CScene_Start::CreateForce()
{
m_vForcePos = CCamera::GetInstance()->GetRealPos(MOUSE_POS);
}
- CAnimation.h
#pragma once
#include "global.h"
class CAnimator;
class CTexture;
//프레임마다 가지고 있을 정보.
//vLT : 좌상단. vSlice : 자를 영역. fDuration : 각 프레임에서 머무는 시간.
//각 프레임에서 머무는 시간.
struct tAnimFrm {
Vec2 vLT;
Vec2 vSlice;
Vec2 vOffset;
float fDuration;
};
class CAnimation
{
private:
wstring m_strName;
CAnimator* m_pAnimator;
CTexture* m_pTex; //Animation이 사용하는 텍스쳐
vector<tAnimFrm> m_vecFrm; //모든 프레임 정보
int m_iCurFrm; //현재 프레임.
float m_fAccTime; //시간 누적
int m_iFrameCount;
bool m_bFinish; //애니메이션이 끝났음을 알림.
public:
void update();
void render(HDC _dc);
void Create(CTexture* _pTex, Vec2 _vLT, Vec2 _vSliceSize, Vec2 _vStep, float _fDuration, UINT iFrameCount);
public:
void Save(const wstring& _strRelativePath);
void Load(const wstring& _strRelativePath);
public:
const wstring& GetName() { return m_strName; }
bool IsFinish() { return m_bFinish; }
public:
void SetName(const wstring& _strName) { m_strName = _strName; }
void SetFrame(int _iframeIDX) {
m_bFinish = false;
m_iCurFrm = _iframeIDX;
m_fAccTime = 0.f;
}
tAnimFrm& GetFrame(int _iIDX) {
return m_vecFrm[_iIDX];
}
int GetMaxFrame() { return static_cast<UINT>(m_vecFrm.size()); }
public:
CAnimation();
~CAnimation();
friend class CAnimator;
};
- CAnimation.cpp
#include "pch.h"
#include "CAnimation.h"
#include "CAnimator.h"
#include "CTimeMgr.h"
#include "CTexture.h"
#include "CObject.h"
#include "CCamera.h"
#include "CPathMgr.h"
#include "CResMgr.h"
CAnimation::CAnimation()
:m_pAnimator(nullptr)
,m_pTex(nullptr)
,m_iCurFrm(0)
,m_fAccTime(0.f)
,m_bFinish(false)
,m_iFrameCount(0)
{
}
//발쪽으로 애니메이션을 맞춤...
//탑뷰 형태의 게임은 발쪽으로. 따라서 총알또한 그만큼 아래로 내려줌...
CAnimation::~CAnimation()
{
}
void CAnimation::update()
{
if (m_bFinish) return;
m_fAccTime += fDT;
//프레임이 계속 돌아가는데, 만약 내가 한 번 재생만 원한다면?
if (m_vecFrm[m_iCurFrm].fDuration < m_fAccTime) {
//일시 정지해서 fDT가 2.344면 프레임 Duration 뺀 값을 넣는게 맞지 않나?
m_iCurFrm++;
if (m_vecFrm.size() <= m_iCurFrm) {
//끝났다는 의미의 -1
m_iCurFrm = -1;
m_bFinish = true;
m_fAccTime = 0.f;
return;
}
m_fAccTime -= m_vecFrm[m_iCurFrm].fDuration;
}
}
void CAnimation::render(HDC _dc)
{
if (m_bFinish) return;
CObject* pObj = m_pAnimator->GetObj();
Vec2 vPos = pObj->GetPos();
vPos += m_vecFrm[m_iCurFrm].vOffset; // Object Position에 Offset만큼 추가 이동 위치.
vPos = CCamera::GetInstance()->GetRenderPos(vPos);
TransparentBlt(_dc,
(int)(vPos.x - m_vecFrm[m_iCurFrm].vSlice.x / 2.f),
(int)(vPos.y - m_vecFrm[m_iCurFrm].vSlice.x / 2.f),
(int)(m_vecFrm[m_iCurFrm].vSlice.x),
(int)(m_vecFrm[m_iCurFrm].vSlice.y),
m_pTex->GetDC(),
(int)(m_vecFrm[m_iCurFrm].vLT.x),
(int)(m_vecFrm[m_iCurFrm].vLT.y),
(int)(m_vecFrm[m_iCurFrm].vSlice.x),
(int)(m_vecFrm[m_iCurFrm].vSlice.y),
RGB(255,0,255)
);
int a = 0;
}
void CAnimation::Create(CTexture* _pTex, Vec2 _vLT, Vec2 _vSliceSize, Vec2 _vStep, float _fDuration, UINT iFrameCount)
{
m_pTex = _pTex;
tAnimFrm frm = {};
for (UINT frameIDX = 0; frameIDX < iFrameCount; frameIDX++) {
frm.fDuration = _fDuration;
frm.vLT = _vLT + _vStep * (float)frameIDX;
frm.vSlice = _vSliceSize;
m_vecFrm.push_back(frm);
}
}
void CAnimation::Save(const wstring& _strRelativePath)
{
wstring strFilePath = CPathMgr::GetInstance()->GetContentPath();
strFilePath += _strRelativePath;
FILE* pFile = nullptr;
_wfopen_s(&pFile, strFilePath.c_str(), L"wb");
assert(pFile);
SaveWstring(m_strName, pFile);
// Animation이 사용하는 텍스쳐
SaveWstring(m_pTex->GetKey(), pFile);
SaveWstring(m_pTex->GetRelativePath(), pFile);
// 프레임 개수
size_t iFrameCount = m_vecFrm.size();
fwrite(&iFrameCount, sizeof(size_t), 1, pFile);
// 모든 프레임 정보
fwrite(m_vecFrm.data(), sizeof(tAnimFrm), iFrameCount, pFile);
fclose(pFile);
}
void CAnimation::Load(const wstring& _strRelativePath)
{
wstring strFilePath = CPathMgr::GetInstance()->GetContentPath();
strFilePath += _strRelativePath;
FILE* pFile = nullptr;
_wfopen_s(&pFile, strFilePath.c_str(), L"rb");
assert(pFile);
LoadWstring(m_strName, pFile);
// 텍스쳐
wstring strTexKey, strTexPath;
LoadWstring(strTexKey, pFile);
LoadWstring(strTexPath, pFile);
m_pTex = CResMgr::GetInstance()->LoadTexture(strTexKey, strTexPath);
// 프레임 개수
size_t iFrameCount = 0;
fread(&iFrameCount, sizeof(size_t), 1, pFile);
// 모든 프레임 정보
m_vecFrm.resize(iFrameCount);
fread(m_vecFrm.data(), sizeof(tAnimFrm), iFrameCount, pFile);
fclose(pFile);
}
- 파일포인터를 이용한 저장, 로드 기능을 구현했다.
- CRigidbody.h
#pragma once
class CObject;
class CRigidbody
{
private:
CObject* m_pOwner; //collider를 소유하고 있는 오브젝트.
Vec2 m_vForce; //크기, 방향.
Vec2 m_vAccel; //가속도.
Vec2 m_vAccelA; //가속도 추가.
Vec2 m_vVelocity; //속도. (크기 : 속력, 방향)
Vec2 m_vMaxVelocity; //최대 속력.
float m_fMass; //강체의 질량.
float m_fFricCoeff; //마찰 계수.
public:
void finalupdate();
private:
void update_gravity();
public:
void AddForce(Vec2 _vF) { m_vForce += _vF; }
void SetMass(float _fMass) { m_fMass = _fMass; }
float GetMass() { return m_fMass; }
float GetSpeed() { return m_vVelocity.Length(); }
Vec2 GetVelocity() { return m_vVelocity; }
void SetVelocity(Vec2 _v) { m_vVelocity = _v; }
void AddVelocity(Vec2 _v) { m_vVelocity += _v; }
void SetMaxVelocity(Vec2 _vVelocity) { m_vMaxVelocity = _vVelocity; }
void SetAccelAlpha(Vec2 _vAccel){ m_vAccelA = _vAccel; }
public:
CRigidbody();
~CRigidbody();
private:
void Move();
public:
friend class CObject;
};
- CRigidbody.cpp
#include "pch.h"
#include "CRigidbody.h"
#include "CTimeMgr.h"
#include "CObject.h"
CRigidbody::CRigidbody()
: m_pOwner(nullptr)
, m_fMass(1.f)
, m_fFricCoeff(150.f)
, m_vMaxVelocity(Vec2(200.f, 200.f))
{
}
CRigidbody::~CRigidbody()
{
}
void CRigidbody::finalupdate()
{
float fForce = m_vForce.Length(); //힘의 크기
if (0.f != fForce) {
m_vForce.Normalize();
float m_fAccel = fForce / m_fMass; //최종적인 가속도.
m_vAccel = m_vForce * m_fAccel;
}
m_vAccel += m_vAccelA; //추가 가속도
m_vVelocity += m_vAccel * fDT; //속도.
//마찰력에 의한 반대방향으로의 가속도 적용.
if (!m_vVelocity.IsZero()) {
Vec2 vFircDir = -m_vVelocity;
vFircDir.Normalize();
Vec2 vFriction = vFircDir * m_fFricCoeff * fDT;
if (m_vVelocity.Length() <= vFriction.Length()) {
//마찰 가속도가 본래 속도보다 더 큰 경우.
m_vVelocity = Vec2(0.f, 0.f);
}
else {
m_vVelocity += vFriction;
}
}
//속도 제한 검사.
//축 별로 따로 검사해야함.
//X축 속도 제한 검사.
if (m_vMaxVelocity.x < abs(m_vVelocity.x)) {
m_vVelocity.x /= abs(m_vVelocity.x);
m_vVelocity.x *= m_vMaxVelocity.x;
}
//Y축 속도 제한 검사.
if (m_vMaxVelocity.y < abs(m_vVelocity.y)) {
m_vVelocity.y /= abs(m_vVelocity.y);
m_vVelocity.y *= m_vMaxVelocity.y;
}
//속도에 따른 이동.
Move();
//힘 초기화.
m_vForce = Vec2(0.f, 0.f);
//가속도 초기화
m_vAccel = Vec2(0.f, 0.f);
//추가 가속도 초기화
m_vAccelA = Vec2(0.f, 0.f);
}
void CRigidbody::update_gravity()
{
AddForce(Vec2(0.f, 500.f));
}
void CRigidbody::Move()
{
float fSpeed = m_vVelocity.Length();
if (0.f != fSpeed) {
Vec2 vPos = m_pOwner->GetPos();
vPos += m_vVelocity * fDT;
m_pOwner->SetPos(vPos);
}
}
- 힘기반 움직임(마찰계수)를 포함하여, 움직인다.
- Gravity는 구현은 되어 있으나, 우리 게임에서 사용하질 않아서.. 소스만 올릴 예정이다.
- AI.h
#pragma once
class CMonster;
class CState;
class AI
{
private:
map<MON_STATE, CState*> m_mapstate;
//STATE 패턴
CMonster* m_pOwner;
CState* m_pCurState; //몬스터의 현재 상태.
public:
void update();
public:
void AddState(CState* _pState);
CState* GetState(MON_STATE _eState);
CMonster* GetOwner() { return m_pOwner; }
void ChangeState(MON_STATE _eNextState);
void SetCurState(MON_STATE _eState);
public:
AI();
~AI();
friend class CMonster;
};
- AI.cpp
#include "pch.h"
#include "AI.h"
#include "CState.h"
AI::AI()
: m_pCurState(nullptr)
, m_pOwner(nullptr)
{
}
AI::~AI()
{
Safe_Delete_Map(m_mapstate);
}
void AI::update()
{
m_pCurState->update();
}
void AI::AddState(CState* _pState)
{
CState* pState = GetState(_pState->GetType());
assert(!pState);
m_mapstate.insert(make_pair(_pState->GetType(), _pState));
_pState->m_pAI = this;
}
CState* AI::GetState(MON_STATE _eState)
{
auto mapIter = m_mapstate.find(_eState);
if (mapIter == m_mapstate.end()) {
return nullptr;
}
return mapIter->second;
}
void AI::ChangeState(MON_STATE _eNextState)
{
CState* pNextState = GetState(_eNextState);
assert(m_pCurState != pNextState);
m_pCurState->Exit();
m_pCurState = pNextState;
m_pCurState->Enter();
}
void AI::SetCurState(MON_STATE _eState)
{
m_pCurState = GetState(_eState);
assert(m_pCurState);
}
- State 패턴(디자인 패턴)으로 이루어져 있다.
- CState.h, cpp
#pragma once
class AI;
class CMonster;
class CState
{
private:
AI* m_pAI;
MON_STATE m_eState;
public:
AI* GetAI() { return m_pAI; }
MON_STATE GetType() { return m_eState; }
CMonster* GetMonster();
public:
virtual void update() = 0;
virtual void Enter() = 0;
virtual void Exit() = 0;
public:
CState(MON_STATE _eState);
virtual ~CState();
friend class AI;
};
#include "pch.h"
#include "CState.h"
#include "CMonster.h"
#include "AI.h"
CState::CState(MON_STATE _eState)
:m_eState(_eState)
{
}
CState::~CState()
{
}
__forceinline CMonster* CState::GetMonster()
{
return m_pAI->GetOwner();
}
- CIdleState.h, cpp
#pragma once
#include "CState.h"
class CIdleState :
public CState
{
private:
public:
virtual void update();
virtual void Enter();
virtual void Exit();
public:
CIdleState();
~CIdleState();
};
#include "pch.h"
#include "CIdleState.h"
#include "CSceneMgr.h"
#include "CScene.h"
#include "CPlayer.h"
#include "CMonster.h"
CIdleState::CIdleState()
: CState(MON_STATE::IDLE)
{
}
CIdleState::~CIdleState()
{
}
void CIdleState::update()
{
CPlayer* pPlayer = (CPlayer*)CSceneMgr::GetInstance()->GetCurScene()->GetPlayer();
// 가만히 있는다.
Vec2 vPlayerPos = pPlayer->GetPos();
CMonster* pMonster = GetMonster();
Vec2 vMonsterPos = pMonster->GetPos();
Vec2 vDiff = vPlayerPos - vMonsterPos;
float fLen = vDiff.Length();
//플레이어가 몬스터의 인식범위 안으로 진입.
if (fLen < pMonster->Getinfo().m_fRecogRange) {
ChangeAIState(GetAI(), MON_STATE::TRACE);
}
// 플레이어의 위치를 받아와서 몬스터의 범위 안에 들어오면
// 추적 상대료 전환.
}
void CIdleState::Enter()
{
}
void CIdleState::Exit()
{
}
- 플레이어 인포에 있는 일정 거리 안에 있다면 Trace로 전환한다.
- CTraceState.h
#pragma once
#include "CState.h"
class CTraceState :
public CState
{
private:
public:
virtual void update();
virtual void Enter();
virtual void Exit();
public:
CTraceState();
~CTraceState();
};
- CTraceState.cpp
#include "pch.h"
#include "CTraceState.h"
#include "CSceneMgr.h"
#include "CScene.h"
#include "CPlayer.h"
#include "CMonster.h"
#include "CTimeMgr.h"
CTraceState::CTraceState()
: CState(MON_STATE::TRACE)
{
}
CTraceState::~CTraceState()
{
}
void CTraceState::update()
{
//타겟팅 된 Player를 쫒아간다.
CPlayer* pPlayer = (CPlayer*)CSceneMgr::GetInstance()->GetCurScene()->GetPlayer();
Vec2 vPlayerPos = pPlayer->GetPos();
CMonster* pMonster = GetMonster();
Vec2 vMonsterPos = pMonster->GetPos();
Vec2 vMonDir = vPlayerPos - vMonsterPos;
vMonDir.Normalize();
vMonsterPos += vMonDir * pMonster->Getinfo().m_fSpeed* fDT;
pMonster->SetPos(vMonsterPos);
}
void CTraceState::Enter()
{
}
void CTraceState::Exit()
{
}
- CMonFactory.h
#pragma once
class CMonster;
enum class MON_TYPE {
NORMAL,
RANGE,
};
class CMonFactory
{
public:
static CMonster* CreateMonster(MON_TYPE _eType, Vec2 _vPos);
private:
private:
CMonFactory() {}
~CMonFactory() {}
};
- CMonFactory.cpp
#include "pch.h"
#include "CMonFactory.h"
#include "CRigidbody.h"
#include "CMonster.h"
#include "AI.h"
#include "CIdleState.h"
#include "CTraceState.h"
CMonster* CMonFactory::CreateMonster(MON_TYPE _eType, Vec2 _vPos)
{
CMonster* pMon = nullptr;
switch (_eType) {
case MON_TYPE::NORMAL:
{
pMon = new CMonster;
pMon->SetPos(_vPos);
tMonInfo info = {};
info.m_fAtt = 1;
info.m_fAttRange = 50.f;
info.m_fRecogRange = 300.f;
info.m_iHP = 100;
info.m_fSpeed = 150.f;
pMon->SetMonInfo(info);
pMon->CreateRigidBody();
pMon->GetRigidbody()->SetMass(1.f);
AI* pAI = new AI;
pAI->AddState(new CIdleState);
pAI->AddState(new CTraceState);
pAI->SetCurState(MON_STATE::IDLE);
pMon->SetAI(pAI);
}
break;
case MON_TYPE::RANGE:
break;
}
assert(pMon);
return pMon;
}
-
전체적으로 이번엔 FSM 기반 AI, 렌더링 최적화, Rigidbody(강체), 애니메이션 저장, 로드, 등을 구현하였다.
-
아래에 엔진 프레임워크 깃허브 구현 링크가 있다. WINAPI 엔진 프레임워크
댓글 남기기