Hey Everyone, I am having problems with UI working for single player instead of multiplayers. So I’m almost finished with a project I’m working in UEFN and the problem that I’m trying to fix is that each player presses the UI button, and the game starts and leaving the other players be stuck in the dialog until pressing the UI button to get started in the middle of the game. So I have a video example of the issue, and let me know if y’all have any helpful thoughts, tips, advices. It would be really helpful, because I’ve tried figuring out what to make it work, but nothing’s working.
Hey Everyone, I am having problems with UI working for single player instead of multiplayers. So I’m almost finished with a project I’m working in UEFN and the problem that I’m trying to fix is that each player presses the UI button, and the game starts and leaving the other players be stuck in the dialog until pressing the UI button to get started in the middle of the game. So I have a video example of the issue, and let me know if y’all have any helpful thoughts, tips, advices. It would be really helpful, because I’ve tried figuring out what to make it work, but nothing’s working.
Hello @NoahD1 how are you?
It seems that when a player clicks on the button, it starts the match for every player but it only removes the UI for the player that clicked the button.
This problem should be solved if you use a “for” to disable the UI for every player that is currently in your island when anybody click on the button.
Other thing you can do, is to check if all the players already clicked the button before starting the match.
If you need more help, please share your code here, so we can check it and make the proper corrections for you!
Hello @BRGJuanCruzMK, I still have the same problem after using for in verse and it’s still not working for multiplayers. But the good thing is it works for one player, but not for other players that are joining the private playtest session and even includes a public release game, and I still need more help. So I’ll share the playtest code from Fortnite Creator Portal, and thank you so much for tips.
Here’s the code:
3780-3345-8791
Hello again @NoahD1 !
When I said “share your code” I meant the Verse code you are using for this feature, so I can check where the error is and fix it for you.
I understand the problem you are having, but I cannot help if I don’t know your Verse code.
I’ll be waiting for it!
Oh, I’m sorry about that, man. I thought you mean like Island code or playtest code, but my bad. I’ll go ahead and share the verse code. Well, I have nine of them in my island, six of them are creative devices and three of them are NPC behaviors.
Here’s two verse code scripts:
title_screen_handler script:
using { /Fortnite.com/Devices }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
# Device to handle the title screen dialog logic and respond to button presses
title_screen_handler := class(creative_device):
\# Editable reference to the main title screen dialog device
@editable
TitleScreenDialog : popup_dialog_device = popup_dialog_device{}
\# Editable reference to the tower defense dialog device
@editable
TowerDefenseDialog : popup_dialog_device = popup_dialog_device{}
\# Editable reference to the end game device
@editable
EndGameDevice : end_game_device = end_game_device{}
@editable
Player1Spawner : player_spawner_device = player_spawner_device{}
@editable
Player2Spawner : player_spawner_device = player_spawner_device{}
@editable
Player3Spawner : player_spawner_device = player_spawner_device{}
@editable
Player4Spawner : player_spawner_device = player_spawner_device{}
\# Indices for dialog buttons
StartGameButtonIndex : int = 0
ExitGameButtonIndex : int = 1
\# Track whether the title screen dialog has been shown already
var TitleScreenShown : logic = false
var TowerDefenseDialogShown : \[agent\]logic = map{}
\# Subscribe to button press events from the TitleScreenDialog when the device starts
OnBegin<override>()<suspends>:void=
if (TitleScreenShown = false):
TitleScreenDialog.Show() # Show only once
set TitleScreenShown = true
TitleScreenDialog.RespondingButtonEvent.Subscribe(OnTitleButtonPressed)
\# Handle button presses in the title screen dialog
OnTitleButtonPressed(Data:tuple(agent, int)):void=
Agent := Data(0)
ButtonIndex := Data(1)
if (ButtonIndex = 0):
\# Close the dialog for the player who pressed the button
TitleScreenDialog.Hide(Agent)
\# Show TowerDefenseDialog only from Start Game button
if (ButtonIndex = StartGameButtonIndex):
Player1Spawner.Enable()
Player2Spawner.Enable()
Player3Spawner.Enable()
Player4Spawner.Enable()
if (not TowerDefenseDialogShown\[Agent\]?):
Print("Start_Game_Button pressed - enabling and showing TowerDefenseDialog for agent.")
TowerDefenseDialog.Enable()
TowerDefenseDialog.Show(Agent)
Print("Start_Game_Button - TowerDefenseDialog enabled/shown.")
if (set TowerDefenseDialogShown\[Agent\] = true) {}
\# Enable all referenced player spawners
PlayerSpawners : \[\]player_spawner_device = array{
Player1Spawner,
Player2Spawner,
Player3Spawner,
Player4Spawner
}
for (Spawner : PlayerSpawners):
Spawner.Enable()
else:
Print("TowerDefenseDialog already shown to this agent. Not showing again.")
\# Handle exit button
else if (ButtonIndex = ExitGameButtonIndex):
EndGameDevice.Enable()
EndGameDevice.Activate(Agent)
Print("Exit_Game_Button pressed - EndGameDevice enabled.")
HideAllDialogs():void=
Players := GetPlayspace().GetPlayers()
for (Player : Players):
TitleScreenDialog.Hide(Player)
tower_defesne_manager script:
using { /Fortnite.com/Devices }
using { /Fortnite.com/Characters }
using { /Verse.org/Simulation }
using { /UnrealEngine.com/Temporary/Diagnostics }
using { /UnrealEngine.com/Temporary/SpatialMath }
using { /UnrealEngine.com/Temporary/UI }
using { /Fortnite.com/UI }
# Persistable data structure for round stats
round_data := class:
RoundNumber : int = 1
PointScore : int = 0
CoinScore : int = 500
# Device to manage tower defense rounds and settings
tower_defense_manager := class(creative_device):
\# Dialogs & Round infrastructure
@editable
TowerDefenseDialog : popup_dialog_device = popup_dialog_device{}
@editable
GateSequencer : cinematic_sequence_device = cinematic_sequence_device{}
\# NPC Spawners
@editable
FastNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
MediumNPCSpawner : npc_spawner_device = npc_spawner_device{}
@editable
StrongNPCSpawner : npc_spawner_device = npc_spawner_device{}
\# End round
@editable
EndRound : end_game_device = end_game_device{}
\# Stats
@editable
PointsStat : stat_creator_device = stat_creator_device{}
@editable
CoinsStat : stat_creator_device = stat_creator_device{}
@editable
RoundTimer : timer_device = timer_device{}
@editable
FixedPointCamera : gameplay_camera_fixed_point_device = gameplay_camera_fixed_point_device{}
@editable
ThirdPersonControls : gameplay_controls_third_person_device = gameplay_controls_third_person_device{}
@editable
Objective : objective_device = objective_device{}
@editable
Player1Spawner : player_spawner_device = player_spawner_device{}
@editable
Player2Spawner : player_spawner_device = player_spawner_device{}
@editable
Player3Spawner : player_spawner_device = player_spawner_device{}
@editable
Player4Spawner : player_spawner_device = player_spawner_device{}
@editable
Rounds : \[\]round_settings_device = array{}
\# Round timer and logic
var CurrentRound : int = 1
TotalRounds : int = 30
RoundDuration : float = 300.0 # seconds (5 min)
var RoundActive : logic = false
var IsRoundActive : logic = false
var DialogShown : logic = false
var RoundStarted : logic = false
var DialogSubscription : ?cancelable = false
\# Persistent storage for player round data
var PlayerRoundData : weak_map(player, round_data) = map{}
\# Array of round settings devices for 30 rounds
var RoundSettings : \[\]round_settings_device = array{}
var CurrentRoundIndex : int = 0
var AgentsInZone : \[agent\]logic = map{}
var PlayerDialogSubscriptions : \[player\]cancelable = map{}
var ActiveDialogPlayer : ?player = false
OnBegin<override>()<suspends>:void=
\# Start: disable all spawners (idle)
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
\# Subscribe to the 1Player spawner's spawn event
Player1Spawner.SpawnedEvent.Subscribe(OnPlayer1Spawned)
Player2Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
Player3Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
Player4Spawner.SpawnedEvent.Subscribe(OnOtherPlayerSpawned)
\# Subscribe to dialog button interaction
TowerDefenseDialog.RespondingButtonEvent.Subscribe(OnTowerDefenseButtonPressed)
\# Cancel and hide timer at game start—do this FIRST no matter what!
Self.RoundTimer.Reset()
Self.RoundTimer.Disable()
set Self.DialogSubscription = option{Self.TowerDefenseDialog.RespondingButtonEvent.Subscribe(Self.OnDialogButtonPressed)}
\# Notice: No call to Self.TowerDefenseDialog.Show() or Enable() here
\# Keep Player1 enabled, and leave Player2, Player3, and Player4 disabled until the game starts.
Self.Player1Spawner.Enable()
Self.Player2Spawner.Disable()
Self.Player1Spawner.Disable()
Self.Player1Spawner.Disable()
OnPlayer1Spawned(Player: agent): void =
\# Show dialog and subscribe only for the player spawned from 1Player spawner
if (FirstPlayer := player\[Player\]):
TowerDefenseDialog.Show(FirstPlayer)
Subscription := TowerDefenseDialog.RespondingButtonEvent.Subscribe(OnDialogButtonPressed)
if (set PlayerDialogSubscriptions\[FirstPlayer\] = Subscription) {}
set ActiveDialogPlayer = option{FirstPlayer}
Print("Waiting for round start...")
OnOtherPlayerSpawned(Player: agent): void =
Print("Player from non-priority spawner joined, dialog not shown.")
\# No dialog/subscription for 2/3/4Player
OnDialogButtonPressed(Agent:agent, ButtonIndex:int):void =
if (ActivePlayer := ActiveDialogPlayer?, Agent = ActivePlayer, ButtonIndex = 0):
if (ButtonIndex = 0 and Self.RoundStarted = false):
set Self.RoundStarted = true
if (DialogSub := Self.DialogSubscription?):
DialogSub.Cancel()
set Self.DialogSubscription = false
\# Dismiss dialog and clean up only for the active player
\# Only the 1Player spawner player can trigger start logic!
if (Subscription := PlayerDialogSubscriptions\[ActivePlayer\]):
Subscription.Cancel()
TowerDefenseDialog.Hide(ActivePlayer)
set PlayerDialogSubscriptions = map{}
set ActiveDialogPlayer = false
Print("Game Started!")
\# Proceed with starting the game...
\# Switch camera back to Fortnite's default third-person view
Self.FixedPointCamera.Disable()
Self.ThirdPersonControls.Enable()
Self.RoundTimer.Enable()
Self.RoundTimer.Reset()
Self.RoundTimer.Start()
\# Reset camera for all players
for (Player : GetPlayspace().GetPlayers()):
TowerDefenseDialog.Show(Player)
DialogSub := TowerDefenseDialog.RespondingButtonEvent.Subscribe(OnDialogButtonPressed)
if (set PlayerDialogSubscriptions\[Player\] = DialogSub) {}
FixedPointCamera.Disable()
spawn{HandleTimerExpiration()}
TowerDefenseDialog.Hide(Player)
set PlayerDialogSubscriptions = map{}
\# Proceed with starting the game...
for (Player -> DialogSub : PlayerDialogSubscriptions):
if (DialogSubscription?):
if (Sub := DialogSubscription?):
Sub.Cancel() # Cancel this player's dialog event subscription
TowerDefenseDialog.Hide(Player) # Hide the dialog for this player
set PlayerDialogSubscriptions = map{} # Clear the map afterwards
\# Create 30 round settings devices and store them in the array
set RoundSettings = for (Index := 0..29):
round_settings_device{}
\# Example: Configure each round's settings
for (RoundIndex -> RoundDevice : RoundSettings):
\# Set up round-specific settings here
\# For example, you could enable/disable certain features per round
\# or configure round-specific rules
Print("Configured round {RoundIndex + 1}")
\# Example function to get a specific round's settings
GetRoundDevice(RoundIndex : int) : void =
if (RoundDevice := RoundSettings\[RoundIndex\]):
RoundDevice
\# Call this when a round ends
HandleRoundEnd() : void =
for (Player : GetPlayspace().GetPlayers()):
if (RoundDevice := RoundSettings\[CurrentRoundIndex\]):
Players := GetPlayspace().GetPlayers()
if (FirstPlayer := Players\[0\]):
RoundDevice.EndRound(FirstPlayer) # Pass agent type!
HandleTimerExpiration()<suspends>:void =
Self.RoundTimer.SuccessEvent.Await()
for (Player : Self.GetPlayspace().GetPlayers()):
Self.EndRound.Activate(Player)
Self.RoundTimer.Reset()
Self.RoundTimer.Disable()
\# Implementation for timer expiration handling
Print("Timer expired")
Players := Self.GetPlayspace().GetPlayers()
if (Players.Length > 0):
\# Start the configured round timer (automatically shows on player HUD)
Self.RoundTimer.Start()
\# Wait for the timer to complete
Self.RoundTimer.SuccessEvent.Await()
\# After the timer finishes, end the round for all players
for (Player : Players):
Self.EndRound.Activate(Player)
else:
Print("Can't find player")
\# Initialize or load persistent data for a player
InitializePlayerData(Player:player):void =
if (ExistingData := PlayerRoundData\[Player\]):
\# Data already exists, do nothing
Print("Loaded existing data for player")
else:
\# Create new data with default values
NewData := round_data{}
if (set PlayerRoundData\[Player\] = NewData):
Print("Initialized new data for player")
\# Save updated round data for a player
SavePlayerRoundData(Player:player):void =
if (CurrentData := PlayerRoundData\[Player\]):
\# Create new data instance with updated values
NewData := round_data{
RoundNumber := CurrentData.RoundNumber + 1,
PointScore := CurrentData.PointScore + 100,
CoinScore := CurrentData.CoinScore + 50
}
if (set PlayerRoundData\[Player\] = NewData):
Print("Saved updated data for player")
OnTowerDefenseButtonPressed(Data:tuple(agent, int)):void=
ButtonIndex := Data(1)
case(ButtonIndex):
\# 0: Start round, enable all spawners, move players, keep dialog shown
0 => HandleStartRound()
\_ => Print("Unknown button pressed")
HandleStartRound():void=
if (CurrentRound > TotalRounds):
Print("All rounds complete.")
return
if (RoundActive = false):
set RoundActive = true
FastNPCSpawner.Enable()
MediumNPCSpawner.Enable()
StrongNPCSpawner.Enable()
FastNPCSpawner.Spawn()
MediumNPCSpawner.Spawn()
StrongNPCSpawner.Spawn()
GateSequencer.Play()
Print("Round {CurrentRound} started.")
\# Move all players to new location & keep dialog shown
for (Player : GetPlayspace().GetPlayers()):
if (Character := Player.GetFortCharacter\[\]):
TargetLocation := vector3{X := 1000.0, Y := 1000.0, Z := 200.0}
if (Character.TeleportTo\[TargetLocation, rotation{}\]):
Print("Moved player to target location at round start.")
\# Start timer for this round
spawn{RoundTimerLoop()}
\# Each round lasts 5 min, up to 30 rounds; stops spawners & activates EndRound each time.
RoundTimerLoop()<suspends>:void=
Print("Started round timer for {RoundDuration} seconds.")
Sleep(RoundDuration)
set RoundActive = false
\# Stop spawners
FastNPCSpawner.Disable()
MediumNPCSpawner.Disable()
StrongNPCSpawner.Disable()
Print("Round {CurrentRound} ended.")
\# Activate EndRound device for all players
EndRound.Enable()
for (Player : GetPlayspace().GetPlayers()):
if (CurrentRoundIndex = 29):
EndRound.Activate(Player)
\# RoundSettings: notify round end
for (Player : GetPlayspace().GetPlayers()):
if (RoundDevice := RoundSettings\[CurrentRoundIndex\]):
RoundDevice.EndRound(Player) # Pass Player (agent)
set CurrentRound += 1
No worries, I didn’t calrify it haha
I’ll be checking your code and reach you again when I have a solution!
Sound good, thank you so much dude.
Hey @NoahD1 how are you?
I’ve been trying to fix this issue but I realized that I don’t understand the flow the game is trying to follow.
Currently, you have the Main Menu, with Start Game and Exit Game buttons. When any player press “Start Game”, the main manu is closed and a new menu with only a “Start Round” button.
Pressing the “Start Game” button on the main menu also starts the round, and that is one of the problems, right?
I assume you want to start the round after everybody is ready (all the players pressed Start Game), is that correct?
I also assume that your don’t want to be able to start the round if there is any “unready” player, do you?
Another thing I’d like to understand is if you want all the players to be able to start the round or onlye the Player 1.
And the last thing, are you sure you need a second “Start” button? Maybe you can start a timer and start the round automatically when everybody is ready.
I’ll be waiting for your answers to keep working on this!