Multiplayer (HATS) - Guide
[SMPL-HATS-02]
This downloadable sample game project showcases the Beamable Multiplayer Feature.
Download
These learning resources provide a better way to build live games in Unity.
Source | Detail |
---|---|
1. Download the HATS Sample Game 2. Open in Unity Editor (Version 2020.3.11f1) 3. Open the Beamable Toolbox 4. Sign-In / Register To Beamable. See Step 1 - Getting Started for more info 5. Open the Scene01Intro Scene6. Play The Scene: Unity → Edit → Play 7. Click "Play" for an easy start. Or do a standalone build of the game and run the build. Then run the Unity Editor. In both running games, choose "Play" to play against yourself. You need to rebuild Addressable Asset Groups before doing a standalone build. To build content in the Editor, open the Addressables Groups window, then select Build > New Build > Default Build Script. 8. Enjoy! *Note: Supports Mac & Windows and includes the Beamable SDK |
Rules of the Game
- Up to four players enter the hexagonal grid and play one round in a series of turns
- In each turn, choose between four actions:
- Move: Go to a free grid cell around you
- Shield: Don't move, but be resistant to incoming fireballs or arrows
- Fireball: Shoot a fireball across the grid
- Arrow: Shoot an arrow
- Commit your turn before the turn times out!
- If you get hit by a fireball, an arrow or get in touch with lava, you die.
- Last player alive wins.
Lava expands! Pay attention to grid cells that are starting to crumble.
Scenes
- Scene01Intro - The main menu. Play this scene first
- Scene02Game - The core gameplay loop
- Scene03Inventory - Allows character modification
- Scene04Settings - Allows game configuration
- Scene05Leaderboards - Shows high scores
Screenshots
Game Maker User Experience
During development, the game maker's user experience is as follows: There are several major parts to this game creation process.
Steps
Follow these steps to get started:
These steps are already complete in the sample project. The instructions here explain the process.
Related Features
More details are covered in related feature page(s).
• Matchmaking - Connect remote players in a room
• Multiplayer - Allow game makers to create multi-user experiences
Step 1. Setup Project
Here are instructions to setup the Beamable SDK and "GameType" content.
Step | Detail |
---|---|
1. Install the Beamable SDK and Register/Login | • See Step 1 - Getting Started for more info. |
2. Open the Content Manager Window | • Unity → Window → Beamable → Open Content Manager |
3. Create the "GameType" content | • Select the content type in the list • Press the "Create" button • Populate the content name |
4. Configure "GameType" content | • Populate the Max Players fieldNote: The other fields are optional and may be needed for advanced use cases like Matchmaking. |
5. Save the Unity Project | • Unity → File → Save Project Best Practice: If you are working on a team, commit to version control in this step |
6. Publish the content | • Press the "Publish" button in the Content Manager Window |
Step 2. Plan the Multiplayer Game Design
See Multiplayer (Plan the Multiplayer Game Design) for more info.
Step 3. Create the Game Code
This step includes the bulk of time and effort in the project.
Step | Detail |
---|---|
1. Create C# game-specific logic | • Implement game logic • Handle player input • Render graphics & sounds Note: This represents the bulk of the development effort. The details depend on the specifics of the game project. |
Step 4. Create the Multiplayer Code
In this section you will find some partial code snippets. Download the project to see the complete code.
Multiplayer Game Simulation
HATS uses a deterministic simulation networking model. All network messages (HatsGameMessage
) are broadcast to every client and fed into a local per-client instance of GameSimulation
. The simulation, in turn, locally generates a series of HatsGameEvent
to be consumed by MonoBehaviours that manage graphics and audio per GameObjects,
SpawnEventHandler
, for instance, instantiates a new player GameObject when receiving a PlayerSpawnEvent
;
public override IEnumerator HandleSpawnEvent(PlayerSpawnEvent evt, Action callback)
{
Debug.Log("Spawning player " + evt.Player.dbid);
yield return null;
var playerGob = Instantiate(playerPrefab, GameProcessor.BattleGridBehaviour.Grid.transform);
playerGob.Setup(GameProcessor, evt.Player);
var localPosition = GameProcessor.BattleGridBehaviour.Grid.CellToLocal(evt.Position);
playerGob.transform.localPosition = localPosition;
callback();
}
Local player input is captured and converted to network messages via PlayerMoveBuilder
:
public void CommitMove()
{
moveBuilderState = PlayerMoveBuilderState.COMMITTED;
NetworkDriver.DeclareLocalPlayerAction(new HatsPlayerMove
{
Dbid = PlayerDbid,
TurnNumber = GameProcessor.Simulation.CurrentTurn,
Direction = MoveDirection,
MoveType = MoveType
});
}
Keep in mind that all messages are broadcast to every client, including the one that sent the message in the first place.
The GameProcessor
spins up the game simulation (GameSimulation
) and the network layer (MultiplayerGameDriver
), connecting both with each other:
public void StartGame(List<long> dbids, BotProfileContent botProfileContent)
{
var messageQueue = MultiplayerGameDriver.Init(roomId, framesPerSecond, new List<long>());
var players = dbids.Select(dbid => new HatsPlayer
{
dbid = dbid
}).ToList();
Simulation = new GameSimulation(BattleGridBehaviour.BattleGrid, framesPerSecond, _configuration, players, botProfileContent, roomId.GetHashCode(), messageQueue);
BattleGridBehaviour.SetupInitialTileChanges();
StartCoroutine(PlayGame());
}
It continuously relays game events to local handlers:
foreach (var evt in Simulation.PlayGame())
{
currentTurn = Simulation.CurrentTurn;
if (evt == null)
{
yield return null;
continue;
}
switch (evt)
{
case PlayerSpawnEvent spawnEvt:
yield return EventHandlers.Handle(this, spawnEvt, handler => handler.HandleSpawnEvent);
break;
case PlayerMoveEvent moveEvt:
yield return EventHandlers.Handle(this, moveEvt, handler => handler.HandleMoveEvent);
break;
...
Leaderboard
Related Guides
A common use case for the feature is covered in the guides. It is recommended to read the guide before continuing with the sample steps below.
• Leaderboards - Display and update per-game leaderboards
Leaderboard-related functionality is handled via LeaderboardScreenController
. It retrieves the current leaderboard in one essential call to Beamable's leaderboard SDK:
view = await beamable.LeaderboardService.GetBoard(LeaderboardRef, 0, 50, focus: beamable.User.id);
In GameOverController
, the winning player updates the leaderboard when receiving a GameOverEvent
:
if (isWinner)
_beamableAPI.LeaderboardService.IncrementScore(_leaderboardRef.Id, 1);
Inventory
Related Guides
A common use case for the feature is covered in the guides. It is recommended to read the guide before continuing with the sample steps below.
• Inventory - Define player inventory and offer items in a store
HATS offers players to buy characters and hats, both being purely decorative items without any effect on gameplay. Players need to earn Gems to buy these items. All items are subtypes of Beamable's ItemContent
and are managed by the Beamable Content Manager. Transactions and available shop listings are handled by CharacterPanelController
, going hand in hand with PlayerInventory
:
public async Task PopulateCharacterShop()
{
var beamable = await Beamable.API.Instance;
var shop = await beamable.CommerceService.GetCurrent(CharacterShopRef.Id);
var playerCharacters = await PlayerInventory.GetAvailableCharacters();
foreach (var listing in shop.listings)
{
...
var itemContentId = listing.offer.obtainItems[0].contentId;
var hasCharacterAlready = playerCharacters.Any(character => character.Id.Equals(itemContentId));
if (hasCharacterAlready) continue; // skip this listing because the player already owns the take
...
public static async Task<List<CharacterContent>> GetAvailableCharacters()
{
var beamable = await Beamable.API.Instance;
var characters = await beamable.InventoryService.GetItems<CharacterContent>();
...
Additional Experiments
Here are some optional experiments game makers can complete in the sample project.
Did you complete all the experiments with success? We'd love to hear about it. Contact us.
Difficulty | Scene | Name | Detail |
---|---|---|---|
Beginner | - | Give yourself gems without winning even a single game | Hint: There is one default Currency type |
Beginner | All Scenes | Change game asset to create a new theme (SciFi, Fantasy, ...) | • Replace (or add) all visible textures and sprites in UI and the game itself • If you add assets, update all image references accordingly |
Intermediate | All Scenes | Add more characters, hats or tile types | • Find the birthday hat and offer a normal and a drunk version • To add a new character, both Prefab and Content need to be updated. Start with duplicating and adapting an existing character Prefab. • Tile types: Have a look at BattleGrid. Hint: All of that requires Content changes |
Intermediate | Scene02Game | Make a surrendered player carry a white flag | • A player can be dead in two ways now • Adapt the way that players are rendered accordingly |
Advanced | Scene02Game | Add arrow frenzy | • Add a powerup that shoots arrows in all directions at once. It should only last one turn. • Little Twist: Shoot arrows sequentially, in clockwise order |
Advanced | All Scenes | Make the game 3D | • For starters, leave GameSimulation alone and work your way through BattleGrid first |
Advanced
This section contains any advanced configuration options and workflows.
Matchmaking
In multiplayer gaming, matchmaking is the process of choosing a room based on criteria (e.g. "Give me a room to play in with 2 total players of any skill level"). Beamable supports matchmaking through its matchmaking service.
See Matchmaking for more info.
For HATS, a single catch-all GameType is used, allowing the player to play against up to three bots or other human contenders, waiting 10 seconds until the match starts.
MatchmakingBehaviour
handles all Matchmaking-related functionality. As soon as a match is found, the 'Scene02Game' scene is loaded:
...
MatchmakingHandle = await _api.Experimental.MatchmakingService.StartMatchmaking(
GameTypeRef.Id,
maxWait: TimeSpan.FromSeconds(_configuration.OverrideMaxMatchmakingTimeout),
updateHandler: handle =>
{
// No updates available at the moment when searching a match.
},
readyHandler: handle =>
{
Debug.Assert(handle.State == MatchmakingState.Ready);
var dbids = MatchmakingHandle.Status.Players;
var gameId = MatchmakingHandle.Status.GameId;
var matchId = handle.Match.matchId;
Debug.Log($"Match is ready! Found matchID={matchId} gameId={gameId}");
Debug.Log($"Starting match with DBIDs={string.Join(",", dbids.ToArray())}");
List<long> dbidsAsLong = dbids.Select(i => long.Parse(i)).ToList();
HatsScenes.LoadGameScene(gameId, dbidsAsLong);
},
timeoutHandler: handle =>
{
Debug.Log($"Matchmaking timed out! state={handle.State}");
IsSearching = false;
OnTimedOut?.Invoke();
}
);
...
Game Security
See Multiplayer (Game Security) for more info.
Playing "Against Yourself"
See Multiplayer (Playing Against Yourself) for more info.
Randomization and Determinism
See Multiplayer (Randomization and Determinism) for more info.
Learning Resources
These learning resources provide a better way to build live games in Unity.
Source | Detail |
---|---|
1. Download the HATS Sample Game 2. Open in Unity Editor (Version 2020.3.11f1) 3. Open the Beamable Toolbox 4. Sign-In / Register To Beamable. See Step 1 - Getting Started for more info 5. Open the Scene01Intro Scene6. Play The Scene: Unity → Edit → Play 7. Click "Play" for an easy start. Or do a standalone build of the game and run the build. Then run the Unity Editor. In both running games, choose "Play" to play against yourself. You need to rebuild Addressable Asset Groups before doing a standalone build. To build content in the Editor, open the Addressables Groups window, then select Build > New Build > Default Build Script. 8. Enjoy! Note: Supports Mac & Windows and includes the Beamable SDK |
Updated about 1 year ago