Framework 3

김성진·2024년 2월 11일

2월 8일 수업 정리

이제 만들어둔 Timber 게임을 Framework로 정리해서 다시 효율적으로 만들어보는 작업을 해보자.

origin Custom

origin 특정 9개 피벗 점 말고도, 커스텀해놓은 피벗점을 사용할 수 있게 해보자.

우선 열거형 origin에 CUSTOM 상수를 추가한다.

그후 Gameobject 클래스에
Origins originPreset = Origins::TL;
멤버 변수를 추가한다.

그후
virtual void SetOrigin(const sf::Vector2f newOrigin)
{
originPreset = Origins::CUSTOM;
origin = newOrigin;
}
로 SetOrigin 메소드를 재정의해주면 지정한 위치가 Preset으로 저장되어서 다시 사용할 수 있다.

spriteGo

기본적으로 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에 저장했다.

이제 실행해보면 배경과 벌, 구름이 나와서 제대로 동작한다.

profile
듀얼리스트

0개의 댓글