2월 8일 수업 정리
이제 만들어둔 Timber 게임을 Framework로 정리해서 다시 효율적으로 만들어보는 작업을 해보자.
origin 특정 9개 피벗 점 말고도, 커스텀해놓은 피벗점을 사용할 수 있게 해보자.
우선 열거형 origin에 CUSTOM 상수를 추가한다.
그후 Gameobject 클래스에
Origins originPreset = Origins::TL;
멤버 변수를 추가한다.
그후
virtual void SetOrigin(const sf::Vector2f newOrigin)
{
originPreset = Origins::CUSTOM;
origin = newOrigin;
}
로 SetOrigin 메소드를 재정의해주면 지정한 위치가 Preset으로 저장되어서 다시 사용할 수 있다.
기본적으로 timber의 오브젝트 구성품들은 gameobject 에 포함되지만, 편리한 관리를 위해 구름, 나무 등등의 sprite들은 spriteGo라는 클래스를 만들어 gameObject 를 상속받아 관리하겠다.
#pragma once
class SpriteGo : public GameObject
{
protected:
sf::Sprite sprite;
public:
SpriteGo (const std::string name);
void SetTexture(const std::string& textureId);
void SetTexture(const sf::Texture& texture);
void SetPosition(const sf::Vector2f pos) override;
void SetOrigin(const sf::Vector2f newOrigin) override;
void SetOrigin(Origins preset) override;
void SetScale(const float newScale) override;
void Draw(sf::RenderWindow& window) override;
};
Gameobject를 상속받고, Init Release 같은 메소드는 Gameobject가 가지고 있으면 충분하니 SpritGo가 가져야 할 멤버 변수와 메소드만 생성했다.
우선 Sprite 자체의 변수를 가지고, 생성자와 텍스처 선택, 포지션과 오리진 스케일 등등만을 재구현 해준다. 특히 아까 만든 preset 기능을 포함해두었다.
#include "pch.h"
#include "SpriteGo.h"
SpriteGo::SpriteGo(const std::string name) : GameObject(name)
{
}
void SpriteGo::SetTexture(const std::string& textureId)
{
sprite.setTexture(ResourceMgr<sf::Texture>::Instance().Get(textureId));
}
void SpriteGo::SetTexture(const sf::Texture& texture)
{
sprite.setTexture(texture);
}
void SpriteGo::SetPosition(const sf::Vector2f pos)
{
GameObject::SetPosition(pos);
sprite.setPosition(pos);
}
void SpriteGo::SetOrigin(const sf::Vector2f newOrigin)
{
originPreset = Origins::CUSTOM;
GameObject::SetOrigin(newOrigin);
sprite.setOrigin(newOrigin);
}
void SpriteGo::SetOrigin(Origins preset)
{
if (preset == Origins::CUSTOM)
{
preset = Origins::TL;
}
originPreset = preset;
origin = Utils::SetOrigin(sprite, originPreset);
}
void SpriteGo::SetScale(const float newScale)
{
sprite.setScale(newScale, newScale);
}
void SpriteGo::Draw(sf::RenderWindow& window)
{
window.draw(sprite);
}
setTexture 에 sprite.setTexture(ResourceMgr<sf::Texture>::Instance().Get(textureId));를 해두면 텍스처의 이름만으로 이 메소드가 호출될때 리소스 매니저의 인스턴스를 자동으로 호출한다.
SetOrigin 재구현시
GameObject::SetOrigin(newOrigin);
sprite.setOrigin(newOrigin);
처럼 두번 작성한 이유는 sprite는 현재의 스프라이트의 오리진을 조정한 것이지만 이후 해당 스프라이트에 소속된 다른 스프라이트들을 한꺼번에 조정할 때 GameObject에 있는 SetOrigin를 통해 같이 조정할 수 있게 했다.
SpriteGo를 상속받은 BgMovingGo 클래스를 만들어서 구름을 구현해볼것이다.
#pragma once
#include "SpriteGo.h"
class BgMovingGo : public SpriteGo
{
protected:
public:
BgMovingGo(const std::string& name = "");
void Update(float dt) override;
void Reset() override;
void Reposition();
sf::FloatRect bounds{ 0.f , 0.f, 1920.f, 1080.f };
sf::Vector2f direction = { 1.0f, 0.f };
float speed = 100.f;
float speedMin = 100.f;
float speedMax = 200.f;
};
기본적인 기능은 spriteGo에 다 있으니, 맨 처음 위치를 정하는 reset과 구역 밖에 나가면 위치를 재조정해주는 Reposition,
그리고 구역을 지정해주는 bounds 와 속도 방향을 설정해준다.
#include "pch.h"
#include "BgMovingGo.h"
BgMovingGo::BgMovingGo(const std::string& name) : SpriteGo(name)
{
}
void BgMovingGo::Update(float dt)
{
position += direction * speed * dt;
SetPosition(position);
if (position.x < bounds.left || position.x > bounds.left + bounds.width)
{
Reposition();
}
}
void BgMovingGo::Reset()
{
SetScale(Utils::RandomRange(0.5f, 2.f));
speed = Utils::RandomRange(speedMin, speedMax);
direction.x = Utils::RandomValue() > 0.5f ? 1.f : -1.f;
sf::Vector2f newPos;
newPos.x = Utils::RandomRange(bounds.left, bounds.left + bounds.width);
newPos.y = Utils::RandomRange(bounds.top, bounds.top + bounds.height);
if (direction.x == -1.f)
{
sprite.setScale(-1 * scale.x, scale.y);
}
SetPosition(newPos);
}
void BgMovingGo::Reposition()
{
SetScale(Utils::RandomRange(0.5f, 2.f));
speed = Utils::RandomRange(speedMin, speedMax);
sf::Vector2f newPos;
if (Utils::RandomValue() > 0.5f)
{
direction.x = 1.f;
newPos.x = bounds.left;
}
else
{
direction.x = -1.f;
newPos.x = bounds.left + bounds.width;
sprite.setScale(-1 * scale.x, scale.y);
}
newPos.y = Utils::RandomRange(bounds.top, bounds.top + bounds.height);
SetPosition(newPos);
}
원리는 이전에 배웟으니, Update에서 이동시켜주고, Reset을 통해 맨처음 생성되거나 초기화 될때 위치와 크기, 벡터 등을 설정해준다.
그후 정해줫던 bounds 를 나가면 Reposition 메소드가 호출되도록 하고, Reposition는 속도와 크기를 변경하고 1/2 확률로 새로운 위치와 방향을 정해준다.
벌도 구름과 비슷하게 spriteGo를 상속받아 BgBeeGo로 구현했다.
비슷한 부분은 생략하고, 벌의 특징인 1초마다 랜덤한 움직임을 새로 만들었다.
void BgBeeGo::Update(float dt)
{
SetPosition(position + direction * speed * dt);
changeDirectionTIme += dt;
sf::Transform rotation;
rotation.rotate((float)(rand()) / RAND_MAX * 360);
if (changeDirectionTIme > 1.0f)
{
direction = rotation * direction;
rotation.rotate((float)(rand()) / RAND_MAX * 360);
speed = 200.f;
changeDirectionTIme = 0;
}
if (position.x < bounds.left)
{
direction.x = Utils::RandomRange(0, 1);
}
if (position.x > bounds.width)
{
direction.x = Utils::RandomRange(-1, 0);
}
if (position.y < bounds.top)
{
direction.y = Utils::RandomRange(0, 1);
}
if (position.y > bounds.height)
{
direction.y = Utils::RandomRange(-1, 0);
}
if (direction.x < 0.f)
{
sprite.setScale(1,1);
}
else
{
sprite.setScale(-1,1);
}
}
changeDirectionTIme 가 1초가 될때마다 방향을 rotate 시켰다. 벌은 화면보다 약간 작게 bounds를 설정해서 해당 위치에 도달하면 반대쪽으로 가도록 했다. 네 방향을 전부 코딩해서 조잡하지만, 세밀한 조정을 위해 일부러 합쳐서 만들지 않았다.
또한 방향에 맞춰 scale을 뒤집어줬다.
게임플레이가 이루어지는 SceneGame 을 만들어서 벌과 구름, 배경을 넣고 확인해보자
#pragma once
class SceneGame : public Scene
{
protected:
public:
SceneGame(SceneIds id);
virtual ~SceneGame() override;
// Scene을(를) 통해 상속됨 ctrl . 해서 순수가상함수구현
void Init() override;
void Release() override;
void Enter() override;
void Exit() override;
void Update(float dt) override;
void Draw(sf::RenderWindow& window) override;
};
#include "pch.h"
#include "SceneGame.h"
#include "SpriteGo.h"
#include "BgMovingGo.h"
#include "BgBeeGo.h"
SceneGame::SceneGame(SceneIds id) : Scene(id)
{
}
SceneGame::~SceneGame()
{
}
void SceneGame::Init()
{
texResMgr.Load("graphics/background.png");
texResMgr.Load("graphics/cloud.png");
texResMgr.Load("graphics/bee.png");
SpriteGo* newSpriteGo = new SpriteGo("BG");
newSpriteGo->SetTexture("graphics/background.png");
sf::FloatRect movingBounds({ -200.f , 0.f , 1920.f + 400.f , 1080.f });
BgMovingGo* newMovingGo = new BgMovingGo("cloud1");
newMovingGo->SetTexture("graphics/cloud.png");
newMovingGo->SetOrigin(Origins::MC);
newMovingGo->bounds = movingBounds;
BgMovingGo* newMovingGo2 = new BgMovingGo("cloud2");
newMovingGo2->SetTexture("graphics/cloud.png");
newMovingGo2->SetOrigin(Origins::MC);
newMovingGo2->bounds = movingBounds;
BgMovingGo* newMovingGo3 = new BgMovingGo("cloud3");
newMovingGo3->SetTexture("graphics/cloud.png");
newMovingGo3->SetOrigin(Origins::MC);
newMovingGo3->bounds = movingBounds;
sf::FloatRect beeBounds({ 200.f , 200.f , 1920.f - 200.f , 1080.f - 200.f });
BgBeeGo* MovingBee = new BgBeeGo("bee");
MovingBee->SetTexture("graphics/bee.png");
MovingBee->SetOrigin(Origins::MC);
MovingBee->bounds = beeBounds;
AddGo(newSpriteGo);
AddGo(newMovingGo);
AddGo(newMovingGo2);
AddGo(newMovingGo3);
AddGo(MovingBee);
}
void SceneGame::Release()
{
Scene::Release();
}
void SceneGame::Enter()
{
Scene::Enter();
}
void SceneGame::Exit()
{
}
void SceneGame::Update(float dt)
{
Scene::Update(dt);
}
void SceneGame::Draw(sf::RenderWindow& window)
{
Scene::Draw(window);
}
씬이 초기화될 때 각 텍스처를 로딩하고, 스프라이트들을 set 해서 gameobject list에 저장했다.
이제 실행해보면 배경과 벌, 구름이 나와서 제대로 동작한다.