Adding Multiplayer shooting

Checked with version: 5.3

-

Difficulty: Intermediate

This section adds networking to the bullets in this example. Both the Bullet prefab and the shooting code will need to be updated.

First, the Bullet prefab needs a NetworkIdentity to identify it as unique on the network, a NetworkTransform to sync the position and rotation of the bullet, and the prefab itself will need to be registered with the NetworkManager as a Spawnable Prefab.

  • Select the Bullet prefab in the Project Window.
  • With the Bullet prefab selected,
  • ... find and add the component: Network > NetworkIdentity.
  • ... find and add the component: Network > NetworkTransform.
  • Set the Network Send Rate on the NetworkTransform component to 0.

The bullet doesn’t change direction or velocity after it’s shot, so it doesn’t need to send movement updates. Each client can reliably calculate the position of the bullet. By setting the send rate to zero so the position is not synchronized over the network, and allowing each Client to calculate to position of the bullet, the overall network traffic is reduced to improve game performance.

description

  • Select the NetworkManager in the Hierarchy Window.
  • With the NetworkManager selected,
  • ... open the Spawn Info foldout.
  • In the list Registered Spawnable Prefabs, add a new row with the + button.
  • With the NetworkManager selected,
  • ... add the Bullet prefab to the new row in the Registered Spawnable Prefabs list.

description

Next, the PlayerController script needs to be updated to make the shooting code network aware.

  • Open the PlayerController script for editing.

Earlier, when we were covering how to make player movement network aware in Networking Player Movment, we discussed the architecture of a project using the Multiplayer Networking HLAPI. The basic principle is that the Server and all of the Clients are executing the same code from the same scripts on the same GameObjects at the same time. One way to control the flow of logic in a script being shared by the Server and multiple Clients was the check against isLocalPlayer to control which objects can execute what code.

Another control is the [Command] attribute. The [Command] attribute indicates that the following function will be called by the Client, but will be run on the Server. Any arguments in the function will automatically be passed to the Server with the Command. Commands can only be sent from the local player object. When making a networked command, the function name must begin with “Cmd”. For more information on the [Command] attribute please see Remote Actions.

  • Add the [Command] attribute to the Fire function, making this a networked command.
  • Change the function name to “CmdFire” by adding the “Cmd” prefix.
[Command]
void CmdFire()

  • Update the function name being called in Update to CmdFire.
CmdFire();

The next concept, one that is somewhat unique to Multiplayer Networking, is the concept of Network Spawning. In the Multiplayer Networking HLAPI “Spawn” means more than just “Instantiate”. It means to create a GameObject on the Server and on all of the Clients connected to the Server. The GameObject will then be managed by the spawning system; state updates are sent to Clients when the object changes on the Server, the GameObject will be destroyed on Clients when it is destroyed on the Server, and the spawned GameObject be added to the set of networked GameObjects that the Server is managing so that if another Client joins the game later, the objects will also be spawned on that Client in the correct state. For more information on Network Spawning please see Object Spawning.

  • Add a NetworkServer.Spawn function to spawn the bullet.
NetworkServer.Spawn(bullet);

The final script should now look like this:

PlayerController

Code snippet

using UnityEngine;
using UnityEngine.Networking;

public class PlayerController : NetworkBehaviour
{
    public GameObject bulletPrefab;
    public Transform bulletSpawn;

    void Update()
    {
        if (!isLocalPlayer)
        {
            return;
        }

        var x = Input.GetAxis("Horizontal") * Time.deltaTime * 150.0f;
        var z = Input.GetAxis("Vertical") * Time.deltaTime * 3.0f;

        transform.Rotate(0, x, 0);
        transform.Translate(0, 0, z);

        if (Input.GetKeyDown(KeyCode.Space))
        {
            CmdFire();
        }
    }

    // This [Command] code is called on the Client …
    // … but it is run on the Server!
    [Command]
    void CmdFire()
    {
        // Create the Bullet from the Bullet Prefab
        var bullet = (GameObject)Instantiate(
            bulletPrefab,
            bulletSpawn.position,
            bulletSpawn.rotation);

        // Add velocity to the bullet
        bullet.GetComponent<Rigidbody>().velocity = bullet.transform.forward * 6;

        // Spawn the bullet on the Clients
        NetworkServer.Spawn(bullet);

        // Destroy the bullet after 2 seconds
        Destroy(bullet, 2.0f);
    }

    public override void OnStartLocalPlayer ()
    {
        GetComponent<MeshRenderer>().material.color = Color.blue;
    }
}
  • Save the PlayerController script.
  • Return to Unity.

Test the current state of the project.

  • Build and Run this scene as a standalone application.
  • Click the Host button from the in-game UI to start this game as a Host.
  • Move the player GameObject.
  • Return to Unity.
  • Enter Play Mode.
  • Click the LAN Client button from the in-game UI to connect to the Host as a Client.

Pressing the spacebar should now create a new bullet GameObject on all Clients for the correct player. Each player should be able to shoot at the other.

Note, however, that the bullet GameObjects simply bounce off the other player and they have no apparent effect on the other player.

  • Close the standalone player.
  • Return to Unity.
  • Exit Play Mode.

Related documentation