공부/SFML

씬 매니저, 프레임워크 클래스, 백그라운드 오브젝트, 실수 난수 출력, 트리

월러비 2025. 6. 30. 18:42

짧은 설명

  • 프레임 워크 클래스 : 메인 루프를 담당하는 클래스다.
    • 리소스 매니저
    • 인풋 매니저
    • 씬 매니저 : 프레임 워크 밑에서 씬들을 관리하는 클래스
      • 싱글톤으로 어디서든 접근할 수 있게 한다.
      • 씬 : 특정 게임 오브젝트들을 관리한다.
        • 업데이트, 드로우, 게임오브젝트 추가 ‘ 제거
      • 게임 씬
      • 타이틀 씬
      • 개발용 테스트 씬 등…
  • 매니저들은 싱글톤으로 만드는게 좋다.
    • 싱글톤은 상속할때 자식 클래스를 템플릿으로 넘겨야한다.
  • 릴리즈는 역순으로 해야한다.
    • 인풋 매니저는 일리즈가 없다.
  • reset과 init을 나눈 이유 : 사용 용도를 구분하기 위해서다.

씬 매니저

  • 컨테이너 생성은 벡터, 리스트, 언오더드 맵 중에서 골라야한다.
  • 씬은 몇개 없다. -> 벡터(일반 배열), 언 오더드 맵 중에서 고르면 좋다.
  • 씬마다 ID(씬 아이디)주기 때문에 배열의 인덱스를 씬 아이디와 키로 맞춰야한다.
  • Scene* : Scene으로 하면 자식 클래스의 사이즈가 다 다르기 때문에 포인터를 붙인것이다.
    • 주소값으로 받게되면 뭘 받아도 똑같은 주소의 메모리크기이기 때문이다.
  • 씬은 하나만 열려있어야하고, 씬을 열려면 열려있는 씬을 닫아야한다.
std::vector<Scene*> scenes; //씬을 동적할당해서 저장한다.
  • 지연해서 추가 및 삭제해야하는것이 있다.
    • 이유 : 업데이트때 씬이 바뀌는 경우 업데이트 중간에 벗어나야하는 경우가 생기기 때문이다. -> 이때 중간에 벗어나기 위해 지연이 필요하다.
SceneIds nextScene = SceneIds::None; //지연을 위한 씬 아이디
  • 열릴 씬의 Enter 함수를 호출해야한다. -> 이걸 지연해서 실행해야한다.
nextScene = id; //다음 열 씬 아이디 저장

void SceneMgr::Update(float dt)
{
	//다음 열 씬이 none이 아니라면 열어야하는 씬이라는 것이다.
	if (nextScene != SceneIds::None)
	{
		scenes[(int)currentScene]->Exit(); //열려있는 씬 닫기
		currentScene = nextScene; //현재 씬을 다음 씬으로 저장
		nextScene = SceneIds::None; //다음 씬은 없으니 초기화 -> 이거 안하면 계속 바뀌는게 무한반복이 되기 때문이다.
		scenes[(int)currentScene]->Enter(); //다음 씬 열기
	}

	scenes[(int)currentScene]->Update(dt);
}

게임 오브젝트 배치

  • Init : 계속 배치
  • Enter / Exit : 생성 및 제거

프레임워크 클래스

  • 프레임 워크 객체 생성 - 메인에서
    • 생성만 하면 동작하게 된다.
    • 그 밑에 속한 매니저가 알아서 동작하게 된다.
int main()
{
    FRAMEWORK.Init(1280, 720, "SFML");
    FRAMEWORK.Do();
    FRAMEWORK.Release();
    

    return 0;
}
  • 시간 기능, 윈도우 생성 기능
//윈도우 생성 / t뒤에 창모드니 전체화면이니 하는것을 지정할 수 있다.
window.create(sf::VideoMode(w, h), t);

//시간 갱신
sf::Time dt = clock.restart();
realDeltaTime = dt.asSeconds(); //실제 시간
deltaTime *= timeScale;
time += deltaTime; //시간은 누적해야한다.
realTime = realDeltaTime; //배율 누적 안된 실제 누적 시간
  • Do() : 미리 사용할 함수를 미리 호출하는 것이다.
    • 루프 및 이벤트 루프가 여기서 작동한다.
while (window.isOpen())
{
		~~~
		
		sf::Event event;
		while (window.pollEvent(event)) { ~~~ }
		
		~~~
}

백그라운드 오브젝트

  • 속도와 방향은 수업이니 약식으로 public으로 설정하지만 나중에 개인 프로젝트에서 고쳐야한다.
  • 구름같이 배치해야할 오브젝트 이미지를 넣고 움직임을 주는 역할을 한다.
auto pos = GetPosition();

pos += direction * speed * dt;
  • 스프라이트 게임 오브젝트 ; 움직이지 않는 이미지의 텍스쳐와 위치, 회전, 크기 등을 설정하여 출력한다.
    • 배경화면
    • 임시 동적할당을 매개변수로 직접 넘겨서 생성해도 괜찮다.
  • 백그라운드 이미지 : 속도, 방향을 받아 움직이는 그림을 출력한다.
    • 구름, 벌 : 움직여야한다.
    • 화면 밖으로 나갈수가 있으니 ‘경계검사’가 필요하다.
      • 윈도우 바운드(영역)이 필요하다.
if (pos.x < -400.f || pos.x > bounds.width + 200.f) //윈도우 경계검사
{
	if (Utils::RandomValue() < 0.5f)
	{
		SetSide(Sides::Right);
	}
	else
	{
		SetSide(Sides::Left);
	}
}
  • 리셋 : 값 초기화 역할이다.
    • 방향을 랜덤으로 놓는것이 좋다.
void BackgroundElement::Reset()
{
	SpriteGameObject::Reset();
	SetOrigin(Origins::MC); //피벗 : 중앙

	SetSide(Sides::Right);
	SetPosition({ 500.f, 0.f });
}

실수 난수 출력

float f = (float)rand() / RAND_MAX;
return min + f * (max - min);
  • max - min : 전체 구간 길이
  • f * (max - min) : 랜덤으로 얼마나 갈지 정한다.
    • f = 0.5 → 절반만큼 이동 → 3.0(전체 구간 길이) * 0.5 : 1.5 → 전체 구간의 반
  • min + ~~ : 시작 위치에서 시작
    • (2.0, 5.0) → 2.0 + 1.5 = 3.5가 출력된다.

트리

  • 가지들을 멤버로 가지고 있어야한다.
  • 기둥 그릴 스프라이트
  • 가지들 스프라이트
std::vector<sf::Sprite> branches;
  • 기둥을 기준으로 왼쪽 오른쪽 배치
SetPosition({ windowBounds.width * 0.5f, 0.f });;

void Tree::SetPosition(const sf::Vector2f& pos)
{
	tree.setPosition(pos);
	for (int i = 0; i < branches.size(); i++)
	{
		branches[i].setPosition(pos.x, i * 150.f);
	}
}
  • 특정 키 입력시 한칸씩 갱신
for (int i = branchesSide.size() - 1; i > 0; i--)
{
	branchesSide[i] = branchesSide[i - 1];
}

branchesSide[0] = (Sides)Utils::RandomRange(0, 3);

for (int i = 0; i < branchesSide.size(); i++)
{
	switch (branchesSide[i])
	{
	case Sides::Left:
		branches[i].setScale(-1.f, 1.f);
		break;

	case Sides::Right:
		branches[i].setScale(1.f, 1.f);
		break;
	}
}
  • 외부에서 마지막 가지 방향을 가져올 수 있게 설정
  • 로테이션, 스케일은 설정하지 않는다.