2월 13일 수업 정리
Utils 에 추가해서 앞으로 많이 쓰일 랜덤한 단위벡터와, 원 내의 랜덤한 위치를 골라주는 메소드를 만들어보자. 예를들어 특정 구역 내에 몬스터 리스폰 지점을 랜덤으로 골라주는 등으로 쓰인다.
sf::Vector2f Utils::RandomOnUnitCircle()
{
sf::Transform rotation;
rotation.rotate(RandomRange(0.f, 360.f));
return rotation * sf::Vector2f (1.f, 0.f);
}
sf::Vector2f Utils::RandomInUnitCircle()
{
return RandomOnUnitCircle() * RandomValue();
}
단위 벡터 sf::Vector2f (1.f, 0.f) 에서 rotate로 방향을 랜덤하게 돌린것을 곱해주는것으로 원을 그렷을때 테두리의 랜덤한 지점을 방향으로 정해주는것과 같은 효과가 있다.
또한 미리 만들어둔 0~1 사이의 값을 내는 RandomValue()를 이용하여 RandomOnUnitCircle()과 곱해주면 랜덤한 방향에서, 0~1까지의 스칼라값을 지닌 벡터. 즉 원 내부의 랜덤한 점을 지정하게 된다.
BgMovingGo* newMovingGo2 = new BgMovingGo("cloud2");
처럼 2 3 으로 붙여서 구름을 필요한만큼 코딩했었는데,
객체지향 언어에선 아주 바보같은 짓이었다.
해당 객체를 컨테이너에 이름으로 저장하고 있었으니 그냥 반복문을 사용하면 되는것이다.
for (int i = 0; i < 3; ++i)
{
BgMovingGo* newMovingGo = new BgMovingGo("cloud" + std::to_string(i));
newMovingGo->SetTexture("graphics/cloud.png");
newMovingGo->SetOrigin(Origins::MC);
newMovingGo->bounds = movingBounds;
AddGo(newMovingGo);
}
어차피 AddGo 가 끝나고 for문이 닫히면 이후 열람은 이름 변수 cloud i 를 통해 해결할 수 있으니 newMovingGo 객체가 이름이 중복되어도 되는 것이다.
남은 sprite 가 있지만 , framework의 완성을 위해 텍스트를 관리하는 TextGo 를 만들어보자. spriteGo 처럼 GameObject를 상속받아서, 필요한 부분만 재구현한다.
#pragma once
#include "GameObject.h"
class TextGo : public GameObject
{
protected:
sf::Text text;
public:
TextGo(const std::string& name = "");
void SetString(const std::string str);
void Set(const sf::Font& texture, int size, std::string str, sf::Color color);
void SetFont(const std::string& textureId);
void SetFont(const sf::Font& 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 SetScale(const sf::Vector2f& s) override;
void SetFlipX(bool flip) override;
void SetFlipY(bool flip) override;
void Draw(sf::RenderWindow& window) override;
};
void TextGo::SetString(const std::string str)
{
text.setString(str);
SetOrigin(originPreset);
}
해당 객체가 가진 text 변수에 텍스트를 세팅한다. originPreset은 기본적으로 TL에 세팅되어있지만 한번 매개변수로 다른 지점을 주면 게임오브젝트에서 상속받은 originPreset변수에 저장되어 텍스트 크기가 바뀌어도 계속 그 지점으로 오리진을 정해준다.
void TextGo::Set(const sf::Font& texture, int size, std::string str, sf::Color color)
{
SetFont(texture);
text.setString(str);
text.setCharacterSize(size);
text.setFillColor(color);
}
텍스트는 세팅할것이 많아서 한번에 해줬다. 로드된 폰트를 세팅,
사이즈 조절, 텍스트 초기화, 색 지정까지 한번에 세팅한다.
void TextGo::SetFont(const std::string& fontId)
{
text.setFont(ResourceMgr<sf::Font>::Instance().Get(fontId));
}
void TextGo::SetFont(const sf::Font& font)
{
text.setFont(font);
}
셋폰트에 반드시 리소스매니저를 불러놓아야한다.
또한 반드시 씬 추상클래스에 생성자
Scene::Scene(SceneIds id) : id(id) , texResMgr(ResourceMgr<sf::Texture>::Instance()) , fontResMgr(ResourceMgr<sf::Font>::Instance())
{
}
로 폰트매니저도 만들어놓아야한다.
다른것은 spriteGo와 동일하고
void TextGo::SetScale(const sf::Vector2f& s)
{
scale = s;
sf::Vector2f spriteScale = scale;
if (isFlipX)
{
scale.x = -scale.x;
}
if (isFlipY)
{
scale.y = -scale.y;
}
}
void TextGo::SetFlipX(bool flip)
{
if (isFlipX == flip)
{
return;
}
isFlipX = flip;
SetScale(scale);
}
void TextGo::SetFlipY(bool flip)
{
if (isFlipY == flip)
{
return;
}
isFlipY = flip;
SetScale(scale);
}
SetScale을 재구현해서 스케일 조정을 하는 동시에, isFlip 변수를 통해 벡터의 x와 y값을 반전시키는 메소드를 만들어 놓았다.
그럼 점수 표시판부터 만들어보자. 점수판만의 기능이 필요하니 UI Score 객체를 만들어서 textGo를 상속받는다.
#pragma once
#include "TextGo.h"
class UIScore : public TextGo
{
protected:
std::string textFormat = "SCORE == ";
int score = 0;
public:
UIScore(const std::string& name = "");
void SetScore(int score);
void Reset() override;
};
score 객체 본인의 text 에는 고정된 글씨 score == 과 계속해서 변하는 숫자가 둘다 포함되어야 한다. 따라서 고정된 글씨를 표현해주는 textFormat을 만들어 저장해주고,
점수를 저장하는 int 변수를 만든다.
그후 생성자와 점수 조정 메소드, 리셋 메소드 정도만 구현해주면 된다.
void UIScore::SetScore(int score)
{
this->score = score;
text.setString(textFormat + std::to_string(score));
}
스코어를 매개변수로 받아서 스코어 변수에 저장하고, format과 합쳐서 string으로 변환해 text에 저장해준다.
void UIScore::Reset()
{
SetScore(0);
}
스코어를 0으로 해두면, 위의 메소드를 받아서 0을 받아 text를 수정한다.
게임씬에 따로 int 변수를 만들어서 더하고 SetScore에 넘기면 실제 게임플레이처럼 동작할것이다.
UI Score를 만들었으니 게임씬에서 호출할 일만 남았다.
객체지향형으로 메소드를 많이 캡슐화 해놓았으니 간단하다.
uiScore = new UIScore("Ui Score");
uiScore->Set(fontResMgr.Get("fonts/KOMIKAP_.ttf"), 40, "", sf::Color::White);
AddGo(uiScore);
이 3줄이 전부이다. 이름을 붙여 동적할당하고, Set함수로 기본 세팅한 뒤 gameObjects에 저장했다.
너무 간단하니, 화면 중앙에 Press Enter to Start 를 출력해보자.
이 메세지는 특별한 기능이 필요 없다. 따라서 TextGo 클래스 자체로 구현할 수 있다. 바로 게임 씬에서 사용할 수 있다는 뜻이다.
uiMsg = new TextGo("Center Message");
uiMsg->Set(fontResMgr.Get("fonts/KOMIKAP_.ttf"), 80, "Press Enter to Start", sf::Color::White);
uiMsg->SetOrigin(Origins::MC);
uiMsg->SetPosition({ 1920.f / 2, 1080.f / 2 });
AddGo(uiMsg);
오리진을 잡고, 중앙에 놓는것까지 두줄만 더 추가해주었다.
게임 씬 업데이트 메소드에서 특정 키를 입력받으면 메시지를 바꾸는 기능을 만들어보자,
void SceneGame::Update(float dt)
{
Scene::Update(dt);
if (InputMgr::GetKeyDown(sf::Keyboard::Num1))
{
uiMsg->SetString("111");
}
if (InputMgr::GetKeyDown(sf::Keyboard::Num2))
{
uiMsg->SetString("1111111");
}
}
인풋매니저 만들어놓은것을 통해, 숫자 1을 누르면, 화면 중앙의 메시지가 111로, 2를 누르면 1111111로 바뀌게 시험해보았다.
피벗 점을 항상 중앙으로 잡아놓았기 때문에 텍스트 크기가 바뀌더라도 항상 중앙에 찍힌다.
이 방법을 이용해서 메시지를 on off 처럼 보이게 할 수 있을것이다.