컴퓨터 그래픽스 : 수학/게임 수학

[게임 프로그래머 입문 올인원] 게임수학 : 포트리스 모작 #3 (100, 101강)

순정법사 2024.04.16

C. 포트리스 모작 #3

6. 포신과 총알 구현하기

w, s키를 누르면 포신의 각도가 변경되도록 구현해볼것임

 

a. 플레이어 키값에 따른 값 설정하기

clamp : 어떤영역에 포함되기 원할 때 사용하는 함수

 

Player의 Update함수에서 키보드 값에 따라 데이터를 변경해준다

 

b. 포신 값에 따라 UI 설정하기 

UI로 표시해주기 위해 포신의 각도를 업데이트해줄 함수 하나 생성하고

 

움직임에 따라 각도를 다르게 설정해준다

 

업데이트 하는 코드에 넣어줘 매 프레임 실행되게 하면

 

이렇게 포신의각도(빨간색)이 플레이어의 무빙에 따라서 잘 움직이고, 아까 구현한 파워게이지 늘리는 코드도 잘 실행된다

 

c. 총알 만들기

1 ) 기본 총알을 만들기

 

강의에서는 미사일과 몬스터를 지우고 총알을 만들었지만 삭제 안할 예정,,

 

당연하게도 Bullet은 Object를 상속받으며 기본 코드는 생성해줘야 함

 

충돌 판정을 감지하기위해 radius를 생성해줄건데, 충돌판정은 모든 오브젝트가 필요한 부분이니까 Object에서 생성해줌

 

_radius를 생성해주고, 정보를 가져올 GetRadius도 만들어줌

 

2) 위 총알을 생성해주기

 

Player의 Update 부분에서 스페이스바를 떼면 속도를 산정해서 총알을 생성해주기

 

그럼 이렇게 날아가지는 않고 총알이 플레이어위치에서 생성됨

 

d. 총알 특성 구현하기

1) 스피드 구현하기

 

스피드를 생성하고 get,set 함수를 구현해주기

 

총알의 스피드에 따라서 포지션을 변경하는 코드를 추가, 어느 범위가 넘어가면 소멸시키는 코드도 생성해준다

 

여기서 총알이 소멸되면, 턴을 종료시켜야하는데 

턴을 종료시키는 코드는 ChangePlayerTurn에 있음

 

➡ 따라서 현재 씬을 가져오는 코드를 생성해줘야함

 

SceneManager에서 현재 씬을 가져오는 코드를 생성해주고 (class Scene으로 전방선언해주자)

 

다시 돌아와서 만들어준 씬을 포트리스씬으로 만들어주고 그 안의 함수를 실행해주자

 

스피드값이 아직 설정된적이 없으니, 입력값으로 받은 데이터를 바탕으로 스피드값을 세팅해줌 (-1인 이유는 각도가 반대로 설정되어있음)

 

파워에 따라 포탄이 나가는 속도가 달라지고, 일정 위치가 넘어가면 바로 다음 씬으로 넘어가게 됨!

 

2) 포물선 구현하기

 

👉 먼저 속도와 가속도에 대해서 공부하기

 

[게임 프로그래머 입문 올인원] 게임수학 : 속도와 가속도 (101강)

A. 속도와 가속도 : 물리 1. 속도와 가속도의 기초 a. 속도와 가속도란? 👉 근데 게임에서는 여기까지 알 필요는 없고 그냥 속도와 가속도의 공식만 알고있으면 됨 출처 :https://www.inflearn.com/course/%E

monamu.tistory.com

 

위에서 공부했듯, 가속도에 대한 개념을 가지고 포물선을 구현함

 

중력이 가속도, 시간은 deltatime이니 속도 변화를 주면 됨

 

중력 코드는 이와같이 만들 수 있음

 

포물선을 그리며 잘 날아간다!

 

3) 바람 영향 구현하기

 

중력 코드와 마찬가지로 바람은 x방향에 영향을 주면 끝

 

x축이 바람의 영향을 받게 설정해준다

 

바람이 들어가니까 엄청 어려워졌다,,

 

4) 소유주 생성하기

 

소유주는 총알뿐만 아니라 펫같은 컨텐츠에서 꼭 필요한 개념

내 총알이 나를 쏘면 안되니, 충돌처리하기 전에 만들어주자

 

소유주 생성하고 get,set함수 작성

 

player가 스페이스바를 누르면 bullet이 생성되니, 여기서 소유주를 설정해주기

 

 

7. 충돌처리하기

a. 총알의 충돌감지

bullet이 update될 때 충돌처리를 해야함

 

플레이어일때, 소유주가 아닐때를 예외처리하고 충돌하면 턴완료하는 코드를 작성해준다

 

 

8. 나머지 부분 구현해주기

a. 스테미너 UI 구현하기 

Update 함수부분 :플레이어가 움직이면 스테미너가 줄고, 없으면 움직이지 못하게 코드를 작성

 

스테미너가 잘 줄어든다

 

b. 키에따른 다른 미사일 구현하기

키버튼에 따라 다른 미사일이 나가도록 설정하고

 

당연히 KeyType에서 Key_1, _2를 추가해줘야 함

 

문제점 1) 총알에서 유도탄 구현하기

 

미사일 내부에서 isSpecial 변수를 이용해 미사일 종류에따라 다른 코드를 구현했지만

이렇게되면 나중에 다양한 미사일이 생길 때 마다 코드의 양이 방대해져 유지보수가 어려울것으로 판단해서

예전에 구현한 미사일 파일을 사용해 구현하려고 했다

 

문제점 2) 미사일 추가하기

 

예전에 사용하던 코드는 총알과 다른 부분이 많아서 수정해야 할 코드가 많았음

 

총알과 미사일은 같은 코드를 사용하도록 하고, 미사일에만 추적기능을 넣어주기

 

먼저 미사일을 직선으로 날아가게 설정해주고

 

타겟이 없다면 쭉 날아가다 타겟을 찾아주고, 그 찾은 타겟을 바탕으로 거리를 구해 충돌범위 안에 오면 충돌하는 코드도 작성

 

+ 위 코드에서 당연히 범위가 넘어가면 소멸되는 코드도 작성해준다

 

처음에 충동시 자폭후 return을 안해서 놀래버림,,

 

그럼 속도쪽에서 좀 어색하지만,, 잘 간다 ><

 

<최종 미사일 코드>

 

◽◽◽

#include "pch.h"
#include "Missile.h"
#include "TimeManager.h"
#include "ObjectManager.h"
#include "FortressScene.h"
#include "SceneManager.h"

Missile::Missile() : Object(ObjectType::Projectile)
{

}

Missile::~Missile()
{

}

void Missile::Init()
{
	// TODO : Data	나중에 이 데이터를 통해서 플레이어의 체력을 깎아줘야함
	//_stat.hp = 1;
	//_stat.maxHp = 1;
	_stat.speed = 300;	//유도탄은 처음 0.2초 이후 설정된 속도로 움직임
	_radius = 20.f;	//충돌 범위
}

void Missile::Update()
{
	float deltaTime = GET_SINGLE(TimeManager)->GetDeltaTime();

	//타겟 찾기
	if (_target == nullptr)	//못찾았다면
	{
		//0.2초 전 쭉 날아가기
		_pos += _speed * deltaTime;

		// 0.2초 후에 유도탄
		_sumTime += deltaTime;
		if (_sumTime >= 0.2f)
		{
			//주변에 있는 오브젝트 가져오기
			const vector<Object*>& objects = GET_SINGLE(ObjectManager)->GetObjects();
			for (Object* object : objects)
			{
				if (object->GetObjectType() != ObjectType::Player)	//플레이어가 아니라면 넘어가고
					continue;
				if (object == _owner)	//소유주여도 넘어가기
					continue;

				_target = object;	//상대 Player면 타겟팅
				break;
			}
		}
	}
	else //위 반복문에서 상대 Player를 찾았다면 
	{
		Vector dir = _target->GetPos() - GetPos();	//벡터를 구하려면 상대위치 - 내위치 
		dir.Normalize();	//단위벡터로 만들어주기

		// 유효한 데이터일까?
		_target->SetPos(_target->GetPos());
		_pos += dir * deltaTime * _stat.speed;	//이동해야하는 위치를 단위벡터 * 시간 * 속도로 구하기

		//충돌
		Vector dir2 = _target->GetPos() - GetPos();	
		float dist = sqrt(dir2.x * dir2.x + dir2.y * dir2.y);	//거리공식	

		if (dist < 20) {
			//종료시키는 코드는 포트리스씬의 ChangePlayerTurn에 있음
			auto* scene = dynamic_cast<FortressScene*>(GET_SINGLE(SceneManager)->GetCurrentScene());	
			if (scene)	
				scene->ChangePlayerTurn();	//턴을 완료하기		

			GET_SINGLE(ObjectManager)->Remove(this);	//자폭시키기	
			return;
		}
	}

	// 범위가 넘어가면 소멸
	if (_pos.y > GWinSizeY * 1.5 || _pos.y < -GWinSizeY * 1.5)
	{
		//종료시키는 코드는 포트리스씬의 ChangePlayerTurn에 있음
		auto* scene = dynamic_cast<FortressScene*>(GET_SINGLE(SceneManager)->GetCurrentScene());
		if (scene)
			scene->ChangePlayerTurn();	//턴을 완료하기

		GET_SINGLE(ObjectManager)->Remove(this);	//자폭시키기
		return;
	}
}

void Missile::Render(HDC hdc)
{
	//충돌판정을 감지할 radius
	Utils::DrawCircle(hdc, _pos, static_cast<int32>(_radius));
 }

 

 

9. 코드의 문제점 찾기

a. 오브젝트 매니저

오브젝트 매니저를 사용하는 것 보다 객체를 각 씬에서 들고있는게 더 좋지만

온라인게임에서는 또 오브젝트 매니저가 유용하게 사용된다

 

오브젝트를 업데이트시 소멸처리를 어떻게 할것인지

 

b. UI매니저의 데이터 관리

지금은 코드가 작아서 상관없지만 결국에는 따로 관리해야한다

 

c. 생포인터 사용

소멸시키는 쪽에서 이 포인터가 안전한지 체크되지 않았음 : 스마트 포인터를 사용해야 함

 

 

 


출처 : https://www.inflearn.com/course/%EA%B2%8C%EC%9E%84-%ED%94%84%EB%A1%9C%EA%B7%B8%EB%9E%98%EB%A8%B8-%EC%9E%85%EB%AC%B8-%EC%98%AC%EC%9D%B8%EC%9B%90-rookiss#curriculum

 

[게임 프로그래머 입문 올인원] C++ & 자료구조/알고리즘 & STL & 게임 수학 & Windows API & 게임 서버 -

어디부터 시작할지 막막한 게임 프로그래밍 입문자를 위한 All-In-One 커리큘럼입니다. C++, 자료구조/알고리즘, STL, 게임 수학, Windows API, 게임 서버 입문으로 이어지는 알찬 커리큘럼으로 게임 프

www.inflearn.com