일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | ||||||
2 | 3 | 4 | 5 | 6 | 7 | 8 |
9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 17 | 18 | 19 | 20 | 21 | 22 |
23 | 24 | 25 | 26 | 27 | 28 |
- Unreal Engine
- gas
- Security
- MLFQ
- reverse gravity
- gameplay effect
- 유니티
- Replication
- 게임개발
- Race condition
- gravity direction
- ret2libc
- CTF
- Rr
- DP
- 언리얼엔진
- animation
- pdlc
- dirty cow
- 언리얼 엔진
- DSP
- gameplay ability
- MAC
- ability task
- unity
- 메카님
- 게임 개발
- stride
- 운영체제
- 유스케이스
- Today
- Total
다양한 기록
[ChronoSpace] Black Hole & White Hole 본문
https://www.youtube.com/watch?v=_TNFaHFzXfM&t=270s
메테리얼은 이거 보고 만들었는데,
내부 로직은 Chrono Space가 오브젝트가 캐릭터 기반이라 아예 다를 것이라
그냥 내부 안보고 새로 만들었다
메테리얼
관련 소스
CSGABlackHole
CSAT_BlackHoleSphere
CSTA_BlackHoleSphere
CSGAWhiteHole
CSWhiteHole (일반 액터임)
화이트홀이 없다 → 블랙홀이 Event Horizon 내부로 들어온 거 제거
화이트홀이 있다 → 블랙홀이 화이트홀 위치로 이동시킴
화이트홀은 CSCharacterPlayer가 보유 → 멀티플레이어 환경에선 각각 로컬 플레이어마다 하나씩 보유 가능
블랙홀
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/TA/CSTA_BlackHoleSphere.h"
#include "AbilitySystemComponent.h"
#include "Abilities/GameplayAbility.h"
#include "Components/SphereComponent.h"
#include "Components/StaticMeshComponent.h"
#include "GameFramework/Character.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "Character/CSCharacterPlayer.h"
#include "Actor/CSWhiteHall.h"
#include "Physics/CSCollision.h"
#include "Kismet/GameplayStatics.h"
#include "ChronoSpace.h"
ACSTA_BlackHoleSphere::ACSTA_BlackHoleSphere()
{
PrimaryActorTick.bCanEverTick = true;
// GravitySphereTrigger
GravitySphereTrigger = CreateDefaultSubobject<USphereComponent>(TEXT("GravitySphereTrigger"));
RootComponent = GravitySphereTrigger;
GravitySphereTrigger->SetSphereRadius(GravityInfluenceRange, true);
GravitySphereTrigger->SetRelativeLocation(FVector(600.0f, 0.0f, 200.0f));
GravitySphereTrigger->SetCollisionProfileName(CPROFILE_CSTRIGGER);
GravitySphereTrigger->OnComponentBeginOverlap.AddDynamic(this, &ACSTA_BlackHoleSphere::OnTriggerBeginOverlap);
GravitySphereTrigger->OnComponentEndOverlap.AddDynamic(this, &ACSTA_BlackHoleSphere::OnTriggerEndOverlap);
// EventHorizonSphereTrigger
EventHorizonSphereTrigger = CreateDefaultSubobject<USphereComponent>(TEXT("EventHorizonSphereTrigger"));
EventHorizonSphereTrigger->SetupAttachment(GravitySphereTrigger);
EventHorizonSphereTrigger->SetSphereRadius(EventHorizonRadius, true);
EventHorizonSphereTrigger->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
EventHorizonSphereTrigger->SetCollisionProfileName(CPROFILE_CSTRIGGER);
EventHorizonSphereTrigger->OnComponentBeginOverlap.AddDynamic(this, &ACSTA_BlackHoleSphere::OnEventHorizonBeginOverlap);
// Static Mesh
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComp"));
StaticMeshComp->SetCollisionEnabled(ECollisionEnabled::NoCollision);
StaticMeshComp->SetupAttachment(GravitySphereTrigger);
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/Mesh/StaticMesh/MaterialSphere.MaterialSphere'"));
if (StaticMeshRef.Object)
{
StaticMeshComp->SetStaticMesh(StaticMeshRef.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterialInstance> MaterialRef(TEXT("/Script/Engine.MaterialInstanceConstant'/Game/Material/MI_BlackHole.MI_BlackHole'"));
if (MaterialRef.Object)
{
StaticMeshComp->SetMaterial(0, MaterialRef.Object);
}
}
void ACSTA_BlackHoleSphere::BeginPlay()
{
Super::BeginPlay();
if (!bShowDebug) return;
if (GravitySphereTrigger)
{
FVector SphereLocation = GravitySphereTrigger->GetComponentLocation();
float SphereRadius = GravitySphereTrigger->GetScaledSphereRadius();
DrawDebugSphere(
GetWorld(),
SphereLocation,
SphereRadius,
12, // 세그먼트 수 (구의 매끄러움)
FColor::Green,
false, // 지속 표시
10, // 지속 시간
0, // 디버그 선 우선순위
2.0f // 선 두께
);
}
}
void ACSTA_BlackHoleSphere::Tick(float DeltaSeconds)
{
Super::Tick(DeltaSeconds);
FVector BlackHoleLocation = GravitySphereTrigger->GetComponentLocation();
for (auto Char = CharactersInSphereTrigger.CreateIterator(); Char; ++Char)
{
if (IsValid(Char.Value()))
{
FVector Power(230000.0f, 230000.0f, 230000.0f);
FVector TargetLocation = Char.Value()->GetActorLocation();
FVector Distance = BlackHoleLocation - TargetLocation;
FVector Direction = Distance.GetSafeNormal();
Char.Value()->GetCharacterMovement()->AddForce(Power * Direction);
}
}
}
void ACSTA_BlackHoleSphere::StartTargeting(UGameplayAbility* Ability)
{
Super::StartTargeting(Ability);
SourceActor = Ability->GetCurrentActorInfo()->AvatarActor.Get();
}
void ACSTA_BlackHoleSphere::ConfirmTargetingAndContinue()
{
if (SourceActor)
{
OnComplete.Broadcast();
}
}
void ACSTA_BlackHoleSphere::OnTriggerBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
ACharacter* DetectedCharacter = Cast<ACharacter>(OtherActor);
if (DetectedCharacter)
{
CharactersInSphereTrigger.Emplace(DetectedCharacter->GetFName(), DetectedCharacter);
}
}
void ACSTA_BlackHoleSphere::OnTriggerEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
ACharacter* DetectedCharacter = Cast<ACharacter>(OtherActor);
if (DetectedCharacter)
{
CharactersInSphereTrigger.Remove(DetectedCharacter->GetFName());
}
}
void ACSTA_BlackHoleSphere::OnEventHorizonBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
ACharacter* OverlapedCharacter = Cast<ACharacter>(OtherActor);
ACSCharacterPlayer* OverlapedCharacterPlayer = Cast<ACSCharacterPlayer>(OtherActor);
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>( UGameplayStatics::GetPlayerCharacter( GetWorld(), 0 ) );
if ( ACSWhiteHall* WhiteHall = Player->GetWhiteHall() )
{
FVector NewLocation = WhiteHall->GetActorLocation();
OtherActor->SetActorLocation(NewLocation);
CharactersInSphereTrigger.Remove(OverlapedCharacter->GetFName());
}
else if (OverlapedCharacter)
{
if (OverlapedCharacterPlayer == nullptr)
{
OtherActor->Destroy();
}
}
}
블랙홀이 영향이 끼치는 범위(GravityInfluenceRange)
물체가 사라지는 범위(EventHorizonRadius)
GravityInfluenceRange에 들어오면 Tick에서 AddForce로 끌어당긴다
void ACSTA_BlackHoleSphere::OnEventHorizonBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
ACharacter* OverlapedCharacter = Cast<ACharacter>(OtherActor);
ACSCharacterPlayer* OverlapedCharacterPlayer = Cast<ACSCharacterPlayer>(OtherActor);
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>( UGameplayStatics::GetPlayerCharacter( GetWorld(), 0 ) );
if ( ACSWhiteHall* WhiteHall = Player->GetWhiteHall() )
{
FVector NewLocation = WhiteHall->GetActorLocation();
OtherActor->SetActorLocation(NewLocation);
CharactersInSphereTrigger.Remove(OverlapedCharacter->GetFName());
}
else if (OverlapedCharacter)
{
if (OverlapedCharacterPlayer == nullptr)
{
OtherActor->Destroy();
}
}
}
EventHorizonRadius에 들어오면 화이트홀이 있는지 체크
화이트홀이 있으면 화이트홀 위치로 텔레포트, 없으면 제거
화이트홀
// Fill out your copyright notice in the Description page of Project Settings.
#include "GA/CSGA_WhiteHall.h"
#include "Actor/CSWhiteHall.h"
#include "Character/CSCharacterPlayer.h"
#include "ChronoSpace.h"
UCSGA_WhiteHall::UCSGA_WhiteHall()
{
}
void UCSGA_WhiteHall::ActivateAbility(const FGameplayAbilitySpecHandle Handle, const FGameplayAbilityActorInfo* ActorInfo, const FGameplayAbilityActivationInfo ActivationInfo, const FGameplayEventData* TriggerEventData)
{
UE_LOG(LogCS, Display, TEXT("UCSGA_WhiteHall::ActivateAbility"));
bool bReplicatedEndAbility = true;
bool bWasCancelled = false;
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>(ActorInfo->AvatarActor);
if ( Player->GetWhiteHall() )
{
Player->ClearWhiteHall();
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled);
return;
}
const FVector SpawnLocation = Player->GetActorLocation();
ACSWhiteHall* WhiteHall = GetWorld()->SpawnActor<ACSWhiteHall>(ACSWhiteHall::StaticClass(), SpawnLocation, FRotator::ZeroRotator);
Player->SetWhiteHall(WhiteHall);
EndAbility(CurrentSpecHandle, CurrentActorInfo, CurrentActivationInfo, bReplicatedEndAbility, bWasCancelled);
}
화이트홀은 게임 어빌리티에서 바로 스폰해준다
현재 플레이어 캐릭터의 위치에 스폰
이미 스폰된게 있으면 제거하는 방식으로 작동
스폰 -> 제거 -> 스폰 -> 제거 ..
// Fill out your copyright notice in the Description page of Project Settings.
#include "Actor/CSWhiteHall.h"
#include "Components/StaticMeshComponent.h"
// Sets default values
ACSWhiteHall::ACSWhiteHall()
{
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMeshComp"));
RootComponent = StaticMeshComp;
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/Mesh/StaticMesh/MaterialSphere.MaterialSphere'"));
if (StaticMeshRef.Object)
{
StaticMeshComp->SetStaticMesh(StaticMeshRef.Object);
}
static ConstructorHelpers::FObjectFinder<UMaterialInstance> MaterialRef(TEXT("/Script/Engine.MaterialInstanceConstant'/Game/Material/MI_WhiteHole.MI_WhiteHole'"));
if (MaterialRef.Object)
{
StaticMeshComp->SetMaterial(0, MaterialRef.Object);
}
}
화이트홀은 액터에 스태틱 메쉬만 달렸다
실행 시 이렇게 된다
캐릭터 AddForce 관련하여 이해하는게 중요했는데,
이전 게시글인 Character 밀기가 상당히 도움이 되었다.
'언리얼 엔진 > ChronoSpace' 카테고리의 다른 글
[ChronoSpace] Gravity Core / Gravity Direction 조정을 통한 Custom Gravity (0) | 2025.01.26 |
---|---|
[ChronoSpace] Weaken Gravity / Material Saturation Setting (0) | 2025.01.26 |
[ChronoSpace] 밀리는 Character (0) | 2025.01.25 |
[ChronoSpace] Static Mesh Scale과 관계없는 Meterial (0) | 2025.01.22 |
[ChronoSpace] 중력 반전 #2 / Box Trigger (0) | 2025.01.17 |