A Demo Project for the UnrealEngineSDK
Loading...
Searching...
No Matches
WidgetController.cpp
Go to the documentation of this file.
1// Copyright(c) Pixo Group. All Rights Reserved.
2
4
5#include "PixoVRCharacter.h"
6#include "Components/BoxComponent.h"
7#include "Components/TimelineComponent.h"
8#include "Components/WidgetComponent.h"
10#include "Kismet/GameplayStatics.h"
11#include "Kismet/KismetMathLibrary.h"
12#include "Serialization/ObjectAndNameAsStringProxyArchive.h"
13#include "UI/BasicWidget.h"
14#include "UI/ButtonListWidget.h"
15#include "Net/UnrealNetwork.h"
16
18 : Super()
19{
20 // Set this actor to call Tick() every frame. You can turn this off to improve performance if you don't need it.
21 PrimaryActorTick.bCanEverTick = true;
22 bReplicates = true;
23
24 BoxForSpawn = CreateDefaultSubobject<UBoxComponent>(FName("BoxForSpawn"));
25 if (BoxForSpawn)
26 {
27 RootComponent = BoxForSpawn;
28 BoxForSpawn->SetBoxExtent(FVector(10.0f, 60.0f, 60.0f));
29 BoxForSpawn->SetGenerateOverlapEvents(false);
30 }
31
32 BoxForRotate = CreateDefaultSubobject<UBoxComponent>(FName("BoxForRotate"));
33 if (BoxForRotate)
34 {
35 BoxForRotate->SetupAttachment(RootComponent);
36 BoxForRotate->SetBoxExtent(FVector(3.0f, 60.0f, 60.0f));
37 }
38
39 WidgetScene = CreateDefaultSubobject<USceneComponent>(FName("WidgetScene"));
40 if (WidgetScene)
41 {
42 WidgetScene->SetupAttachment(RootComponent);
43 WidgetScene->SetIsReplicated(true);
44 }
45
46 WidgetComponent = CreateDefaultSubobject<UWidgetComponent>(FName("Widget"));
48 {
49 WidgetComponent->SetupAttachment(WidgetScene);
50 WidgetComponent->SetRelativeScale3D(FVector(0.1f, 0.1f, 0.1f));
51 WidgetComponent->SetDrawSize(FVector2D(1200.0f, 1200.0f));
52 WidgetComponent->SetTickWhenOffscreen(true);
53 WidgetComponent->SetIsReplicated(true);
54 }
55
56 OpenCloseTimeline = CreateDefaultSubobject<UTimelineComponent>(TEXT("OpenCloseTimeline"));
57
58 BoxForTouch = CreateDefaultSubobject<UBoxComponent>(FName("BoxForTouch"));
60 {
61 BoxForTouch->SetupAttachment(RootComponent);
62 BoxForTouch->SetBoxExtent(FVector(60.0f, 60.0f, 60.0f));
63 BoxForTouch->SetGenerateOverlapEvents(false);
64 }
65
66 static ConstructorHelpers::FObjectFinder<USoundWave> OpenSoundFinder(TEXT("SoundWave'/PixoCore/Audio/UI/UI_Open.UI_Open'"));
67 OpenSound = OpenSoundFinder.Object;
68 static ConstructorHelpers::FObjectFinder<USoundWave> CloseSoundFinder(TEXT("SoundWave'/PixoCore/Audio/UI/UI_Close.UI_Close'"));
69 CloseSound = CloseSoundFinder.Object;
70 static ConstructorHelpers::FObjectFinder<UCurveFloat> OpenCloseCurveFinder(TEXT("CurveFloat'/PixoCore/Blueprints/UI/OpenCloseWidgetCurve.OpenCloseWidgetCurve'"));
71 OpenCloseFloatCurve = OpenCloseCurveFinder.Object;
72}
73
74// Called when the game starts or when spawned
76{
77 Super::BeginPlay();
80
81 BoxForRotate->OnComponentBeginOverlap.AddDynamic(this, &AWidgetController::BeginOverlap);
82 BoxForRotate->OnComponentEndOverlap.AddDynamic(this, &AWidgetController::EndOverlap);
83
85 {
87 OpenCloseTimeline->SetTimelineFinishedFunc(TimelineFinished);
88 OpenCloseTimeline->SetLooping(false);
89 OpenCloseTimeline->SetIgnoreTimeDilation(true);
90 }
91 else
92 {
93 UE_LOG(LogTemp, Warning, TEXT("Timeline Curve Doesn't Set"));
94 }
95}
96
97// Called every frame
98void AWidgetController::Tick(float DeltaTime)
99{
100 Super::Tick(DeltaTime);
101
102 const bool IsWidgetExist = WidgetComponent && WidgetComponent->GetUserWidgetObject();
103 const APawn* FirstPlayerPawn = UGameplayStatics::GetPlayerPawn(GetWorld(), 0);
104
105 if (FirstPlayerPawn && IsWidgetExist)
106 {
107 if (CanRotate())
108 {
109 const float TargetYaw = UKismetMathLibrary::FindLookAtRotation(FinalLocation, FirstPlayerPawn->GetActorLocation()).Yaw;
110 FRotator ResultYaw = FMath::RInterpConstantTo(BoxForRotate->GetComponentRotation(), FRotator(0.0f, TargetYaw, 0.0f), DeltaTime, 200.0f);
111 BoxForRotate->SetWorldRotation(ResultYaw);
112 if (OverlapArray.Num() <= 0)
113 {
114 ResultYaw = FMath::RInterpConstantTo(WidgetComponent->GetComponentRotation(), FRotator(0.0f, TargetYaw, 0.0f), DeltaTime, 200.0f);
115 WidgetComponent->SetWorldRotation(ResultYaw);
116 }
117 }
118
119 const FVector InstigatorLocation = FirstPlayerPawn->GetActorLocation();
120 if (!InstigatorLocation.Equals(FVector::ZeroVector, 1.0f))
121 {
122 const float SquaredDistance2D = FVector::DistSquared2D(InstigatorLocation, FinalLocation);
123 UBasicWidget* BasicWidget = Cast<UBasicWidget>(WidgetComponent->GetUserWidgetObject());
124 BasicWidget->ProcessSquaredDistanceToPlayer(SquaredDistance2D, DeltaTime);
125 }
126 }
127}
128
130{
131 if (WidgetInfo)
132 {
133 InitWidgetLocally(WidgetInfo);
134 }
135}
136
138{
139 if (WidgetInfo->bModal && CurrentWidget)
140 {
141 WidgetQueue.AddTail(CurrentWidget); // need check
143 }
144
145 const bool NeedToCreateNewWidget = CurrentWidget == nullptr || CurrentWidget->GetGuid() != WidgetInfo->Guid;
146
147 if (NeedToCreateNewWidget)
148 {
149 if (WidgetInfo->bIsUpdateRequest)
150 {
151 UE_LOG(LogTemp, Warning, TEXT("WidgetController can't open widget by UpdateRequest"));
152 return;
153 }
154
155 UUserWidget* NewWidget = CreateWidget(GetWorld(), WidgetInfo->WidgetClass);
156 WidgetComponent->SetWidget(NewWidget);
158 }
159
160 if (WidgetInfo->bUseTouchToInteractWithWidget)
161 {
162 BoxForTouch->SetGenerateOverlapEvents(true);
163 BoxForTouch->OnComponentBeginOverlap.AddDynamic(this, &AWidgetController::TouchBeginOverlap);
164 BoxForTouch->OnComponentEndOverlap.AddDynamic(this, &AWidgetController::TouchEndOverlap);
165 }
166
167 if (UBasicWidget* BasicWidget = Cast<UBasicWidget>(WidgetComponent->GetUserWidgetObject()))
168 {
169 InitBasicWidget(BasicWidget, WidgetInfo);
170 BasicWidget->OnPlayerWentAway.BindUObject(this, &AWidgetController::CloseWidget);
171 }
172
174 {
176 }
177}
178
180{
181 BasicWidget->Init(WidgetInfo);
182 SetActorEnableCollision(true);
183
184 // if (CurrentWidget && CurrentWidget->IsForcedToExecuteCloseCallback())
185 // {
186 // CurrentWidget->OnExitMenuEventDelegate.ExecuteIfBound(nullptr);
187 // }
188 CurrentWidget = BasicWidget;
189 CurrentWidget->SetCustomGuid(WidgetInfo->Guid);
190
191 if (WidgetInfo->UseCustomWidgetPosition)
192 {
193 FinalLocation = WidgetInfo->Location;
194 FinalRotation = WidgetInfo->Rotator;
195 }
196 else
197 {
198 FinalLocation = GetActorLocation();
199 FinalRotation = GetActorRotation();
200 }
201
202 bRotateAllowed = WidgetInfo->bCanRotate;
204
205 UWorld* ThisWorld = GetWorld();
206 if (OpenSound && ThisWorld && ThisWorld->bAllowAudioPlayback && !ThisWorld->IsNetMode(NM_DedicatedServer) && bSoundAllowed)
207 {
208 UGameplayStatics::PlaySound2D(ThisWorld, OpenSound);
209 }
210}
211
212void AWidgetController::TouchBeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
213 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
214{
215 if (const APixoVRHand* HandOverlapping = Cast<APixoVRHand>(OtherActor))
216 {
217 IHandAnimationInterface::Execute_SetAnimationGripState(HandOverlapping->HandMesh->GetAnimInstance(), EPixoVRGripState::PointFinger);
218 HandOverlapping->TouchComponent->Activate();
219 }
220}
221
222void AWidgetController::TouchEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
223 UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
224{
225 if (const APixoVRHand* HandOverlapping = Cast<APixoVRHand>(OtherActor))
226 {
227 IHandAnimationInterface::Execute_SetAnimationGripState(HandOverlapping->HandMesh->GetAnimInstance(), EPixoVRGripState::Open);
228 HandOverlapping->TouchComponent->Deactivate();
229 }
230}
231
236
238{
239 if (CurrentWidget->IsValidLowLevel())
240 return true;
241
242 return false;
243}
244
246{
247 if (auto Tail = WidgetQueue.GetTail())
248 {
249 CurrentWidget = Tail->GetValue();
250 WidgetQueue.RemoveNode(Tail);
251 WidgetComponent->SetWidget(nullptr);
253 }
254 else if (WidgetComponent->GetUserWidgetObject())
255 {
256 ensure(CurrentWidget);
257 CurrentWidget->OnClickExitMenuEvent(FText::GetEmpty());
258 CurrentWidget->MarkPendingKill();
259 CurrentWidget = nullptr;
260
261 BoxForTouch->OnComponentBeginOverlap.Clear();
262 BoxForTouch->OnComponentEndOverlap.Clear();
263
264 SetActorEnableCollision(false);
266
267 UWorld* ThisWorld = GetWorld();
268 if (CloseSound && ThisWorld && ThisWorld->bAllowAudioPlayback && !ThisWorld->IsNetMode(NM_DedicatedServer) && bSoundAllowed)
269 {
270 UGameplayStatics::PlaySound2D(ThisWorld, CloseSound);
271 }
272 }
273}
274
276{
277 OpenCloseTimeline->PlayFromStart();
278}
279
281{
282 StopLookAt();
283 OpenCloseTimeline->ReverseFromEnd();
284}
285
290
295
300
302{
303 if (CurrentWidget)
304 {
305 WidgetComponent->SetWidget(CurrentWidget);
306 CurrentWidget->SetVisibility(ESlateVisibility::Visible);
307 }
308}
309
311{
312 if (CurrentWidget)
313 {
314 CurrentWidget->SetVisibility(ESlateVisibility::Collapsed);
315 WidgetComponent->SetWidget(nullptr);
316 }
317}
318
319void AWidgetController::GetLifetimeReplicatedProps(TArray<FLifetimeProperty>& OutLifetimeProps) const
320{
321 Super::GetLifetimeReplicatedProps(OutLifetimeProps);
322 DOREPLIFETIME(AWidgetController, FinalLocation);
323 //DOREPLIFETIME(AWidgetController, StartLocation);
324 DOREPLIFETIME(AWidgetController, FinalRotation);
325 //DOREPLIFETIME(AWidgetController, StartRotation);
326 DOREPLIFETIME(AWidgetController, WidgetComponent);
327 DOREPLIFETIME(AWidgetController, OpenCloseTimeline);
328}
329
331{
332 WidgetScene->SetWorldScale3D(FVector(Value, Value, Value));
333 if (OpenCloseTimeline->IsPlaying())
334 {
335 SetActorLocation(FinalLocation);
336 FRotator NewRotation = FinalRotation;
337 SetActorRotation(NewRotation);
338 BoxForRotate->SetWorldRotation(NewRotation);
339 WidgetComponent->SetWorldRotation(NewRotation);
340 }
341}
342
344{
345 if (OpenCloseTimeline->GetPlaybackPosition() > 0)
346 {
347 SetActorLocation(FinalLocation);
348 StartLookAt();
349 }
350 else
351 {
352 WidgetComponent->SetWidget(nullptr);
353 }
354}
355
356void AWidgetController::BeginOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
357{
358 if (OtherComp != BoxForTouch)
359 {
360 OverlapArray.AddUnique(OtherComp);
361 }
362}
363
364void AWidgetController::EndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
365{
366 if (OtherComp != BoxForTouch)
367 {
368 OverlapArray.Remove(OtherComp);
369 }
370}
APixoVRHand is an actor class that represents a hand in the VR environment.
Definition PixoVRHand.h:99
AActor class representing a widget controller that manages widget functionality.
void HandleTimelineFinished()
UFUNCTION()
virtual void BeginPlay() override
bool bRotateAllowed
Is allowed the widget rotates to character or not.
FRotator FinalRotation
UPROPERTY(BlueprintReadOnly, Replicated, Meta = (ExposeOnSpawn = true), Category = "Widget")
void HandleTimeLineFloat(float Value)
UFUNCTION()
bool IsWidgetInited()
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "WidgetController")
void InitWidgetLocally(UBasicWidgetInfo *WidgetInfo)
TDoubleLinkedList< UBasicWidget * > WidgetQueue
void BeginOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
UFUNCTION()
void TouchEndOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex)
UFUNCTION()
bool bStartLookAtAllowed
Is allowed the widget rotates to character or not.
FVector FinalLocation
UPROPERTY(EditAnywhere, BlueprintReadWrite, Replicated, Meta = (ExposeOnSpawn = true),...
FOnTimelineEvent TimelineFinished
This delegate will be fired when the timeline reaches the end.
TArray< UPrimitiveComponent * > OverlapArray
UPROPERTY()
void InitBasicWidget(UBasicWidget *BasicWidget, UBasicWidgetInfo *WidgetInfo)
bool CanRotate()
UFUNCTION(BlueprintCallable, BlueprintPure, Category = "Widget")
virtual void Tick(float DeltaTime) override
void CloseWidget()
UFUNCTION(BlueprintCallable, Category = "WidgetController")
UWidgetComponent * WidgetComponent
UPROPERTY(VisibleAnywhere, Replicated, BlueprintReadWrite, Category = "Components")
void StartLookAt()
UFUNCTION(BlueprintCallable, Category = "Widget")
UBoxComponent * BoxForRotate
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Components")
void EndOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex)
UFUNCTION()
bool bFinalLocationPendingChange
UPROPERTY()
virtual void GetLifetimeReplicatedProps(TArray< FLifetimeProperty > &OutLifetimeProps) const override
USoundBase * OpenSound
UPROPERTY(EditAnywhere, BlueprintReadWrite)
FOnTimelineFloat TimelinePostUpdate
This delegate will be fired to be informed when the timeline has a new update.
void OpenWidgetAnim()
UFUNCTION(BlueprintCallable, Category = "Widget")
void CloseWidgetAnim()
UFUNCTION(BlueprintCallable, Category = "Widget")
UTimelineComponent * OpenCloseTimeline
UPROPERTY(VisibleAnywhere, Replicated, BlueprintReadWrite, Category = "Components")
UBasicWidget * CurrentWidget
UPROPERTY()
UBoxComponent * BoxForSpawn
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Components")
UCurveFloat * OpenCloseFloatCurve
UPROPERTY(EditDefaultsOnly, Category = "Widget")
void StopLookAt()
UFUNCTION(BlueprintCallable, Category = "Widget")
void InitWidget(UBasicWidgetInfo *WidgetInfo)
UFUNCTION(BlueprintCallable, Category = "WidgetController")
void ActivateCurrentWidget()
UFUNCTION(BlueprintCallable, Category = "Widget")
USoundBase * CloseSound
UPROPERTY(EditAnywhere, BlueprintReadWrite)
USceneComponent * WidgetScene
UPROPERTY(VisibleAnywhere, BlueprintReadWrite, Category = "Components")
void DeactivateCurrentWidget()
UFUNCTION(BlueprintCallable, Category = "Widget")
UBoxComponent * BoxForTouch
UPROPERTY(VisibleAnywhere, Replicated, BlueprintReadWrite, Category = "Components")
void TouchBeginOverlap(UPrimitiveComponent *OverlappedComponent, AActor *OtherActor, UPrimitiveComponent *OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult &SweepResult)
UFUNCTION()
Base class for user widgets in the PixoCore module.
Definition BasicWidget.h:24
const FGuid & GetGuid() const
Definition BasicWidget.h:47
void ProcessSquaredDistanceToPlayer(const float SquaredDistanceToPlayerCm, const float DeltaSec)
Processes the squared distance to the player.
virtual void OnClickExitMenuEvent(FText ButtonName)
Event handler for the "ExitMenu" button click event.
void SetCustomGuid(const FGuid &NewGuid)
Definition BasicWidget.h:46
virtual void Init(UBasicWidgetInfo *WidgetInfo)
Initializes the widget with the provided widget information.
UDataAsset class representing basic widget information.
Definition UIData.h:56
bool bCanRotate
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:127
bool UseCustomWidgetPosition
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:148
FVector Location
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "UseCustomWidgetPosition == true"...
Definition UIData.h:171
TSubclassOf< UBasicWidget > WidgetClass
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:96
bool bPlayOpenCloseSound
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:134
FRotator Rotator
UPROPERTY(EditAnywhere, BlueprintReadWrite, meta = (EditCondition = "UseCustomWidgetPosition == true"...
Definition UIData.h:177
FGuid Guid
UPROPERTY()
Definition UIData.h:156
bool bUseTouchToInteractWithWidget
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:141
bool bModal
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:113
bool bIsUpdateRequest
UPROPERTY(EditAnywhere, BlueprintReadWrite)
Definition UIData.h:120