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 부분도 설정 한다
- 펄린 노이즈
- 자연스럽게 정렬된("부드럽게") 의사 난수 시퀀스를 생성하기 때문에 보다 더 유기적인 모습
https://ko.khanacademy.org/computing/computer-programming/programming-natural-simulations/programming-noise/a/perlin-noise
- FMath::Fmod()
- 함수는 실수나 부동 소수점 숫자의 계산에서 주로 사용되며, 나눗셈 후 나머지 값을 구할 때 유용합니다. 이 함수를 사용하면 특히 숫자가 음수인 경우에도 올바른 결과를 얻을 수 있습니다.
- ex)
FMath::Fmod(10.5f, 3.0f)를 호출하면 10.5를 3.0으로 나눈 나머지인 1.5를 반환
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);
}
}