3D Multiplayer Battle Royale Game Unity, C#

👤 Sharing: AI
Okay, here's a basic outline and code snippets to get you started with a very simplified 3D multiplayer battle royale game in Unity, using C#.  This is a conceptual foundation, and you'll need to expand it significantly for a real game.

**Concept**

The example will focus on:

1.  **Basic Player Movement:**  A simple character controller.
2.  **Networking:**  Using Unity's built-in networking (UNET, though deprecated, it's simplest for a starting example) to synchronize player positions.
3.  **Simple Zone:**  A shrinking area (a basic circle) to simulate the battle royale zone.
4.  **Very Basic Game Logic:** Respawning, tracking remaining players.

**Important Considerations Before You Start**

*   **Unity Version:**  This example uses Unity's older UNET networking.  For a modern game, you *should* use more current solutions like:
    *   **Mirror:** A community-developed UNET replacement. Free and widely used.
    *   **Unity's Netcode for GameObjects (NGO):** Unity's newer official networking.  More complex but actively developed.
    *   **Photon Unity Networking (PUN):**  A popular commercial solution.
*   **Complexity:**  A full battle royale is *extremely* complex.  This is a stripped-down example to illustrate the core concepts.
*   **Asset Store:** Use the Asset Store! Many free and paid assets can help you with things like character models, animations, and environments.
*   **Scaling:** UNET/Mirror/NGO can be scaled up.

**Steps to Implement**

1.  **Create a New Unity Project:**  Create a new Unity project (3D template).
2.  **Setup the Scene:**  Add a basic plane for the ground.  Create a simple character prefab (a cube will do to start).
3.  **Write the Player Movement Script (PlayerMovement.cs):**

```csharp
using UnityEngine;
using UnityEngine.Networking;

public class PlayerMovement : NetworkBehaviour
{
    public float speed = 5f;
    public float rotationSpeed = 500f;  // Rotation speed
    public GameObject cameraPrefab; // Assign camera prefab in inspector
    private Camera playerCamera;

    void Start()
    {
        if (!isLocalPlayer)
        {
            return; // Only enable movement/camera for the local player
        }

        GameObject cameraInstance = Instantiate(cameraPrefab);
        playerCamera = cameraInstance.GetComponent<Camera>();
        cameraInstance.transform.SetParent(transform, false);
        cameraInstance.transform.localPosition = new Vector3(0, 2, -5); // Adjust position as needed

        // Disable other player's cameras and behaviours.
        PlayerMovement[] allPlayerMovements = FindObjectsOfType<PlayerMovement>();
        foreach (PlayerMovement player in allPlayerMovements)
        {
            if (player != this)
            {
                Destroy(player.GetComponent<Camera>());
                player.enabled = false;
            }
        }
    }


    void Update()
    {
        if (!isLocalPlayer)
        {
            return; // Only move the local player
        }

        // Movement
        float horizontalInput = Input.GetAxis("Horizontal");
        float verticalInput = Input.GetAxis("Vertical");

        Vector3 movement = new Vector3(horizontalInput, 0f, verticalInput) * speed * Time.deltaTime;
        transform.Translate(movement);

        // Rotation
        float mouseX = Input.GetAxis("Mouse X"); // Get mouse input

        transform.Rotate(Vector3.up, mouseX * rotationSpeed * Time.deltaTime); // Rotate horizontally based on mouse

    }

    void OnDisable()
    {
        if (playerCamera != null)
        {
            Destroy(playerCamera.gameObject);
        }
    }
}
```

*   **Explanation:**

    *   `NetworkBehaviour`:  Base class for networked objects.
    *   `isLocalPlayer`: Checks if this object is controlled by the local player. Crucial for networked games!
    *   `speed`:  Movement speed.
    *   `Update()`:  Gets input (WASD) and moves the character.  Uses `transform.Translate()` for simplicity.
    *   `transform.Rotate()`: Rotates the player based on mouse X.
    *   Add a Camera: The added camera will attach to the player if they're local, otherwise, it will be deleted

4.  **Create a Network Manager (NetworkManagerBR.cs):**

```csharp
using UnityEngine;
using UnityEngine.Networking;
using System.Collections;

public class NetworkManagerBR : NetworkManager
{
    public GameObject playerPrefab;
    public Transform[] spawnPoints;  // Assign spawn points in the Inspector
    private int currentPlayerIndex = 0;


    public override void OnServerAddPlayer(NetworkConnection conn, short playerControllerId)
    {
        // Choose a spawn point
        Transform spawnPoint = spawnPoints[currentPlayerIndex % spawnPoints.Length];
        currentPlayerIndex++;

        // Instantiate the player prefab at the spawn point
        GameObject player = Instantiate(playerPrefab, spawnPoint.position, spawnPoint.rotation);
        NetworkServer.AddPlayerForConnection(conn, player, playerControllerId);


    }

    public override void OnClientConnect(NetworkConnection conn)
    {
        base.OnClientConnect(conn);
        Debug.Log("Client connected to server");
    }

    public override void OnServerDisconnect(NetworkConnection conn)
    {
        NetworkServer.DestroyPlayersForConnection(conn);
        Debug.Log("A Client disconnected from server");
    }


    public override void OnStopServer()
    {
        Debug.Log("Server has stopped");
    }
}
```

*   **Explanation:**

    *   `NetworkManager`:  Handles the overall networking.
    *   `playerPrefab`:  The player prefab that will be spawned.
    *   `OnServerAddPlayer`:  Called when a player connects to the server.  This is where you spawn the player.  It selects a spawn point from an array.  `NetworkServer.AddPlayerForConnection` makes it visible to the client.

5.  **Create the Zone Shrinking Script (ZoneShrinker.cs):**

```csharp
using UnityEngine;

public class ZoneShrinker : MonoBehaviour
{
    public float initialRadius = 50f;
    public float shrinkRate = 2f; // Units per second
    public float minRadius = 5f;

    private float currentRadius;

    void Start()
    {
        currentRadius = initialRadius;
        transform.localScale = new Vector3(initialRadius * 2, 1, initialRadius * 2);  // Visual representation
    }

    void Update()
    {
        if (currentRadius > minRadius)
        {
            currentRadius -= shrinkRate * Time.deltaTime;
            currentRadius = Mathf.Max(currentRadius, minRadius); // Ensure it doesn't go below minRadius
            transform.localScale = new Vector3(currentRadius * 2, 1, currentRadius * 2);  // Update visual
        }
    }

    // Add a method to check if a player is inside the zone:
    public bool IsInsideZone(Vector3 position)
    {
        Vector3 center = transform.position; // Zone's center
        float distance = Vector3.Distance(center, position);
        return distance <= currentRadius;
    }
}
```

*   **Explanation:**

    *   `initialRadius`: The starting radius of the zone.
    *   `shrinkRate`:  How much the radius decreases per second.
    *   `minRadius`:  The smallest the zone can get.
    *   `Update()`: Shrinks the zone over time.
    *   `IsInsideZone()`:  A function to check if a player is inside the zone.  You'd use this to apply damage to players outside the zone.

**Unity Editor Setup**

1.  **Create the Player Prefab:**

    *   Create a new 3D object (e.g., a Cube).
    *   Add the `PlayerMovement` script to it.
    *   Add a `NetworkIdentity` component to the cube.  Check the "Local Player Authority" box.
    *   Turn it into a Prefab (drag it from the Hierarchy to the Project window).
2.  **Create Spawn Points:**

    *   Create empty GameObjects in the scene.  Position them where you want players to spawn.
    *   Name them something like "SpawnPoint1", "SpawnPoint2", etc.
3.  **Create the Network Manager GameObject:**

    *   Create an empty GameObject in the scene (e.g., "NetworkManager").
    *   Add the `NetworkManagerBR` script to it.
    *   Drag the `Player` prefab to the `Player Prefab` slot in the Inspector for the `NetworkManagerBR` component.
    *   Drag all the spawn point GameObjects to the `Spawn Points` array in the Inspector.
4.  **Create the Zone GameObject:**

    *   Create a 3D Object (e.g., a Cylinder, scaled to be very flat).  This will visually represent the zone.
    *   Add the `ZoneShrinker` script to it.
    *   Position it in the center of your play area.
    *   Adjust the `initialRadius`, `shrinkRate`, and `minRadius` values in the Inspector to your liking.
5.  **Configure Networking:**

    *   In the Inspector for the `NetworkManager` GameObject, set the `Spawn Method` to `Round Robin`.
    *   (Optional) You can set the `Max Size` to control how many players can join.

**To Run the Example**

1.  **Build the project:** Go to "File" -> "Build Settings".  Add the current scene to the "Scenes In Build" list. Build the game (e.g., to a folder named "Build").
2.  **Run two instances:** Run the built executable and then run the Unity editor. In the Unity editor, click the "Host" button on the NetworkManagerHUD. In the built game, click the "Client" button.  You should see two players.

**Further Development**

*   **Health and Damage:**  Implement health, weapon firing, and damage calculation.  Use `[Command]` and `[ClientRpc]` attributes for networked actions.
*   **Items and Loot:**  Add items that players can pick up (weapons, armor, healing items).
*   **Visuals:**  Replace the simple cubes with proper character models and animations.
*   **UI:**  Add a UI to display health, remaining players, zone information, etc.
*   **Advanced Zone:** Implement a more complex zone (irregular shapes, multiple phases).
*   **Lobby:** Create a lobby system to allow players to join before the game starts.
*   **Anti-Cheat:** This is essential for any multiplayer game.

**Example of Adding Damage (Requires Health Script)**

First, create a `Health.cs` script:

```csharp
using UnityEngine;
using UnityEngine.Networking;

public class Health : NetworkBehaviour
{
    public int maxHealth = 100;
    [SyncVar]
    public int currentHealth;

    public bool destroyOnDeath = true;

    public event System.Action<int, int> healthChanged; // Event for UI update (current, max)

    void Start()
    {
        currentHealth = maxHealth;
        healthChanged?.Invoke(currentHealth, maxHealth);
    }

    public void TakeDamage(int amount)
    {
        if (!isServer) return;  // Only the server applies damage

        currentHealth -= amount;
        currentHealth = Mathf.Clamp(currentHealth, 0, maxHealth); // Ensure health doesn't go below 0

        healthChanged?.Invoke(currentHealth, maxHealth); // Raise the event

        if (currentHealth <= 0)
        {
            Die();
        }
    }


    void Die()
    {
        Debug.Log("Player Died!");
        // Respawn:
        if (destroyOnDeath)
        {
            NetworkServer.Destroy(gameObject);
        }
        // Alternatively, respawn in a location
        // else {
        //   RpcRespawn();
        //}
    }

    // Rpc to respawn the player on clients
    [ClientRpc]
    void RpcRespawn()
    {
        // Implement respawn logic here
        // E.g., move the player to a random spawn point
    }
}
```

Then, add a basic weapon script (e.g., attached to the player):

```csharp
using UnityEngine;
using UnityEngine.Networking;

public class Weapon : NetworkBehaviour
{
    public int damageAmount = 10;
    public float fireRate = 0.5f;  // Shots per second
    public float range = 100f;

    private float nextFireTime = 0f;

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

        if (Input.GetButton("Fire1") && Time.time > nextFireTime)
        {
            nextFireTime = Time.time + 1f / fireRate;
            CmdFire();
        }
    }

    [Command]
    void CmdFire()
    {
        RaycastHit hit;
        if (Physics.Raycast(transform.position, transform.forward, out hit, range))
        {
            Health targetHealth = hit.transform.GetComponent<Health>();
            if (targetHealth != null)
            {
                // Inform client to take damage to health.
                targetHealth.TakeDamage(damageAmount); // Deal damage on the server

            }
        }
    }
}
```

Add both scripts to the Player prefab.

*Explanation*

*Health.cs*
*   `SyncVar`: is synchronized across all clients.
*   `TakeDamage` takes damage to currentHealth.
*   `healthChanged`: an event to trigger UI updates.
*   `Die` destroys the player, or triggers a respawn.
* *Weapon.cs*
*   `[Command]`:  This tells Unity that the `CmdFire` function is a *command* that should be executed on the server.  Only the owning client can call commands.
*   Raycasting to find enemies.
*   `CmdFire` function will invoke `TakeDamage` on the server to another client.

This is a basic framework. You'll need to expand upon it, adding more features, polish, and proper art assets to create a truly engaging battle royale game. Remember to prioritize learning the networking fundamentals. Good luck!
👁️ Viewed: 10

Comments