[ Unreal Engine 5 / #18 Game 고도화 (etc) ]

SeungWoo·2024년 10월 10일
0

[ Ureal Engine 5 / 수업 ]

목록 보기
19/31
post-thumbnail

Level Streaming

https://dev.epicgames.com/documentation/ko-kr/unreal-engine/level-streaming-overview-in-unreal-engine

  • project Setting의 preset 세팅으로 만든다

Niagara 모듈 추가

// Copyright Epic Games, Inc. All Rights Reserved.

using UnrealBuildTool;

public class magician_Project : ModuleRules
{
	public magician_Project(ReadOnlyTargetRules Target) : base(Target)
	{
		PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs;

		PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "EnhancedInput", "UMG", "AIModule", "GameplayTasks", "Niagara" });
	}
}

  • DoorCollider 이라는 이름의 Actor를 하나 만든다.

DoorCollider.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "DoorCollider.generated.h"

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

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

	UFUNCTION()
	void OverLapLevelLoadBeging(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

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


public:
	UPROPERTY(VisibleAnywhere)
	class UBoxComponent* colliderComp;

	UPROPERTY(VisibleAnywhere)
	class UStaticMeshComponent* meshComp;

	UPROPERTY(VisibleAnywhere)
	class UNiagaraComponent* niagaraFxComp;

	UPROPERTY(EditAnywhere, Category = "Level")
	FName LevelToLoad;
};

DoorCollider.cpp

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


#include "DoorCollider.h"
#include "Components\BoxComponent.h"
#include "Components\StaticMeshComponent.h"
#include "NiagaraComponent.h"
#include "CTPSPlayer.h"
#include "Kismet\GameplayStatics.h"

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

	colliderComp = CreateDefaultSubobject<UBoxComponent>(TEXT("BoxComp"));
	SetRootComponent(colliderComp);

	meshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("MeshComp"));
	meshComp->SetupAttachment(colliderComp);

	niagaraFxComp = CreateDefaultSubobject<UNiagaraComponent>(TEXT("FxComp"));
	niagaraFxComp->SetupAttachment(colliderComp);


}

// Called when the game starts or when spawned
void ADoorCollider::BeginPlay()
{
	Super::BeginPlay();
	
	colliderComp->OnComponentBeginOverlap.AddDynamic(this, &ADoorCollider::OverLapLevelLoadBeging);

}

void ADoorCollider::OverLapLevelLoadBeging(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	ACTPSPlayer* _myCharacter = Cast<ACTPSPlayer>(OtherActor);

	if (OtherActor && LevelToLoad != "")
	{
		GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Red, TEXT("OverLapLevelLoadBeging"));
		
		FLatentActionInfo LatenInfo;
		UGameplayStatics::LoadStreamLevel(this, LevelToLoad, true,true, LatenInfo);
		//UGameplayStatics::UnloadStreamLevel(this, LevelToLoad,LatenInfo, true);
	}

}
  • 만든 클래스를 기반으로 BP를 만든뒤 collider 설정을 한다

  • player 부분도 설정 한다

  • int32 Linkage
    • 지연 작업이 종료될 때 실행할 특정 링크를 나타내는 인덱스입니다.
    • 내부적으로 액션 시스템이 해당 링크를 통해 후속 작업을 찾습니다.
  • int32 UUID
    • 지연 작업을 고유하게 식별하기 위한 값입니다. 같은 액터에 대해 여러 개의 지연 작업이 있을 때 이를 구별하는 데 사용됩니다.
  • FName ExecutionFunction
    • 지연 작업이 완료되었을 때 호출할 함수의 이름입니다. 이 함수는 지연 작업을 시작한 클래스 내에 정의되어 있어야 합니다.
  • AActor CallbackTarget*
    • 지연 작업이 완료되었을 때 호출할 함수를 포함하는 액터입니다. 일반적으로 이 액터는 비동기 작업을 트리거한 액터입니다.
  • int32 LinkID (이전 버전에서는 Linkage로 불림)
    • 여러 지연 작업을 관리할 때 사용되는 고유 식별자입니다. 예를 들어, 같은 지연 작업이 여러 개의 액션을 실행할 수 있기 때문에 이를 구분하기 위해 사용됩니다.

  • 사용 예시
    • 비동기 레벨 로드 : UGameplayStatics::LoadStreamLevel 함수를 호출할 때 FLatentActionInfo를 전달합니다. 이때 비동기 작업이 끝나면 OnLevelLoaded 함수가 호출되도록 설정합니다.
    • 작업 완료 후 콜백 : 레벨 로드 작업이 완료되면 OnLevelLoaded 함수가 자동으로 호출되어 후속 처리를 진행할 수 있습니다.

Streaming 될 표식 위치를 위한 간단 Portal 문 Niagara 제작

  • ortex Force (회전 힘) < – > Drag (저항 힘)

시계 만들기

시간에 따른 Directional Light 변화하기

  • FMath::Fmod()
    • 함수는 실수나 부동 소수점 숫자의 계산에서 주로 사용되며, 나눗셈 후 나머지 값을 구할 때 유용합니다. 이 함수를 사용하면 특히 숫자가 음수인 경우에도 올바른 결과를 얻을 수 있습니다.
    • ex)
      FMath::Fmod(10.5f, 3.0f)를 호출하면 10.5를 3.0으로 나눈 나머지인 1.5를 반환

보간을 활용한 색(값) 변화

실습 : TArray 형태로 코드를 개선 하기

clock.h

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

#pragma once

#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "Clock.generated.h"

#define ONE_YEAR 31104000 // 360 일 기준 1년/ 초
#define ONE_MONTH 259200 // 30일 기준 1달/ 초
#define ONE_DAY 86400 // 1일 기준 24시간 / 초
#define ONE_HOUR 3600 // 1일 24초
#define ONE_MINUTE 60 // 


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

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

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


public:
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Time")
	float CurrentDay;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Time")
	float CurrentHour;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Time")
	float CurrentMinute;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Time")
	float CurrentSecond;
	
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Time")
	float TotalSeconds;

	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Time")
	float TimeScale = 1.0f;

	UFUNCTION(BlueprintCallable)
	FString GetTimeByTotalSec(float totalSec);

	UPROPERTY(EditAnywhere, Category = "Sun")
	class AActor* sun;


	UPROPERTY(EditAnywhere, Category = "Sun")
	TArray<FLinearColor> SunColors;

	UPROPERTY(VisibleAnywhere, Category = "sun")
	int32 CurrentyColorIndex;

protected:
	void UpdateTime(float DeltaSec);

	void RotationDirectionalLightWithTime(AActor* TargetActor);

	void UpdateSunColorByHourMinute(AActor* TargetActor);
};

clock.cpp

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


#include "Clock.h"
#include "Engine/DirectionalLight.h"
#include "Components/DirectionalLightComponent.h"

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

}

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

// 30 FPS, 60 FPS -> 1초에 30/60번 렌더링
// Called every frame
void AClock::Tick(float DeltaTime) // 한 프레임이 완료되기까지 걸리는 시간
{
	Super::Tick(DeltaTime);
	UpdateTime(DeltaTime);
	RotationDirectionalLightWithTime(sun);

	//GetTimeByTotalSec(TotalSeconds);
}

FString AClock::GetTimeByTotalSec(float totalSec)
{
	// year = totalSec / ONE_YEAR 입력값을 1년으로 나눔 
	// month = totalSec / ONE_MONTH % 12 1달로 나눈 후 12로 나눈 나머지 값
	CurrentDay = (int)totalSec / ONE_DAY % 30; 
	// 1일으로 나눈 후 30으로 나눈 나머지 값
	CurrentHour = (int)totalSec / ONE_HOUR % 24;
	// 1시간을 나눈 후 24로 나눈 나머지 값
	CurrentMinute = (int)totalSec / ONE_MINUTE % 60;
	// 1분으로 나눈 후 60으로 나눈 나머지 값 
	CurrentSecond = (int)totalSec / 60;
	// 60초로 나눈 나머지 값

	FString Now;
	Now = "D : " + FString::SanitizeFloat(CurrentDay)
		+ "- H : " + FString::SanitizeFloat(CurrentHour)
		+ "- M : " + FString::SanitizeFloat(CurrentMinute)
		+ "- S : " + FString::SanitizeFloat(CurrentSecond);

	GEngine->AddOnScreenDebugMessage(-1, 5.0f, FColor::Green, (TEXT("%s"), *Now));
	return Now;
}

void AClock::UpdateTime(float DeltaSec)
{
	TotalSeconds += DeltaSec * TimeScale; // DetaSeconds를 더하여 시간 업데이트

	CurrentDay = TotalSeconds / 86400;
	// 1일은 86400초
	// 나눗셈 -> 컴퓨터 사칙연산 나눗셈( x ) => 1/10 -> 1* 0.1 
	
	int32 RemainingSecondsAfterDays = (int)TotalSeconds % 86400;
	// 일을 제외한 나머지 초를 계산
	
	CurrentHour = RemainingSecondsAfterDays / 3600;
	// 시간을 초에서 시간으로 변환
	
	int32 RemainingSecondsAfterHours = RemainingSecondsAfterDays % 3600; // 시간을 제외한 나머지 초
	
	CurrentMinute = RemainingSecondsAfterHours / 60; 
	// 분을 초에서 분으로 변환
	
	CurrentSecond = RemainingSecondsAfterHours % 60; 
	// 분을 제외한 나머지 초를 초로 사용 

	// 왜 기본 단위를 초로 사용하는가 ?
	// -> frame 개념인 Realtime이기에 --> DetalSec연산이 적합 
	// 나머지를 활용한, 단위 별 값 구하기
	
}

void AClock::RotationDirectionalLightWithTime(AActor* TargetActor)
{
	if (TargetActor)
	{
		ADirectionalLight* DirectionalLight = Cast<ADirectionalLight>(TargetActor);
		if (DirectionalLight)
		{
			//float _AnglePerHour = 360.0f / 24.0f; // 24시간을 360도로 나눔 // = 15
			//float HourAngle = FMath::Fmod(CurrentHour * _AnglePerHour, 360.0f);
			// 
			
		
			// 시간에 따른 각도 계산
			float _HourAngle = FMath::Fmod(CurrentHour * 15, 360.0f);
			// 분에 따른 각도 계산 
			float _MinuteAngle = (CurrentMinute / 60.0f) * 15;
			// 초에 따른 각도 계산
			float _SecondAngle = (CurrentSecond / 60.0f) * (15 / 60.0f);
			// 시간, 분, 초에 따른 총 각도 계산
			float _CurrentAngle = _HourAngle + _MinuteAngle + _SecondAngle;

			FRotator _NewRotation = FRotator(_CurrentAngle, 0.0f, 0.0f);
			DirectionalLight->SetActorRotation(_NewRotation);
		}
	}

}

void AClock::UpdateSunColorByHourMinute(AActor* TargetActor)
{
	UDirectionalLightComponent* _DirectionLightComp = Cast<ADirectionalLight>(TargetActor)->GetComponent();
	float _LightTime = CurrentHour + (CurrentMinute / 60);

	_LightTime = FMath::Fmod(_LightTime, 24.0f);
	const float InterpolationFactor = (_LightTime - (CurrentyColorIndex * 3)) / 3.0f;

	CurrentyColorIndex = FMath::FloorToInt(_LightTime / 3.0f) % SunColors.Num();

	if (CurrentyColorIndex + 1 < SunColors.Num())
	{
		const FLinearColor InterpolationColor = FLinearColor::LerpUsingHSV
		(
			SunColors[CurrentyColorIndex],
			SunColors[CurrentyColorIndex + 1],
			InterpolationFactor
		);
	
		_DirectionLightComp->SetLightColor(InterpolationColor);
	}
	else
	{
		const FLinearColor InterpolationColor = FLinearColor::LerpUsingHSV
		(
			SunColors[CurrentyColorIndex],
			SunColors[0],
			InterpolationFactor
		); 

		_DirectionLightComp->SetLightColor(InterpolationColor);
	}

}

profile
This is my study archive

0개의 댓글