UNet系统初次使用——联网Boxing游戏(1)
UNet系统初次使用——联网Boxing游戏(2)
从这篇开始,便详细介绍player的实现。
player组件的uml图如下
我们便来依次讲解如何实现。
操作规则
- W —— 前进
- S —— 后退
- A —— 左移
- D —— 右移
- 鼠标左键 —— 左拳
- 鼠标邮件 —— 右拳
- 空格 —— 防御
- 鼠标移动 —— 视角转动
五、player移动 (PlayerMovement)
(1)
首先给player挂载Character Controller。
然后新建一个PlayerMovement的C#代码,将其挂载到player上。
PlayerMovement主要解决的是前后左右移动和视角转动的功能。
(2)
两个变量记录移动速度和旋转速度。
public float moveSpeed;
public float mouseTurnSpeed;
引用挂载的CharcterController组件
private CharacterController characterController;
void Start () {
characterController = GetComponent<CharacterController>();
}
两个属性MoveDirection和RotationAmount
public Vector3 MoveDirection
{
get
{
if (!isLocalPlayer)
{
return syncDirection;
}
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 direction = Mathf.Abs(v) >= Mathf.Abs(h) ? transform.forward * v : transform.right * h;
CmdSetDirection(direction);
return direction;
}
}
public float RotationAmount
{
get
{
float rotationAmount = Input.GetAxis("Mouse X");
return rotationAmount;
}
}
然后在Fixedupdate中实现移动和转向。使用isLocalPlayer确保只有本地player可以被操控。
private void FixedUpdate()
{
if (!isLocalPlayer)
{
return;
}
Move(MoveDirection);
Turn(RotationAmount);
}
void Move(Vector3 direction)
{
characterController.Move(direction * moveSpeed * Time.deltaTime);
}
void Turn(float rotationAmount)
{
transform.Rotate(transform.up, rotationAmount * mouseTurnSpeed * Time.deltaTime);
}
这样我们基本实现了人物在本地的移动和旋转。但是如何到同步到服务器和其他的客户端呢。
(3)
为了解决这个问题,我们给Player挂载上NetworkIdentity,NetworkTransform这两个组件, 并且修改PlayerMovement,让其继承NetworkBehaviour类。
public class PlayerMovement : NetworkBehaviour
当你想挂载继承了NetworkBehavier类的代码,NetworkIdentity是必须的。
然后NetworkTransform可以不断同步人物的Transform给服务器,然后由服务器同步给各个客户端,于是就可以使得其他客户端的人物跟本地一样移动和旋转了。
但是我们移动的时候,前后左右需要有不同的动画。所以我们用一个同步变量来记录我们的方向,并且同步给服务器和各个客户端。
[SyncVar]
private Vector3 syncDirection;
[Command]
private void CmdSetDirection(Vector3 direction)
{
syncDirection = direction;
}
这样基本实现了人物的移动。
六、player血量
首先我们构建两个血条,分别是player和敌人的血条。
完成之后给两个Slider分别加上LocalPlayerHP和OnlinePlayerHP两个tag。
因为player是动态加载的,所以需要通过tag来动态绑定属于自己的血条。
(1)
我们新建个PlayerHealth的代码,挂载到player上。
修改PlayerHealth继承NetworkBehaviour。
maxHealthy记录最大血量
同步变量healthy记录并同步当前血量
public float maxHealthy;
[SyncVar (hook = "HealthyChange")]
public float healthy;
在服务器修改当前血量,然后同步给各个客户端。当某个人物被打倒的时候,实际上在两个客户端和服务器上都达到了,如果三个地方的人物都扣血,那么实际上总共扣了3次,这是不合理的。所以我们规定只有在服务器才能修改血量。
public void TakeDamage(float damageAmount)
{
if (!isServer)
{
return;
}
healthy -= damageAmount;
}
private void HealthyChange(float healthy)
{
this.healthy = healthy;
HpSlider.value = healthy;
if (isLocalPlayer && healthy <= 30)
{
blood.gameObject.SetActive(true);
}
}
根据isLocalPlayer绑定相应的血条。
private Slider HpSlider;
private void BindHPSlider()
{
if (isLocalPlayer)
{
HpSlider = GameObject.FindWithTag("LocPlayerHP").GetComponent<Slider>();
}
else
{
HpSlider = GameObject.FindWithTag("OnlinePlayerHP").GetComponent<Slider>();
}
}
添加低血量特效
public Image bloodPrefab;
private Image blood;
private void InstanceBlood()
{
if (!isLocalPlayer)
{
return;
}
blood = Instantiate(bloodPrefab);
blood.transform.parent = GameObject.FindWithTag("Canvas").transform;
blood.rectTransform.offsetMax = Vector2.zero;
blood.rectTransform.offsetMin = Vector2.zero;
blood.gameObject.SetActive(false);
blood.transform.SetAsFirstSibling();
}
在start中绑定Slider和低血量特效
public void Start()
{
BindHPSlider();
if (HpSlider == null)
{
Debug.Log("Canvas Load Fail");
}
InstanceBlood();
}