728x90
반응형
Game Programming in c++을 개인적으로 공부하며 배운 것들에 대해 기록하였습니다.
초보자인 제게는 나뉘어져 있는 클래스 파일, 헤더 파일들이 머릿속에서 쉽게 정리되지 않아
전체 구조를 한화면에 담고 코드의 흐름을 화살표로 나타내 보았습니다.
이책을 공부하는 저와 같은 초보자에게 조금이라도 도움이 되었으면 하는 마음에 공유하게 되었습니다.
아래는 계층 구조.jpg의 화살표를 따라 프로그램 진행 흐름을 표시한 의사 코드입니다.
bool success = game.Initialize();
LoadData();
mShip = new Ship(this);
:Actor(game)
mGame->AddActor(this);
mActors.emplace_back(actor); //game.mActors 벡터에 추가
SpriteComponent* sc = new SpriteComponent(this, 150);
:Component(owner)
mOwner->AddComponent(this);
mComponents.insert(iter, component); //mShip::mComponents (Ship *mShip, mComponents는 private 설정)
mOwner->GetGame()->AddSprite(this);
mSprites.insert(iter, sprite); //game::mSprites
sc->SetTexture(game->GetTexture("Assets/Ship.png"));
mTextures.emplace(fileName.c_str(), tex);
SDL_QueryTexture(texture, nullptr, nullptr, &mTexWidth, &mTexHeight);
InputComponent* ic = new InputComponent(this);
:MoveComponent(owner)
:Component(owner, updateOrder)
mOwner->AddComponent(this);
mComponents.insert(iter, component); //mShip::mComponents
ic->SetForwardKey(SDL_SCANCODE_W);
ic->SetBackKey(SDL_SCANCODE_S);
ic->SetClockwiseKey(SDL_SCANCODE_A);
ic->SetCounterClockwiseKey(SDL_SCANCODE_D);
ic->SetMaxForwardSpeed(300.0f);
ic->SetMaxAngularSpeed(Math::TwoPi);
mShip->SetPosition(Vector2(512.0f, 384.0f));
mShip->SetRotation(Math::PiOver2);
const int numAsteroids = 20;
for (int i = 0; i < numAsteroids; i++)
new Asteroid(this); //Asteroid noname_astr = new Asteroid(this);
Actor(game)
mGame->AddActor(this);
mActors.emplace_back(actor); //game::mActors
mCircle = nullptr;
Vector2 randPos = Random::GetVector(Vector2::Zero, Vector2(1024.0f, 768.0f));
SetPosition(randPos);
Actor:: void SetPosition(const Vector2& pos) {mPosition = pos;}
const Vector2& pos = randPos;
mPosition = pos; //mPosition = randPos;
SetRotation(Random::GetFloatRange(0.0f, Math::TowPi));
SpriteComponent* sc = new SpriteComponent(this);
:Component(owner)
mOwner->AddComponent(this);
mComponents.insert(iter, component);
mOwner->GetGame()->AddSprite(this);
mSprites.insert(iter, sprite);
sc->SetTexture(game->GetTexture("Assets/Asteroid.png"));
MoveComponent* mc = new MoveComponent(this);
:Component(owner, updateOrder)
mOwner->AddComponent(this);
mComponents.insert(iter, component);
mc->SetForwardSpeed(150,0f);
mCircle = new CircleComponent(this);
:Component(owner)
mOwner->AddComponent(this);
mComponents.insert(iter, component);
mCircle->SetRadius(40.0f);
game->AddAsteroid(this);
mAsteroids.emplace_back(ast);
mTicksCount = SDL_GetTicks();
game.RunLoop();
ProcessInput();
while (SDL_PollEvent(&event))
switch (event.type)
case SDL_QUIT:
mIsRunning = false;
const Uint8* keyState = SDL_GetKeyboardState(NULL);
mUpdatingActors = true;
for (auto actor : mActors) //ⓐ&mShip, ⓑ&noname_astr
actor->ProcessInput(keyState); //키 입력 처리
ⓐ mShip::actor->ProcessInput(keyState);
for (auto comp : mComponents) //[&mShip::&sc, &ic], [&noname_astr::&sc, &mc, &mCircle]
comp->ProcessInput(keyState);
① mShip::sc->ProcessInput(keyState);
Component::virtual void ProcessInput(const uint8_t* keyState) {} //키 입력에 따른 mShip 변화
② mShip::ic->ProcessInput(keyState);
float forwardSpeed = 0.0f;
if (keyState[mForwardKey])
forwardSpeed += mMaxForwardSpeed; //전방 키 입력 시 mShip 전방 속도 300
if (keyState[mBackKey])
forwardSpeed -= mMaxForwardSpeed;
SetForwardSpeed(forwardSpeed);
void SetForwardSpeed(float speed) {mForwardSpeed = speed;} //mShip 속도 세팅
float angluarSpeed = 0.0f;
if (keyState[mClockwiseKey])
angularSpeed += mMaxAngularSpeed;
if (keyState[mCounterClockwiseKey])
angularSpeed -= mMaxAngularSpeed;
SetAngularSpeed(angularSpeed);
ActorInput(keyState);
Ship::ActorInput(const uint8_t* keyState)
if (keyState[SDL_SCANCODE_SPACE] && mLaserCooldown <=0.0f)
Laser* laser = new Laser(GetGame());
:Actor(game)
mState(Eactive), mPosition(Vector2::Zero),
mScale(1.0f), mRotation(0.0f), mGame(game)
mGame->AddActor(this)
mActors.emplace_back(actor);
mActors.emplace_back(laser); //game.mActors 벡터에 추가
SpriteComponent* sc = new SpriteComponent(this); //this = laser;
SpriteComponent::SpriteComponent(Actor* owner, int drawOrder) //owner=laser;
:Component(owner)
Component::Component(Actor* owner, int updateOrder)
:mOwner(owner)
,mUpdateOrder(updateOrder)
mOwner->AddComponent(this); //laser->AddComponent(sc);
void Actor::AddComponent(Component* component) //component = sc;
int myOrder = component->GetUpdateOrder();
auto iter = mComponent.begin(); //mComponents가 빈 경우 .end()가 채워짐
for( ; iter != mComponents.end(); ++iter)
{
//iter = mComponents.end(), 탈출
}
mComponents.insert(iter, component); //laser->mComponents.insert(iter, sc);
,mTexture(nullptr), mDrawOrder(drawOrder), mTexWidth(0), mTexHeight(0)
mOwner->GetGame()->AddSprite(this); //laser->game->AddSprite(sc);
void Game::AddSprite(SpriteComponent* sprite) //sprite = (laser->sc);
int myDrawOrder = sprite->GetDrawOrder();
auto iter = mSprites.begin();
for ( ; iter != mSprites.end(); ++iter)
if (myDrawOrder < (*iter)->GetDrawOrder())
break;
mSprites.insert(iter, sprite); //game.mSprites.insert(iter, laser->sc);
sc->SetTexture(game->GetTexture("Assets/Laser.png"));
SDL_Texture* Game::GetTexture(const std::string& fileName)
SDL_Texture* tex = nullptr;
SDL_Surface* surf = IMG_Load(fileName.c_str());
tex = SDL_CreateTextureFromSurface(mRenderer, surf);
SDL_FreeSurface(surf);
mTextures.emplace(fileName.c_str(), tex); //game.mTextures에 {"Laser.png", tex_from_Laser.png} 등록
return tex;
void SpriteComponent::SetTexture(SDL_Texture* texture)
mTexture = texture;
SDL_QueryTexture(texture, nullptr, nullptr, &mTexWidth, &mTexHeight);
MoveComponent* mc = new MoveComponent(this); //this = laser;
MoveComponent::MoveComponent(class Actor* owner, int updateOrder) //updateOrder=10;
:Component(owner, updateOrder)
Component::Component(Actor* owner, int updateOrder)
:mOwner(owner)
,mUpdateOrder(updateOrder)
mOwner->AddComponent(this); //this = mc;
void Actor::AddComponent(Component* component)
int myOrder = component->GetUpdateOrder();
auto iter = mComponent.begin();
mComponents.insert(iter, component); //laser->mComponents.insert(iter, mc);
,mAngularSpeed(0.0f), mForwardSpeed(0.0f)
mc->SetForwardSpeed(800.0f);
mForwardSpeed = speed;
mCircle = new CircleComponent(this); //this = laser;
CircleComponent::CircleComponent(class Actor* owner)
:Component(owner)
Component::Component(Actor* owner, int updateOrder) //updateOrder=100;
Component::Component(this, Actor* owner, int updateOrder) //this=mCircle;
:mOwner(owner)
,mUpdateOrder(updateOrder)
mOwner->AddComponent(this);
laser->AddComponent(mCircle);
void Actor::AddComponent(Component* component)
mComponents.insert(iter, component); //laser->mComponents.insert(iter, mCircle);
,mRadius(0.0f)
mCircle->SetRadius(11.0f);
mRadius = radius;
laser->SetPosition( GetPosition() );
laser->SetPosition( this.GetPosition() ); //this = mShip;
const Vector2& GetPosition() const {return mPosition;}
laser->SetPosition(mShip::mPosition); //private::mShip->mPosition 접근 불가
void SetPosition(const Vector2& pos) {mPosition = pos;}
laser::mPosition = mShip::mPosition;
laser->SetRotation( GetRotation() );
laser->SetRotation( this.GetRotation() ); //this = mShip;
float GetRotation() const {return mRotation;}
laser->SetRotation(mShip::mRotation);
void SetRotation(float rotation) {mRotation = rotation;}
laser::mRotation = mShip::mRotation;
mLaserCooldown = 0.5f;
ⓑ noname_astr::actor->ProcessInput(keyState);
for (auto comp : mComponents) //[&mShip::&sc, &ic], [&noname_astr::&sc, &mc, &mCircle]
comp->ProcessInput(keyState);
① noname_astr::sc->ProcessInput(keyState);
Component::virtual void ProcessInput(const uint8_t* keyState) {}
② noname_astr::mc->ProcessInput(keyState);
Component::virtual void ProcessInput(const uint8_t* keyState) {}
③ noname_astr::mCircle->ProcessInput(keyState);
Component::virtual void ProcessInput(const uint8_t* keyState) {}
ActorInput(keyState);
void Actor::ActorInput(const uint8_t* keyState) {}
mUpdatingActors = false;
UpdateGame();
while (!SDL_TICKS_PASSED(SDL_GetTicks(), mTicksCount + 16))
float deltaTime = (SDL_GetTicks() - mTicksCount) / 1000.0f;
mUpdatingActors = true;
for (auto actor : mActors) //mShip, noname_astr, laser
actor->Update(deltaTime);
ⓐ mShip->Update(deltaTime);
void Actor::Update(float deltaTime)
UpdateComponents(deltaTime);
void Actor::UpdateComponents(float deltaTime)
for(auto comp : mComponents) //[&mShip::&sc, &ic]
comp->Update(deltaTime);
② mShip::sc->Update(deltaTime);
SpriteComponent::Component::Update(float deltaTime) {}
① mShip::ic->Update(deltaTime);
InputComponent::MoveComponent::Update(floate deltaTime)
if (!Math::NearZero(mAngularSpeed)) //각속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
float rot = mOwner->GetRotation(); //마지막 회전 값을 불러온다
float rot = mShip->Actor::GetRotation();
float rot = mShip->mRotation;
rot += mAngularSpeed * deltaTime; //각속도 * 시간
mOwner->SetRotation(rot);
mShip->Actor::SetRotation(rot);
mShip->mRotation = rot; //회전 값을 저장
if (!Math::NearZero(mForwardSpeed) //전방속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
Vector2 pos = mOwner->GetPosition();
Vector2 pos = mShip->Actor::GetPosition();
Actor::const Vector2& GetPosition() const {return mPosition;}
Vector2 pos = mShip->mPosition;
pos += mOwner->GetForward() * mForwardSpeed * deltaTime;
"pos = pos + [ return Vector2 ( Math::Cos(mRotation) * mForwardSpeed(300) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(300) * deltaTime ) ]"
if (pos.x < 0.0f) {pos.x = 1022.0f;} //왼쪽 화면 밖으로 이동 시 오른쪽 화면으로 이동
else if (pos.x > 1024.0f) {pos.x = 2.0f;}
if (pos.y < 0.0f) {pos.y = 766.0f;}
else if (pos.y > 768.0f) { pos.y = 2.0f;}
mOwner ->SetPosition(pos);
mShip->Actor::SetPosition(pos); //void SetPosition(const Vector2& pos) {mPosition = pos;}
mShip->mPosition = pos;
UpdateActor(deltaTime);
Ship::UpdateActor(float deltaTime)
mLaserCooldown -= deltaTime;
ⓑ noname_astr->Update(deltaTime);
void Actor::Update(float deltaTime)
UpdateComponents(deltaTime);
void Actor::UpdateComponents(float deltaTime)
for(auto comp : mComponents) //[&noname_astr::&sc, &mc, &mCircle]
comp->Update(deltaTime);
② noname_astr::sc->Update(deltaTime);
SpriteComponent::Component::Update(float deltaTime) {}
① noname_astr::mc->Update(deltaTime);
MoveComponent::Update(float deltaTime)
if (!Math::NearZero(mAngularSpeed)) //각속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
float rot = mOwner->GetRotation(); //마지막 회전 값을 불러온다
float rot =noname_astr->Actor::GetRotation();
float rot = noname_astr->mRotation;
rot += mAngularSpeed * deltaTime; //각속도 * 시간
mOwner->SetRotation(rot);
noname_astr->Actor::SetRotation(rot);
noname->mRotation = rot; //회전 값을 저장
if (!Math::NearZero(mForwardSpeed) //전방속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
Vector2 pos = mOwner->GetPosition();
Vector2 pos = noname_astr->Actor::GetPosition();
Actor::const Vector2& GetPosition() const {return mPosition;}
Vector2 pos = noname_astr->mPosition;
pos += mOwner->GetForward() * mForwardSpeed * deltaTime;
"pos = pos + [ Vector2 ( Math::Cos(mRotation) * mForwardSpeed(150) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(150) * deltaTime ) ]"
if (pos.x < 0.0f) {pos.x = 1022.0f;} //왼쪽 화면 밖으로 이동 시 오른쪽 화면으로 이동
else if (pos.x > 1024.0f) {pos.x = 2.0f;}
if (pos.y < 0.0f) {pos.y = 766.0f;}
else if (pos.y > 768.0f) { pos.y = 2.0f;}
mOwner ->SetPosition(pos);
noname_astr->Actor::SetPosition(pos); //void SetPosition(const Vector2& pos) {mPosition = pos;}
noname_astr->mPosition = pos;
③ noname_astr::mCircle->Update(deltaTime);
Component::Update(float deltaTime) {}
UpdateActor(deltaTime);
void Actor::UpdateActor(float deltaTime) {}
ⓒ laser->Update(deltaTime);
void Actor::Update(float deltaTime)
UpdateComponents(deltaTime);
void Actor::UpdateComponents(float deltaTime)
for(auto comp : mComponents) //[laser::sc, mc, mCircle]
comp->Update(deltaTime);
② laser::sc->Update(deltaTime);
SpriteComponent::Component::Update(float deltaTime) {}
① laser::mc->Update(deltaTime);
MoveComponent::Update(float deltaTime)
if (!Math::NearZero(mAngularSpeed)) //각속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
float rot = mOwner->GetRotation(); //마지막 회전 값을 불러온다
float rot =laser->Actor::GetRotation();
float rot = laser->mRotation;
rot += mAngularSpeed * deltaTime; //각속도 * 시간
mOwner->SetRotation(rot);
laser->Actor::SetRotation(rot);
laser->mRotation = rot; //회전 값을 저장
if (!Math::NearZero(mForwardSpeed) //전방속도가 0에 가깝지 않으면 실행 (입력 있었을 때)
Vector2 pos = mOwner->GetPosition();
Vector2 pos = laser->Actor::GetPosition();
Actor::const Vector2& GetPosition() const {return mPosition;}
Vector2 pos = laser->mPosition;
pos += mOwner->GetForward() * mForwardSpeed * deltaTime;
"pos = pos + [ Vector2 ( Math::Cos(mRotation) * mForwardSpeed(800) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(800) * deltaTime ) ]"
if (pos.x < 0.0f) {pos.x = 1022.0f;} //왼쪽 화면 밖으로 이동 시 오른쪽 화면으로 이동
else if (pos.x > 1024.0f) {pos.x = 2.0f;}
if (pos.y < 0.0f) {pos.y = 766.0f;}
else if (pos.y > 768.0f) { pos.y = 2.0f;}
mOwner ->SetPosition(pos);
laser->Actor::SetPosition(pos);
void SetPosition(const Vector2& pos) {mPosition = pos;}
laser->mPosition = pos;
③ laser::mCircle->Update(deltaTime);
CircleComponent::Component::Update(float deltaTime) {}
UpdateActor(deltaTime);
void Laser::UpdateActor(float deltaTime)
mDeathTimer -= deltaTime;
if (mDeathTimer <= 0.0f)
SetState(Edead);
else
for (auto ast : GetGame()->GetAsteroids())
Actor::class Game* getGame() {return mGame;}
Game::std::vector<class Asteroid*>& GetAsteroids() { return mAsteroids; }
for (auto ast : game->GetAsteroids())
for (auto ast : mAsteroids)
if ( Intersect ( *mCircle, *( ast->GetCircle() ) ) )
if ( Intersect ( *(laser::mCircle), *(noname_astr::mCircle) ) )
bool Intersect(const CircleComponent& a, const CircleComponent& b) //교집합 판단
Vector2 diff = a.GetCenter() - b.GetCenter();
const Vector2& CircleComponent::GetCenter() const
return mOwner->GetPosition();
return mOwner->mPosition;
Vector2 diff = a.mPosition - b.mPosition;
float distSq = diff.LengthSq();
float distSq = (diff.x * diff.x) + (diff.y * diff.y)
float radiiSq = a.GetRadius() + b.GetRadius();
float CircleComponent::GetRadius() const
return mOwner->GetScale() * mRadius;
float radiiSq = (a.mScale * a.mRadius) + (b.mScale * b.mRadius);
radiiSq *= radiiSq;
return distSq <= radiiSq;
SetState(Edead);
void SetState(State state) {mState = state;}
laser->mState = Edead;
ast->SetState(Edead);
noname_astr->void SetState(State state) {mState = state;}
noname_astr->mState = Edead;
break;
mUpdatingActors = false;
for (auto pending : mPendingActors)
mActors.emplace_back(pending); //pendingActors를 mActors로 이동
mPendingActors.clear();
std::vector<Actor*> deadActors;
for (auto actor : mActors)
if (actor->GetState() == Actor::Edead)
deadActors.emplace_back(actor);
for (auto actor : deadActors)
delete actor;
GenerateOutput();
SDL_SetRenderDrawColor(mRenderer, 220, 220, 220, 255); //(mRenderer, R, G, B, A) 하나의 색을 지정
SDL_RenderClear(mRenderer); //현재 그리기 색상으로 후면버퍼를 지운다 (후면 버퍼를 단색으로 클리어)
for (auto sprite : mSprites) // [mShip::sc], [noname_astr::sc], [laser::sc]
sprite->Draw(mRenderer);
ⓐ mShip::sc->Draw(mRenderer);
mShip::sc->SpriteComponent::Draw(mRenderer);
if (mTexture) //Ship::sc->SetTexture(tex) {mTexture = tex;}
SDL_Rect r;
r.w = static_cast<int>(mTexWidth * mOwner->GetScale()); //64 * 1.0f
r.h = static_cast<int>(mTexHeight * mOwner->GetScale()); //64 * 1.0f
r.x = static_cast<int>(mOwner->GetPosition().x - r.w / 2); //(512 + 1 * 300 * 0.05) - 64 / 2
"pos = pos + [ return Vector2 ( Math::Cos(mRotation) * mForwardSpeed(300) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(300) * deltaTime ) ]"
r.y = static_cast<int>(mOwner->GetPosition().y - r.h / 2); //(384 + 0 * 300 * 0.05) - 64 / 2
SDL_RenderCopyEx(renderer,
mTexture, // 그릴 텍스쳐
nullptr, //srcrect 그릴 텍스쳐의 일부 영역 (전체 영역은 nullptr)
&r, //그려질 목적지의 사각형 영역
-Math::ToDegrees(mOwner->GetRotation()), //dobule angle 회전각도 (라디안을 각도로 변환)
-Math::ToDegrees(mShip->Actor::GetRotation())
nullptr, //회전중심점 (중심이면 nullptr) SDL Pointer* Center
SDL_FLIP_NONE//텍스쳐를 플립(튀기다)하는 방법 SDL_RenderFlip flip
);
ⓑ noname_astr::sc->Draw(mRenderer);
noname_astr::sc->SpriteComponent::Draw(mRenderer);
if (mTexture) //noname_astr::sc->SetTexture(tex) {mTexture = tex;}
SDL_Rect r;
r.w = static_cast<int>(mTexWidth * mOwner->GetScale()); //64 * 1.0f
r.h = static_cast<int>(mTexHeight * mOwner->GetScale()); //64 * 1.0f
r.x = static_cast<int>(mOwner->GetPosition().x - r.w / 2); //(random.x + 1 * 150 * 0.05) - 64 / 2
"pos = pos + [ return Vector2 ( Math::Cos(mRotation) * mForwardSpeed(150) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(150) * deltaTime ) ]"
r.y = static_cast<int>(mOwner->GetPosition().y - r.h / 2); //(ramdom.y + 0 * 150 * 0.05) - 64 / 2
SDL_RenderCopyEx(renderer,
mTexture, // 그릴 텍스쳐
nullptr, //srcrect 그릴 텍스쳐의 일부 영역 (전체 영역은 nullptr)
&r, //그려질 목적지의 사각형 영역
-Math::ToDegrees(mOwner->GetRotation()), //dobule angle 회전각도 (라디안을 각도로 변환)
-Math::ToDegrees(mShip->Actor::GetRotation())
nullptr, //회전중심점 (중심이면 nullptr) SDL Pointer* Center
SDL_FLIP_NONE//텍스쳐를 플립(튀기다)하는 방법 SDL_RenderFlip flip
);
ⓒ laser::sc->Draw(mRenderer);
laser::sc->SpriteComponent::Draw(mRenderer);
if (mTexture) //laser::sc->SetTexture(tex) {mTexture = tex;}
SDL_Rect r;
r.w = static_cast<int>(mTexWidth * mOwner->GetScale()); //16 * 1.0f
r.h = static_cast<int>(mTexHeight * mOwner->GetScale()); //16 * 1.0f
r.x = static_cast<int>(mOwner->GetPosition().x - r.w / 2); //(mShip.x + 1 * 800 * 0.05) - 16 / 2
"pos = pos + [ return Vector2 ( Math::Cos(mRotation) * mForwardSpeed(800) * deltaTime,
-Math::Sin(mRotation) * mForwardSpeed(800) * deltaTime ) ]"
r.y = static_cast<int>(mOwner->GetPosition().y - r.h / 2); //(mShip.y + 0 * 800 * 0.05) - 16 / 2
SDL_RenderCopyEx(renderer,
mTexture, // 그릴 텍스쳐
nullptr, //srcrect 그릴 텍스쳐의 일부 영역 (전체 영역은 nullptr)
&r, //그려질 목적지의 사각형 영역
-Math::ToDegrees(mOwner->GetRotation()), //dobule angle 회전각도 (라디안을 각도로 변환)
-Math::ToDegrees(mShip->Actor::GetRotation())
nullptr, //회전중심점 (중심이면 nullptr) SDL Pointer* Center
SDL_FLIP_NONE//텍스쳐를 플립(튀기다)하는 방법 SDL_RenderFlip flip
);
SDL_RenderPresent(mRenderer);
728x90
반응형
'코드 > C++' 카테고리의 다른 글
버튼 클릭 시 메세지 출력 (0) | 2023.05.16 |
---|---|
Chapter04_Game programming in c++ (0) | 2022.09.17 |
개발 일지 (0) | 2022.07.28 |
GetPixel (0) | 2022.07.27 |
참조 (Ampersand) (0) | 2022.07.27 |
댓글