1.新建空物体,为管理脚本的物体:manager,挂载NetworkManager,kcpTransport,NetworkManagerHud.
2.设置玩家出生点,spawnPoint,设置好初始化的position的位置(*),挂载NetworkStartPosition的脚本
3.新建Player的预制体,挂载NetworkIdentity,NetworkTransform(unreliable)的脚本,还有自己的自定义脚本,注意自定义脚本需要继承NetworkBehaviour,这个在编辑的时候需要改改.下面是主玩家的移动代码MainPlayerControll.cs.
4.新建turret的预制体,挂载rigibody, NetworkIdentity, TurretMove,“rigibody需要关闭重力”.
using Mirror;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using System.Globalization;
public class MainPlayerControll : NetworkBehaviour
{
public float speed = 5;
public float Rspeed = 25;
public GameObject turretPrefab;
public Transform firepoint;
[SyncVar] public int health = 10;
int lastHealth = 0;
public TextMesh nameTxt;
public override void OnStartLocalPlayer()
{
base.OnStartLocalPlayer();
Camera.main.transform.SetParent(transform);
Camera.main.transform.localPosition = new Vector3(0, 0.5f, 0);
Camera.main.transform.localRotation = Quaternion.identity;
}
public override void OnStopLocalPlayer()
{
base.OnStopLocalPlayer();
Camera.main.transform.SetParent(null);
}
void Update()
{
if (health != lastHealth)
{
nameTxt.text = $"{gameObject.name} {health}";
lastHealth = health;
}
if (!isLocalPlayer) return;
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 movement = transform.forward * verticalInput * speed * Time.deltaTime;
Vector3 rotation = new Vector3(0f, horizontalInput * Rspeed * Time.deltaTime, 0f);
transform.Translate(movement, Space.World);
transform.Rotate(rotation);
if (Input.GetKeyDown(KeyCode.Space))
{
CmdFire();
}
}
[Command]
void CmdFire()
{
GameObject projectile = Instantiate(turretPrefab, firepoint.position, firepoint.rotation);
NetworkServer.Spawn(projectile);
}
[ServerCallback]
void OnTriggerEnter(Collider other)
{
if (other.GetComponent<TurretMove>() != null)
{
--health;
if (health == 0)
NetworkServer.Destroy(gameObject);
}
}
}
using Mirror;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
public class TurretMove : NetworkBehaviour
{
public float destroyAfter = 2;
public Rigidbody rigidBody;
public float force = 1000;
public override void OnStartServer()
{
Invoke(nameof(DestroySelf), destroyAfter);
}
void Start()
{
rigidBody.AddForce(transform.forward * force);
}
// destroy for everyone on the server
[Server]
void DestroySelf()
{
NetworkServer.Destroy(gameObject);
}
[ServerCallback]
void OnTriggerEnter(Collider co) => DestroySelf();
}
2.注意:在manager的NetworkManager中,需要给PlayerPrefab赋值,拖入制作的player预制体,transport中拖入同物体的kcpTransport组件.playerspawnMethod选择RoundRobin.
player预制体的MainPlayerControll脚本需要拖入子弹的预制体,还要拖入一个开火点,开火点:就是创建一个空物体放在player上作为开火点.
子弹只需要把自己的的rigibody拖进去到TurretMode.cs脚本里
主玩家脚本里的广播的字段 需要这样声明写 [SyncVar] public int health = 10;
一些被动碰撞,触发的方法也要加上 [ServerCallback]
[ServerCallback]
void OnTriggerEnter(Collider other)
{
if (other.GetComponent<TurretMove>() != null)
{
--health;
if (health == 0)
NetworkServer.Destroy(gameObject);
}
}
主动触发 如开火:需要加 [Command]
[Command]
void CmdFire()
{
GameObject projectile = Instantiate(turretPrefab, firepoint.position, firepoint.rotation);
NetworkServer.Spawn(projectile);
}
[Command]:用于在客户端调用服务器上的方法。客户端可以使用该标记调用服务器上的方法,并将其作为命令进行处理。方法名称必须以 “Cmd” 开头。
[ClientRpc]:用于在服务器上调用并在所有连接的客户端上执行的方法。服务器可以使用该标记调用方法,并在所有连接的客户端上执行。方法名称可以任意命名。
[TargetRpc]:用于在服务器上调用并在特定客户端上执行的方法。服务器可以使用该标记调用方法,并在特定的客户端上执行。方法名称可以任意命名。
[ServerCallback]:用于指示方法只在服务器上执行,不在客户端上执行。该标记用于在服务器上定义特定逻辑或处理服务器端的事件。方法名称可以任意命名。
[Command]、[ClientRpc] 和 [TargetRpc] 方法必须在继承自 NetworkBehaviour 的脚本中使用,并且需要正确配置网络连接和同步设置。
[Command] 方法只能从客户端调用,并且只在服务器上执行。
[ClientRpc] 方法只能在服务器上调用,并在所有连接的客户端上执行。
[TargetRpc] 方法只能在服务器上调用,并且只在特定的客户端上执行。
[ServerCallback] 方法只在服务器上执行,不在客户端上执行。
[Command] 标记的方法名称必须以 “Cmd” 开头。
[ClientRpc]、[TargetRpc] 和 [ServerCallback] 标记的方法名称可以任意命名。