본문으로 바로가기

200810_TopDownShooting.zip
2.33MB

 

 

1. Player

  a. Head, Body 구분하여 Animation 출력

[ Isaac class (Player) ]

더보기
#pragma once

#include "Framework/Math/Transform.h"
#include "Object/GameObject/Isaac_Head.h"
#include "Object/GameObject/Isaac_Body.h"
#include "Object/GameObject/Bullet.h"

class Isaac
{
public:
	Isaac_Head* head;
	Isaac_Body* body;
	//
	vector<Bullet*> bullets;
	int bulletCount;
	//
	Vector2 moveDir;
	Vector2 dir;
	//
	float speed;
	bool isAttack;

public:
	Isaac();
	~Isaac();
	//
	void Init();
	//
	void Update();
	void Render();
	void PostRender();
	//
	void Control();
	void Move();
	void Attack();
	void SetDir();
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "Isaac.h"

Isaac::Isaac()
	:speed(400.0f), bulletCount(50), moveDir(0, 0), dir(0, 0),
	isAttack(false)

{
	Init();
}

Isaac::~Isaac()
{
	delete head;
	delete body;
	//
	for (auto bullet : bullets)
		delete bullet;
	bullets.clear();
}

void Isaac::Init()
{
	head = new Isaac_Head;
	body = new Isaac_Body;
	//
	Bullet* bullet{};
	for (int i = 0; i < bulletCount; ++i)
	{
		bullet = new Bullet;
		bullet->isActive = false;
		bullets.emplace_back(bullet);
	}
}

void Isaac::Update()
{
	Control();
	//
	head->Update();
	body->Update();
	//
	for (auto bullet : bullets)
		bullet->Update();
}

void Isaac::Render()
{
	body->Render();
	head->Render();
	//
	for (auto bullet : bullets)
		bullet->Render();
}

void Isaac::PostRender()
{
	ImGui::Text("[Isaac]");
	ImGui::Text("Dir : %.3f, %.3f", dir.x, dir.y);
	ImGui::Text("[Head]");
	ImGui::SliderFloat2("headPos", (float*)&head->pos, 0.0f, 1000.0f);
	ImGui::Text("[Body]");
	ImGui::SliderFloat2("bodyPos", (float*)&body->pos, 0.0f, 1000.0f);
}

void Isaac::Control()
{
	SetDir();
	Attack();
	Move();
}

void Isaac::Move()
{
	if (isAttack)
		return;
	//
	head->Move(moveDir, speed);
	body->Move(moveDir, speed);
}

void Isaac::Attack()
{
	if (KEY_DOWN(VK_LBUTTON))
	{
		isAttack = true;
		for (auto bullet : bullets)
		{
			if (bullet->isActive)
				continue;
			//
			bullet->Fire(dir, head->pos);
			return;
		}
	}
	if (KEY_UP(VK_LBUTTON))
		isAttack = false;
}

void Isaac::SetDir()
{
	moveDir = {};
	if (KEY_PRESS('D'))
	{
		moveDir.x = 1.0f;
		dir.x = 1.0f;
	}
	if (KEY_PRESS('A'))
	{
		moveDir.x = -1.0f;
		dir.x = -1.0f;
	}
	if (KEY_PRESS('W'))
	{
		moveDir.y = 1.0f;
		dir.y = 1.0f;
	}
	if (KEY_PRESS('S'))
	{
		moveDir.y = -1.0f;
		dir.y = -1.0f;
	}
	//
	if (KEY_UP('A') || KEY_UP('D'))
		dir.x = 0;
	if (KEY_UP('W') || KEY_UP('S'))
		dir.y = 0;
}
/////////////////////////////////////////////////////////////////////////////////////////////

[ Isaac_Head class ]

더보기
#pragma once

#include "Framework/Math/Transform.h"

class Isaac_Head : public Transform
{
public:
	enum ActionType
	{
		HEAD_LEFT,
		HEAD_UP,
		HEAD_RIGHT,
		HEAD_DOWN,
	};
	//
public:
	Sprite* sprite;
	//
	vector<Action*> actions;
	int curAction;
	//
public:
	Isaac_Head();
	~Isaac_Head();
	//
	void Update();
	void Render();
	//
	void LoadAction(string file, Action::Type type, float speed = 0.1f);
	//
	void SetAction(int type);
	//
	void Move(Vector2 moveDir, float speed);
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "Isaac_Head.h"

Isaac_Head::Isaac_Head()
	:curAction(HEAD_DOWN)
{
	sprite = new Sprite;
	string path = "Textures/Isaac/";
	LoadAction(path + "Isaac_Head", Action::LOOP);
}

Isaac_Head::~Isaac_Head()
{
	delete sprite;
	//
	for (auto action : actions)
		delete action;
	actions.clear();
}

void Isaac_Head::Update()
{
	Action::Clip curClip = actions[curAction]->GetCurClip();
	sprite->SetAction(curClip);
	actions[curAction]->Update();
	sprite->Update();
	scale = curClip.size;
	UpdateWorld();
}

void Isaac_Head::Render()
{
	ALPHA_BLEND_STATE->SetState();
	SetWorldBuffer();
	sprite->Render();
}

void Isaac_Head::LoadAction(string file, Action::Type type, float speed)
{
	XmlDocument* document = new XmlDocument();
	string xmlfile = file + ".xml";
	document->LoadFile(xmlfile.c_str());
	//
	XmlElement* atlas = document->FirstChildElement();
	//
	string pngfile = file + ".png";
	wstring imagePath{};
	imagePath.assign(pngfile.begin(), pngfile.end()); // string -> wstring
	//
	Texture* texture = Texture::Add(imagePath);
	//
	XmlElement* action = atlas->FirstChildElement();
	for (; action != nullptr; action = action->NextSiblingElement())
	{
		vector<Action::Clip> clips{};
		XmlElement* sprite = action->FirstChildElement();
		//
		while (sprite != nullptr)
		{
			float x{}, y{}, w{}, h{};
			x = sprite->FloatAttribute("x");
			y = sprite->FloatAttribute("y");
			w = sprite->FloatAttribute("w");
			h = sprite->FloatAttribute("h");
			clips.emplace_back(x, y, w, h, texture);
			//
			sprite = sprite->NextSiblingElement();
		}
		//
		actions.emplace_back(new Action(clips, type, speed));
	}
	//
	delete document;
}

void Isaac_Head::SetAction(int type)
{
	if (curAction != type)
	{
		curAction = type;
		actions[curAction]->Play();
	}
}

void Isaac_Head::Move(Vector2 moveDir, float speed)
{
	pos.x += moveDir.x * speed * DELTA;
	pos.y += moveDir.y * speed * DELTA;
	//
	if (moveDir.x == 1.0f)
		SetAction(HEAD_RIGHT);
	else if (moveDir.x == -1.0f)
		SetAction(HEAD_LEFT);
	if (moveDir.y == 1.0f)
		SetAction(HEAD_UP);
	else if (moveDir.y == -1.0f)
		SetAction(HEAD_DOWN);
}

[ Isaac_Body class ]

더보기
#pragma once

#include "Framework/Math/Transform.h"

class Isaac_Body : public Transform
{
public:
	enum ActionType
	{
		BODY_IDLE_H,
		BODY_MOVE_H,
		BODY_IDLE_V,
		BODY_MOVE_V,
	};
	//
public:
	Sprite* sprite;
	//
	vector<Action*> actions;
	int curAction;
	//
	bool isRight;
	//
public:
	Isaac_Body();
	~Isaac_Body();
	//
	void Update();
	void Render();
	//
	void LoadAction(string file, Action::Type type, float speed = 0.1f);
	//
	void SetAction(int type);
	//
	void Move(Vector2 moveDir, float speed);
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "Isaac_Body.h"

Isaac_Body::Isaac_Body()
	:curAction(BODY_IDLE_V), isRight(false)
{
	sprite = new Sprite;
	pos = { 0, -15.0f };
	string path = "Textures/Isaac/";
	LoadAction(path + "Isaac_Body", Action::LOOP);
}

Isaac_Body::~Isaac_Body()
{
	delete sprite;
	//
	for (auto action : actions)
		delete action;
	actions.clear();
}

void Isaac_Body::Update()
{
	Action::Clip curClip = actions[curAction]->GetCurClip();
	sprite->SetAction(curClip);
	actions[curAction]->Update();
	sprite->Update();
	scale = curClip.size;
	UpdateWorld();
}

void Isaac_Body::Render()
{
	ALPHA_BLEND_STATE->SetState();
	SetWorldBuffer();
	sprite->Render();
}

void Isaac_Body::LoadAction(string file, Action::Type type, float speed)
{
	XmlDocument* document = new XmlDocument();
	string xmlfile = file + ".xml";
	document->LoadFile(xmlfile.c_str());
	//
	XmlElement* atlas = document->FirstChildElement();
	//
	string pngfile = file + ".png";
	wstring imagePath{};
	imagePath.assign(pngfile.begin(), pngfile.end()); // string -> wstring
	//
	Texture* texture = Texture::Add(imagePath);
	//
	XmlElement* action = atlas->FirstChildElement();
	for (; action != nullptr; action = action->NextSiblingElement())
	{
		vector<Action::Clip> clips{};
		XmlElement* sprite = action->FirstChildElement();
		//
		while (sprite != nullptr)
		{
			float x{}, y{}, w{}, h{};
			x = sprite->FloatAttribute("x");
			y = sprite->FloatAttribute("y");
			w = sprite->FloatAttribute("w");
			h = sprite->FloatAttribute("h");
			clips.emplace_back(x, y, w, h, texture);
			//
			sprite = sprite->NextSiblingElement();
		}
		//
		actions.emplace_back(new Action(clips, type, speed));
	}
	//
	delete document;
}

void Isaac_Body::SetAction(int type)
{
	if (curAction != type)
	{
		curAction = type;
		actions[curAction]->Play();
	}
}

void Isaac_Body::Move(Vector2 moveDir, float speed)
{
	if (moveDir.x == -1)
	{
		if (isRight)
		{
			rot.y = PI;
			isRight = false;
		}
	}
	if (moveDir.x == 1)
	{
		if (!isRight)
		{
			rot.y = 0;
			isRight = true;
		}
	}
	//
	pos.x += moveDir.x * speed * DELTA;
	pos.y += moveDir.y * speed * DELTA;
	//
	if (moveDir.x == 1.0f || moveDir.x == -1.0f)
		SetAction(BODY_MOVE_H);
	else if (moveDir.y == 1.0f || moveDir.y == -1.0f)
		SetAction(BODY_MOVE_V);
	//
	if (KEY_UP('D') || KEY_UP('A'))
		SetAction(BODY_IDLE_H);
	else if (KEY_UP('W') || KEY_UP('S'))
		SetAction(BODY_IDLE_V);
}

 

2. Bullet

  a. 오브젝트 풀링, Player에 포함되어 Scene에서 충돌 처리

더보기
#pragma once

#include "Framework/Math/Transform.h"
#include "Framework/Animation/Action.h"

class Bullet : public Transform
{
private:
	Sprite* sprite;
	Vector2 dir;
	//
	Action::Clip curClip;
	//
	float speed;
	float lifeTime;
	float lifeTimeCounter;
	//
	Collider* collider;

public:
	Bullet();
	~Bullet();
	//
	void Update();
	void Render();
	//
	void Fire(Vector2 dir, Vector2 pos);
	void Move();
	void Hit();
	//
	void LifeTime();
	Collider* GetCollider() { return collider; }
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "Bullet.h"

Bullet::Bullet()
	:dir(0, 0), speed(500.0f), lifeTime(3.0f), lifeTimeCounter(0)
{
	sprite = new Sprite;
	sprite->SetTexture(L"Textures/Isaac/Bullet.png");
	/* SetClip */
	curClip.startPos = { 0,0 };
	curClip.size = { sprite->GetSize().x, sprite->GetSize().y };
	curClip.texture = sprite->GetTexture();
	//
	collider = new RectCollider(sprite->GetSize(), this);
	collider->isActive = false;
}

Bullet::~Bullet()
{
	delete sprite;
	delete collider;
}

void Bullet::Update()
{
	if (!isActive) return;
	//
	Move();
	LifeTime();
	//
	sprite->SetAction(curClip);
	sprite->Update();
	scale = sprite->GetSize();
	UpdateWorld();
	//
	collider->Update();
}

void Bullet::Render()
{
	if (!isActive) return;

	ALPHA_BLEND_STATE->SetState();
	//
	SetWorldBuffer();
	//
	sprite->Render();
	//
	collider->Render();
}

void Bullet::Fire(Vector2 dir, Vector2 pos)
{
	if (dir.x == 0 && dir.y == 0)
		return;
	//
	isActive = true;
	//
	collider->isActive = true;
	//
	this->dir = dir;
	this->pos = pos;
}

void Bullet::Move()
{
	pos.x += dir.x * speed * DELTA;
	pos.y += dir.y * speed * DELTA;
}

void Bullet::Hit()
{
	isActive = false;
	EFFECT->Play("hit", pos);
}

void Bullet::LifeTime()
{
	lifeTimeCounter += DELTA;
	if (lifeTimeCounter >= lifeTime)
	{
		lifeTimeCounter = 0;
		isActive = false;
	}
}

 

2. Enemy

  a. Move 애니메이션, 패턴 구현

  b. Scene에서 vector Container로 관리

더보기
#pragma once

#include "Framework/Math/Transform.h"

class Enemy : public Transform
{
public:
	enum ActionType
	{
		DOWN,
		RIGHT,
		UP,
	};
	//
public:
	Sprite* sprite;
	//
	vector<Action*> actions;
	int curAction;
	//
	int curPattern;
	float patternTime;
	float patternCounter;
	//
	Vector2 moveDir;
	float speed;
	//
	Collider* collider;
	//
public:
	Enemy();
	~Enemy();
	//
	void Update();
	void Render();
	//
	void LoadAction(string file, Action::Type type, float speed = 0.1f);
	//
	void SetAction(int type);
	//
	void Move();
	void AIPattern();
	//
	void SetPos(Vector2 value) { pos = value; }
	void Damage();
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "Enemy.h"

Enemy::Enemy()
	:curAction(DOWN), speed(100.0f), moveDir(0, 0),
	patternCounter(0), patternTime(3.0f)

{
	sprite = new Sprite;
	string path = "Textures/Isaac/";
	LoadAction(path + "Enemy", Action::LOOP);
	//
	Vector2 colSize = actions[curAction]->GetCurClip().size;
	collider = new RectCollider(colSize, this);
}

Enemy::~Enemy()
{
	delete collider;
	//
	for (auto action : actions)
		delete action;
	actions.clear();
	//
	delete sprite;
}

void Enemy::Update()
{
	if (!isActive) return;
	//
	Action::Clip curClip = actions[curAction]->GetCurClip();
	sprite->SetAction(curClip);
	actions[curAction]->Update();
	sprite->Update();
	scale = curClip.size;
	UpdateWorld();
	//
	AIPattern();
	Move();
	//
	collider->Update();
}

void Enemy::Render()
{
	ALPHA_BLEND_STATE->SetState();
	SetWorldBuffer();
	sprite->Render();
	//
	collider->Render();
}

void Enemy::LoadAction(string file, Action::Type type, float speed)
{
	XmlDocument* document = new XmlDocument();
	string xmlfile = file + ".xml";
	document->LoadFile(xmlfile.c_str());
	//
	XmlElement* atlas = document->FirstChildElement();
	//
	string pngfile = file + ".png";
	wstring imagePath{};
	imagePath.assign(pngfile.begin(), pngfile.end()); // string -> wstring
	//
	Texture* texture = Texture::Add(imagePath);
	//
	XmlElement* action = atlas->FirstChildElement();
	for (; action != nullptr; action = action->NextSiblingElement())
	{
		vector<Action::Clip> clips{};
		XmlElement* sprite = action->FirstChildElement();
		//
		while (sprite != nullptr)
		{
			float x{}, y{}, w{}, h{};
			x = sprite->FloatAttribute("x");
			y = sprite->FloatAttribute("y");
			w = sprite->FloatAttribute("w");
			h = sprite->FloatAttribute("h");
			clips.emplace_back(x, y, w, h, texture);
			//
			sprite = sprite->NextSiblingElement();
		}
		//
		actions.emplace_back(new Action(clips, type, speed));
	}
	//
	delete document;
}

void Enemy::SetAction(int type)
{
	if (curAction != type)
	{
		curAction = type;
		actions[curAction]->Play();
	}
}

void Enemy::Move()
{
	pos.x += moveDir.x * speed * DELTA;
	pos.y += moveDir.y * speed * DELTA;
	//
	if(moveDir.x == -1)
		rot.y = PI;
	else
		rot.y = 0;
}

void Enemy::AIPattern()
{
	patternCounter += DELTA;
	if (patternCounter < patternTime)
		return;
	//
	curPattern = rand() % 4;
	switch (curPattern)
	{
	case 0: // Down
		moveDir = { 0, -1.0f };
		SetAction(DOWN);
		break;
	case 1: // Right
		moveDir = { +1.0f, 0 };
		SetAction(RIGHT);
		break;
	case 2: // Up
		moveDir = { 0, +1.0f };
		SetAction(UP);
		break;
	case 3: // Left
		moveDir = { -1.0f, 0 };
		SetAction(RIGHT);
		break;
	default:
		break;
	}
	patternCounter = 0;
}

void Enemy::Damage()
{
}

 

3. Game Scene

  a. Camera Action ( Shake, Zoom ) 구현

    a-1. Transform* mouseTF를 선언하여 마우스 우클릭 시 Camera로 mouse 위치 Follow

    b-1. a-1 상태에서 마우스 좌클릭시 이펙트 출력 및 Camera 상태 초기화(Player Target Follow)

  b. Enemy <-> Player->Bullet 간 충돌처리

더보기
#pragma once

#include "Object/GameObject/BackGround.h"
#include "Object/GameObject/Isaac.h"
#include "Object/GameObject/Enemy.h"

class EffectScene : public Scene
{
private:
	Isaac* isaac;
	BackGround* bg;
	vector<Enemy*> enemies;
	int enemyCount;
	//
	Transform* mouseTF;
	bool isSnipe;
public:
	EffectScene();
	~EffectScene();
	//
	virtual void Update() override;
	virtual void Render() override;
	virtual void PostRender() override;
	//
	void InitScene();
	//
	void Func();
	void Func1_SetTarget();
	void Func2_FreeTarget();
	void Func3_SnipeMode();
	//
	void SetMouseTF();
	//
	void Collision();
	void CollisionBulletToEnemy();
};
/////////////////////////////////////////////////////////////////////////////////////////////
#include "Framework.h"
#include "EffectScene.h"

EffectScene::EffectScene()
	:enemyCount(10), isSnipe(false)
{
	InitScene();
}

EffectScene::~EffectScene()
{
	delete mouseTF;
	delete isaac;
	delete bg;
	//
	for (auto enemy : enemies)
		delete enemy;
	enemies.clear();
}

void EffectScene::Update()
{
	bg->Update();
	isaac->Update();
	//
	for (auto enemy : enemies)
		 enemy->Update();
	//
	Collision();
	//
	SetMouseTF();
	//
	Func();
	//
}

void EffectScene::Render()
{
	bg->Render();
	isaac->Render();
	//
	for (auto enemy : enemies)
		 enemy->Render();
}

void EffectScene::PostRender()
{
	ImGui::Text("MousePos : %.3f, %.3f", MOUSE->GetMousePos().x, MOUSE->GetMousePos().y);
	ImGui::Text("MouseTF : %.3f, %.3f", mouseTF->pos.x, mouseTF->pos.y);
	//bg->PostRender();
	isaac->PostRender();
}

void EffectScene::InitScene()
{
	/* Set MouseTransform */
	mouseTF = new Transform;

	/* Set Effects */
	EFFECT->Add("hit", 20, L"Textures/Isaac/Effect_Bullet_Hit.png", 10, 1, 0.05f, true);
	EFFECT->Add("snipe", 1, L"Textures/Isaac/Effect_Bullet_Hit.png", 6, 2, 0.05f, true);
	/* Set Objects */
	isaac = new Isaac;
	bg = new BackGround;
	/* Set Enemy */
	Enemy* enemy{};
	Vector2 tmpPos = { -CENTER_X * 0.5f, CENTER_Y * 0.5f };
	for (int i = 0; i < enemyCount; ++i)
	{
		tmpPos.x += 50.0f;
		//
		enemy = new Enemy;
		enemy->SetPos(tmpPos);
		enemy->isActive = true;
		//
		enemies.emplace_back(enemy);
	}
	/* Set Camera */
	Vector2 bgLeftBottom = bg->GetSize() * -0.5f;
	Vector2 bgRightTop = bg->GetSize() * 0.5f;
	//
	CAMERA->SetLeftBottom(bgLeftBottom);
	CAMERA->SetRightTop(bgRightTop);
	//
	CAMERA->SetTarget((Transform*)(isaac->head));
	//CAMERA->scale *= 1.6f;
}

void EffectScene::Func()
{
	Func1_SetTarget();
	Func2_FreeTarget();
	Func3_SnipeMode();
}

void EffectScene::Func1_SetTarget()
{
	if (KEY_DOWN(VK_F3))
	{
		CAMERA->SetTarget((Transform*)isaac->head);
		Vector2 scale = { 1.0f, 1.0f };
		CAMERA->SetScale(scale);
	}
}

void EffectScene::Func2_FreeTarget()
{
	if (KEY_DOWN(VK_F2))
	{
		CAMERA->SetTarget(nullptr);
		Vector2 scale = { 1.0f, 1.0f };
		CAMERA->SetScale(scale);
	}
}

void EffectScene::Func3_SnipeMode()
{
	if (KEY_DOWN(VK_RBUTTON))
	{
		isSnipe = true;
		CAMERA->SetTarget(mouseTF);
		//Vector2 scale = { 1.0f, 1.0f };
		Vector2 scale = { 3.0f, 3.0f };
		CAMERA->SetScale(scale);
	}
}

void EffectScene::SetMouseTF()
{
	if (!isSnipe)
		return;
	
	/* Mouse Screen Pos To World Pos */
	// 화면 중심을 기준으로 하기 위해 Mouse위치 비율값에 - 0.5f
	float screenRate_X = (MOUSE->GetMousePos().x / (float)WIN_WIDTH) - 0.5f;
	float screenRate_Y = (MOUSE->GetMousePos().y / (float)WIN_HEIGHT) - 0.5f;
	float scaledWidth = ((float)WIN_WIDTH / CAMERA->scale.x);
	float scaledHeight = ((float)WIN_HEIGHT / CAMERA->scale.y);
	Vector2 camOffset = isaac->head->pos;
	mouseTF->pos.x = (scaledWidth * screenRate_X) + camOffset.x;
	mouseTF->pos.y = (scaledHeight * screenRate_Y) + camOffset.y;
	
	// mouseTF->pos = MOUSE->GetMousePos() - CAMERA->pos;
	if (KEY_DOWN(VK_LBUTTON))
	{
		EFFECT->Play("snipe", mouseTF->pos);
		CAMERA->ShakeStart(20.0f, 2.0f, 20.0f);
		//
		CAMERA->SetTarget((Transform*)isaac->head);
		Vector2 scale = { 1.0f, 1.0f };
		CAMERA->SetScale(scale);
		isSnipe = false;
	}
}

void EffectScene::Collision()
{
	CollisionBulletToEnemy();
}

void EffectScene::CollisionBulletToEnemy()
{
	for (auto enemy : enemies)
	{
		if (!enemy->isActive)
	//
			continue;
		//
		for (auto bullet : isaac->bullets)
		{
			if (!bullet->isActive)
				continue;
			//
			if (enemy->collider->IsCollision(bullet->GetCollider()))
			{
				bullet->Hit();
				enemy->Damage();
				return;
			}
		}
	}
}

'DX11 2D Game Programming' 카테고리의 다른 글

DX11_2020_0814_금_Store_FileIO(tsv)_Button  (0) 2020.08.14
DX11_2020_0812_화_OutLine_PSShader  (0) 2020.08.12
DX11_2020_0807_금_Camera  (0) 2020.08.07
DX11_2020_0806_목_FX  (0) 2020.08.05
DX11_2020_0730_목_Animation(XML)  (0) 2020.07.30