프로그래밍 공부
작성일
2023. 12. 1. 15:42
작성자
WDmil
728x90

Object와 Player간의 상호작용을 사용해보자.

 

Animation은, 각 객체간 다른 Animation이 적용되어야 하고.

 

Collision작업은 각 객체가 다른 Collision을 적용해야한다.

 

그러나, Render작업은 Buffer에 한번에 데이터를 전달하고 작업해야한다.

 

MonsterManager를 생성하고, 해당 전역객체를 사용해서 몬스터를 생성하고 움직이게 관리해보자.

 

각 객체는 쉽게 말하자면 하이브마인드 와 비슷하게. 중앙관리체계로 동작해야 한다.

 

이러한 방식은 FSM이론과 행동 객체화 방식을 따르는것 이 편하고 좋다.


MonsterManager

#pragma once

// MonsterManager 클래스는 Singleton 패턴을 따르며 게임 내 몬스터를 관리하는 역할을 수행합니다.
class MonsterManager : public Singleton<MonsterManager>
{
private:
    // 몬스터 풀의 크기를 나타내는 상수
    const UINT POOL_SIZE = 100;

    // 몬스터 생성 간격을 나타내는 상수 (초 단위)
    const float SPAWN_INTERVAL = 1.0f;

    // 몬스터 생성 위치의 범위를 나타내는 상수
    const float SPAWN_RANGE = 20.0f;

private:
    // Singleton 클래스에 대한 friend 선언
    friend class Singleton;

    // 생성자 - 외부에서 인스턴스화를 방지하며 필요한 초기화를 수행합니다.
    MonsterManager();

    // 소멸자 - 메모리 누수를 방지하기 위해 몬스터 관리에 사용된 자원을 정리합니다.
    ~MonsterManager();

public:
    // 게임 업데이트 시 호출되는 함수
    void Update();

    // 몬스터 렌더링을 처리하는 함수
    void Render();

    // GUI에서 몬스터 정보를 렌더링하는 함수
    void GUIRender();

    // 몬스터 매니저의 대상(Transform)을 설정하는 함수
    void SetTarget(Transform* target) { this->target = target; }

    // 몬스터 매니저의 대상(Transform)을 반환하는 함수
    Transform* GetTarget() { return target; }

private:
    // 몬스터 생성을 처리하는 내부 함수
    void Spawn();

private:
    // 몬스터 인스턴싱에 사용되는 3D 모델 애니메이터
    ModelAnimatorInstancing* modelInstancing;

    // 몬스터 객체를 저장하는 벡터 컨테이너
    vector<TopViewMonster*> monsters;

    // 몬스터의 대상(Transform)
    Transform* target;

    // 몬스터 생성 간격을 추적하기 위한 변수
    float spawnTime = 0.0f;
};

우선, 싱글톤 형태로 생성하여 어디에서도 접근이 가능하도록 제작한다.

 

Manager는 각 객체에 대해 생성을 쉽게 관리할 수 있게 해줄것이다.

 

그리고, BUffer의 최대값을 미리 지정해야 하기 때문에, POOL_SIZE를 설정하고. Buffer데이터의 사이즈를 미리 정의해준다.

 

몬스터의 생성간격, 위치범위를 미리 설정해서 어떤 위치에 생성되는지, 어떨때 마다 생성되는지를 만들어준다.

 

 

몬스터의 객체 저장벡터는. 그때그때 생성될것 같지만, 사실 미리 다 전부 정의해놓고. 필요한 객체가 생길때 마다.

 

렌더되지않은 객체를 찾은뒤. 해당 객체를 활성화하고 생성위치에 생성해주는것 이다.

 

#include "Framework.h"

// MonsterManager 생성자
MonsterManager::MonsterManager()
{
    // 몬스터에 사용될 3D 모델 애니메이터 초기화
    modelInstancing = new ModelAnimatorInstancing("Monster");
    modelInstancing->ReadClip("Walk");
    modelInstancing->ReadClip("Attack1");
    modelInstancing->ReadClip("Hit1");
    modelInstancing->CreateTexture();

    // 몬스터 배열 크기 설정
    monsters.resize(POOL_SIZE);

    // 몬스터 객체 초기화 및 모델 인스턴스에 추가
    for (TopViewMonster*& monster : monsters)
    {
        monster = new TopViewMonster(modelInstancing->Add());
    }
}

// MonsterManager 소멸자
MonsterManager::~MonsterManager()
{
    // 할당된 몬스터 객체들을 메모리에서 해제
    for (TopViewMonster* monster : monsters)
        delete monster;
}

// 게임 업데이트 시 호출되는 함수
void MonsterManager::Update()
{
    // 몬스터 생성 간격 업데이트
    spawnTime += DELTA;

    // 일정 간격마다 몬스터 생성 함수 호출
    if (spawnTime >= SPAWN_INTERVAL)
    {
        spawnTime -= SPAWN_INTERVAL;
        Spawn();
    }

    // 모델 인스턴싱 업데이트
    modelInstancing->Update();

    // 각 몬스터의 업데이트 함수 호출
    for (TopViewMonster* monster : monsters)
        monster->Update();
}

// 몬스터 렌더링을 처리하는 함수
void MonsterManager::Render()
{
    // 모델 인스턴싱 렌더링
    modelInstancing->Render();

    // 각 몬스터의 렌더링 함수 호출
    for (TopViewMonster* monster : monsters)
        monster->Render();
}

// GUI에서 몬스터 정보를 렌더링하는 함수
void MonsterManager::GUIRender()
{
    // 모델 인스턴싱의 GUI 렌더링
    modelInstancing->GUIRender();

    // 각 몬스터의 GUI 렌더링 함수 호출
    for (TopViewMonster* monster : monsters)
        monster->GUIRender();
}

// 몬스터 생성을 처리하는 함수
void MonsterManager::Spawn()
{
    // 비활성화된 몬스터를 찾아 생성
    for (TopViewMonster* monster : monsters)
    {
        if (!monster->IsActive())
        {
            // 몬스터를 활성화하고 생성 위치 설정
            monster->SetActive(true);
            Vector3 pos = target->GetGlobalPosition();
            Vector3 direction;
            direction.x = MATH->Random(-1.0f, 1.0f);
            direction.z = MATH->Random(-1.0f, 1.0f);

            pos += direction.GetNormalized() * SPAWN_RANGE;
            monster->SetLocalPosition(pos);

            // 생성 후 바로 리턴하여 하나의 몬스터만 생성
            return;
        }
    }
}

 

몬스터가 IsActive되어있지 않을경우,(활성화 되어있지 않을경우) 를 찾아. 생성될 때 해당되는 몬스터를 위치값 조정해주고 렌더처리를 해준다.


TopVIewMonster

 

#pragma once

// TopViewMonster 클래스는 CapsuleCollider를 상속받아 몬스터의 기본 동작을 정의합니다.
class TopViewMonster : public CapsuleCollider
{
private:
    // 몬스터의 상태를 표현하는 열거형
    enum ActionState
    {
        PATROL, // 순찰 상태
        TRACE,  // 추적 상태
        ATTACK, // 공격 상태
        NONE    // 상태 없음
    };

public:
    // 생성자 - 몬스터의 초기 설정을 수행하며 Transform을 전달 받습니다.
    TopViewMonster(Transform* transform);

    // 소멸자 - 몬스터 관련 자원을 정리합니다.
    ~TopViewMonster();

    // 몬스터의 업데이트를 처리하는 함수
    void Update();

    // 몬스터를 렌더링하는 함수
    void Render();

    // 몬스터의 정보를 GUI로 렌더링하는 함수
    void GUIRender();

private:
    // 몬스터의 현재 행동 상태를 결정하는 함수
    void CheckAction();

    // 몬스터의 행동 상태를 설정하는 함수
    void SetAction(ActionState state);

    // 몬스터의 순찰 동작을 처리하는 함수
    void Patrol();

    // 몬스터의 추적 동작을 처리하는 함수
    void Trace();

    // 몬스터의 공격 동작을 처리하는 함수
    void Attack();

    // 몬스터의 다양한 행동을 생성하는 함수
    void CreateActions();

private:
    // 몬스터의 3D 모델 Transform
    Transform* meshTransform;

    // 몬스터의 추적 대상(Transform)
    Transform* target = nullptr;

    // 몬스터의 이동 속도
    float moveSpeed = 5.0f;

    // 몬스터의 회전 속도
    float rotSpeed = 10.0f;

    // 몬스터의 현재 속도 벡터
    Vector3 velocity;

    // 몬스터의 현재 행동 상태
    ActionState curState = NONE;

    // 몬스터의 다양한 행동을 담는 벡터
    vector<MonsterAction*> actions;
};

 

몬스터의 충돌과 행동패턴을 정의한다.

 

각 객체가 Player와 충돌했는지 판별 또는 거리가 얼마나 되는지 판별하며. 행동패턴을 바꾸어주는 객체이다.

 

각 객체는 Collider와 curState를 가지고 있으며, 해당되는 curState가 되었을 시. 해당되는 행동을 실행한다.

 

각 행동은 vector형태로 주소값이 정의되어있다.

 

#include "Framework.h"

// TopViewMonster 클래스 생성자
TopViewMonster::TopViewMonster(Transform* transform) : meshTransform(transform)
{
    // 전달받은 Transform을 부모로 설정하고 필요한 초기화 수행
    transform->SetParent(this);
    transform->SetTag("TopViewMonster");
    transform->Load();

    // 몬스터는 처음에는 비활성화 상태로 시작
    isActive = false;

    // 몬스터의 다양한 행동을 생성하는 함수 호출
    CreateActions();

    // 초기 행동 상태를 PATROL로 설정
    SetAction(PATROL);
}

// TopViewMonster 클래스 소멸자
TopViewMonster::~TopViewMonster()
{
    // 몬스터의 소멸자에서는 추가적인 정리 작업이 필요하지 않음
}

// 몬스터의 업데이트를 처리하는 함수
void TopViewMonster::Update()
{
    // 몬스터가 비활성화 상태인 경우 업데이트를 수행하지 않음
    if (!IsActive()) return;

    // 부모 클래스의 UpdateWorld 함수 호출
    UpdateWorld();
}

// 몬스터를 렌더링하는 함수
void TopViewMonster::Render()
{
    // 몬스터가 비활성화 상태인 경우 렌더링을 수행하지 않음
    if (!IsActive()) return;

    // 현재 행동 상태에 따라 해당하는 행동 수행
    CheckAction();
    actions[curState]->Update();

    // CapsuleCollider의 Render 함수 호출
    __super::Render();
}

// 몬스터의 정보를 GUI로 렌더링하는 함수
void TopViewMonster::GUIRender()
{
    // 몬스터가 비활성화 상태인 경우 GUI 렌더링을 수행하지 않음
    if (!IsActive()) return;

    // CapsuleCollider의 GUIRender 함수 호출
    __super::GUIRender();
}

// 몬스터의 행동 상태를 체크하는 함수
void TopViewMonster::CheckAction()
{
    // 대상이 설정되어 있지 않다면 MonsterManager에서 대상을 가져옴
    if (target == nullptr)
        target = MonsterManager::Get()->GetTarget();

    // 몬스터와 대상 간의 거리 계산
    float distance = (localPosition - target->GetLocalPosition()).Length();

    // 거리에 따라 행동 상태 설정
    if (distance < 10)
        SetAction(TRACE);
    else if (distance >= 10)
        SetAction(PATROL);
}

// 몬스터의 행동 상태를 설정하는 함수
void TopViewMonster::SetAction(ActionState state)
{
    // 현재 상태와 동일한 상태로 설정되면 무시
    if (curState == state) return;

    // 상태 변경 시 현재 상태 업데이트 및 해당 상태의 행동 시작
    curState = state;
    actions[state]->Start();
}

// 몬스터의 순찰 동작을 처리하는 함수 (추후 구현 필요)
void TopViewMonster::Patrol()
{
    // 순찰 동작 구현
}

// 몬스터의 추적 동작을 처리하는 함수 (추후 구현 필요)
void TopViewMonster::Trace()
{
    // 추적 동작 구현
}

// 몬스터의 공격 동작을 처리하는 함수 (추후 구현 필요)
void TopViewMonster::Attack()
{
    // 공격 동작 구현
}

// 몬스터의 다양한 행동을 생성하는 함수
void TopViewMonster::CreateActions()
{
    // 순찰 및 추적 동작을 담은 행동 객체들을 생성하고 벡터에 추가
    actions.push_back(new MonsterPatrol(this));
    actions.push_back(new MonsterTrace(this));
}

 

몬스터의 초기행동은 PATROL로 생성한다.

 

그리고, 첫 생성시 isActive를 false로하여. 첫 생성자가 중앙에 나타나지 않게 한다.

 

모든 행동은 IsActive가 활성화 상태가 아닐경우. 동작하지 않는다.

 

IsActive는 MonsterManager가 바꿍준다.

 

몬스터의 상태는 ChackAction을 통해 관리한다.

 

상태값의 target이 nullptr일 경우, Target을 플레이어로 재정의 해준다.

 

몬스터와 대상간의 거리를 계산하여. 거리값이 10이상일경우. 순찰하고. 10보다 작을경우 추적한다.

 

SetAction은 전에 만들었던것 과 같다.

 

curState가 중복으로 실행되지 않도록 하며. 아닐경우 새로운 curState로 정의하고. 해당되는 Animation을 실행한다.

 

createAction을 통해 동작함수들을 Vector에 추가한다.


MonsterAction

#pragma once

class TopViewMonster;

class MonsterAction
{
public:
	MonsterAction(TopViewMonster* monster)
		: monster(monster) {};

	virtual void Update() = 0;
	virtual void Start() {};

protected:
	void Move(Vector3 direction ,float MoveSpeed ,float rotSpeed);
	void LookAtRotate(Vector3 direction, float rotSpeed);

protected:
	TopViewMonster* monster;
};

 

Monster의 행동에 대해 정의해주는 인터페이스 클래스이다.

 

위 함수를 상속받아서 각 함수가 동작하게 된다.

#include "Framework.h"

// MonsterAction 클래스의 Move 함수 구현
void MonsterAction::Move(Vector3 direction, float MoveSpeed, float rotSpeed)
{
    // 몬스터를 주어진 방향으로 이동시키고 주어진 속도로 회전
    monster->Translate(direction.GetNormalized() * MoveSpeed * DELTA);
    LookAtRotate(direction, rotSpeed);
}

// MonsterAction 클래스의 LookAtRotate 함수 구현
void MonsterAction::LookAtRotate(Vector3 direction, float rotSpeed)
{
    // 몬스터의 현재 전방 벡터와 목표 방향 벡터 간의 외적 계산
    Vector3 cross = Vector3::Cross(monster->GetForward(), direction);

    // 외적 결과에 따라 몬스터를 회전시킴
    if (cross.y < FLT_EPSILON)
        monster->Rotate(Vector3::Up() * rotSpeed * DELTA);
    else if (cross.y > -FLT_EPSILON)
        monster->Rotate(Vector3::Down() * rotSpeed * DELTA);
}

 

공통되게 행동하게 하는. Move와 Rotate는 미리 구현해주고 상속받는 함수에서 사용한다.

 


MonsterPatrol

 

몬스터가 순찰행동하는 것 을 정의한다.

#pragma once

// MonsterPatrol 클래스는 MonsterAction을 상속받아 몬스터의 순찰 행동을 정의합니다.
class MonsterPatrol : public MonsterAction
{
private:
    // 순찰 범위를 나타내는 상수
    const float RANGE = 10.0f;

public:
    // 생성자 - TopViewMonster 포인터를 전달받아 초기화
    MonsterPatrol(TopViewMonster* monster);

    // MonsterAction을 통해 상속된 순찰 행동 업데이트 함수
    void Update() override;

    // 순찰 행동이 시작될 때 호출되는 함수
    void Start() override;

private:
    // 순찰 목표 위치를 설정하는 함수
    void SetDestPos();

private:
    // 순찰 동작의 이동 속도
    float moveSpeed = 3.0f;

    // 순찰 동작의 회전 속도
    float rotSpeed = 10.0f;

    // 순찰 목표 위치
    Vector3 destPos;
};
#include "Framework.h"

// MonsterPatrol 클래스 생성자
MonsterPatrol::MonsterPatrol(TopViewMonster* monster)
    : MonsterAction(monster)
{
}

// MonsterAction을 통해 상속된 순찰 행동 업데이트 함수
void MonsterPatrol::Update()
{
    // 목표 위치까지의 방향 벡터 계산
    Vector3 direction = destPos - monster->GetLocalPosition();
    direction.y = 0;

    // 계산된 방향으로 이동 및 회전
    Move(direction, moveSpeed, rotSpeed);

    // 목표 위치에 도달했을 때 새로운 목표 위치 설정
    if (direction.Length() < 1.0f)
        SetDestPos();
}

// 순찰 행동이 시작될 때 호출되는 함수
void MonsterPatrol::Start()
{
    // 초기 목표 위치 설정
    SetDestPos();
}

// 순찰 목표 위치를 설정하는 함수
void MonsterPatrol::SetDestPos()
{
    // 무작위로 방향을 생성하여 목표 위치 설정
    Vector3 direction;
    direction.x = MATH->Random(-1.0f, 1.0f);
    direction.z = MATH->Random(-1.0f, 1.0f);

    destPos = monster->GetLocalPosition() + direction.GetNormalized() * RANGE;
}

 

목적된 위치까지 이동하고, 이동했을 경우. 다른위치로 다시 설정하여 이동한다.


MonsterTrace

#pragma once

// MonsterTrace 클래스는 MonsterAction을 상속받아 몬스터의 추적 행동을 정의합니다.
class MonsterTrace : public MonsterAction
{
public:
    // 생성자 - TopViewMonster 포인터를 전달받아 초기화
    MonsterTrace(TopViewMonster* monster);

    // MonsterAction을 통해 상속된 추적 행동 업데이트 함수
    void Update() override;

    // 추적 행동이 시작될 때 호출되는 함수
    void Start() override;

private:
    // 추적 동작의 이동 속도
    float moveSpeed;

    // 추적 대상(Transform)
    Transform* target = nullptr;
};
#include "Framework.h"

// MonsterTrace 클래스 생성자
MonsterTrace::MonsterTrace(TopViewMonster* monster)
    : MonsterAction(monster)
{
}

// MonsterAction을 통해 상속된 추적 행동 업데이트 함수
void MonsterTrace::Update()
{
    // 추적 대상까지의 방향 벡터 계산
    Vector3 direction = target->GetLocalPosition() - monster->GetLocalPosition();

    // 계산된 방향으로 이동 및 회전
    Move(direction, 5, 10);
}

// 추적 행동이 시작될 때 호출되는 함수
void MonsterTrace::Start()
{
    // MonsterManager에서 대상을 가져와 설정
    target = MonsterManager::Get()->GetTarget();
}

움직임을 정의해주는 함수는 더 간단하다. 그저 계산된 방향으로 이동하게 한다.

 

Move는 MonsterAction에서 정의함으로. 방향까지 다시 재정의해준다.

 

 

728x90