Fusion Golf
Overview
The Fusion Golf sample demonstrates an approach on how to create an arcade golf racing game with a focus on physics, using a server authoritative, client predicted model. It features a session browser as well as direct connecting via room codes. Hosts can modify various game settings such as max time per hole, max shots, whether or not players will collide with one another, and if the session is visible in the session browser. The sample has an 18 hole course with various physics-driven objects such as cannons, spinners, boost tunnels, and elevator platforms.
Fusion Golf was originally created using Fusion 1.0; however, it has been ported to Fusion 2.0, but preserves a majority of the functionality of the Fusion 1.0 version.
Before You Start
To run the sample, first create a Fusion AppId in the PhotonEngine Dashboard and paste it into the App Id Fusion
field in Real Time Settings (reachable from the Fusion menu). Then load the Menu
scene in the Scenes
folder and press Play
.
Download
Version | Release Date | Download | |
---|---|---|---|
2.0.1 | May 27, 2024 | Fusion Golf 2.0.1 Build 557 |
Highlights
- Full networked game state system (pre-game, intro, play, outro, and post-game)
- Spectating other players
- Customizable game settings (collision, number of holes, max shots, max time)
- Synchronized state of objects in the world
- Lobby Browser (and the ability to make rooms private)
- Physics focused objects (cannons, spinners, elevator platforms, boost tunnels)
- Customizable player visuals (RGB Sliders)
- Region settings, nickname setting, graphics options
Project
Folder Structure
The main Scripts folder /Scripts
has a subfolder Networking
which contains scripts whose primary function is interfacing with Fusion.
Notable scripts:
PlayerRegistry
The PlayerRegistry stores a reference to each player in the room, and provides utility methods for counting, selecting, and performing actions on one or many players. The default behavior of the utility functions is to ignore players who are spectators, but will operate on the full collection of players if the includeSpectators
parameter is true.
Spectators
Spectators are players who have explicitly chosen to watch rather than participate in the game. A spectator's PlayerObject
will never have a Controller
assigned.
GameState
The flow and behaviour of the game logic is controlled by the GameState
NetworkBehaviour. GameState
defines an enum of the phases of the game, which the networked StateMachine
property uses as its states.
StateMachine<T>
defines a StateHooks
class with 3 fields: onEnter
, onExit
, and onUpdate
. When using the StateMachine
class, each enum state may have StateHooks
defining what happens upon entering, exiting, and while actively staying in the state.
Getting Into a Game
Users can host or join a session using either the session browser or directly via the session code. Entering a session code is optional if the user chooses to host, as it will be generated automatically if left blank. Once in a session, the code will be displayed at the top of the screen.
The session code is accessed via:
runner.SessionInfo.Name
Region Selection
Before choosing to host or join, users are presented with a dropdown to select which Photon Cloud region to use. The dropdown options have been selected as per the Regions Documentation.
Matchmaker
Hosting, joining, and the session browser are all handled by the Matchmaker
class. It serves as a wrapper for the NetworkRunner.StartGame
method and, in the event of a connection error, forwards the error to the DisconnectUI
to be displayed to the user.
Handling Input
Input is polled by PlayerInputBehaviour.cs
into the custom INetworkInput
struct PlayerInput
. Input in this sample consists of only clicking and moving the mouse.
PlayerInput
is relatively simple, and contains only 3 fields:
isDragging
- whether the player is holding down their mouse buttondragDelta
- how much the player's mouse has been dragged downwardyaw
- theAngle
in which the player is facing
This data is processed by thePutter
player script and facilitates, in addition to putting the ball, both the local player and spectators seeing the appropriate UI.
The Player
Players are handled in 2 parts:
PlayerObject
- contains thePlayerRef
value this object is associated with, a reference to the player's controller (Putter
), index in the room, nickname, selected color, and gameplay data relating to scorePutter
- responsible for responding to input for physics and UI
Migration Notes
As previously mentioned, Fusion Golf was ported to Fusion 2.0 from Fusion 1.0. You can read more about migrating from Fusion 1.0 to Fusion 2.0 here. The following are some of the changes made during that porting process.
- Physics: Fusion 2.0 uses a separate add-on for physics. Once imported, the
NetworkRunner
used in the game now requires theRunnerSimulatePhysics3D
components. Because this game relies heavily on physics simulation, it's important to note this component'sClient Physics Simulation
property should be set to "Simulate Always" as illustrated:
Additionally, any NetworkRigidbody
component should be replaced with NetworkRigidbody3D
. The Sync Parent
parameter should be set to false in this sample as pictured:
- Scripts & Prototyping: The following folder,
Assets/Fusion/Scripts
, which is present in Fusion 1.0 and contains various tools for prototyping such asPlayerSpawnerPrototype
, no longer exists in Fusion 2.0. While the numerous scripts in this directory can be upgraded, many of them were redundant, overly complicated, and/or unnecessary, so these items were removed from this sample. The only new class that needed to be created wasPlayerSpawner
, aSimulationBehaviour
attached to the mainNetworkRunner
prefab that handles spawning aNetworkObject
when a player joins and despawning when one leaves:
C#
public class PlayerSpawner : SimulationBehaviour, IPlayerJoined, IPlayerLeft
{
public NetworkObject playerObject;
public void PlayerJoined(PlayerRef player)
{
// In a ClientServer topology, only the server can spawn players.
if (Runner.Topology == Topologies.ClientServer)
{
if (Runner.CanSpawn)
{
Runner.Spawn(playerObject, inputAuthority: player);
}
}
// In a shared topology, every player can spawn, however, we only want the local player to spawn their own player
else if (Runner.LocalPlayer == player)
{
Runner.Spawn(playerObject, inputAuthority: player);
}
}
public void PlayerLeft(PlayerRef player)
{
bool canDespawn = (Runner.Topology == Topologies.ClientServer && Runner.IsServer) ||
(Runner.Topology == Topologies.Shared && Runner.IsSharedModeMasterClient);
if (canDespawn)
{
PlayerObject leavingPlayer = PlayerRegistry.GetPlayer(player);
Runner.Despawn(leavingPlayer.Object);
}
}
}
FixedUpdateNetwork
: In Fusion 2.0,FixedUpdateNetwork
does not run on proxies. This can cause some strange behaviours to occur when porting. For example, in this sample,GameState
had some issues where it was only executing state transitions on the host. The following line was added near the end of itsSpawned
method:
C#
...
// Ensures that FixedUpdateNetwork is called for all proxies.
Runner.SetIsSimulated(Object, true);
...
- Spawning: In Fusion 1.0, if players try to call
Runner.Spawn
but they do not have authority to do so -- such as a client in a game that usesClientServer
topology -- the spawning will silently fail. In Fusion 2.0, trying to spawn objects when the client is unable to, will cause an exception to be thrown and can cause various problems. In this sample, before callingRunner.Spawn
, a check is done usingRunner.CanSpawn
beforehand to prevent this error.
3rd Party Assets
The Golf Sample includes several assets provided courtesy of their respective creators. The full packages can be acquired for your own projects at their respective site:
- Skybox Extended Shader by BOXOPHOBIC
- Kenney's Minigolf Kit by Kenney
- Toon Water Shader by Erik Roystan Ross
- Low Poly Cliff Pack by Broken Vector
- Low Poly Tree Pack by Broken Vector
- Fredoka One font by Milena B Design
IMPORTANT: To use them in a commercial project, it is required to purchase a license from the respective creators.
Back to top