【Unity3D-Mirror多人坦克大战】坦克和炮台移动的同步、右键视角的切换(三)

13.坦克和炮台的同步

【坑点二】Unet版本的hook函数在Mirror中是不一样的。
【填坑】
以前是[SyncVar(hook="onFunction")],且onFunction函数的参数为一个;
现在是[SyncVar(hook=nameof(onFunction))],其中onFunction的参数必须2个,第一个是旧值,第二个是新值。如onFunction(size oldValue, size newValue)。
由于NetworkTransform实现的是Player整体位置的同步,而炮台的旋转无法实现同步,所以需要另外重写,player.cs更新如下代码实现炮台移动同步
public class Player : NetworkBehaviour
{
    ……
    
    //将以下变量在面板中隐藏
    [HideInInspector] 
    //当turretRotation变量发生改变时调用OnTurretRotation函数
    [SyncVar(hook=nameof(RotateTurret))] public int turretRotation;
    
    ……

    private void FixedUpdate()
    {
        ……
        Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); // 返回从摄像机到当前鼠标点的一个射线
        Plane plane = new Plane(Vector3.up, Vector3.up); // 新建一个平面,因为玩家对象在(0,0,0),所以参数中的法线和经过的点都是(0,1,0)
        float distance = 0f;
        Vector3 hitPos = Vector3.zero;
        // 判断平面和射线是否相交,并将射线起点到交点的距离返回给distance
        if (plane.Raycast(ray, out distance)) 
        {
            hitPos = ray.GetPoint(distance) - transform.position; // 获得射线在distance位置的坐标 - 玩家的坐标 = 相对玩家的交点坐标
        }
        Vector2 direction = new Vector2(hitPos.x, hitPos.z);
        if (direction != Vector2.zero)
        {
            // 因为炮台的z轴要指向射线和平面的交点,所以用LookRotation函数,只需要用到forward参数即可(z轴指向forward),
            // 最后返回一个表示旋转的四元数,并得到它转换后欧拉角中绕y轴旋转的角度
            turretRotation = (int) (Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y)).eulerAngles.y); 
            turret.rotation = Quaternion.Euler(-90, turretRotation, 0);   // 客户端实现转向
            CmdRotateTurret(turretRotation); // 同步到服务器
        }
        
        ……
        
    }
    
    ……
    [Command]
    void CmdRotateTurret(int value)
    {
        turretRotation = value;  //在服务器上修改该值
    }
    
    void RotateTurret(int oldValue, int newValue)  // 得到在服务器上的值,然后运行函数,把结果同步到客户端
    {
        //因为炮台旋转,实际上是绕y轴旋转的,所以将y轴旋转的角度封装成四元数赋值给炮台的rotation即可
        turret.rotation = Quaternion.Euler(-90, newValue, 0); //因为炮台本来就旋转了90,所以这里要不变
    }
    
    ……
}
测试
而坦克移动的同步,由于没有把NetworkTransform的配置读明白,就自己写了实现了,在player.cs更新如下代码实现坦克移动同步
public class Player : NetworkBehaviour
{
    ……

    private void FixedUpdate()
    {
        if (!isLocalPlayer) return; // 如果不是本地角色,就跳过
        Vector2 moveDir;
        // 获取键盘输入的水平和纵向的值,分别是1和-1,代表两个方向,并传给moveDir这个方向变量
        if (Input.GetAxisRaw("Horizontal") == 0 && Input.GetAxisRaw("Vertical") == 0)
        {
            moveDir.x = 0;
            moveDir.y = 0;
        }
        else
        {
            moveDir.x = Input.GetAxisRaw("Horizontal");  //左返回-1,右返回1
            moveDir.y = Input.GetAxisRaw("Vertical");  //下返回-1,上返回1
            CmdMove(moveDir,camFollow.camTransform.eulerAngles.y); // 将客户端的移动方向和摄像机方向传给服务器端,以实现同步
        }
        ……
        
    }
    
    [Command] //让服务端运行的函数,函数开头加Cmd,运行结束后同步到所有客户端
    void CmdMove(Vector2 direction, float cam_y)
    {
        if (direction != Vector2.zero) // 如果方向变量不是零向量
        {
            transform.rotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y))*Quaternion.Euler(0,cam_y,0); // 调整方向,因为u3d的平面是x和z轴
            //transform.forward是一个变量,它是根据当前方向计算出的方向变量 
            Vector3 movementDir = transform.forward * MoveSpeed * Time.deltaTime; // 当前方向的单位向量 * 速度 * 时间 = 当前方向的偏移量  
            rb.MovePosition(rb.position + movementDir);
        }
    }
    ……
}
测试
【坑点三】 客户端 运行坦克时的速度比 服务端 运行坦克时慢,难道是网络传输延迟,但是局域网的延迟应该忽略不计。
【填坑】
当读了NetworkTransform的文档发现,只需要一步就可以实现上述操作
这样在代码中就不用写[command]了,代码如下
public class Player : NetworkBehaviour
{
    ……

    private void FixedUpdate()
    {
        if (!isLocalPlayer) return; // 如果不是本地角色,就跳过
        Vector2 moveDir;
        // 获取键盘输入的水平和纵向的值,分别是1和-1,代表两个方向,并传给moveDir这个方向变量
        if (Input.GetAxisRaw("Horizontal") == 0 && Input.GetAxisRaw("Vertical") == 0)
        {
            moveDir.x = 0;
            moveDir.y = 0;
        }
        else
        {
            moveDir.x = Input.GetAxisRaw("Horizontal");  //左返回-1,右返回1
            moveDir.y = Input.GetAxisRaw("Vertical");  //下返回-1,上返回1
            if (moveDir != Vector2.zero) // 如果方向变量不是零向量
            {
                // 调整方向,因为u3d的平面是x和z轴,然后再乘上摄像机方向的四元数
                transform.rotation = Quaternion.LookRotation(new Vector3(moveDir.x, 0, moveDir.y))
                //transform.forward是一个变量,它是根据当前方向计算出的方向变量 
                Vector3 movementDir = transform.forward * MoveSpeed * Time.deltaTime; // 当前方向的单位向量 * 速度 * 时间 = 当前方向的偏移量  
                rb.MovePosition(rb.position + movementDir);
            }
        }
……
        
    }
    
    ……
}
测试(可以看到这样的配置写法,客户端坦克的运行速度就和服务器一致了)

14.右键视角的切换

在CamFollow.cs添加如下代码:
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamFollow : MonoBehaviour
{
    ……
    private Vector3 m_camRot;
    
    [HideInInspector] public Transform camTransform;
    
    private void Awake()
    {
        camTransform = transform;
    }
    void Update()
    {
        ……
        if (Input.GetMouseButton(1))
        {
            m_camRot = camTransform.eulerAngles;
            float rh = Input.GetAxis("Mouse X"); // 返回鼠标在x轴上的偏移量,左负
            m_camRot.y += rh * 4;  // 乘上倍数,增加右键切换速度
            // 暂不支持纵向切换,因为视角会跑到地图外,留坑
            // float rv = Input.GetAxis("Mouse Y"); // 返回鼠标在y轴上的偏移量,下负
            // m_camRot.x -= rv * 4;   
            camTransform.eulerAngles = m_camRot;
        }
    }
}
在Player.cs添加如下代码:
public class Player : NetworkBehaviour
{
    ……

    private void FixedUpdate()
    {
        ……
        else
        {
            moveDir.x = Input.GetAxisRaw("Horizontal");  //左返回-1,右返回1
            moveDir.y = Input.GetAxisRaw("Vertical");  //下返回-1,上返回1
            if (moveDir != Vector2.zero) // 如果方向变量不是零向量
            {
                // 调整方向,因为u3d的平面是x和z轴,然后再乘上摄像机方向的四元数
                transform.rotation = Quaternion.LookRotation(new Vector3(moveDir.x, 0, moveDir.y))*Quaternion.Euler(0,camFollow.camTransform.eulerAngles.y,0); 
                //transform.forward是一个变量,它是根据当前方向计算出的方向变量 
                Vector3 movementDir = transform.forward * MoveSpeed * Time.deltaTime; // 当前方向的单位向量 * 速度 * 时间 = 当前方向的偏移量  
                rb.MovePosition(rb.position + movementDir);
            }
        }
        ……
        
    }
    
    ……
}
通过鼠标右键的监听,改变摄像机的旋转角度后,坦克的方向没有变,所以通过如下代码可以让坦克的方向随摄像机一起改变:
transform.rotation = Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y))*Quaternion.Euler(0,camFollow.camTransform.eulerAngles.y,0);
主要是在之前代码的后面乘上了Quaternion.Euler(0,camFollow.camTransform.eulerAngles.y,0),得到摄像机在y轴上旋转的角度,然后再把坦克在y轴上旋转该角度。

测试

  • 0
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

~Lomiss~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值