30. 스킬 구현 2 - 아우렐리온 솔 W
*** 공부 방법 ***
1. 코딩을 해야 하는 부분은 첫 부분에 변수나 함수, 메소드에 대한 선언이 코드블럭으로 표시되어 있다. //ex) MakeFunction(); 2. 코드블럭 하단에는 해당 선언에 대한 구현 로직이 작성되어 있다. 처
hyrule.tistory.com
- 이번에도 이미지로 설명이 더 빠를 것 같아 이미지로 설명을 대신한다.
- 해당 내용을 구현해보자.
< 로직 >
- 초기화할 때 주인 포인터를 등록하고, 해당 주인 포인터를 따라다녀야 한다.
- 별들은 모두 거리를 동일하게 유지한 채 회전한다.
- (연습을 위해)Shift + 2 키를 누르면 아우렐리온 솔의 w를 누른 것과 동일한 효과를 발동시킨다.
- 그냥 원을 그려주는 클래스를 CPlayer에서 3개 동적 할당하여 돌리는 방법도 있고,
아예 해당 기능을 담당하는 클래스를 만들어 원하는 게임오브젝트에 붙여주는 방법도 있고
다양한 방법으로 구현 가능하다.
-- 어떻게 하든 똑같은 모습이 나오도록 구현해보자.
//SolBullet.h
#pragma once
#include "GameObject.h"
class CSolBullet :
public CGameObject
{
friend class CScene;
private:
//회전 반경
float m_RotatingRadius;
//회전 속도
float m_RotatingAngleSpeed;
//각도 저장
float m_RotatingAngle;
//주위를 회전할 총알의 갯수와 그에 따른 각도
int m_SolBulletNum;
float m_SolBulletNumAngle;
//실제 표시되는 총알의 중심점 m_BulletNum개
std::vector<Vector2> m_SolBulletCenter;
//스킬 관련 변수들
//시전 중인지 여부
bool m_isCasting;
//시전 방향(현재 확장 중인지 다시 원래로 돌아가는 중인지)
float m_SolSkillDir;
//스킬의 지속 시간
float m_SkillDuration;
//남아있는 지속 시간
float m_SkillDurationLeft;
//회전 속도 최솟값(기본값)
float m_RotatingAngleSpeedMin;
//회전 속도 최댓값
float m_RotatingAngleSpeedMax;
//회전 반경 최솟값
float m_RotatingRadiusMin;
//회전 반경 최댓값
float m_RotatingRadiusMax;
//확대 속도
float m_ExpandSpeed;
protected:
CSolBullet();
CSolBullet(const CSolBullet& Obj);
virtual ~CSolBullet();
public:
bool Init(CGameObject* Obj);
void Update(float DeltaTime);
void Render(HDC hDC, float DeltaTime);
void SetSolBulletNumAngle(int Num);
//실제 스킬 로직 메소드
bool SkillLogic(float DeltaTime);
//이 클래스를 달고있는 클래스에서 스킬을 사용할 때 호출하는 메소드
void EnableSkill()
{
m_isCasting = true;
}
};
//SolBullet.cpp
#include "SolBullet.h"
CSolBullet::CSolBullet()
{
}
CSolBullet::CSolBullet(const CSolBullet& Obj) :
CGameObject(Obj),
m_isCasting(Obj.m_isCasting),
m_SolSkillDir(Obj.m_SolSkillDir),
m_SkillDuration(Obj.m_SkillDuration),
m_SkillDurationLeft(Obj.m_SkillDurationLeft),
m_RotatingAngleSpeedMin(Obj.m_RotatingAngleSpeedMin),
m_RotatingAngleSpeedMax(Obj.m_RotatingAngleSpeedMax),
m_RotatingRadiusMin(Obj.m_RotatingRadiusMin),
m_RotatingRadiusMax(Obj.m_RotatingRadiusMax),
m_ExpandSpeed(Obj.m_ExpandSpeed)
{
}
CSolBullet::~CSolBullet()
{
}
bool CSolBullet::Init(CGameObject* Obj)
{
CGameObject::Init(Obj);
//무조건 주인 오브젝트가 있어야 하므로 혹시나 등록되지 않았으면 false 반환해서 삭제
if (!m_MasterObject)
return false;
m_RotatingRadius = 100.f;
m_RotatingAngleSpeed = 180.f;
m_RotatingAngle = 0.f;
m_isCasting = false;
//2초에 걸쳐서 넓어졌다가 2초에 걸쳐서 돌아온다.
m_RotatingRadiusMin = 100.f;
m_RotatingRadiusMax = 200.f;
m_RotatingAngleSpeedMin = 200.f;
m_RotatingAngleSpeedMax = 400.f;
m_SkillDuration = 5.f;
m_SkillDurationLeft = 5.f;
m_ExpandSpeed = 50.f;
//일단 주위를 회전할 총알 기본 3개로 설정
SetSolBulletNumAngle(3);
SetSize(50.f, 50.f);
SetPivot(0.5f, 0.5f);
return true;
}
void CSolBullet::Update(float DeltaTime)
{
this->CGameObject::Update(DeltaTime);
//주인 오브젝트를 따라다닌다.
m_Pos = m_MasterObject->GetPos();
//매 프레임 총알의 회전량을 구한다.
m_RotatingAngle += m_RotatingAngleSpeed * DeltaTime;
//각도가 360도를 넘어가면, 360도를 뺴줘서 숫자가 너무 커지지 않도록 해준다.
if (m_RotatingAngle >= 360.f)
m_RotatingAngle -= 360.f;
size_t size = m_SolBulletCenter.size();
for (size_t i = 0; i < size; ++i)
{
//라디안값을 구한다.
float RotAngleRad = DegreeToRadian(m_RotatingAngle + (i * m_SolBulletNumAngle));
//라디안값을 통해 토네이도 총알의 중심점을 구해낸다.
m_SolBulletCenter[i].x = m_Pos.x + (cosf(RotAngleRad) * m_RotatingRadius);
m_SolBulletCenter[i].y = m_Pos.y + (sinf(RotAngleRad) * m_RotatingRadius);
}
//스킬 사용 처리
if (m_isCasting)
{
m_SkillDurationLeft -= DeltaTime;
//SkillLogic이 작동을 마치고 True를 반환하면
if (SkillLogic(DeltaTime))
{
m_isCasting = false;
m_SkillDurationLeft = m_SkillDuration;
}
}
}
void CSolBullet::Render(HDC hDC, float DeltaTime)
{
CGameObject::Render(hDC, DeltaTime);
size_t size = m_SolBulletCenter.size();
for (size_t i = 0; i < size; ++i)
{
//구한 토네이도 총알의 중심점으로부터 총알을 그려낸다.
Vector2 RenderLeftTop = m_SolBulletCenter[i] - (m_Size * m_Pivot);
Ellipse(hDC, (int)RenderLeftTop.x,
(int)RenderLeftTop.y,
(int)(RenderLeftTop.x + m_Size.x),
(int)(RenderLeftTop.y + m_Size.y));
}
}
void CSolBullet::SetSolBulletNumAngle(int Num)
{
m_SolBulletNum = Num;
m_SolBulletNumAngle = 360.f / Num;
m_SolBulletCenter.resize(Num);
}
bool CSolBullet::SkillLogic(float DeltaTime)
{
//아직 시전시간이 남아있으면 증가
if (m_SkillDurationLeft >= 0.f)
m_SolSkillDir = 1.f;
//시전시간이 끝났으면 감소
else
m_SolSkillDir = -1.f;
//위의 값에 따라 증가 혹은 감소
m_RotatingAngleSpeed += m_SolSkillDir * m_ExpandSpeed * DeltaTime * 2.f;
m_RotatingRadius += m_SolSkillDir * m_ExpandSpeed * DeltaTime;
//반경이 최대값을 넘어가면 최대값으로 고정(반경, 속도 둘다)
if (m_RotatingRadius >= m_RotatingRadiusMax)
{
m_RotatingRadius = m_RotatingRadiusMax;
m_RotatingAngleSpeed = m_RotatingAngleSpeedMax;
}
//반대로 반경이 최솟값(기본값)이하로 떨어지면 최솟값으로 고정
else if (m_RotatingRadius <= m_RotatingRadiusMin)
{
m_RotatingRadius = m_RotatingRadiusMin;
m_RotatingAngleSpeed = m_RotatingAngleSpeedMin;
//여기에 들어왔다는 것은 시전이 끝났다는 얘기이므로 스킬을 다시 사용가능하게 변경한다.
m_isCasting = false;
m_SkillDurationLeft = m_SkillDuration;
//true 반환 = 스킬 시전 종료
return true;
}
//false 반환 = 스킬 시전 중
return false;
}
* 아우렐리온 솔의 스킬 작동 방식은 기존의 스킬들과 약간 다르다.
- 다른 스킬들은 스킬을 시전할 떄 객체가 생성되는 방식이었지만,
이 스킬은 스킬을 시전하면 생성되어있는 객체가 변화하는 방식이기 떄문이다.
- 이 스킬은 스킬 작동 메소드를 따로 만들어서 호출하는 방식으로 구현해주어야 한다.
- 입력 메소드 등록 및 스킬 초기화 과정은 여러번 작성했으므로 생략한다.
//Player.cpp
void CPlayer::AurelionSol()
{
//쿨타임이면 그냥 return
if (m_SkillCoolTimeSet[(int)EPlayerSkill::AurelionSol].isCoolTime)
return;
EnterSkillCoolTIme((int)EPlayerSkill::AurelionSol);
//m_Slave 목록에서 찾아도 되지만 최적화를 위해 포인터 변수에 저장해 놓고 호출하였다.
m_Sol->EnableSkill();
}
< 결과 >