본문 바로가기
DirectX/[갠플] Maple Story 모작

22-07-18

by GameStudy 2022. 7. 18.

본 모작은 상업적 용도가 아닌, 공부 목적의 모작입니다.


 

 

1. 레벨 나누기 

  1.1 구현할 레벨들

    Note) 최대한 적으면서도 핵심적인 레벨 구현

      최대한 핵심에 집중할 수 있게끔, 레벨은 최소한도로 잡아보았습니다.

      로그인 화면 -> 시간의 신전 입구 -> 시간의 신전(마을의 역할) -> 사냥터 -> 보스맵

    

  1.2 레벨 구현

    Note) 다음은 레벨 중, LoginLevel 클래스를 구현한 예입니다.

      다른 레벨 또한, 똑같은 형식으로 클래스를 구성해 보았습니다.

      - 코드 재사용성을 위한 상속.

        엔진 프로그래머가 작성한 GameEngineLevel을 상속받아, 코드 재사용성을 높혔습니다.

      - 암시적으로 기본 생성자 구현되는 것을 막아서, 컨텐츠 프로그래머가 염두에 두게끔 했습니다.

      - 복사 생성자, 대입 생성자 또한 암시적으로 구현되는 것을 막아서

        개체간의 얕은 복사를 막았습니다.

      - override 키워드를 통해서, 부모 클래스의 메서드를 오버라이드 했음을 명시적으로 표시했습니다.

        이를 통해서, 만약 자식 메서드의 시그니처가 부모 메서드의 시그니처와 다를 경우를 막을수 있었습니다.

// <hide/>

// LoginLevel.h

#pragma once
#include <GameEngineCore/GameEngineLevel.h>

// 설명 :
class LoginLevel : public GameEngineLevel
{
public:
    // constrcuter destructer
    LoginLevel();
    ~LoginLevel();

    // delete Function
    LoginLevel(const LoginLevel& _Other) = delete;
    LoginLevel(LoginLevel&& _Other) noexcept = delete;
    LoginLevel& operator=(const LoginLevel& _Other) = delete;
    LoginLevel& operator=(LoginLevel&& _Other) noexcept = delete;

protected:
    void Start() override;
    void Update(float _DeltaTime) override;
    void End() override;

private:

};
// <hide/>

// LoginLevel.cpp

#include "PreCompile.h"
#include "LoginLevel.h"

LoginLevel::LoginLevel() 
{
}

LoginLevel::~LoginLevel() 
{
}

void LoginLevel::Start()
{
}

void LoginLevel::Update(float _DeltaTime)
{
}

void LoginLevel::End()
{
}

 

  1.3 Login Level 디자인

    - LoginLevel에는 배경 텍스쳐, 로그인 버튼, 게임 종료버튼 세가지를 일단 배치하고자 합니다.

      앞에서 준비한 각 물체의 텍스쳐들을 활용하여서 띄워주었습니다.

      아래는 로그인 레벨에서 어떤 식으로 개체를 생성했는지와 배경 텍스쳐를 띄우는 코드입니다.

// <hide/>

// LoginLevel.cpp

#include "PreCompile.h"
#include "LoginLevel.h"
#include "GlobalContentsValue.h"
#include "LoginBackground.h"
#include "LoginButton.h"
#include "ExitButton.h"

#include <GameEngineBase/GameEngineInput.h>
#include <GameEngineCore/GameEngineCameraActor.h>

...

void LoginLevel::Start()
{
    ...
    
    mpLoginBackground = CreateActor<LoginBackground>(OBJECTORDER::BackGround);
    mpLoginButton = CreateActor<LoginButton>(OBJECTORDER::UI);
    mpExitButton = CreateActor<ExitButton>(OBJECTORDER::UI);
}

...
// <hide/>

// Temple0BackGround.cpp

#include "PreCompile.h"
#include "Temple0BackGround.h"

#include <GameEngineBase/GameEngineWindow.h>
#include <GameEngineCore/GameEngineTextureRenderer.h>

Temple0BackGround::Temple0BackGround()
    : mpRenderer(nullptr)
    , mfWidth(0)
    , mfHeight(0)
    , mfPositionX(0)
    , mfPositionY(0)
{
}

...

void Temple0BackGround::Start()
{
    mfWidth = 2327.f; // 너무 매직 넘버라서, 엔진 프로그래머분께 Getter 메서드를 요청드리고자 합니다.
    mfHeight = 935.f;
    mpRenderer = CreateComponent<GameEngineTextureRenderer>();
    mpRenderer->GetTransform().SetLocalScale({ mfWidth, mfHeight, 1 });

    mpRenderer->SetTexture("Temple0BackGround.png", 0);
}

...

    - 문제는 마우스를 로그인 버튼, 게임 종료버튼에 올려두었을때 이미지가 바뀌어야 했습니다.

      그래서 아래와 같은 작업을 진행하였습니다.

      1. 마우스 위치정보 가져오는 함수 구글링 -> GetCursorPos();

      2. 가져온 정보를 적절하게 캐스팅하고, 엔진단에서 Debug관련 내용을 확인하여 콘솔에 띄워보았습니다.

      3. 띄워보니, 모니터 기준의 위치를 반환하는 함수였습니다. 모니터가 아닌, 

        게임 윈도우를 기준으로 위치를 반환해주어야 해서 ScreenToClien() 함수의 도움을 받았습니다.

      4. 그렇게해서 얻어진 마우스 좌표정보를 데카르트 좌표계에 맞게끔 연산을 진행했습니다.

      5. 위 1~4번 과정을 매 프레임마다 진행하였습니다. 아래는 해당 코드입니다.

// <hide/>

// ContentsCore.cpp

...

POINT  ContentsCore::mptMousePos; // 마우스 위치를 반환하는 Getter와 마우스 위치 저장용 멤버를 
                                  // static으로 선언해서 ContentsCore 개체 생성 없이 얻어올 수 있게끔 하였습니다.
                                  // 또한, 어차피 게임 내에서 마우스는 하나기에 마우스 위치 또한 하나가 되게끔 정적 개념을 활용하였습니다.
float4 ContentsCore::mf4MousePos;

...

void ContentsCore::Update(float _DeltaTime)
{
    GetCursorPos(&mptMousePos);
    ScreenToClient(GameEngineWindow::GetHWND(), &mptMousePos);
    mf4MousePos.x = (float)mptMousePos.x - (GameEngineWindow::GetScale().x / 2.f);
    mf4MousePos.y = -(float)mptMousePos.y + (GameEngineWindow::GetScale().y / 2.f);
}

...

    - 구해낸 마우스 정보를 통해서 아래와같이 활용하였습니다.

...

void LoginButton::Update(float _DeltaTime)
{
	mf4MousePos = ContentsCore::GetMousePos();
	if (mfPositionX - (mfWidth / 2) <= mf4MousePos.x && mf4MousePos.x <= mfPositionX + (mfWidth / 2)
	 && mfPositionY - (mfHeight / 2) <= mf4MousePos.y && mf4MousePos.y <= mfPositionY + (mfHeight / 2))
	{
		mpRenderer->SetFrame(1);

		if (true == GameEngineInput::GetInst()->IsPress("MouseLButtonDown"))
		{
			mpRenderer->SetFrame(2);
			// 알파블랜딩 추가하고 싶음.
			GEngine::ChangeLevel("TempleOfTime0");
		}
	}
	else
	{
		mpRenderer->SetFrame(0);
	}
}

...

 

  1.4 레벨 공통 기능

    - Login 레벨을 제외하고, 나머지 모든 인게임 레벨들은 카메라의 이동이 제한되어야 합니다.

      해당 레벨의 맵 텍스쳐 크기를 확인해서, 그 크기를 벗어나지 못하게끔 하고자 합니다.

    - 근데, 이 기능을 만들어서 하나의 레벨만 쓸것이 아니기때문에, 상속을 활용해보고자 합니다.

      ContentsLevel 클래스를 아래와 같이 만듭니다.

// <hide/>

// ContentsLevel.h

#pragma once
#include <GameEngineCore/GameEngineLevel.h>

// 설명 :
class GameEngineCameraActor;
class Player;
class Portal;
class ContentsLevel : public GameEngineLevel
{
protected: // 자식 레벨들이 사용할 수 있게끔 접근제어자 protected.
    GameEngineCameraActor* mpCamera;
    Player*                mpPlayer;
    Portal*                mpPortal;
    float4                 mf4CameraPosition;
    float4                 mf4MapSize;
    float4                 mf4WindowSize;

public:
    // constrcuter destructer
    ContentsLevel();
    ~ContentsLevel();

    // delete Function
    ContentsLevel(const ContentsLevel& _Other) = delete;
    ContentsLevel(ContentsLevel&& _Other) noexcept = delete;
    ContentsLevel& operator=(const ContentsLevel& _Other) = delete;
    ContentsLevel& operator=(ContentsLevel&& _Other) noexcept = delete;

protected:
    void LimitCameraMoving();

};
// <hide/>

// ContentsLevel.cpp

#include "PreCompile.h"
#include "ContentsLevel.h"

#include <GameEngineBase/GameEngineInput.h>
#include <GameEngineCore/GameEngineCameraActor.h>
#include <GameEngineBase/GameEngineWindow.h>

ContentsLevel::ContentsLevel()
    : mpCamera(nullptr)
    , mpPlayer(nullptr)
    , mpPortal(nullptr)
    , mf4CameraPosition{}
    , mf4MapSize{}
    , mf4WindowSize{}
{
}

ContentsLevel::~ContentsLevel()
{
}

void ContentsLevel::LimitCameraMoving()
{
    float4 f4NextCameraPosition;
    mf4WindowSize = GameEngineWindow::GetScale();
    mf4CameraPosition = mpCamera->GetTransform().GetLocalPosition();
    f4NextCameraPosition = mpCamera->GetTransform().GetLocalPosition();
    mf4MapSize = { 2327.f, 955.f, 0.f, 0.f };
    if (mf4CameraPosition.x <= -(mf4MapSize.x / 2.f) + (mf4WindowSize.x / 2.f) + 10.f)
    {
        f4NextCameraPosition.x = -(mf4MapSize.x / 2.f) + (mf4WindowSize.x / 2.f) + 10.f;
        mpCamera->GetTransform().SetLocalPosition(f4NextCameraPosition);
    }
    if ((mf4MapSize.x / 2.f) - (mf4WindowSize.x / 2.f) - 10.f <= mf4CameraPosition.x)
    {
        f4NextCameraPosition.x = (mf4MapSize.x / 2.f) - (mf4WindowSize.x / 2.f) - 10.f;
        mpCamera->GetTransform().SetLocalPosition(f4NextCameraPosition);
    }
    if (mf4CameraPosition.y <= -(mf4MapSize.y / 2.f) + (mf4WindowSize.y / 2.f) + 10.f)
    {
        f4NextCameraPosition.y = -(mf4MapSize.y / 2.f) + (mf4WindowSize.y / 2.f) + 10.f;
        mpCamera->GetTransform().SetLocalPosition(f4NextCameraPosition);
    }
    if ((mf4MapSize.y / 2.f) - (mf4WindowSize.y / 2.f) - 10.f <= mf4CameraPosition.y)
    {
        f4NextCameraPosition.y = (mf4MapSize.y / 2.f) - (mf4WindowSize.y / 2.f) - 10.f;
        mpCamera->GetTransform().SetLocalPosition(f4NextCameraPosition);
    }
}

'DirectX > [갠플] Maple Story 모작' 카테고리의 다른 글

22-07-27  (0) 2022.07.27
22-07-25  (0) 2022.07.25
22-07-23  (0) 2022.07.23
22-07-20  (0) 2022.07.20
22-07-17  (0) 2022.07.17

댓글