[Unreal] 03.C++ 스크립트로 변환

본 문서는 어소트락 언리얼엔진 게임프로그래머 양성과정의 강의를 토대로 필기한 내용입니다

C++로 스크립트 생성

스폰

PlayerCharacter.h


UCLASS()
class STUDY_240601_API APlayerCharacter : public ACharacter
{
...

protected:
	// TObjectPtr은 객체의 메모리 주소를 저장함
	UPROPERTY(VisibleAnywhere)
	TObjectPtr<USpringArmComponent> mArm; // 언리얼 UObject용 포인터 선언
	UPROPERTY(VisibleAnywhere)
	TObjectPtr<UCameraComponent> mCamera;

	// TSubclassOf는 UClass(클래스 타입) 정보를 저장한다
	TSubclassOf<AActor> mBulletClass;

...

protected:
	void OnAttack(const FInputActionValue& InputValue); // OnAttack 함수 선언
}

PlayerCharacter.cpp

APlayerCharacter::APlayerCharacter()
{
 	...

	static ConstructorHelpers::FClassFinder<AActor>BulletClassAsset(TEXT("/Script/Engine.Blueprint'/Game/Test/BP_Bullet.BP_Bullet_C'")); // 경로 마지막에 _C 붙여주기

	if (BulletClassAsset.Succeeded()) mBulletClass = BulletClassAsset.Class; // 불렛클래스에셋 생성을 성공하면 미리 만들어 둔 mBulletClass 클래스 타입에 클래스정보
}
  • GetWorld()
    • 전체 월드를 셀을 나눠놓고 그 셀에 소속된 부분을 만들어 낼 수 있다
    • Level, GameMode, WorldSettings, WorldPartitian
    • World안에 Level이 소속되있는 개념
  • 스폰을 위해서는 클래스 정보가 필요함
    • TSubclassOf는 UClass(클래스 타입) 정보를 저장함
    • TSubclassOf<AActor> mBulletClass;
    • 클래스 - 객체 관계를 잘 구분지어 생각하기
    • 경로 마지막에 _C 붙여주기!

Input

  • UObject 타입으로 클래스 생성

InputData.h

UCLASS()
class STUDY_240601_API UDefaultInputData : public UObject
{

	...

public:
	UInputAction* mMove = nullptr;
	UInputAction* mAttack = nullptr;
};

InputData.cpp

// Fill out your copyright notice in the Description page of Project Settings.


#include "InputData.h"

UDefaultInputData::UDefaultInputData()
{

    ...

	static ConstructorHelpers::FObjectFinder<UInputAction>Attack(TEXT("/Script/EnhancedInput.InputAction'/Game/Test/Input/IA_Attack.IA_Attack'")); // 정적 변수로 선언 (프로그램과 생명주기를 함께함)

	if (Attack.Succeeded()) mAttack = Attack.Object;
	
	static ConstructorHelpers::FObjectFinder<UInputAction>Shield(TEXT("/Script/EnhancedInput.InputAction'/Game/Test/Input/IA_Shield.IA_Shield'"));

	if (Shield.Succeeded()) mShield = Shield.Object;

}

스킬

Shield를 만들고 플레이어 기준 방어벽을 앞뒤양옆 네방향으로 세우고 시계방향으로 회전

  • 생성할 오브젝트인 ShieldActor를 하나 만든다
  • SetLifeSpan() : 일정시간 뒤에 오브젝트 파괴하는 함수

ShieldActor.h

#pragma once

#include "../GameInfo.h"
#include "GameFramework/Actor.h"
#include "ShieldActor.generated.h"

UCLASS()
class STUDY_240601_API AShieldActor : public AActor
{
	GENERATED_BODY()
	
public:	
	// Sets default values for this actor's properties
	AShieldActor();

protected:
	UPROPERTY(VisibleAnywhere)
	TObjectPtr<UStaticMeshComponent> mMesh; // 언리얼 UObject용 포인터 선언

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

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

};

ShieldActor.cpp

#include "ShieldActor.h"

// Sets default values
AShieldActor::AShieldActor()
{
 	// 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;

	mMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("Mesh"));

	SetRootComponent(mMesh);

	static ConstructorHelpers::FObjectFinder<UStaticMesh>MeshAsset(TEXT("/Script/Engine.StaticMesh'/Game/Test/Cube.Cube'"));
	if (MeshAsset.Succeeded()) mMesh->SetStaticMesh(MeshAsset.Object);

	mMesh->SetRelativeScale3D(FVector(0.2, 0.5, 0.5));

	SetLifeSpan(5.f); // 일정 시간 뒤에 오브젝트 파괴하는 함수
}

// Called when the game starts or when spawned
void AShieldActor::BeginPlay()
{
	Super::BeginPlay();
	
}

// Called every frame
void AShieldActor::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

}

PlayerCharacter.h


UCLASS()
class STUDY_240601_API APlayerCharacter : public ACharacter
{
...

protected:
	// 누적 시간 (쿨타임마다 초기화)
	float mShieldTime = 0.f;
	// 스킬 쿨타임
	float mShieldCoolDown = 6.f;
	// 스킬 사용가능 여부 불린 값
	bool mShieldEnable = true;

protected:
	void OnShield(const FInputActionValue& InputValue); // OnShield 함수 선언

PlayerCharacter.cpp

#include "../Skill/ShieldActor.h"

APlayerCharacter::APlayerCharacter()
{
	mRotation = CreateDefaultSubobject<USceneComponent>(TEXT("Rotation"));
	mRotationMovement = CreateDefaultSubobject<URotatingMovementComponent>(TEXT("RotationMovement"));

	// Shield 회전
	mRotation->SetupAttachment(RootComponent); // 로테이션을

	mRotationMovement->SetUpdatedComponent(mRotation);
	mRotationMovement->RotationRate.Yaw = 180.0;
}

// Called every frame
void APlayerCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);

	// 실드 스킬이 사용 불가능 상태일 경우 CoolDown 계산
	if (mShieldEnable == false)
	{
		mShieldTime += DeltaTime;
		if (mShieldTime >= mShieldCoolDown)
		{
			mShieldEnable = true;
			mShieldTime = 0;
		}
	}
}

void APlayerCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	...

	// Shield 함수
	EnhancedInput->BindAction(InputData->mShield, ETriggerEvent::Started, this, &APlayerCharacter::OnShield);

}

void APlayerCharacter::OnShield(const FInputActionValue& InputValue)
{
	// 실드 스킬을 사용할 수 있는지 판단하고 사용할 수 없을 경우 바로 함수를 빠져나간다
	if (mShieldEnable == false) return;

	// 사용 불가능 상태로 만듬
	mShieldEnable = false;

	// 월드 정보를 미리 만들고 부모에 상속시키는 방법
	FVector Location[4];
	Location[0] = GetActorLocation() + GetActorForwardVector() * 200.f;
	Location[1] = GetActorLocation() + GetActorRightVector() * 200.f;
	Location[2] = GetActorLocation() + GetActorForwardVector() * -200.f;
	Location[3] = GetActorLocation() + GetActorRightVector() * -200.f;

	for (int32 i = 0; i < 4; i++)
	{
		FRotator Rot = GetActorRotation() + FRotator(0.0, i * 90.0, 0.0);

		// StaticClass() : UClass 정보를 얻어옴
		AShieldActor* Shield = GetWorld()->SpawnActor<AShieldActor>(AShieldActor::StaticClass(), Location[i], Rot);

		// Shield의 부모로 플레이어를 지정한다
		// Shield->AttachToActor : 부모로 지정된 Actor의 RootComponent에 붙여준다
		// Shield->AttachToComponent : 원하는 컴포넌트를 지정하여 붙여준다

		// KeepWorldTransform : 월드 정보를 유지하며 붙여준다
		// KeepRelativeTransform : 상대 정보를 유지하며 붙여준다
		Shield->AttachToComponent(mRotation, FAttachmentTransformRules::KeepWorldTransform); // 월드 정보를 유지하면서 부모에 붙여주기
		
	}
}

회전

입력액션

  • Vector2D로 설정
  • MouseX (마우스 횡이동), MouseY (마우스 종이동)
  • bUseControllerRotationYaw : true 일때, 폰의 Yaw가 컨트롤러 Yaw 로테이션과 매칭된다
  • 부모로부터 상속받은 회전값의 상속을 끊음
    • mArm->bInheritPitch = false;
    • mArm->bInheritYaw = true;
    • mArm->bInheritRoll = false;

실습

  • w,s 누르면 -> 탱크 이동
  • a,d 누르면 -> 탱크 좌우 회전
  • 마우스 좌우회전 -> 헤드 좌우 회전
  • 마우스 상하회전 -> 포신 상하 회전
  • 마우스 왼쪽클릭 -> 불렛 발사
  • 스킬 마우스 1번 -> 불렛 3발 동시 발사
  • 일정 시간에 한 번씩 -> 불렛 5발 발사

© 2023 Jinsoo Lee. All rights reserved.

Powered by Hydejack v9.1.6