본문 바로가기

언리얼C++게임개발/02.액터의 설계

[이득우C++게임개발] 액터의 설계 - 언리얼 콘텐츠의 구성요소

언리얼 레벨에디터 뷰포트에 보이는 작업공간은 현실 세계를 모방한 가상공간으로 월드라고 부른다. 월드는 공간, 시간, 물리, 렌더링으로 이루어지는더 더 자세한건 툴바의 세팅>월드세팅 메뉴를 통해 확인 가능하다.

컨텐츠를 구성하는 요소로는 액터,레벨, 컴포넌트등이 있다. 액터는 배우, 레벨은 무대, 컴포넌트는 배우와 레벨을 꾸며주는 다양한 기술 효과라고 이해하면 편할듯 하자.

 

액터의 설계

우리가 앞에서 Fountain이라는 C++클래스를 만들었지만 배치해보면 아무것도 보이지 않는데 아웃라이너에서 이름은 확인할 수 있다. 우리는 C++클래스의 이름만 지워주었지 아직 아무것도 설정한게 없어서 그렇다. 블루프린트하고 좀 다르다.

언리얼 C++프로젝트 구성은 4.15버전 부터 IWYU(Include What You Use)라는 필요한 헤드들만 최소기능으로 참조하도록 템플릿이 변경됐다. 기본헤드에 선언되어지지 않은 함수들은 그때 그때 포함시켜야 한다. ㅠㅠ

CoreMinimal.h 대신 EngineMinimal.h를 사용하면 기본적인 헤드들을 포함해서 컴파일 에러를 줄여준다.

자 이제 분수를 보이게 하기 위해 VS에디터의 Fountain.h탭을 선택한다.

#pragma once

//#include "CoreMinimal.h"  //오브젝트가 필요한 최소기능만 선언된 헤드
#include "EngineMinimal.h" //CoreMinimal.h보다 좀더 제작에 필요한 기능이 선언된 헤드
#include "GameFramework/Actor.h"  //액터클래스의 헤드
#include "Fountain.generated.h"  //언리얼 헤더툴을 이용해 언리얼에 필요한 부가정보를 포함 맨마지막에 위치해야한.

UCLASS()  //언리얼 오브젝트임을 선언하기 위한 매크로
class ARENABATTLE_API AFountain : public AActor  //모쥴명_API를 선언해 외부모듈에 공개 _declspec(dllexport)
{  //Class이름은 액터는 A 아닌건 U를 파일명앞에 접두사로 붙인다. 
	GENERATED_BODY() //UCLASS()와 동시에 선언하는 매크로
	
public:	
	// Sets default values for this actor's properties
	AFountain();  //클래스 생성자

protected:
	// Called when the game starts or when spawned
	virtual void BeginPlay() override;

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
};

UCLASS : 언리얼오브젝트를 선언하기 위한 매크로 내부에는 GENERATED_BODY()와 같이 사용

클래스이름 접두어 : 액터는 A 컴포넌트와 같은 나머지는 U가 붙는다.

generated.h 헤더파일 : 소스코드를 컴파일 하기 이전에 언리얼 엔진은 언리얼 헤더툴이라는 도구를 사용해 클래스 선언을 분석하고 언리얼 실행 환경에 필요한 부가정보를 별도의 파일에 생성되는 파일이다. 모든 정보를 읽어야 하기 때문에 항상마지막에 생성된다. CDO(Create Default Object)가 포함된다.

외부모듈 공개 여부 : 클래스명 앞에 프로젝트명_API가 선언되어 있는데 익 없으면 다른 모듈에서 해당 객체에 접근할 수 없다. 윈도우 DLL시스템의 규칙이다.

 

 

헤더 아래 부분에  StaticMeshComponent를 지정할 포인터를 선언한다. UStaticMeshComponet라는 클래스는 언리얼의 특수객체로 언리얼에디터와 연결하기 위해 UPROPERTY() 매크로를 꼭 사용해야한다.  이러면 언리얼에서 메모리 관리를 해주는데 다 되는건 아니고 UObject만 해당된다. 인스턴스가 뷰포트에서 사라질때 언리얼이 알아서 delete없이 메모리상에서 지워줘 댕글링포인트 생성을 막아준다.

인생언리얼에서는 UStaticMeshComponent앞에 class를 붙여 전방선언을 하는데 헤더파일이 포함되 무거워지는걸 방지하는 거다.

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;
	UPROPERTY() //언리얼실행환경이 객체를 자동으로 관리하게 한는 매크로
	UStaticMeshComponent* Body; //객체가 StaticMesh컴포넌트를 지정할수 있는포인터 선언
	UPROPERTY() //언리얼오브젝트라는 객체에만 적용할 수 있다.
	UStaticMeshComponent* Water; //객체가 StaticMesh컴포넌트를 지정할수 있는포인터 선언
};

Fountain.cpp로 이동해서 다음코드를 생성자에 입력한다. 이코드는 그냥 외워야 한다.

원래는 UStaticMeshComponent를 사용하기 위해 "Components/StaticMeshComponent.h"를 선언해줘야하는데 헤더에서
CoreMinimal.h"대신  "EngineMinimal.h"를 사용해서 인지 에러가안난다. 

#include "Fountain.h"

// Sets default values
AFountain::AFountain()
{
 	// Set this actor to call Tick() every frame.  You can turn this off to improve performance if you don't need it.
	PrimaryActorTick.bCanEverTick = true;
	//객체를 생상하기 위해 new대신 CreateDefaultSubobject<객체타입>()을 사용한다.
	Body = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("BODY"));  
	//TEXT()의 문자열은 컴포넌트의 해시값을 생성하기에 중복되지 않아야 한다.
	Water = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("WATER"));
	//TEXT()는 모든 플랫폼에서 2바이트 문자열체계를 동일하기 유지하는 매크로다
	RootComponent = Body; // Body를 객체의 Root로 해준다. 다른설정방법은 SetRootComponent(Body)
	Water->SetupAttachment(Body); //Water를 Body의 자식으로 해준다.
}

책에서는 Visual Studio에서 빌드후 언리얼에디터로 돌아가라고 했는데 생성자를 변경했을 경우는 꼭 언리얼에디터를 끄고 VS에서 오른쪽 빈삼각형을 눌러 빌드해서 자동으로 언리얼에디터가 로딩되게 해야한다. 앞으로 이설명은 생략한다. 이걸 몰라 언리얼을 포기한다. ㅎㅎ

언리얼에디터가 열리면 Fountain에 끌어다 놓으면

뭔가 작은공이 보이고 디텔일 패널에서 Body(BODY)를 선택하면 모든기능이 비활성화 되어 있다. 자식으로 연결한 Water도 보이지 않고

Visual Studio에서 Fountain.h의 UPROPERTY()를 UPROPERTY(VisibleAnywhere)로 변경한다.

public:	
	// Called every frame
	virtual void Tick(float DeltaTime) override;

	//언리얼실행환경이 객체를 자동으로 관리하게 한는 매크로
	//객체가 StaticMesh컴포넌트를 지정할수 있는포인터 선언
	UPROPERTY(VisibleAnywhere) 
	UStaticMeshComponent* Body; 
	
	//객체가 StaticMesh컴포넌트를 지정할수 있는포인터 선언
	UPROPERTY(VisibleAnywhere) 
	UStaticMeshComponent* Water; 
};

언리얼을 끌로 빌드하고 언리얼에디터를 자동로딩시킨다.  Fountain객체를 확인해보면 Body와 Water의 내용등이 활성화 되어 있다.

Body의 Static Mesh탭으로 이동해서 foun를 검색해서 SM_Plains_Castle_Fontain_01을 선택하면 레벨에 분수대가 보인다.

Location을 리셋해서 0,0,0으로 해준다.

Water StaticMeshComponent를 선택하고 StaticMesh탭에서 SM_Plains_Castle_Fontain_02를 선택하고 Location을 리셋후 위에서 흐를수 있게 Z를 135로 변경해준다. 작은 분수대가 위에 나타난다.

작은 분수대 water컴포넌트는 Z위치가135인데 객체를 레벨에 끌어 놓을때 마다 재설정하지 않으려면 C++클래스에 코드를 넣어준다. 생성자 맨 아래 SetupAttachmetn() 밑에 넣어준다.

	Water->SetupAttachment(Body); //Water를 Body의 자식으로 해준다.
	Water->SetRelativeLocation(FVector(0, 0, 135)); // 자식의 위치를 부모보다 위로 z:135 이동시킨다.

액터를 수정했으므로 VS에서 빌드후 언리얼에디터가 자동로딩된후 Fountain Water Transform을 확인해보면 아무것도 변한게 없어 보이지만 오른쪽 리셋버튼이 없어져 Z:135가 디폴트 값이 된걸 알 수 있다.

이제 액터에 조명과 찰랑거리는 물 이펙트를 추가하겠다. Fountain.h의 public: 맨아래 추가한다.

	//라이트효과
	UPROPERTY(VisibleAnywhere)
	UPointLightComponent* Light;

	//파티클효과
	UPROPERTY(VisibleAnywhere)
	UParticleSystemComponent* splash;
};

빌딩후 언리얼자동리로딩후 레벨의 Foutain객체의 디테일의 Spalsh의 Particle컴포넌트탬의 Template를 P_Water_Fountain_Splash_Base_01에셋으로 선택한다.

레벨에서 물이 흘로 나오고 분수대 안이 밝아진걸 알수 있다.