Karts
Overview
The Fusion Kart sample demonstrates an approach on how to make a racing game using a Server Authoritative, Client Predicted model where players can create and join rooms using the names as room identifiers.
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). The photon app settings assets can be selected from the Fusion menu Fusion > Realtime Settings
.
Download
Version | Release Date | Download | ||
---|---|---|---|---|
1.1.7 | Jun 21, 2023 | Fusion Karts 1.1.7 Build 198 |
Highlights
The project has a complete game loop, allowing up to 8 users to join each other and race competitively to see who will win, as well as practice together without timers on a track with infinite laps. The main features include:
- Two different modes (Race and Practice)
- Two race tracks
- Karts with varying stats
- Shared Items like boosts/banana obstacle, coins
- Scriptable Object based item system, which is extendable
- Setting up rooms as a host using codes for clients to join, handles edge-cases like "room not found", "room is full", "room is currently in a session" and so on.
Folder Structure
The main Scripts folder /Scripts
has subfolders including Networking
and Fusion Helpers
which pertain to networking specific components, while the other subfolders contain the various interfaces and manager components, etc, which govern the gameplay.
Game Launcher
The GameLauncher.cs
class is responsible for setting the user as a Host
or Client
depending on which option they select from the Playmode menu. It holds a reference to the UI and is responsible for governing things like spawning players, despawning players, and shutting down.
First-Time Launch
When launching the sample for the first time, users will be prompted to supply a nickname (if desired) and select a Region. This screen can be accessed at any time from the Options Menu. The nickname provided is used in three places:
- When a user creates or joins a room a UI item is instantiated with their supplied nickname.
- The nickname is instantiated above each kart during a race in a world-space canvas.
- In the results screen at the end of a race, the nickname is used to show 1st, 2nd, 3rd place and so on.
Rooms
The Fusion Karts sample uses rooms, an abstraction on top of Photon Cloud Sessions. For this sample, a maximum of 8 users can connect to a room at once. This cap can optionally be set to between 1 and 8.
Creating A Room
The CreateGameUI
script stores a reference to the various UI elements under the Create Room Screen
found in the Canvas hierarchy. The Input field for the Room Code
will set an ID for this room. When a user tries to join a lobby they will be prompted with a similar input field and must present a recognised code. The slider for the Player Count allows hosts to clamp the number of users in the room by setting ServerInfo.MaxUsers
in the CreateGameUI
script. There are two different Game Modes and also two different Tracks included in the sample which can be selected during room creation via the following methods which set the appropriate integer values in ServerInfo.cs
:
C#
public void SetGameType(int gameType)
{
ServerInfo.GameMode = gameType;
}
public void SetTrack(int trackId)
{
ServerInfo.TrackId = trackId;
trackImage.sprite = ResourceManager.Instance.tracks[trackId].trackIcon;
}
Joining A Room
To join a room a user must provide a room code. If a user tries to join a room that doesn't exist they will prompted with a UI message to indicate such. A user is not able to join a room that is currently in a session and must also ensure they are in the same Region as the room they are trying to connect to. The JoinGameUI
script holds a reference to the input field to supply the required code. The confirm button is made uninteractable if there is no text supplied in the input field, as creating a room with no code is impossible.
C#
private void SetLobbyName(string lobby)
{
ClientInfo.LobbyName = lobby;
confirmButton.interactable = !string.IsNullOrEmpty(lobby);
}
Readying up
The ready state of each user is displayed beside their nickname in the player list, with a user being ready signified by a green checkmark. All players must first ready up before the race is allowed to begin. The EnsureAllPlayersReady
function in LobbyUI.cs
is subscribed to the PlayerChanged
event of each RoomPlayer
NetworkBehaviour, whose IsReady
networked property invokes via the OnChanged
callback provided through Fusion's Networked
attribute. When called, the function checks that each RoomPlayer
has its IsReady
boolean set to true, and then calls LevelManager.LoadTrack
with the sceneIndex
parameter set to the corresponding index for the selected track. This method sets Fusions active scene which in turn invokes the registered scene object provider.
Handling Input
This sample uses the new Input System, and leverages the InputAction
class to handle support for both keyboard and gamepad controls. The new Input System to make supporting various controllers easy. There is keyboard and joystick support out of the box, including haptic feedback.
Keyboard
- A and D or Left and Right Arrow Keys to Steer
- W or Up Arrow Key to Accelerate
- S or Down Arrow Key to Reverse
- Alt to Look Behind
- Space to Hop/Drift
- Shift to use Horn/Item
Gamepad
- Left Analog stick to Steer
- South Button to Accelerate
- East Button to Reverse
- D-Pad Down to Look Behind
- Right Bumper to Hop/Drift
- Left Bumper use Horn/Item
(supports controller rumble)
Karts
Karts are comprised of many individual behaviours, all deriving from KartComponent
, with a KartEntity
serving as a hub between them and providing periphery. The components are:
KartAnimator
- references to theAnimator
component, visual elements such as particle systems and trails, and methods for playing animations and effects.KartAudio
- references to each persistent audio source on the kart, and responsible for the engine pitch and volume, and playing the drifting audio.KartCamera
- controls the camera perspective, field of view, and speed lines particle systemKartController
- most of the logic and networking occurs here; acceleration, steering, drifting and boosting, as well as rotating the wheels and orienting to the road.KartInput
- polls for input locally and handlesInputAction
enable/disable and callbacksKartLapController
- handles the current lap and checkpoint for the kartKartItemController
- handles the behaviour when pressing the use item button
Additionally, KartEntity
holds a reference to the GameUI
HUD interface, which is not in itself a part of the component structure, but provides a way to outwardly reflect the state of the kart to the player.
Pickups
Pickups is the term used in this sample to label the loosely-defined set of entities with which karts can interact. All pickups implement the ICollidable
interface, and specific behaviour is dictated by the ICollidable.Collide
implementation. The KartEntity
is responsible for initiating the interaction, which is carried out in the OnTriggerStay
method, and to which a reference to the KartEntity
instance is passed. The use of OnTriggerStay
is due to the unreliable nature of the OnTriggerEnter
function in a network context.
- Coins: Coins serve no functional purpose in terms of gameplay, but have the function of demonstrating a basic entity which can be collected by a single player, are despawned, and subsequently add to a counter which is displayed in the UI.
- Item Box: While not being a pickup themselves, item boxes also implement
ICollidable
to facilitate their behaviour. Item boxes are responsible for assigning aPowerup
to a kart when hit, via theKartEntity.SetHeldItem
method.
Powerups
Powerups are a special kind of pickup which is obtained from item boxes, and can be used at an arbitrary time after collection per the player's discretion.
There are two components constituting a powerup; the Powerup
ScriptableObject, and the abstract SpawnedPowerup
class.
SpawnedPowerup
inherits from ICollidable
and implements ICollidable.Collide
virtually, so as to be an optional implementation for derived classes. A virtual Init
method is also provided for optional initialization, which is invoked immediately after being spawned by the Powerup.Use
method.
- Banana: When used, the banana is dropped behind the kart as a physical entity, and acts as a hazard for players. The
BananaPowerup
overridesSpawnedPowerup.Collide
to facilitate spinning out a kart which has run over it. - Boost: Provides an instant level 2 boost. The boost powerup has no physical representation like the banana does, and as such despawns promptly after the boost has been given.