I have a simple game like diep.io that is networked using UNET. When the player clicks, bullets start firing and are instantiated locally and spawned on the server via a BulletSpawner object. The shooting script is contained as a child of a child of the main player object. Here is my code.
Shoot.cs script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class Shoot : NetworkBehaviour {
public bool leftClick;
public bool rightClick;
public float bulletInterval;
public GameObject bulletPrefab;
public float bulletSpeed;
private BulletSpawner bulletSpawner;
private void Start()
{
bulletSpawner = GameObject.FindObjectOfType<BulletSpawner>();
}
// Update is called once per frame
void Update () {
if (transform.parent.parent.GetComponent<NetworkIdentity>().isLocalPlayer)
{
if (leftClick && Input.GetMouseButtonDown(0))
{
StartCoroutine("ShootBullet");
}
if (leftClick && Input.GetMouseButtonUp(0))
{
StopCoroutine("ShootBullet");
}
if (rightClick && Input.GetMouseButtonDown(1))
{
StartCoroutine("ShootBullet");
}
if (rightClick && Input.GetMouseButtonUp(1))
{
StopCoroutine("ShootBullet");
}
}
}
IEnumerator ShootBullet()
{
var spawnPosition = transform.position;
var spawnRotation = transform.parent.transform.rotation;
var bullet = (GameObject)Instantiate(bulletPrefab, spawnPosition, spawnRotation);
bullet.GetComponent<SetColor>().Set(transform.parent.parent.GetComponentInChildren<RandomColorFromList>().colors[transform.parent.parent.GetComponentInChildren<RandomColorFromList>().myColorIndex]);
bullet.GetComponent<Rigidbody2D>().velocity = bullet.transform.right * bulletSpeed;
bulletSpawner.CmdSpawn(bulletPrefab);
yield return new WaitForSeconds(bulletInterval);
StartCoroutine("ShootBullet");
}
}
BulletSpawner.cs script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class BulletSpawner : NetworkBehaviour {
[Command]
public void CmdSpawn (GameObject objectToSpawn)
{
NetworkServer.Spawn(objectToSpawn);
}
}
Does anyone have any ideas? The bullets only appear on other clients when shot on the server. Bullets always appear locally.
CmdSpawn is trying to spawn a prefab. That won’t work. You instantiate a prefab, and then spawn the instantiated gameobject. You probably have some error or warning in the player log for the server.
Also, I have doubts that you can pass a prefab in a command. I believe you’d need to send some other identifier to the server to tell the server what prefab to use.
Additionally, if you instantiate a bullet locally on the client, and also do so on the server, the original client is going to end up with 2 bullets instead of 1. You’re going to want to design a system for merging these two bullets on that client when the second bullet appears.
Sorry, my bad! I accidentally wrote bulletPrefab instead of bullet in the bulletSpawner.CmdSpawn command. However, it still doesn’t work. Only the server bullets get propagated across the network.
Aha, I figured out why it wasn’t working. When I set the Editor as a client and the build as the server, I got this error: “Trying to send command for object without authority.” How do I give the client permission to proceed?
I attached the code and put it on the parent GameObject, any ideas?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Networking;
public class Shoot : NetworkBehaviour {
public bool leftClick;
public bool rightClick;
public float bulletInterval;
public GameObject bulletPrefab;
public float bulletSpeed;
public Transform spawnPoint;
// Update is called once per frame
void Update () {
if (isLocalPlayer)
{
if (leftClick && Input.GetMouseButtonDown(0))
{
StartCoroutine("ShootBullet");
}
if (leftClick && Input.GetMouseButtonUp(0))
{
StopCoroutine("ShootBullet");
}
if (rightClick && Input.GetMouseButtonDown(1))
{
StartCoroutine("ShootBullet");
}
if (rightClick && Input.GetMouseButtonUp(1))
{
StopCoroutine("ShootBullet");
}
}
}
IEnumerator ShootBullet()
{
var spawnPosition = spawnPoint.position;
var spawnRotation = transform.rotation;
var bullet = (GameObject)Instantiate(bulletPrefab, spawnPosition, spawnRotation);
bullet.GetComponent<SetColor>().Set(GetComponentInChildren<RandomColorFromList>().colors[GetComponentInChildren<RandomColorFromList>().myColorIndex]);
bullet.GetComponent<Rigidbody2D>().velocity = bullet.transform.right * bulletSpeed;
CmdSpawn(bullet);
yield return new WaitForSeconds(bulletInterval);
StartCoroutine("ShootBullet");
}
[Command]
public void CmdSpawn(GameObject objectToSpawn)
{
NetworkServer.Spawn(objectToSpawn);
Debug.Log("Spawned!");
}
}
You’re still not instantiating the bullet on the server, so the server has nothing to spawn even if you were able to successfully call CmdSpawn. On the server, instantiate the prefab, and on the returned instantiated GameObject call NetworkServer.Spawn. The server knows nothing about gameobjects the clients are instantiating locally on their own, so won’t be able to spawn them because as far as the server is concerned they don’t exist.
As far as assigning authority, you would spawn the object this script is attached to on the server with NetworkServer.SpawnWithClientAuthority, or you can switch authority with AssignClientAuthority. Or you can just send commands through that client’s Player GameObject.