일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
- MAC
- 메카님
- DP
- photon fusion2
- 언리얼엔진
- stride
- MLFQ
- Unreal Engine
- Rr
- DSP
- gas
- Delegate
- 게임 개발
- animation
- dirty cow
- ret2libc
- Replication
- 유니티
- 언리얼 엔진
- Race condition
- 운영체제
- Multiplay
- Security
- boss monster
- gameplay ability system
- unity
- CTF
- 유스케이스
- 게임개발
- ability task
- Today
- Total
다양한 기록
[ChronoSpace] 키 아이템과 상호작용 with Subsystem Singleton (ClockworkLabyrinth) 본문
[ChronoSpace] 키 아이템과 상호작용 with Subsystem Singleton (ClockworkLabyrinth)
라구넹 2025. 2. 13. 03:39- 맵 배치용 액터(키 아이템) : CSLabyrinthKey
F키로 상호작용하여 획득
라비린스 키를 맵에 20개 뿌려둠 : 1층에 10개 2층에 7개 3층에 3개
실제 활성화는 그 중에서 10개
- 모아야 하는 건 5개키 아이템을 바칠 곳 : CSLabyrinthKeyAltar (제단)
F키로 상호작용하여 키 제출
5개 이상 제출 시 맵 클리어 (처음 나선 맵으로 이동시킴)
일단 상호작용 해야 하니 상호작용을 추가
CharacterPlayer에 F에 인풋 액션 만들고 오브젝트가 오버랩되면 델리게이트를 구독하는 방식
DECLARE_DYNAMIC_MULTICAST_DELEGATE(FInteractionDelegate);
// Interaction Section
public:
FInteractionDelegate OnInteract;
void Interact();
void ACSCharacterPlayer::Interact()
{
OnInteract.Broadcast();
}
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "GameFramework/Actor.h"
#include "CSLabyrinthKey.generated.h"
UCLASS()
class CHRONOSPACE_API ACSLabyrinthKey : public AActor
{
GENERATED_BODY()
public:
ACSLabyrinthKey();
UFUNCTION()
void OnTriggerBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult);
UFUNCTION()
void OnTriggerEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);
UFUNCTION()
void Interact();
protected:
UPROPERTY(VisibleAnywhere, Category = "Trigger", Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class USphereComponent> SphereTrigger;
UPROPERTY(VisibleAnywhere, Category = "Mesh", Meta = (AllowPrivateAccess = "true"))
TObjectPtr<class UStaticMeshComponent> StaticMeshComp;
float TriggerRange = 100.0f;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Actor/CSLabyrinthKey.h"
#include "Character/CSCharacterPlayer.h"
#include "Components/StaticMeshComponent.h"
#include "Components/SphereComponent.h"
#include "Physics/CSCollision.h"
#include "ChronoSpace.h"
// Sets default values
ACSLabyrinthKey::ACSLabyrinthKey()
{
bReplicates = true;
// SphereTrigger
SphereTrigger = CreateDefaultSubobject<USphereComponent>(TEXT("GravitySphereTrigger"));
SphereTrigger->SetSphereRadius(TriggerRange, true);
SphereTrigger->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
RootComponent = SphereTrigger;
SphereTrigger->SetCollisionProfileName(CPROFILE_CSTRIGGER);
SphereTrigger->SetIsReplicated(true);
// Static Mesh
StaticMeshComp = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("StaticMesh"));
StaticMeshComp->SetRelativeLocation(FVector(0.0f, 0.0f, 0.0f));
StaticMeshComp->SetupAttachment(SphereTrigger);
StaticMeshComp->SetCollisionProfileName(CPROFILE_CSCAPSULE);
StaticMeshComp->SetIsReplicated(true);
static ConstructorHelpers::FObjectFinder<UStaticMesh> StaticMeshRef(TEXT("/Script/Engine.StaticMesh'/Game/Mesh/StaticMesh/BlockSphere.BlockSphere'"));
if (StaticMeshRef.Object)
{
StaticMeshComp->SetStaticMesh(StaticMeshRef.Object);
}
float MeshRadius = 50.0f;
float MeshScale = (TriggerRange / MeshRadius) * 0.75f;
StaticMeshComp->SetRelativeScale3D(FVector(MeshScale, MeshScale, MeshScale));
SphereTrigger->OnComponentBeginOverlap.AddDynamic(this, &ACSLabyrinthKey::OnTriggerBeginOverlapCallback);
SphereTrigger->OnComponentEndOverlap.AddDynamic(this, &ACSLabyrinthKey::OnTriggerEndOverlapCallback);
}
void ACSLabyrinthKey::OnTriggerBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>(OtherActor);
if ( Player )
{
Player->OnInteract.Clear();
Player->OnInteract.AddDynamic(this, &ACSLabyrinthKey::Interact);
}
}
void ACSLabyrinthKey::OnTriggerEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>(OtherActor);
if (Player)
{
Player->OnInteract.Clear();
}
}
void ACSLabyrinthKey::Interact()
{
UE_LOG(LogCS, Log, TEXT("ACSLabyrinthKey - Interact"));
}
일단 테스트용으로 메시는 적당히 넣어두고
F누르니까 로그 나오는 걸 보니 정상 작동
그래서 고민 점 : 싱글턴 만들어서 키 아이템 관리하고 싶음
그런데 프로젝트 세팅에 있는 게임 싱글턴 클래스는 하나만 만들 수 있음
저 안에서 싱글턴 여러 개 관리할 수도 있겠지만, SubSystem으로 싱글톤 만드는게 더 깔끔해보임\
정확히는 UGameInstanceSubsystem을 상속받아서 만들 거나, UWorldSubsystem 상속 받아서 만들면 되는데
전자는 레벨(맵)이 바뀌어도 유지되고, 후자는 레벨이 바뀌면 리셋됨
라비린스의 키는 레벨이 바뀌었을 때 유지할 필요는 없음
월드 서브시스템 상속받아서 만들 것임
이거 상속
// Fill out your copyright notice in the Description page of Project Settings.
#pragma once
#include "CoreMinimal.h"
#include "Subsystems/WorldSubsystem.h"
#include "CSLabyrinthKeyWorldSubsystem.generated.h"
/**
*
*/
UCLASS()
class CHRONOSPACE_API UCSLabyrinthKeyWorldSubsystem : public UWorldSubsystem
{
GENERATED_BODY()
public:
UCSLabyrinthKeyWorldSubsystem();
virtual void Initialize(FSubsystemCollectionBase& Collection) override;
virtual void Deinitialize() override;
FORCEINLINE void SetLabyrinthKeyCount(int8 InCount) { LabyrinthKeyCount = InCount; }
FORCEINLINE const int8 GetLabyrinthKeyCount() const { return LabyrinthKeyCount; }
private:
int8 LabyrinthKeyCount;
};
// Fill out your copyright notice in the Description page of Project Settings.
#include "Subsystem/CSLabyrinthKeyWorldSubsystem.h"
UCSLabyrinthKeyWorldSubsystem::UCSLabyrinthKeyWorldSubsystem()
{
LabyrinthKeyCount = 0;
}
void UCSLabyrinthKeyWorldSubsystem::Initialize(FSubsystemCollectionBase& Collection)
{
Super::Initialize(Collection);
LabyrinthKeyCount = 0;
}
void UCSLabyrinthKeyWorldSubsystem::Deinitialize()
{
Super::Deinitialize();
}
싱글톤 이렇게 만들어주고
void ACSLabyrinthKey::Interact()
{
UCSLabyrinthKeyWorldSubsystem* LabyrinthKeySubsystem = GetWorld()->GetSubsystem<UCSLabyrinthKeyWorldSubsystem>();
if (LabyrinthKeySubsystem)
{
LabyrinthKeySubsystem->SetLabyrinthKeyCount((LabyrinthKeySubsystem->GetLabyrinthKeyCount()) + 1);
UE_LOG(LogCS, Log, TEXT("ACSLabyrinthKey - Interact : %d"), LabyrinthKeySubsystem->GetLabyrinthKeyCount());
}
Destroy();
}
Interact 구현
근데 이러니까 클라이언트가 Destroy해도 권한 없어서 삭제 안됨
RPC 처리까지 해줘야 함
당연히 플레이어 Interaction을 RPC 해야 함 저 오브젝트에서가 아니라
해도 작동이야 하겠다만 확장성 챙기자
사실 오브젝트에다 RPC 쓰고 있다가 뭔가 이상해서 지우고 플레이어로 했다
public:
FInteractionDelegate OnInteract;
UFUNCTION(Server, Reliable)
void ServerInteract();
void Interact();
void ACSCharacterPlayer::ServerInteract_Implementation()
{
OnInteract.Broadcast();
}
void ACSCharacterPlayer::Interact()
{
if ( HasAuthority() )
{
OnInteract.Broadcast();
}
else
{
ServerInteract();
}
}
이렇게 세팅해주면 된다
로그 찍히는 걸 보니 싱글턴 적용도 잘 되고 동기화도 잘 됐다
이제 다음으로는 'F 키를 눌러 상호작용' 멘트를 띄우고 싶다
위젯 만들고 (그냥 글씨만 띄우는 거라 GAS 안쓸 거라 기본 사용)
UPROPERTY(VisibleAnywhere)
TObjectPtr<class UWidgetComponent> InteractionPromptComponent;
///////////////////////
// Widget
InteractionPromptComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("InteractionPromptComponent"));
InteractionPromptComponent->SetupAttachment(SphereTrigger);
InteractionPromptComponent->SetRelativeLocation(FVector(0.0f, 0.0f, 100.0f));
static ConstructorHelpers::FClassFinder<UUserWidget> InteractionPromptWidgetRef(TEXT("/Game/Blueprint/UI/BP_InteractionPrompt.BP_InteractionPrompt_C"));
if (InteractionPromptWidgetRef.Class)
{
InteractionPromptComponent->SetWidgetClass(InteractionPromptWidgetRef.Class);
InteractionPromptComponent->SetWidgetSpace(EWidgetSpace::Screen);
InteractionPromptComponent->SetDrawSize(FVector2D(500.0f, 30.f));
InteractionPromptComponent->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}
InteractionPromptComponent->SetVisibility(false);
/////
void ACSLabyrinthKey::OnTriggerBeginOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepHitResult)
{
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>(OtherActor);
if ( Player )
{
Player->OnInteract.Clear();
InteractionPromptComponent->SetVisibility(true);
Player->OnInteract.AddDynamic(this, &ACSLabyrinthKey::Interact);
}
}
void ACSLabyrinthKey::OnTriggerEndOverlapCallback(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{
ACSCharacterPlayer* Player = Cast<ACSCharacterPlayer>(OtherActor);
if (Player)
{
InteractionPromptComponent->SetVisibility(false);
Player->OnInteract.Clear();
}
}
이러면 가까이 갔을 때만 위젯이 보인다
'언리얼 엔진 > ChronoSpace' 카테고리의 다른 글
[ChronoSpace] TActorIterator를 이용한 LabyrinthKey 활성화 (Random Activation of Actor) (ClockworkLabyrinth) (0) | 2025.02.13 |
---|---|
[ChronoSpace] Map Design - Clockwork Labyrinth 뼈대 (0) | 2025.02.10 |
[ChronoSpace] Preivew Box 로컬 클라이언트만 보이게 하기 (0) | 2025.02.06 |
[ChronoSpace] 크기가 변하는 BoxComponent의 Offset 계산 (0) | 2025.02.06 |
[ChronoSpace] Static Box Bug? (0) | 2025.02.06 |