Why is my UI not showing in packaged build?

So I am making a local multiplayer game and I have this system where it would put out a number on the top of the player, showing which player you are. For some reason, this number system works in editor but when I build it, there is no number on the top of any players. What’s weird about this is that the rest of the UI seems to work (item duration, item icon, etc) when put on the top of the player’s head. Here is what it looks like :

This is on the editor :

This is on build :

Here is a snippet of the code :

Gamemode Class :

#include "BrawlbblesGameMode.h"

#include "BrawlbblesGameInstance.h"
#include "Camera.h"
#include "EngineUtils.h"
#include "SpawnPoint.h"
#include "Algo/ForEach.h"
#include "Algo/RandomShuffle.h"
#include "Android/AndroidPlatformApplicationMisc.h"
#include "GameFramework/InputDeviceLibrary.h"
#include "Kismet/GameplayStatics.h"

ABrawlbblesGameMode::ABrawlbblesGameMode()
{
	bStartPlayersAsSpectators = false;
	DefaultPawnClass = nullptr;
}

void ABrawlbblesGameMode::BeginPlay()
{
	Super::BeginPlay();

	UBrawlbblesGameInstance* GameInstance = Cast<UBrawlbblesGameInstance>(GetWorld()->GetGameInstance());

	if (GameInstance)
	{
		if (!GameInstance->bIsGameStarted)
		{
			GameInstance->bIsGameStarted = true;
			
			// ✅ Load and add the Controls UI to the viewport
			if (ControlsUIClass)
			{
				ControlsUI = CreateWidget<UUserWidget>(GetWorld(), ControlsUIClass);
				if (ControlsUI)
				{
					ControlsUI->AddToViewport();
					UE_LOG(LogTemp, Warning, TEXT("Showing Controls UI. Waiting for input to start..."));
				}
			}

			// ✅ Set a persistent timer to remove UI and start the game
			GetWorld()->GetTimerManager().SetTimer(
				GameStartTimerHandle, 
				this, 
				&ABrawlbblesGameMode::StartGameAfterDelay, 
				5.0f, 
				false
			);
		}
	}

	// Find all SpawnPoints in the world
	SpawnPoints.Empty();
	TArray<AActor*> FoundActors;
	UGameplayStatics::GetAllActorsOfClass(this, ASpawnPoint::StaticClass(), FoundActors);
	for (AActor* Actor : FoundActors)
	{
		if (ASpawnPoint* SpawnPoint = Cast<ASpawnPoint>(Actor))
		{
			SpawnPoints.Add(SpawnPoint);
		}
	}

	Algo::RandomShuffle(SpawnPoints);

	// Remove auto-spawned player
	if (APlayerController* AutoPlayer = UGameplayStatics::GetPlayerController(GetWorld(), 0))
	{
		UGameplayStatics::RemovePlayer(AutoPlayer, true);
	}

	// Restore players if switching levels
	if (GameInstance && GameInstance->StoredPlayerIndexes.Num() > 0)
	{
		UE_LOG(LogTemp, Warning, TEXT("Restoring Players from Game Instance..."));

		AmountOfPlayers = GameInstance->StoredPlayerIndexes.Num();
		PlayerScores.Empty();

		// Recreate players in the correct order using ControllerId
		for (const auto& Elem : GameInstance->StoredPlayerIndexes)
		{
			int32 ControllerId = Elem.Key;
			int32 PlayerIndex = Elem.Value;

			FString Error = "Error";
			// ✅ FIX: Try to get the existing Local Player instead of always creating a new one
			ULocalPlayer* ExistingLocalPlayer = GetGameInstance()->FindLocalPlayerFromControllerId(ControllerId);
			ULocalPlayer* LocalPlayer = ExistingLocalPlayer ? ExistingLocalPlayer : GetGameInstance()->CreateLocalPlayer(PlayerIndex, Error, true);
			
			if (!LocalPlayer)
			{
				UE_LOG(LogTemp, Error, TEXT("Failed to create local player %d: %s"), PlayerIndex, *Error);
				continue;
			}

			APlayerController* PlayerController = LocalPlayer->GetPlayerController(GetWorld());
			if (!PlayerController)
			{
				UE_LOG(LogTemp, Error, TEXT("Failed to get player controller for local player %d"), PlayerIndex);
				continue;
			}

			// Restore scores
			int32 RestoredScore = GameInstance->StoredPlayerScores.Contains(ControllerId) ? GameInstance->StoredPlayerScores[ControllerId] : 0;
			PlayerScores.Add(PlayerController, RestoredScore);
			UE_LOG(LogTemp, Warning, TEXT("Restored Score: Player %d -> %d"), PlayerIndex, RestoredScore);

			// Destroy any auto-spawned pawn
			if (APawn* ExistingPawn = PlayerController->GetPawn())
			{
				ExistingPawn->Destroy();
			}

			// Restore character mapping
			if (GameInstance->StoredControllerToCharacter.Contains(ControllerId))
			{
				if (ABrawlbblesCharacter* StoredCharacter = GameInstance->StoredControllerToCharacter[ControllerId].Get())
				{
					// ✅ FIX: Reset the character's position to a spawn point
					FTransform SpawnTransform = SpawnPoints[PlayerIndex % SpawnPoints.Num()]->GetActorTransform();
					StoredCharacter->SetActorTransform(SpawnTransform);

					PlayerController->Possess(StoredCharacter);
					StoredCharacter->EnableInput(PlayerController);

					// ✅ FIX: Ensure UI is updated
					FText PlayerNumber = FText::AsNumber(PlayerIndex + 1);
					StoredCharacter->SetPlayerName(PlayerNumber);

					int32 ColorIndex = PlayerIndex % PlayerTextColors.Num();
					StoredCharacter->SetPlayerTextColor(PlayerTextColors[ColorIndex]);

					continue;
				}
			}

			// Spawn a new character if no stored character
			SpawnPlayer(PlayerController, PlayerIndex);
		}

		// Clear stored data
		GameInstance->StoredPlayerScores.Empty();
		GameInstance->StoredControllerToCharacter.Empty();
		GameInstance->StoredPlayerIndexes.Empty();
	}
	else
	{
		// Normal game start
		SpawnLocalPlayers();
	}

	// ✅ Load and add the Score UI to the viewport
	if (ScoreUIClass)
	{
		UScoreUI* ScoreWidget = CreateWidget<UScoreUI>(GetWorld(), ScoreUIClass);
		if (ScoreWidget)
		{
			ScoreUI = ScoreWidget;
			ScoreUI->InitializeScoreUI(AmountOfPlayers);
			ScoreUI->AddToViewport();
			ScoreUI->SetVisibility(ESlateVisibility::Hidden); // ✅ Hides UI initially
		}
	}

	// Spawn and assign shared camera
	SpawnSharedCamera();

	// Mark game as started
	bIsGameStarted = true;
}

void ABrawlbblesGameMode::HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer)
{
	Super::HandleStartingNewPlayer_Implementation(NewPlayer);

	if (ABrawlbblesCharacter* Character = Cast<ABrawlbblesCharacter>(NewPlayer->GetPawn()))
	{
		Character->StartController();
	}
}

void ABrawlbblesGameMode::PlayerEliminated(APlayerController* Killer, APlayerController* Victim)
{
	if (bIsWinUIActive) return;
	
	if (Victim)
	{
		EliminatedPlayers.Add(Victim); // Track eliminated player
	}

	if (Killer && Killer != Victim)
	{
		if (PlayerScores.Contains(Killer))
		{
			PlayerScores[Killer] += 1;
			UE_LOG(LogTemp, Warning, TEXT("Player %s scored! Total Score: %d"),
				*Killer->GetName(), PlayerScores[Killer]);

			// ✅ Update the UI with all players' scores
			UpdateAllScoresInUI();
			
			// ✅ Update the UI
			if (ScoreUI)
			{
				int32 MaxScore = ScoreUI->GetMaxScore();
				if (PlayerScores[Killer] >= MaxScore)
				{
					ScoreUI->SetVisibility(ESlateVisibility::Visible);
					ScoreUI->UpdateScore(Killer->GetPlatformUserId(), PlayerScores[Killer]);

					// ✅ Delay before showing "Player X Wins" UI
					FTimerHandle WinUITimer;
					GetWorldTimerManager().SetTimer(WinUITimer, [this, Killer]()
					{
						ShowWinUI(Killer);
					}, 2.0f, false); // Show after 2 seconds

					return; // Skip further processing
				}
			}
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("ERROR: Player %s is not in PlayerScores!"), *Killer->GetName());
		}
	}

	int AlivePlayers = 0;
	APlayerController* LastAlivePlayer = nullptr;
	for (const auto& Elem : PlayerScores)
	{
		APlayerController* PlayerController = Elem.Key;
		if (PlayerController && PlayerController->GetPawn() && !EliminatedPlayers.Contains(PlayerController))
		{
			AlivePlayers++;
			LastAlivePlayer = PlayerController;
		}
	}

	UE_LOG(LogTemp, Warning, TEXT("Alive players count: %d"), AlivePlayers);

	// ✅ Show score UI only if one player remains
	if (AlivePlayers == 1 && ScoreUI)
	{
		ScoreUI->SetVisibility(ESlateVisibility::Visible);
	}

	
	// ✅ FIX: Assign camera to the last alive player
	if (Victim && Victim->GetViewTarget() == SharedCamera)
	{
		UE_LOG(LogTemp, Warning, TEXT("Eliminated player's view target was the shared camera. Reassigning..."));
        
		// Find a new player to assign the camera to
		APlayerController* NewCameraOwner = nullptr;

		for (const auto& Elem : PlayerScores)
		{
			APlayerController* PlayerController = Elem.Key;
			if (PlayerController && PlayerController->GetPawn() && !EliminatedPlayers.Contains(PlayerController))
			{
				NewCameraOwner = PlayerController;
				break;
			}
		}

		if (NewCameraOwner)
		{
			UE_LOG(LogTemp, Warning, TEXT("New camera owner: %s"), *NewCameraOwner->GetName());
			NewCameraOwner->SetViewTarget(SharedCamera);
		}
		else
		{
			UE_LOG(LogTemp, Warning, TEXT("No alive players found to assign the camera."));
		}
	}

	
	if (AlivePlayers <= 1)
	{
		UE_LOG(LogTemp, Warning, TEXT("Restarting game in 3 seconds..."));

		// ✅ Show the Score UI before switching levels
		if (ScoreUI)
		{
			ScoreUI->SetVisibility(ESlateVisibility::Visible);
		}
		
		FTimerHandle RestartTimer;
		GetWorldTimerManager().SetTimer(RestartTimer, this, &ABrawlbblesGameMode::SwitchLevel, 3.0f, false);
	}
}

void ABrawlbblesGameMode::SwitchLevel()
{
	FString CurrentLevel = GetWorld()->GetMapName();
	CurrentLevel.RemoveFromStart(GetWorld()->StreamingLevelsPrefix);
	FString NextLevel = (CurrentLevel == "GalaxyLevel") ? "Fishbowl" : "GalaxyLevel";

	UBrawlbblesGameInstance* GameInstance = GetGameInstance<UBrawlbblesGameInstance>();
	if (GameInstance)
	{
		GameInstance->StoredPlayerScores.Empty();
		GameInstance->StoredPlayerIndexes.Empty();
		GameInstance->StoredControllerToCharacter.Empty();

		GameInstance->SetMusicParameter("Invincible", 0.0f);
		GameInstance->SetMusicParameter("Pause", 0.0f);

		// Force re-enable input for all players
		EnableAllPlayers();

		APlayerController* LastAlivePlayer = nullptr;
		// Store player data using ControllerId
		for (const auto& Elem : PlayerScores)
		{
			APlayerController* PlayerController = Elem.Key;
			if (PlayerController)
			{
				int32 ControllerId = PlayerController->GetLocalPlayer()->GetControllerId();
				GameInstance->StoredPlayerScores.Add(ControllerId, Elem.Value);
				GameInstance->StoredPlayerIndexes.Add(ControllerId, GameInstance->StoredPlayerIndexes.Num());

				if (ABrawlbblesCharacter* BrawlbblesPlayerCharacter = Cast<ABrawlbblesCharacter>(PlayerController->GetPawn()))
				{
					GameInstance->StoredControllerToCharacter.Add(ControllerId, BrawlbblesPlayerCharacter);
				}

				// ✅ FIX: Ensure the last alive player has the camera before switching levels
				if (!EliminatedPlayers.Contains(PlayerController))
				{
					LastAlivePlayer = PlayerController;
				}
			}
		}

		// ✅ FIX: Assign the camera before switching levels
		if (LastAlivePlayer && SharedCamera)
		{
			UE_LOG(LogTemp, Warning, TEXT("Assigning camera to last alive player before switching levels."));
			LastAlivePlayer->SetViewTarget(SharedCamera);
		}

		// Reset Game Mode variables
		ResetGameModeVariables();

		// Reset camera reference
		SharedCamera = nullptr;

		// Open the next level
		UGameplayStatics::OpenLevel(this, FName(*NextLevel));

		RestoreLocalPlayers();
	}
}

void ABrawlbblesGameMode::AssignCameraToAllPlayers()
{
	if (!SharedCamera)
	{
		return;
	}
	
	// Assign the same camera to every local player
	for (int i = 0; i < AmountOfPlayers; i++)
	{
		APlayerController* PlayerController = UGameplayStatics::GetPlayerController(GetWorld(), i);
		if (PlayerController)
		{
			PlayerController->SetViewTarget(SharedCamera);
			PlayerController->bAutoManageActiveCameraTarget = false;
			// 🔥 Log the new view target for verification
			UE_LOG(LogTemp, Warning, TEXT("Player %d now has ViewTarget: %s"), 
				i, 
				*GetNameSafe(PlayerController->GetViewTarget()));
		}
	}
}

void ABrawlbblesGameMode::ResetGameModeVariables()
{
	// Reset all relevant game mode variables
	PlayerScores.Empty();
	SpawnPoints.Empty();
	AmountOfPlayers = 0;
	bIsGameStarted = false;

	// Stop any active timers
	GetWorldTimerManager().ClearAllTimersForObject(this);
}

void ABrawlbblesGameMode::SpawnLocalPlayers()
{
	/*if (UInputDeviceLibrary::GetAllActiveUsers(OutUsers) > 0)
	{
		for (int i = 0; i < OutUsers.Num(); i++)
		{
			FString OutError;
			ULocalPlayer* NewLocalPlayer = GetGameInstance()->CreateLocalPlayer(i, OutError, true);
			UE_LOG(LogTemp, Warning, TEXT("Detected active users: %d"), OutUsers.Num());
			UE_LOG(LogTemp, Warning, TEXT("Local Player %d, Controller ID: %d"), i, NewLocalPlayer->GetControllerId());
			if (NewLocalPlayer)
			{
				APlayerController* NewController = NewLocalPlayer->GetPlayerController(GetWorld());
				if (NewController)
				{
					NewController->bAutoManageActiveCameraTarget = false;

					// Destroy any auto-spawned pawn
					if (APawn* AutoPawn = NewController->GetPawn())
					{
						AutoPawn->Destroy();
					}

					// Initialize score
					PlayerScores.Add(NewController, 0);

					// Spawn the player
					SpawnPlayer(NewController, i);
				}
			}
			AmountOfPlayers++;
		}
	}*/

	int32 MaxLocalPlayers = 4; // Adjust based on your game design

	for (int i = 0; i < MaxLocalPlayers; i++)
	{
		FString OutError;
		ULocalPlayer* NewLocalPlayer = GetGameInstance()->CreateLocalPlayer(i, OutError, true);

		if (NewLocalPlayer)
		{
			APlayerController* NewController = NewLocalPlayer->GetPlayerController(GetWorld());
			if (NewController)
			{
				NewController->bAutoManageActiveCameraTarget = false;

				// Destroy any auto-spawned pawn
				if (APawn* AutoPawn = NewController->GetPawn())
				{
					AutoPawn->Destroy();
				}

				// Initialize score tracking
				PlayerScores.Add(NewController, 0);

				// Spawn and assign the player
				SpawnPlayer(NewController, i);

				UE_LOG(LogTemp, Warning, TEXT("✅ Local Player %d Created: Controller ID %d"), i, NewController->GetLocalPlayer()->GetControllerId());
			}
			else
			{
				UE_LOG(LogTemp, Error, TEXT("❌ Failed to get Player Controller for Local Player %d"), i);
			}
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("❌ Failed to create Local Player %d: %s"), i, *OutError);
		}
	}

	AmountOfPlayers = MaxLocalPlayers;
}

void ABrawlbblesGameMode::SpawnPlayer(APlayerController* PlayerController, int PlayerIndex)
{
	if (!PlayerController || SpawnPoints.Num() == 0)
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to spawn player: Invalid controller or no spawn points available."));
		return;
	}

	FTransform SpawnTransform = SpawnPoints[PlayerIndex % SpawnPoints.Num()]->GetActorTransform();
	FActorSpawnParameters SpawnParams;
	SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

	ABrawlbblesCharacter* NewPlayerPawn = GetWorld()->SpawnActor<ABrawlbblesCharacter>(PlayerCharacter, SpawnTransform, SpawnParams);
	if (NewPlayerPawn)
	{
		PlayerController->Possess(NewPlayerPawn);
		UE_LOG(LogTemp, Warning, TEXT("Player %d Possessed Character: %s"), PlayerIndex, *NewPlayerPawn->GetName());
		NewPlayerPawn->EnableInput(PlayerController);

		FText PlayerNumber = FText::AsNumber(PlayerIndex + 1);
		NewPlayerPawn->SetPlayerName(PlayerNumber);

		int32 ColorIndex = PlayerIndex % PlayerTextColors.Num();
		NewPlayerPawn->SetPlayerTextColor(PlayerTextColors[ColorIndex]);
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to spawn character for player %d"), PlayerIndex);
	}

	
}

void ABrawlbblesGameMode::SpawnSharedCamera()
{
	if (!SharedCamera && CameraActorClass)
	{
		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AlwaysSpawn;

		SharedCamera = GetWorld()->SpawnActor<ACamera>(CameraActorClass, FVector(0, 0, 2000), FRotator(-45, 90, 0), SpawnParams);
	}

	if (SharedCamera)
	{
		GetWorldTimerManager().SetTimerForNextTick(this, &ABrawlbblesGameMode::AssignCameraToAllPlayers);
	}
}

void ABrawlbblesGameMode::ShowWinUI(APlayerController* WinningPlayer)
{
	if (!WinUIClass) return; // Ensure the widget class is assigned

	// ✅ Set flag to prevent Score UI from being triggered
	bIsWinUIActive = true;
	
	// ✅ Hide Score UI when the Win UI appears
	if (ScoreUI)
	{
		ScoreUI->SetVisibility(ESlateVisibility::Hidden);
	}
	
	UBrawlbblesGameInstance* GameInstance = Cast<UBrawlbblesGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));

	if (GameInstance)
	{
		// Example: Set "Invincible" to 1 (Activate)
		GameInstance->SetMusicParameter("Win", 1.0f);
	}
	
	// ✅ Create the "Player X Wins" UI
	WinUI = CreateWidget<UUserWidget>(GetWorld(), WinUIClass);
	if (WinUI)
	{
		WinUI->AddToViewport();

		// ✅ Set "Player X Wins!" text
		if (UTextBlock* WinText = Cast<UTextBlock>(WinUI->GetWidgetFromName(TEXT("WinText"))))
		{
			int32 PlayerIndex = WinningPlayer->GetLocalPlayer()->GetControllerId();
			FText WinMessage = FText::Format(NSLOCTEXT("Brawlbbles", "WinMessage", "Player {0} Wins!"), FText::AsNumber(PlayerIndex + 1));
			WinText->SetText(WinMessage);
		}
	}

	// ✅ Prevent further gameplay interactions
	DisableAllPlayers();
    
	// ✅ Restart the game after showing the win message
	FTimerHandle RestartTimer;
	GetWorldTimerManager().SetTimer(RestartTimer, this, &ABrawlbblesGameMode::RestartGame, 10.0f, false);
}

void ABrawlbblesGameMode::DisableAllPlayers()
{
	for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
	{
		APlayerController* PlayerController = It->Get();
		if (PlayerController)
		{
			PlayerController->SetIgnoreMoveInput(true);
			PlayerController->SetIgnoreLookInput(true);
		}
	}
}

void ABrawlbblesGameMode::EnableAllPlayers()
{
	for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
	{
		APlayerController* PC = It->Get();
		if (PC)
		{
			PC->SetIgnoreMoveInput(false);
			PC->SetIgnoreLookInput(false);
			UE_LOG(LogTemp, Warning, TEXT("Input re-enabled for: %s"), *PC->GetName());
		}
	}
}

void ABrawlbblesGameMode::RestartGame()
{
	UBrawlbblesGameInstance* GameInstance = GetGameInstance<UBrawlbblesGameInstance>();
	if (GameInstance)
	{
		GameInstance->ResetStoredData();
		// Example: Set "Invincible" to 1 (Activate)
		GameInstance->SetMusicParameter("Win", 0.0f);
		// Assign and play the event
		GameInstance->MusicComponent->SetEvent(GameInstance->BackgroundMusicEvent);
		GameInstance->MusicComponent->Play();
		GameInstance->bIsGameStarted = false;
	}

	bIsWinUIActive = false;

	SwitchLevel();
}

void ABrawlbblesGameMode::UpdateAllScoresInUI()
{
	if (!ScoreUI) return; // Ensure the UI exists

	UE_LOG(LogTemp, Warning, TEXT("Updating all player scores in UI..."));

	// Iterate through all players and update their scores
	for (const auto& Elem : PlayerScores)
	{
		APlayerController* PlayerController = Elem.Key;
		int32 Score = Elem.Value;

		if (PlayerController)
		{
			int32 PlayerIndex = PlayerController->GetLocalPlayer()->GetControllerId();
			ScoreUI->UpdateScore(PlayerIndex, Score);
		}
	}
}

void ABrawlbblesGameMode::RestoreLocalPlayers()
{
	for (int32 i = 0; i < AmountOfPlayers; i++)
	{
		FString OutError;
		ULocalPlayer* LocalPlayer = GetGameInstance()->CreateLocalPlayer(i, OutError, true);

		if (LocalPlayer)
		{
			APlayerController* NewController = LocalPlayer->GetPlayerController(GetWorld());
			if (NewController)
			{
				NewController->bAutoManageActiveCameraTarget = false;
				SpawnPlayer(NewController, i);
			}
		}
	}
}

void ABrawlbblesGameMode::TogglePauseMenu(APlayerController* RequestingPlayer)
{
	if (!PauseUIClass) return;

	if (bIsPaused)
	{
		UBrawlbblesGameInstance* GameInstance = GetGameInstance<UBrawlbblesGameInstance>();
		if (GameInstance)
		{
			GameInstance->SetMusicParameter("Pause", 0.0f);
		}
		// ✅ Unpause the game
		if (PauseUI)
		{
			PauseUI->RemoveFromParent();
			PauseUI = nullptr;
		}

		// ✅ Restore normal game speed
		UGameplayStatics::SetGlobalTimeDilation(GetWorld(), 1.0f);

		// ✅ Restore input for all players
		for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
		{
			APlayerController* PC = It->Get();
			if (PC)
			{
				PC->bShowMouseCursor = false;
				PC->SetIgnoreMoveInput(false);
				PC->SetIgnoreLookInput(false);
				PC->EnableInput(PC);
			}
		}

		UE_LOG(LogTemp, Warning, TEXT("Game Unpaused!"));
	}
	else
	{
		UBrawlbblesGameInstance* GameInstance = GetGameInstance<UBrawlbblesGameInstance>();
		if (GameInstance)
		{
			GameInstance->SetMusicParameter("Pause", 1.0f);
		}
		// ✅ Slow down time to 0 (freeze the game)
		UGameplayStatics::SetGlobalTimeDilation(GetWorld(), 0.0f);

		// ✅ Disable movement but keep input active
		for (FConstPlayerControllerIterator It = GetWorld()->GetPlayerControllerIterator(); It; ++It)
		{
			APlayerController* PC = It->Get();
			if (PC)
			{
				PC->bShowMouseCursor = true;
				PC->SetIgnoreMoveInput(true);
				PC->SetIgnoreLookInput(true);
				PC->EnableInput(PC);  // ✅ Keep listening for unpause
			}
		}

		// ✅ Show Pause UI
		if (!PauseUI)
		{
			PauseUI = CreateWidget<UUserWidget>(RequestingPlayer, PauseUIClass);
			if (PauseUI)
			{
				PauseUI->AddToViewport();
			}
		}

		UE_LOG(LogTemp, Warning, TEXT("Game Paused!"));
	}

	bIsPaused = !bIsPaused;
}

void ABrawlbblesGameMode::StartGameAfterDelay()
{
	UE_LOG(LogTemp, Warning, TEXT("5 seconds passed! Starting game..."));

	// ✅ Remove Controls UI
	if (ControlsUI)
	{
		ControlsUI->RemoveFromParent();
		ControlsUI->SetVisibility(ESlateVisibility::Hidden);
		ControlsUI = nullptr;
	}
}

Player Class :

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

#include "BrawlbblesCharacter.h"

#include "BrawlbblesGameInstance.h"
#include "BrawlbblesGameMode.h"
#include "Camera.h"
#include "EngineUtils.h"
#include "Engine/LocalPlayer.h"
#include "Components/CapsuleComponent.h"
#include "GameFramework/CharacterMovementComponent.h"
#include "GameFramework/SpringArmComponent.h"
#include "GameFramework/Controller.h"
#include "EnhancedInputComponent.h"
#include "EnhancedInputSubsystems.h"
#include "FMODBlueprintStatics.h"
#include "FMODEvent.h"
#include "InputActionValue.h"
#include "Projectile.h"
#include "Camera/CameraActor.h"
#include "Components/DecalComponent.h"
#include "Components/Image.h"
#include "Components/ProgressBar.h"
#include "Components/TextBlock.h"
#include "Components/WidgetComponent.h"
#include "Kismet/GameplayStatics.h"
#include "Kismet/KismetMathLibrary.h"

DEFINE_LOG_CATEGORY(LogTemplateCharacter);

//////////////////////////////////////////////////////////////////////////
// ABrawlbblesCharacter

ABrawlbblesCharacter::ABrawlbblesCharacter()
{
	// Create attack hitbox
	AttackHitbox = CreateDefaultSubobject<USphereComponent>(TEXT("AttackHitbox"));
	AttackHitbox->SetupAttachment(GetMesh()); // Attach to the character
	AttackHitbox->SetSphereRadius(75.0f); // Set hitbox size
	AttackHitbox->SetCollisionEnabled(ECollisionEnabled::NoCollision); // Disable by default
	AttackHitbox->SetCollisionObjectType(ECC_WorldDynamic);
	AttackHitbox->SetCollisionResponseToAllChannels(ECR_Overlap);
	
	AttackHitbox->SetGenerateOverlapEvents(true);

	// Ensure capsule detects item overlaps
	GetCapsuleComponent()->SetCollisionResponseToAllChannels(ECR_Ignore);
	GetCapsuleComponent()->SetCollisionResponseToChannel(ECC_WorldDynamic, ECR_Overlap);
	GetCapsuleComponent()->SetGenerateOverlapEvents(true);

	// Bind overlap event
	AttackHitbox->OnComponentBeginOverlap.AddDynamic(this, &ABrawlbblesCharacter::OnAttackHitboxOverlap);
	AttackHitbox->OnComponentEndOverlap.AddDynamic(this, &ABrawlbblesCharacter::OnAttackHitboxEndOverlap);
	
	// Set size for collision capsule
	GetCapsuleComponent()->InitCapsuleSize(42.f, 96.0f);
		
	// Don't rotate when the controller rotates. Let that just affect the camera.
	bUseControllerRotationPitch = false;
	bUseControllerRotationYaw = false;
	bUseControllerRotationRoll = false;

	// Configure character movement
	GetCharacterMovement()->bOrientRotationToMovement = false; // Character moves in the direction of input...	
	GetCharacterMovement()->RotationRate = FRotator(0.0f, 500.0f, 0.0f); // ...at this rotation rate

	// Note: For faster iteration times these variables, and many more, can be tweaked in the Character Blueprint
	// instead of recompiling to adjust them
	GetCharacterMovement()->JumpZVelocity = 700.f;

	GetCharacterMovement()->MinAnalogWalkSpeed = 20.f;

	// Reduce gravity scale for a lighter feel
	GetCharacterMovement()->GravityScale = 0.2f; 
	
	GetCharacterMovement()->GroundFriction = 3.0f;

	// Reduce acceleration for a slower response, making the character feel buoyant
	GetCharacterMovement()->MaxAcceleration = 1200.0f;

	// Reduce braking deceleration to prevent abrupt stopping
	GetCharacterMovement()->BrakingDecelerationWalking = 800.0f;
	GetCharacterMovement()->BrakingDecelerationFalling = 200.0f;

	// Increase air control for smoother in-air movement
	GetCharacterMovement()->AirControl = 2.0f;

	// Lower max speed slightly to balance floatiness
	GetCharacterMovement()->MaxWalkSpeed = 400.0f;

	// Enable flying if you want more control in the air
	GetCharacterMovement()->SetMovementMode(MOVE_Flying);

	// Create the widget component
	PlayerIndicatorWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("PlayerIndicator"));
	PlayerIndicatorWidget->SetupAttachment(RootComponent);

	// Position it above the player's head
	PlayerIndicatorWidget->SetRelativeLocation(FVector(0.f, 0.f, 120.f)); 

	// Set it to always face the camera
	PlayerIndicatorWidget->SetWidgetSpace(EWidgetSpace::Screen);

	// Set default size
	PlayerIndicatorWidget->SetDrawSize(FVector2D(200.f, 50.f));

	// Create the decal component
	ShadowDecal = CreateDefaultSubobject<UDecalComponent>(TEXT("ShadowDecal"));
	ShadowDecal->SetupAttachment(RootComponent);

	// Set the decal size (adjust as needed)
	ShadowDecal->DecalSize = FVector(100.0f, 200.0f, 200.0f);

	// Ensure the shadow faces downward
	ShadowDecal->SetRelativeRotation(FRotator(-90, 0, 0));

	ShadowPlane = CreateDefaultSubobject<UStaticMeshComponent>(TEXT("ShadowPlane"));
	ShadowPlane->SetupAttachment(RootComponent);

	// Disable collision (so it doesn't interfere with physics)
	ShadowPlane->SetCollisionEnabled(ECollisionEnabled::NoCollision);

	// Ensure the plane is hidden in-game (but still receives decals)
	ShadowPlane->SetCastShadow(false);
	ShadowPlane->SetHiddenInGame(false);

	// Adjust scale to fit character size
	ShadowPlane->SetWorldScale3D(FVector(1.5f, 1.5f, 1.0f));

	// Create the widget component for the item duration bar
	ItemDurationWidget = CreateDefaultSubobject<UWidgetComponent>(TEXT("ItemDurationWidget"));
	ItemDurationWidget->SetupAttachment(RootComponent);

	// Position it above the player's head, slightly above the player indicator
	ItemDurationWidget->SetRelativeLocation(FVector(0.f, 0.f, 150.f));

	// Set it to always face the camera
	ItemDurationWidget->SetWidgetSpace(EWidgetSpace::Screen);

	// Set default size
	ItemDurationWidget->SetDrawSize(FVector2D(200.f, 20.f));

	// Create the widget component for item icon
	ItemIconWidgetComponent = CreateDefaultSubobject<UWidgetComponent>(TEXT("ItemIconWidget"));
	ItemIconWidgetComponent->SetupAttachment(RootComponent);

	// Position it above the player's head
	ItemIconWidgetComponent->SetRelativeLocation(FVector(0.f, 0.f, 180.f));

	// Set it to always face the camera
	ItemIconWidgetComponent->SetWidgetSpace(EWidgetSpace::Screen);

	// Set default size
	ItemIconWidgetComponent->SetDrawSize(FVector2D(100.f, 100.f)); 

	// Initially hide it
	ItemIconWidgetComponent->SetVisibility(false);
}

void ABrawlbblesCharacter::Tick(float DeltaTime)
{
	Super::Tick(DeltaTime);
	
	// Get the scale of the player
	float ScaleFactor = GetActorScale3D().X; // Assuming uniform scaling

	// Adjust movement speed based on scale
	GetCharacterMovement()->MaxWalkSpeed = BaseWalkSpeed * ScaleFactor;
    
	if (!bIsRotating && !GetCharacterMovement()->bOrientRotationToMovement)
	{
		FVector Velocity = GetVelocity();
		if (!Velocity.IsNearlyZero()) // Only update rotation if moving
		{
			FRotator CurrentRotation = GetActorRotation();
			FRotator DesiredRotation = Velocity.Rotation();
			FRotator NewRotation = FMath::RInterpTo(CurrentRotation, DesiredRotation, DeltaTime, 5.0f);
			SetActorRotation(NewRotation);
		}
	}
	
	// Adjust jump velocity based on scale
	GetCharacterMovement()->JumpZVelocity = BaseJumpVelocity * ScaleFactor;

	// Adjust air control for smoother aerial movement at larger scales
	GetCharacterMovement()->AirControl = BaseAirControl * ScaleFactor;

	// Keep PlayerIndicatorWidget at the correct world position
	if (PlayerIndicatorWidget)
	{
		float HeightOffset = GetCapsuleComponent()->GetScaledCapsuleHalfHeight() + 30.f;
		FVector NewLocation = GetActorLocation() + FVector(0.f, 0.f, HeightOffset);
		PlayerIndicatorWidget->SetWorldLocation(NewLocation);
	}
	
	// Keep ItemDurationWidget at the correct position
	if (ItemDurationWidget)
	{
		float HeightOffset = GetCapsuleComponent()->GetScaledCapsuleHalfHeight() + 50.f;
		FVector NewLocation = GetActorLocation() + FVector(0.f, 0.f, HeightOffset);
		ItemDurationWidget->SetWorldLocation(NewLocation);
	}

	// Update the item duration UI if the timer is active
	if (GetWorld()->GetTimerManager().IsTimerActive(ItemDurationTimerHandle))
	{
		float RemainingTime = GetWorld()->GetTimerManager().GetTimerRemaining(ItemDurationTimerHandle);
		UpdateItemDuration(RemainingTime);
	}
	
	if (!ShadowPlane) return;

	FVector Start = GetActorLocation();
	FVector End = Start - FVector(0, 0, 10000.0f); // Large downward trace

	FHitResult Hit;
	FCollisionQueryParams CollisionParams;
	CollisionParams.AddIgnoredActor(this); // Ignore the player itself

	// Perform the raycast
	if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_Visibility, CollisionParams))
	{
		// Move the shadow to the hit location
		FVector ShadowLocation = Hit.Location + FVector(0, 0, 5.0f); // Small offset to prevent z-fighting
		ShadowPlane->SetWorldLocation(ShadowLocation);

		// Ensure the shadow remains flat on the surface
		FRotator ShadowRotation = FRotationMatrix::MakeFromZ(Hit.Normal).Rotator();
		ShadowPlane->SetWorldRotation(FRotator(0.f, ShadowRotation.Yaw, 0.f)); 
	}
	else
	{
		// Hide shadow if no ground detected
		ShadowPlane->SetWorldLocation(End);
	}
}

void ABrawlbblesCharacter::BeginPlay()
{
	Super::BeginPlay();

	//Add Input Mapping Context
	if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(DefaultMappingContext, 0);
		}

		AActor* MainCamera = UGameplayStatics::GetActorOfClass(GetWorld(), ACamera::StaticClass());

		PlayerController->SetViewTarget(MainCamera);
	}

	TSubclassOf<UUserWidget> PlayerIndicatorClass = LoadObject<UClass>(nullptr, TEXT("/Game/UI/WBP_PlayerIndicator.WBP_PlayerIndicator_C"));
	if (PlayerIndicatorClass)
	{
		PlayerIndicatorWidget->SetWidgetClass(PlayerIndicatorClass);
	}
	
	PlayerIndicatorWidget->SetVisibility(true);
	
	// Load Shadow Material
	UMaterialInterface* ShadowMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Game/Materials/M_Shadow.M_Shadow"));
	if (ShadowMaterial)
	{
		ShadowDecal->SetDecalMaterial(ShadowMaterial);
	}

	// Load Shadow Plane Material
	UMaterialInterface* ShadowPlaneMaterial = LoadObject<UMaterialInterface>(nullptr, TEXT("/Game/Materials/M_ShadowPlane.M_ShadowPlane"));
	if (ShadowPlaneMaterial)
	{
		ShadowPlane->SetMaterial(0, ShadowPlaneMaterial);
	}

	// Load Static Mesh for Shadow Plane
	UStaticMesh* PlaneMesh = LoadObject<UStaticMesh>(nullptr, TEXT("/Engine/BasicShapes/Plane.Plane"));
	if (PlaneMesh)
	{
		ShadowPlane->SetStaticMesh(PlaneMesh);
	}

	// Load Item Duration Widget Class
	TSubclassOf<UUserWidget> ItemWidgetClass = LoadObject<UClass>(nullptr, TEXT("/Game/UI/WBP_ItemDurationBar.WBP_ItemDurationBar_C"));
	if (ItemWidgetClass)
	{
		ItemDurationWidget->SetWidgetClass(ItemWidgetClass);
	}

	ItemDurationWidget->SetVisibility(false);
}

void ABrawlbblesCharacter::StartController()
{
	//Add Input Mapping Context
	if (APlayerController* PlayerController = Cast<APlayerController>(Controller))
	{
		if (UEnhancedInputLocalPlayerSubsystem* Subsystem = ULocalPlayer::GetSubsystem<UEnhancedInputLocalPlayerSubsystem>(PlayerController->GetLocalPlayer()))
		{
			Subsystem->AddMappingContext(DefaultMappingContext, 0);
		}

		AActor* MainCamera = UGameplayStatics::GetActorOfClass(GetWorld(), ACamera::StaticClass());

		PlayerController->SetViewTarget(MainCamera);
	}
}

void ABrawlbblesCharacter::SetPlayerName(FText NewName)
{
	if (PlayerIndicatorWidget)
	{
		// ✅ Ensure widget is initialized
		if (!PlayerIndicatorWidget->GetUserWidgetObject())
		{
			PlayerIndicatorWidget->InitWidget();
		}

		// ✅ Ensure widget is valid before accessing text block
		if (UUserWidget* Widget = PlayerIndicatorWidget->GetUserWidgetObject())
		{
			if (UTextBlock* NameText = Cast<UTextBlock>(Widget->GetWidgetFromName(TEXT("PlayerNameText"))))
			{
				NameText->SetText(NewName);
			}
		}
	}
}

void ABrawlbblesCharacter::SetPlayerTextColor(FLinearColor NewColor)
{
	if (UUserWidget* Widget = PlayerIndicatorWidget->GetUserWidgetObject())
	{
		if (UTextBlock* NameText = Cast<UTextBlock>(Widget->GetWidgetFromName(TEXT("PlayerNameText"))))
		{
			NameText->SetColorAndOpacity(NewColor);
		}
	}
}


//////////////////////////////////////////////////////////////////////////
// Input

void ABrawlbblesCharacter::SetupPlayerInputComponent(UInputComponent* PlayerInputComponent)
{
	// Set up action bindings
	if (UEnhancedInputComponent* EnhancedInputComponent = Cast<UEnhancedInputComponent>(PlayerInputComponent)) {
		
		// Jumping
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Started, this, &ACharacter::Jump);
		EnhancedInputComponent->BindAction(JumpAction, ETriggerEvent::Completed, this, &ACharacter::StopJumping);

		// Moving
		EnhancedInputComponent->BindAction(MoveAction, ETriggerEvent::Triggered, this, &ABrawlbblesCharacter::Move);

		// Rotate
		EnhancedInputComponent->BindAction(RotateAction, ETriggerEvent::Triggered, this, &ABrawlbblesCharacter::Rotate);

		// Attack
		EnhancedInputComponent->BindAction(AttackAction, ETriggerEvent::Triggered, this, &ABrawlbblesCharacter::Attack);
		
		// Item
		EnhancedInputComponent->BindAction(ItemAction, ETriggerEvent::Triggered, this, &ABrawlbblesCharacter::Item);

		EnhancedInputComponent->BindAction(PauseAction, ETriggerEvent::Triggered, this, &ABrawlbblesCharacter::PauseGame);
	}
	else
	{
		UE_LOG(LogTemplateCharacter, Error, TEXT("'%s' Failed to find an Enhanced Input component! This template is built to use the Enhanced Input system. If you intend to use the legacy system, then you will need to update this C++ file."), *GetNameSafe(this));
	}
}

inline void ABrawlbblesCharacter::PauseGame()
{
	if (ABrawlbblesGameMode* GameMode = Cast<ABrawlbblesGameMode>(UGameplayStatics::GetGameMode(GetWorld())))
	{
		GameMode->TogglePauseMenu(Cast<APlayerController>(GetController()));
	}
}


void ABrawlbblesCharacter::OnAttackHitboxOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult)
{
	Enemy = Cast<ABrawlbblesCharacter>(OtherActor);
	if (Enemy && Enemy != this) // Ensure it's another player
	{
		bCanDamage = true;
		// Change material color to red
		/*UMaterialInstanceDynamic* DynMaterial = Enemy->GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
		if (DynMaterial)
		{
			DynMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::Red);
			DynMaterial->SetScalarParameterValue(FName("Emissive"), 1.0);
			bCanDamage = true;
		}*/

		// Reset color after a short delay
		/*FTimerHandle ResetColorTimer;
		GetWorld()->GetTimerManager().SetTimer(ResetColorTimer, [Enemy]()
		{
			if (Enemy && Enemy->GetMesh())
			{
				UMaterialInstanceDynamic* ResetMaterial = Enemy->GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
				if (ResetMaterial)
				{
					ResetMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::White);
					ResetMaterial->SetScalarParameterValue(FName("Emissive"), 0.0);
				}
			}
		}, 0.5f, false);*/
	}
}

void ABrawlbblesCharacter::OnAttackHitboxEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
	UPrimitiveComponent* OtherComp, int32 OtherBodyIndex)
{

	
	Enemy = Cast<ABrawlbblesCharacter>(OtherActor);
	if (Enemy && Enemy != this) // Ensure it's another player
	{
		bCanDamage = false;
		Enemy = nullptr;
		/*
		UMaterialInstanceDynamic* ResetMaterial = Enemy->GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
		if (ResetMaterial)
		{
			ResetMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::White);
			ResetMaterial->SetScalarParameterValue(FName("Emissive"), 0.0);
			bCanDamage = false;
		}*/
	}
}

void ABrawlbblesCharacter::UpdateShadowPosition()
{
	if (!ShadowDecal) return;

	// Raycast from the player straight down
	FVector Start = GetActorLocation();
	FVector End = Start - FVector(0, 0, 100000.0f); // Large downward trace

	FHitResult Hit;
	FCollisionQueryParams CollisionParams;
	CollisionParams.AddIgnoredActor(this); // Ignore the player itself

	// Perform the raycast
	if (GetWorld()->LineTraceSingleByChannel(Hit, Start, End, ECC_GameTraceChannel1, CollisionParams))
	{
		// Move the decal to the hit location
		FVector DecalLocation = Hit.Location + FVector(0, 0, 10.0f); // Prevent Z-fighting
		ShadowDecal->SetWorldLocation(DecalLocation);
		ShadowDecal->SetVisibility(true);

		// Keep the decal facing straight down
		FRotator NewRotation = FRotator(-90.0f, 0.0f, 0.0f);
		ShadowDecal->SetWorldRotation(NewRotation);
	}
	else
	{
		// If no ground is hit, place it far below
		ShadowDecal->SetWorldLocation(End);
		ShadowDecal->SetVisibility(true);
	}
}

void ABrawlbblesCharacter::DisableFarting()
{
	CurrentItemType = EItemType::None; // Remove the fart ability
	GetWorld()->GetTimerManager().ClearTimer(FartCooldownTimerHandle);
}

void ABrawlbblesCharacter::Fart()
{
	UFMODEvent* FartEvent = LoadObject<UFMODEvent>(nullptr, TEXT("FMODEvent'/Game/FMOD/Events/Fart.Fart'"));
	if (FartEvent)
	{
		UFMODBlueprintStatics::PlayEventAtLocation(GetWorld(), FartEvent, GetActorTransform(), true);
	}
	LaunchCharacter(GetActorForwardVector() * FartForce, false, false);

	// If this is the first fart, start the 2-second timer
	if (CurrentItemType == EItemType::Fart)
	{
		// Ensure the timer is only set once and isn't reset incorrectly
		if (!GetWorld()->GetTimerManager().IsTimerActive(FartCooldownTimerHandle))
		{
			ClearItemDuration();
			StartItemDuration(MaxFartTime);
			GetWorld()->GetTimerManager().SetTimer(FartCooldownTimerHandle, this, &ABrawlbblesCharacter::DisableFarting, MaxFartTime, false);
		}
	}
}

void ABrawlbblesCharacter::Milk()
{
	if (bIsInvincible) return; // Ignore if already invincible

	bIsInvincible = true;

	ClearItemDuration();
	StartItemDuration(InvincibilityDuration);

	// Get the game instance and cast it
	UBrawlbblesGameInstance* GameInstance = Cast<UBrawlbblesGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));

	if (GameInstance)
	{
		// Example: Set "Invincible" to 1 (Activate)
		GameInstance->SetMusicParameter("Invincible", 1.0f);
	}
	
	// Start invincibility timer
	GetWorld()->GetTimerManager().SetTimer(InvincibilityTimerHandle, this, &ABrawlbblesCharacter::DisableInvincibility, InvincibilityDuration, false);

	// Start the rainbow effect ticking every frame
	GetWorld()->GetTimerManager().SetTimer(RainbowEffectTimerHandle, this, &ABrawlbblesCharacter::UpdateRainbowEffect, RainbowTickRate, true);
}


void ABrawlbblesCharacter::WaterGun()
{
	if (!bCanUseWaterGun) 
	{
		bCanUseWaterGun = true;

		ClearItemDuration();
		StartItemDuration(WaterGunDuration);
		
		// ✅ Always reset the timer on fire
		GetWorld()->GetTimerManager().ClearTimer(WaterGunTimerHandle);
		GetWorld()->GetTimerManager().SetTimer(WaterGunTimerHandle, this, &ABrawlbblesCharacter::DisableWaterGun, WaterGunDuration, false);
	}

	if (ProjectileClass)
	{
		FVector SpawnLocation = GetActorLocation() + GetActorForwardVector() * 200.0f;
		FRotator SpawnRotation = GetActorRotation();

		FActorSpawnParameters SpawnParams;
		SpawnParams.SpawnCollisionHandlingOverride = ESpawnActorCollisionHandlingMethod::AdjustIfPossibleButAlwaysSpawn;

		UFMODEvent* ShootEvent = LoadObject<UFMODEvent>(nullptr, TEXT("FMODEvent'/Game/FMOD/Events/Shoot.Shoot'"));
		if (ShootEvent)
		{
			UFMODBlueprintStatics::PlayEventAtLocation(GetWorld(), ShootEvent, GetActorTransform(), true);
		}
		AProjectile* SpawnedProjectile = GetWorld()->SpawnActor<AProjectile>(ProjectileClass, SpawnLocation, SpawnRotation, SpawnParams);
		if (SpawnedProjectile)
		{
			SpawnedProjectile->SetInstigatorCharacter(this); // Set the shooter as the instigator
		}
	}
}

float ABrawlbblesCharacter::TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent,
	AController* EventInstigator, AActor* DamageCauser)
{
	if (bIsInvincible) return 0.0f; // Ignore damage if invincible
	
	float ActualDamage = Super::TakeDamage(DamageAmount, DamageEvent, EventInstigator, DamageCauser);

	UMaterialInstanceDynamic* DynMaterial = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
	if (DynMaterial)
	{
		DynMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::Red);
		DynMaterial->SetScalarParameterValue(FName("Emissive"), 1.0);
	}

	// Reset color after a short delay
	FTimerHandle ResetColorTimer;
	GetWorld()->GetTimerManager().SetTimer(ResetColorTimer, [this]()
	{
		UMaterialInstanceDynamic* ResetMaterial = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
		if (ResetMaterial)
		{
			ResetMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::White);
			ResetMaterial->SetScalarParameterValue(FName("Emissive"), 0.0);
		}
	}, 0.5f, false);

	// Apply knockback effect
	if (DamageCauser)
	{
		FVector KnockbackDirection = (GetActorLocation() - DamageCauser->GetActorLocation()).GetSafeNormal();
		//KnockbackDirection.Z = 0.5f; // Slight vertical lift
		float KnockbackForce = 5000.0f; // Adjust force as needed

		LaunchCharacter(KnockbackDirection * KnockbackForce, true, true);
	}

	
	// Reduce health
	Health -= ActualDamage;
    
	// Log damage
	UE_LOG(LogTemp, Warning, TEXT("%s took %f damage!"), *GetName(), ActualDamage);

	// Check if the character is dead
	if (Health <= 0)
	{
		UFMODEvent* PlayerPopEvent = LoadObject<UFMODEvent>(nullptr, TEXT("FMODEvent'/Game/FMOD/Events/PlayerPop.PlayerPop'"));
		if (PlayerPopEvent)
		{
			UFMODBlueprintStatics::PlayEventAtLocation(GetWorld(), PlayerPopEvent, GetActorTransform(), true);
		}
		else
		{
			UE_LOG(LogTemp, Error, TEXT("Failed to load Playerpop FMOD event!"));
		}
		Die(Cast<APlayerController>(DamageCauser->GetInstigatorController()));
	}

	return ActualDamage;
}

void ABrawlbblesCharacter::Die(APlayerController* Killer)
{
	ABrawlbblesGameMode* GameMode = Cast<ABrawlbblesGameMode>(UGameplayStatics::GetGameMode(GetWorld()));
	if (GameMode)
	{
		GameMode->PlayerEliminated(Killer, Cast<APlayerController>(GetController()));
	}
	
	Destroy();
}

void ABrawlbblesCharacter::SetItemIcon(FName ItemName)
{
	// Load Player Indicator Widget Class
	TSubclassOf<UUserWidget> ItemIconWidgetClass = LoadObject<UClass>(nullptr, TEXT("/Game/UI/WBP_ItemIconWidget.WBP_ItemIconWidget_C"));

	if (ItemIconWidgetClass)
	{
		ItemIconWidgetComponent->SetWidgetClass(ItemIconWidgetClass);
	}
	
	if (!ItemIconWidgetComponent) return;

	UUserWidget* Widget = ItemIconWidgetComponent->GetUserWidgetObject();
	if (!Widget) return;

	UImage* IconImage = Cast<UImage>(Widget->GetWidgetFromName(TEXT("ItemIcon")));
	if (!IconImage) return;

	UTexture2D* IconTexture = nullptr;
	
	if (ItemName == "Milk")
	{
		IconTexture = MilkTexture;
	}
	else if (ItemName == "WaterGun")
	{
		IconTexture = WaterGunTexture;
	}
	else if (ItemName == "Fart")
	{
		IconTexture = FartTexture;
	}

	if (IconImage && IconTexture)
	{
		IconImage->SetBrushFromTexture(IconTexture);
		PlayerIndicatorWidget->SetVisibility(false);
		ItemIconWidgetComponent->SetVisibility(true);
	}
}

void ABrawlbblesCharacter::DisableAttackHitbox()
{
	AttackHitbox->SetCollisionEnabled(ECollisionEnabled::NoCollision);
}

void ABrawlbblesCharacter::DisableWaterGun()
{
	bCanUseWaterGun = false;
	GetWorld()->GetTimerManager().ClearTimer(WaterGunTimerHandle);
	CurrentItemType = EItemType::None;
}

void ABrawlbblesCharacter::UpdateRainbowEffect()
{
	if (!GetMesh()) return;

	static float Hue = 0.0f;
	Hue += 5.0f; // Increase hue each tick
	if (Hue >= 360.0f) Hue = 0.0f; // Loop hue value

	// Convert HSV to RGB
	FLinearColor RainbowColor = FLinearColor::MakeFromHSV8(Hue, 255, 255);

	// Apply dynamic material color
	UMaterialInstanceDynamic* DynMaterial = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
	if (DynMaterial)
	{
		DynMaterial->SetVectorParameterValue(FName("BaseColor"), RainbowColor);
	}
}

void ABrawlbblesCharacter::DisableInvincibility()
{
	bIsInvincible = false;

	CurrentItemType = EItemType::None;

	// Stop the rainbow effect
	GetWorld()->GetTimerManager().ClearTimer(RainbowEffectTimerHandle);

	// Get the game instance and cast it
	UBrawlbblesGameInstance* GameInstance = Cast<UBrawlbblesGameInstance>(UGameplayStatics::GetGameInstance(GetWorld()));

	if (GameInstance)
	{
		// Example: Set "Invincible" to 1 (Activate)
		GameInstance->SetMusicParameter("Invincible", 0.0f);
	}

	// Reset material color to normal (white)
	UMaterialInstanceDynamic* DynMaterial = GetMesh()->CreateAndSetMaterialInstanceDynamic(0);
	if (DynMaterial)
	{
		DynMaterial->SetVectorParameterValue(FName("BaseColor"), FLinearColor::White);
	}
}

void ABrawlbblesCharacter::UpdateItemDuration(float RemainingTime)
{
	if (UUserWidget* Widget = ItemDurationWidget->GetUserWidgetObject())
	{
		if (UProgressBar* ProgressBar = Cast<UProgressBar>(Widget->GetWidgetFromName(TEXT("ItemDurationProgressBar"))))
		{
			float Progress = FMath::Clamp(RemainingTime / MaxItemDuration, 0.0f, 1.0f);
			ProgressBar->SetPercent(Progress);
		}
	}
}


void ABrawlbblesCharacter::Move(const FInputActionValue& Value)
{
	const FVector2D MovementVector = Value.Get<FVector2D>();

	if (Controller != nullptr)
	{
		// Get the Camera's Rotation
		const FRotator CameraRotation = UGameplayStatics::GetPlayerCameraManager(GetWorld(), 0)->GetTransformComponent()->GetComponentRotation();
        
		// Get Movement Directions based on Camera Rotation
		const FVector ForwardDirection = UKismetMathLibrary::GetForwardVector(CameraRotation);
		const FVector RightDirection = UKismetMathLibrary::GetRightVector(CameraRotation);

		// Compute movement input direction
		FVector MovementDirection = (ForwardDirection * MovementVector.Y) + (RightDirection * MovementVector.X);
		MovementDirection.Z = 0; // Ensure no vertical influence

		if (!MovementDirection.IsNearlyZero()) // Only rotate if there is movement input
		{
			// Get the desired rotation to face the movement direction
			FRotator DesiredRotation = MovementDirection.Rotation();

			// Interpolate towards the desired rotation for smooth rotation
			FRotator NewRotation = FMath::RInterpTo(GetActorRotation(), DesiredRotation, GetWorld()->GetDeltaSeconds(), 10.0f);
			SetActorRotation(NewRotation);
		}

		// Apply movement
		AddMovementInput(ForwardDirection, MovementVector.Y);
		AddMovementInput(RightDirection, MovementVector.X);
	}
}

void ABrawlbblesCharacter::Rotate(const FInputActionValue& Value)
{
	// Get input as a Vector2D (X = Up/Down, Y = Left/Right)
	const FVector2D RotationInput = Value.Get<FVector2D>();

	if (GetCharacterMovement())
	{
		// Disable auto-rotation with movement
		GetCharacterMovement()->bOrientRotationToMovement = false;
		bIsRotating = true;
	}

	// Apply rotation based on stick input
	float YawRotation = RotationInput.X * RotationMultiplier * GetWorld()->GetDeltaSeconds();  // Left/Right
	float PitchRotation = RotationInput.Y * RotationMultiplier * GetWorld()->GetDeltaSeconds(); // Up/Down

	// Get current rotation
	FRotator NewRotation = GetActorRotation();

	// Modify yaw (left/right)
	NewRotation.Yaw += YawRotation;

	// Modify pitch (up/down) and clamp it to prevent flipping
	NewRotation.Pitch = FMath::Clamp(NewRotation.Pitch + PitchRotation, -85.0f, 85.0f);

	// Apply rotation
	SetActorRotation(NewRotation);

	// Store this as the last manual rotation
	TargetRotation = NewRotation;
}

void ABrawlbblesCharacter::Attack(const FInputActionValue& Value)
{
	if (!bCanAttack) return; // If on cooldown, do nothing

	bCanAttack = false; // Disable attacking

	UFMODEvent* PunchEvent = LoadObject<UFMODEvent>(nullptr, TEXT("FMODEvent'/Game/FMOD/Events/Punch.Punch'"));
	if (PunchEvent)
	{
		UFMODBlueprintStatics::PlayEventAtLocation(GetWorld(), PunchEvent, GetActorTransform(), true);
	}
	else
	{
		UE_LOG(LogTemp, Error, TEXT("Failed to load Punch FMOD event!"));
	}

	// Enable AttackHitbox
	AttackHitbox->SetCollisionEnabled(ECollisionEnabled::QueryOnly);
	
	if (AttackAnimMontage)
	{
		PlayAnimMontage(AttackAnimMontage, 1);
		if (bCanDamage)
		{
			if (Enemy)
			{
				UGameplayStatics::ApplyDamage(Enemy, AttackDamage, GetController(), this, UDamageType::StaticClass());
			}
		}
	}

	// Start timer to disable attack hitbox after 0.5 seconds
	GetWorld()->GetTimerManager().SetTimer(AttackHitboxTimer, this, &ABrawlbblesCharacter::DisableAttackHitbox, 0.5f, false);

	// Start the cooldown timer for attacking
	GetWorld()->GetTimerManager().SetTimer(AttackCooldownTimer, this, &ABrawlbblesCharacter::ResetAttackCooldown, AttackCooldown, false);
}

void ABrawlbblesCharacter::ResetAttackCooldown()
{
	bCanAttack = true;
}

void ABrawlbblesCharacter::Item(const FInputActionValue& Value)
{
	switch (CurrentItemType)
	{
		case EItemType::Fart:
			Fart();
			break;
		case EItemType::Milk:
			Milk();
			break;
		case EItemType::WaterGun:
			WaterGun();
			break;
	}
}

void ABrawlbblesCharacter::Jump()
{
	Super::Jump();

	if (GetCharacterMovement()->IsMovingOnGround())
	{
		JumpCurrentCount = 0; // Reset jump count when on the ground
		State = 0;
	}

	if (JumpCurrentCount < MaxJumpCount) 
	{
		LaunchCharacter(FVector(0, 0, GetCharacterMovement()->JumpZVelocity), false, true);
		JumpCurrentCount++;

		// Optional: Add a tiny horizontal push to jumps for a softer feel
		AddMovementInput(GetActorForwardVector(), 0.1f);
		State = 500;
	}
}

void ABrawlbblesCharacter::Landed(const FHitResult& Hit)
{
	Super::Landed(Hit);
    
	// Apply a small bounce effect on landing
	float BounceStrength = FMath::Clamp(GetCharacterMovement()->Velocity.Z * -0.3f, -100.f, 150.f);
	GetCharacterMovement()->Velocity.Z = BounceStrength;
}

void ABrawlbblesCharacter::StartItemDuration(float Duration)
{
	MaxItemDuration = Duration;
	ToggleItemUI(true); // Hide player indicator, show item bar
	GetWorld()->GetTimerManager().SetTimer(ItemDurationTimerHandle, this, &ABrawlbblesCharacter::ClearItemDuration, Duration, false);
}

void ABrawlbblesCharacter::ClearItemDuration()
{
	UpdateItemDuration(0);
	ToggleItemUI(false); // Show player indicator, hide item bar
	GetWorld()->GetTimerManager().ClearTimer(ItemDurationTimerHandle);
}

void ABrawlbblesCharacter::ToggleItemUI(bool bItemActive)
{
	if (PlayerIndicatorWidget)
	{
		PlayerIndicatorWidget->SetVisibility(!bItemActive);
	}
	if (ItemIconWidgetComponent)
	{
		ItemIconWidgetComponent->SetVisibility(false);
	}
	if (ItemDurationWidget)
	{
		ItemDurationWidget->SetVisibility(bItemActive);
	}
}

It’s hard to say. I noticed that you have hardcoded paths instead of references. These paths might differ in a build. I recommend subclassing UDeveloperSettings and storing your class references there.

You haven’t included your headers, so there might something going on with garbage collection as well.

Also, you should add more logs and see where it fails. You often check if something exists before running some code, but nothing is logged when a class fails to load or an object doesn’t exist.

Header Files :

GameMode :

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

#pragma once

#include "CoreMinimal.h"
#include "BrawlbblesCharacter.h"
#include "ScoreUI.h"
#include "SpawnPoint.h"
#include "Components/TextBlock.h"
#include "GameFramework/GameModeBase.h"
#include "BrawlbblesGameMode.generated.h"

class ACamera;

UCLASS(minimalapi)
class ABrawlbblesGameMode : public AGameModeBase
{
	GENERATED_BODY()

public:
	ABrawlbblesGameMode();
	
	// To add players to the arena.
	virtual void BeginPlay() override;
	
	virtual void HandleStartingNewPlayer_Implementation(APlayerController* NewPlayer) override;
	
	// Called when a player is eliminated
	void PlayerEliminated(APlayerController* Killer, APlayerController* Victim);

	// Restart and respawn players
	//void RestartGame();

	void SwitchLevel();

	void AssignCameraToAllPlayers();

	void ResetGameModeVariables();

	void SpawnLocalPlayers();

	void SpawnPlayer(APlayerController* PlayerController, int PlayerIndex);

	void SpawnSharedCamera();

	void ShowWinUI(APlayerController* Player);

	void DisableAllPlayers();

	void EnableAllPlayers();

	void RestartGame();

	void UpdateAllScoresInUI();

	void RestoreLocalPlayers();

protected:
	// Player Score Tracking
	TMap<APlayerController*, int32> PlayerScores;

	// List of available spawn points
	TArray<ASpawnPoint*> SpawnPoints;

	// Public Variables
	TArray<FPlatformUserId> OutUsers;

	UPROPERTY(EditAnywhere)
	TSubclassOf<ABrawlbblesCharacter> PlayerCharacter;

	// Camera class to spawn at runtime
	UPROPERTY(EditDefaultsOnly, Category = "Camera")
	TSubclassOf<ACamera> CameraActorClass;

	UPROPERTY()
	ACamera* SharedCamera;

	TSet<APlayerController*> EliminatedPlayers; // Add this to your class

	UPROPERTY(EditAnywhere)
	TSubclassOf<UScoreUI> ScoreUIClass;

	// 🔹 Reference to the Score UI widget
	UPROPERTY(BlueprintReadOnly, Category = "UI")
	UScoreUI* ScoreUI;

	UPROPERTY(EditDefaultsOnly, Category = "UI")
	TSubclassOf<UUserWidget> WinUIClass;

	UUserWidget* WinUI;

public:
	// Amount of players in the game
	int32 AmountOfPlayers = 0;
	
	// Define 8 unique colors for players
	TArray<FLinearColor> PlayerTextColors = {
		FLinearColor::Red, 
		FLinearColor::Blue, 
		FLinearColor::Green, 
		FLinearColor::Yellow, 
		/*FLinearColor::Purple, 
		FLinearColor::Cyan, 
		FLinearColor::Orange, 
		FLinearColor::Magenta*/
	};

	bool bIsGameStarted = false;
	
	UPROPERTY(EditDefaultsOnly, Category = "UI")
	TSubclassOf<UUserWidget> PauseUIClass;

	UPROPERTY()
	UUserWidget* PauseUI;

	bool bIsPaused = false;

	void TogglePauseMenu(APlayerController* RequestingPlayer);

private:
	bool bIsWinUIActive = false;

	UPROPERTY(EditDefaultsOnly, Category = "UI")
	TSubclassOf<UUserWidget> ControlsUIClass;

	UUserWidget* ControlsUI;

	FTimerHandle GameStartTimerHandle; // ✅ Persistent timer handle

	UFUNCTION()
	void StartGameAfterDelay();
	
};

Player Class :



#pragma once

#include "CoreMinimal.h"
#include "InputActionValue.h"
#include "Components/WidgetComponent.h"
#include "GameFramework/Character.h"
#include "Logging/LogMacros.h"
#include "ItemType.h"
#include "ItemIconWidget.h"
#include "Components/SphereComponent.h"
#include "BrawlbblesCharacter.generated.h"

class USpringArmComponent;
class UCameraComponent;
class UInputMappingContext;
class UInputAction;
class AProjectile;
struct FInputActionValue;

DECLARE_LOG_CATEGORY_EXTERN(LogTemplateCharacter, Log, All);

UCLASS(config=Game)
class ABrawlbblesCharacter : public ACharacter
{
	GENERATED_BODY()
	
	/** MappingContext */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputMappingContext* DefaultMappingContext;

	/** Jump Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* JumpAction;

	/** Move Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* MoveAction;

	/** Rotate Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* RotateAction;

	/** Rotate Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* AttackAction;

	/** Item Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* ItemAction;

	/** Item Input Action */
	UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = Input, meta = (AllowPrivateAccess = "true"))
	UInputAction* PauseAction;

public:
	ABrawlbblesCharacter();

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

	void BeginPlay() override;
	
	void StartController();

	void SetPlayerName(FText NewName);

	void SetPlayerTextColor(FLinearColor NewColor);

	void SetItemIcon(FName ItemName);

	void PauseGame();

protected:

	/** Called for movement input */
	void Move(const FInputActionValue& Value);

	/** Called for rotate input */
	void Rotate(const FInputActionValue& Value);

	/** Called for Attack Input*/
	void Attack(const FInputActionValue& Value);

	void ResetAttackCooldown();

	/** Called for Item Input*/
	void Item(const FInputActionValue& Value);

	virtual void Jump() override;

	virtual void Landed(const FHitResult& Hit) override;

	// APawn interface
	virtual void SetupPlayerInputComponent(class UInputComponent* PlayerInputComponent) override;

	// Called when hitbox overlaps with another actor
	UFUNCTION()
	void OnAttackHitboxOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
		UPrimitiveComponent* OtherComp, int32 OtherBodyIndex, bool bFromSweep, const FHitResult& SweepResult);

	UFUNCTION()
	void OnAttackHitboxEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor,
		UPrimitiveComponent* OtherComp, int32 OtherBodyIndex);

	void UpdateShadowPosition();

	void DisableAttackHitbox(); // Function to disable attack hitbox\

	void DisableWaterGun();

	void UpdateRainbowEffect();
	void DisableInvincibility();

	void UpdateItemDuration(float RemainingTime);

	void StartItemDuration(float Duration);
	void ClearItemDuration();
	void ToggleItemUI(bool bItemActive);
	
private:
	void DisableFarting();
	
	void Fart();

	void Milk();

	void WaterGun();

	float TakeDamage(float DamageAmount, FDamageEvent const& DamageEvent, AController* EventInstigator, AActor* DamageCauser);
	
	void Die(APlayerController* Killer);
	
public:
	// Public variables
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI")
	UWidgetComponent* PlayerIndicatorWidget;
	
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	int RotationMultiplier = 200;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float FartForce = 1000.0f;

	EItemType CurrentItemType = EItemType::None;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float MaxJumpCount = 2;

	//Animation variables
	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	float State = 0;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UAnimMontage* AttackAnimMontage;
	
	// Attack Hitbox
	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Combat")
	USphereComponent* AttackHitbox;

	FTimerHandle AttackHitboxTimer; // Timer for disabling attack hitbox

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* MilkTexture;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* WaterGunTexture;

	UPROPERTY(EditAnywhere, BlueprintReadWrite)
	UTexture2D* FartTexture;


protected:
	bool bCanAttack = true;
	FTimerHandle AttackCooldownTimer;

	UPROPERTY(EditDefaultsOnly, Category = "Combat")
	float AttackCooldown = 1.0f;

	// Attack Damage
	UPROPERTY(EditDefaultsOnly, Category = "Combat")
	float AttackDamage = 50.0f;

	// Player Stats
	UPROPERTY(EditAnywhere)
	float Health = 100;

	// Enemy Variable
	ABrawlbblesCharacter* Enemy;

	UPROPERTY(EditAnywhere, Category="Movement")
	float BaseWalkSpeed = 400.0f; // Default movement speed
	
	UPROPERTY(EditAnywhere, Category="Movement")
	float BaseJumpVelocity = 700.0f;

	UPROPERTY(EditAnywhere, Category="Movement")
	float BaseAirControl = 2.0f;

	UPROPERTY(VisibleAnywhere, Category = "Effects")
	UDecalComponent* ShadowDecal;
	
	FTimerHandle WaterGunTimerHandle;
	bool bCanUseWaterGun = false;
	float WaterGunDuration = 10.0f; // Adjust duration as needed

	bool bIsInvincible = false;
	FTimerHandle InvincibilityTimerHandle;
	float InvincibilityDuration = 10.0f;

	FTimerHandle RainbowEffectTimerHandle;
	float RainbowTickRate = 0.05f; // Adjust for smoother/faster color change

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI")
	UWidgetComponent* ItemDurationWidget;

	FTimerHandle ItemDurationTimerHandle;
	float MaxItemDuration = 10.0f; // Example duration

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI", meta = (AllowPrivateAccess = "true"))
	UWidgetComponent* ItemIconWidgetComponent;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "UI", meta = (AllowPrivateAccess = "true"))
	TSubclassOf<UUserWidget> PlayerIndicatorClass;


private:
	bool bIsRotating = false;
	FRotator TargetRotation;
	
	float MaxFartTime = 10.0f; // Max spam duration
	FTimerHandle FartCooldownTimerHandle;

	UPROPERTY(EditDefaultsOnly, Category = "WaterGun")
	TSubclassOf<AProjectile> ProjectileClass;

	bool bCanDamage = false;

	// Invisible shadow plane for decals
	UPROPERTY(VisibleAnywhere, Category = "Shadow")
	UStaticMeshComponent* ShadowPlane;
	
};

I’ve added the h file to the reply! I’ll try out your solution and I’ll let u know how it goes!