오늘은 캐릭터를 움직여 볼 것이다.

 

일단 블루프린트의 Set Actor Location이라는 기능부터 알아보자

1.Set Actor Location

Set Actor Location

 

Set Actor Location은 말 그대로 액터의 위치를 정해주는 기능이다. 

그리고 결과사진을 보면 블루프린트가 먼저 작동하고 그다음에 구와 벡터가 표시된다는 것을 알 수 있다.

 

이제 코드로 구현해보자 코드에서는 SetActorLocation을 통해 구현할 수 있다.

	SetActorLocation(FVector(0.f, 0.f, 50.f));

결과

 

2.Set Actor Rotation

Set Actor Rotation은 말 그대로 액터를 각도 회전시키는 함수이다. 

블루프린트에서는 Set Actor Rotation노드를 추가해주는 것으로 구현할 수 있다.

Set Actor Rotation

 

이제 코드로 구현해보자

SetActorRotation 함수와 FRotator변수를 사용하여 회전해주었다. 

FRotator의 각 변수는 피치(Pitch, X축), 요(Yaw, Z축), 롤(Roll, Y축) 순서이다.

	SetActorRotation(FRotator(0.f, 90.f, 0.f));

결과화면

3.Add World Offset & Rotation

 

Add World Offset, Add World Rotation 을 통해월드좌표에서 벡터를 더주고 회전을 시킬 수 있다.

Add World Offset
Add World Rotation

이제 코드로 구현해보자

해당 부분을 구현하기전에 매 프레임마다 구가 이동하는 모습을 볼 수 있게 줄 매크로함수를 만들어주자

#pragma once
#include "DrawDebugHelpers.h"

#define DRAW_SPHERE(Location) if (GetWorld()) DrawDebugSphere(GetWorld(),Location,25.f,12,FColor::Red,true);
#define DRAW_SPHERE_SingleFrame(Location) if (GetWorld()) DrawDebugSphere(GetWorld(),Location,25.f,12,FColor::Red,false,-1.f);
#define DRAW_LINE(StartLocation,EndLocation) if(GetWorld()) DrawDebugLine(World, StartLocation,EndLocation, FColor::Red, true, -1.f, 0, 1.f);
#define DRAW_LINE_SingleFrame(StartLocation,EndLocation) if(GetWorld()) DrawDebugLine(World, StartLocation,EndLocation, FColor::Red, false, -1.f, 0, 1.f);
#define DRAW_POINT(Location) if(GetWorld()) DrawDebugPoint(World, Location, 15.f, FColor::Red, true);
#define DRAW_POINT_SingleFrame(Location) if(GetWorld()) DrawDebugPoint(World, Location, 15.f, FColor::Red, false,-1.f);
#define DRAW_VECTOR(StartLocation,EndLocation) if (GetWorld()) \
	{ \
		DrawDebugLine(World, StartLocation, EndLocation, FColor::Red, true, -1.f, 0, 1.f); \
		DrawDebugPoint(World, EndLocation, 15.f, FColor::Red, true); \
	}
#define DRAW_VECTOR_SingleFrame(StartLocation,EndLocation) if (GetWorld()) \
	{ \
		DrawDebugLine(World, StartLocation, EndLocation, FColor::Red, false, -1.f, 0, 1.f); \
		DrawDebugPoint(World, EndLocation, 15.f, FColor::Red, false,-1.f); \
	}

 

그리고 이제 틱이벤트에 AddActorWorldOffset을 통해 구가 움직이도록 해보자 

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

	AddActorWorldOffset(FVector(1.f, 0.f, 0.f));
	DRAW_SPHERE_SingleFrame(GetActorLocation());
}

AddActorWorldOffset

매 프레임마다 움직이고 있는 모습을 볼 수 있다 하지만 프레임의 차이가 있기때문에 움직임속도의 차이가 있을 것이다.

일단 우리는 프레임을 고정시켜주어 일정한 속도를 내도록 해주자

프레임 고정

하지만 프레임단위로 이동하는 방식보다는 어느 환경에서나 동일하게 흘러가는 델타타임을 이용하는 방법이 좋다. 

움직이는 정도에 Delta Time을 곱해주는 것으로 처리하면 된다. 

// Called every frame
void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	
	//움직이는 정도 - cm/s
	float MovementRate = 50.f;

	// MovementRate*DeltaTime -> cm/s * s/frame = cm/frame
	AddActorWorldOffset(FVector(MovementRate*DeltaTime, 0.f, 0.f));
	DRAW_SPHERE_SingleFrame(GetActorLocation());
}

결과

그렇게 되면 프레임속도 에 상관없이 50cm씩 움직이는 구를 볼 수 있다. 이제 프레임 고정을 없애주자

 

이제 여기에 회전을 추가해보자 

회전 또한 프레임속도에 독립적으로 수행될 수 있도록 Delta Time을 곱해준다.

회전하는 모습을 볼 수 있도록 벡터그리는 함수를 추가해주자

// Called every frame
void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	
	//움직이는 정도 - cm/s
	float MovementRate = 50.f;
	float RotationRate = 45.f;
	
	// MovementRate*DeltaTime -> cm/s * s/frame = cm/frame
	AddActorWorldOffset(FVector(MovementRate*DeltaTime, 0.f, 0.f));
	AddActorWorldRotation(FRotator(0.f, RotationRate*DeltaTime, 0.f));
	DRAW_SPHERE_SingleFrame(GetActorLocation());
	DRAW_VECTOR_SingleFrame(GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100.f);
}

 

구 회전

 

4. 삼각함수 활용

 

이번에는 삼각함수를 활용해보자 

사인 함수를 활용하여 물체가 사인주기에 맞춰서 위아래로 움직이게 해보자 

블루프린트에서는 Sin(Radius) 노드를 통해 World Offset의 Z좌표를 바꾸어주었다.

Sin

 

이제 코드로 구현해보자 

시간이 지날때마다 늘어나거나 줄어드는 값을 구현하기 위해 float 변수를 추가하고 이를 통해 Sin 함수값을 얻어온다. 이때 Sin 함수는 FMath에 있다.

// Called every frame
void AItem::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	
	RunningTime += DeltaTime;

	float DeltaZ = 0.25f*FMath::Sin(RunningTime*5.f);

	AddActorWorldOffset(FVector(0.f, 0.f, DeltaZ));

	DRAW_SPHERE_SingleFrame(GetActorLocation());
	DRAW_VECTOR_SingleFrame(GetActorLocation(), GetActorLocation() + GetActorForwardVector() * 100.f);
}

위아래로만 움직이는 모습을 볼 수 있다.

이제 헤더파일의 변수를 선언하여 주기값과 진폭값을 변경할 수 있도록하자

private:
	float RunningTime;
	float Amplitude = 0.25f; //진폭
	float TimeConstant = 5.f;		//x 계수값
};

 

이제 이값을 블루프린팅상에 나타나도록 해보자 

이러한 기능은 UPROPERTY 매크로 함수로 지정해줄 수 있다.

UPROPERTY(EditDefaultsOnly)
float Amplitude = 0.25f; //진폭

 

이렇게 코드를 작성한 뒤 핫리로딩 해주면 아이템 블루프린트의 디테일 패널에 Amplitude 값이 보인다.  이 값을 수정할 수 도 있다.

EditDefaultsOnly 지정값으로 블루프린트 상에서만 수정이 가능하다.

그냥 아이템의 디테일 패널에는 보이지않는 것을 알 수 있다.

만약 EditInstanceOnly로 지정해주게 된다면 기본 디테일창에 보이게 된다.

이 경우에는 블루프린트의 디테일에서는 보이지않게 된다.

UPROPERTY(EditInstanceOnly)
float TimeConstant = 5.f;		//x 계수값

만약 EditAnywhere로 바꿔주게 된다면 둘 다의 디테일창에서 보이게 되고 수정할 수 있다. 

	UPROPERTY(EditAnywhere)
	float Amplitude = 0.25f; //진폭

	UPROPERTY(EditAnywhere)
	float TimeConstant = 5.f;		//x 계수값

 

그리고 보이긴하지만 편집할 수 없게 만들 수 도 있다.

UPROPERTY(VisibleDefaultsOnly);			//블루프린트
UPROPERTY(VisibleInstanceOnly);			//인스턴스상
UPROPERTY(VisibleAnywhere);				//둘다
float RunningTime;

VisibleAnywhere

이제 이 변수를 이벤트그래프에서 사용할 수 있도록 만들어보자

이때 BlueprintReadOnly라는 매개변수를 넣어주면 되는데 이때 Private로 선언된 변수에서는 작동하지않는다.

UPROPERTY(EditAnywhere,BlueprintReadOnly)

 

이렇게 설정해주면 블루프린트 상에서 가져와 쓸 수 있다. 지금은 ReadOnly라 Get만 쓸 수 있다

 

이것을 BlueprintReadWrite로 바꿔주게 되면 Set도 사용할 수 있게 된다.

또한 카테고리를 지정해 줄 수 있다.

UPROPERTY(EditAnywhere, BlueprintReadWrite,Category="Sine Parameters")
float Amplitude = 0.25f; //진폭

UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Sine Parameters")
float TimeConstant = 5.f;		//x 계수값

 

프라이버으로 지정된 변수를 가져오고 싶을 때는 meta=(AllowPrivateAccess="true")로 지정하고 만들어 주면 된다.

UPROPERTY(VisibleAnywhere,BlueprintReadOnly,meta=(AllowPrivateAccess="true"));
float RunningTime;

 

 

변수뿐만 아니라 함수또한 이런방식으로 보이게 할 수 있다. 

함수는 UFUNCTION(BlueprintCallable)을 통해 블루프린트 상에 보이게 할 수 있다.

	UFUNCTION(BlueprintCallable)
	float TransformedSin(float Value);

 

만약 이것을 UFUNCTION(BlueprintPure) 으로 지정한다면 Get Actor Location과 같이 계산하는 값을 반환해주는 용도로 사용할 수 있다.

	UFUNCTION(BlueprintPure)
	float TransformedSin(float Value);

 

 

★ ★ ★ 제네릭 함수 - 템플릿 문법

 

C++의 템플릿 문법은 제네릭 프로그래밍을 가능하게 하여, 클래스나 함수를 작성할 때 특정 데이터 타입에 의존하지 않고, 다양한 데이터 타입을 처리할 수 있도록 한다.

사용하는 방법은 함수위에 template<typename T>을 선언하고 밑에 typename으로 선언한 제네릭 변수를 통해 함수를 구성해주면 된다. 

template<typename T>
inline T AItem::Avg(T First, T Second)
{
	return (First + Second) / 2;
}

 

사용할 때는 아래와 같이 사용해주면 된다.

Avg<int32>(1, 3);
UE_LOG(LogTemp, Warning, TEXT("Avg : %d"), Avg<int32>(1, 3));

 

이제 액터에 스태틱매쉬를 붙여 게임화면에 보일 수 있도록하자.
블루프린트에서 스태틱매쉬를 추가한 뒤 Sphere을 추가해주자 

 

이 것을 코드로 구현해 보겠다. 

일단 코드 상에서 인스턴스를 건드리려면 아직 액터들이 모두 생성되지않은 생성자 단계가 아니라 BeginPlay이후에 이루어져야한다. 

또한 이 컴포넌트의 하위 객체를 이용할 때는 템플릿 함수를 사용하여 지정해주어야한다.

이 함수는 포인터를 반환해준다.

 

그럼 이제 매쉬를 저장해줄 포인터 변수를 선언해주자

	UStaticMeshComponent* ItemMesh;

 

그리고 생성자에서 매쉬를 붙여주자

AItem::AItem()
{
 	// 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;

	ItemMesh = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ItemMeshComponent"));
	RootComponent = ItemMesh;
}

 

'게임공부 > Unreal Engine' 카테고리의 다른 글

[Unreal Engine][C++]6.움직이기  (1) 2024.07.16
[Unreal Engine][C++]5.Pawn 클래스  (0) 2024.07.15
[Unreal Engine][C++]3. 디버그  (0) 2024.07.10
[Unreal Engine][C++]2. C++살펴보기  (0) 2024.07.09
[Unreal Engine][C++]1.시작  (0) 2024.07.08

+ Recent posts