【Unity3D-Mirror多人坦克大战】坦克生成、移动和摄像机跟随(二)

目录

7.创建角色

8.在坦克上面创建血条

9.为游戏对象添加NetworkTransform

10.角色移动

11.摄像机相随

12.炮台移动

测试


7.创建角色

新建一个立方体Player,取消它的外形渲染,然后将位置放在(0,0,0),同时将预制体拉入场景中,成为Player的子物体,同时它的位置也设置为(0,0,0),最后将Mesh Renderer组件移除,这样就看不到网格轮廓
由于坦克物体太大,因此缩放一下
同时将Player的碰撞属性设置一下
因为坦克的一些部件是单独的,而转向过程中需要多个物体同时进行,所以要进行合体

8.在坦克上面创建血条

首先创建一个画布,将画布设置世界空间,即可以被摄像机看到
在画布下新建一个text,然后设置相关属性,表示坦克的名字
角色创建完毕后,将该物体保存成预制体,并在场景中删除

9.为游戏对象添加NetworkTransform

NetworkTransform是一个可以在客户端同步物体对象位置、旋转和缩放到服务器的组件,添加它时必须要加入NetworkIdentity,之后将信息从服务器同步到其余客户端

10.角色移动

在Player物体中添加Player脚本
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Mirror;
public class Player : NetworkBehaviour
{
    private Rigidbody rb;
    public float MoveSpeed = 8; //水平和垂直的速度
    
    private void Awake()
    {
        rb = transform.GetComponent<Rigidbody>();
    }

    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");
            moveDir.y = Input.GetAxisRaw("Vertical");
            Move(moveDir);
        }
    }
    void Move(Vector2 direction = default(Vector2))
    {
        if (direction != Vector2.zero) // 如果方向变量不是零向量
        {
            transform.rotation = Quaternion.LookRotation(new Vector3(direction.x, direction.y, 0)); // 调整方向
            //transform.forward是一个变量,它是根据当前方向计算出的方向变量
            Vector3 movementDir = transform.forward * MoveSpeed * Time.deltaTime; // 当前方向的单位向量 * 速度 * 时间 = 当前方向的偏移量  
            rb.MovePosition(rb.position + movementDir);
        }
    }
    
}
其中速度*Time.deltaTime保证在不同帧数下角色移动的速度是一样的,也就是与帧数无关。
知识补充:
(1)Update是在每次渲染新的一帧的时候才会调用,也就是说,这个函数的更新频率和设备的性能有关。在性能好的机器上可能fps60,差的可能小些。这会导致同一个游戏在不同的机器上效果不一致,有的快有的慢,因为Update的执行间隔不一样了。
(2)FixedUpdate是在固定的时间间隔执行,不受游戏帧率的影响。 Tick:在处理Rigidbody的时候最好用FixedUpdate

11.摄像机相随

新建CamFollow脚本,添加到GameScene场景的主摄像机上
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamFollow : MonoBehaviour
{
    public Transform target;
    public float distance = 10f;
    public float height = 5f;
    void Update()
    {
        if (!target) return; //没有获得玩家坐标就return
        
        //获取当前摄像机的旋转角度,返回一个四元数
        Quaternion currentRotation = Quaternion.Euler(0, transform.eulerAngles.y, 0);
        Vector3 pos = target.position; //得到玩家的坐标
        // 四元素*(0,0,1)=当前旋转方向上1单位的距离,再*距离=当前旋转方向上的距离(一个Vector)
        pos -= currentRotation * Vector3.forward * Mathf.Abs(distance);
        pos.y = target.position.y + Mathf.Abs(height); //在y方向上偏移一定的高度,相当于将摄像机的x和z轴定死
        transform.position = pos; // 将计算完后的pos赋值给摄像机
        
        transform.LookAt(target); // 当前摄像机指向玩家
        
        // 画蛇添足,对游戏效果没有影响,留坑
        //transform.position = target.position - transform.forward * Mathf.Abs(distance);
    }
}
由于Player是预制体,所以无法得到target的transform,因此需要在Player脚本中写一段代码,表示被创建时,将它的transform信息传给主摄像机的CamFollow脚本的target中即可,就实现了映射。
【坑点一】重写OnStartLocalPlayer无效,在Awake函数中有效,添加的代码如下:
Camera.main.GetComponent<CamFollow>().target = transform;
填坑不小心把Player中Networkidentity中弄成了强制隐藏,因为界面上会有Mirror的标志,所以设置成隐藏,没想到这里的隐藏是使它失效,应该设置成默认。
最后添加以下函数有效
public override void OnStartLocalPlayer()
{
    Camera.main.GetComponent<CamFollow>().target = transform;
}
如此可以实现摄像机跟随

12.炮台移动

在Player.cs的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位置的坐标 - 玩家的坐标 = 相对玩家的交点坐标
}
RotateTurret(new Vector2(hitPos.x, hitPos.z)); // 因为炮台转向的是在一个平面上进行的,在unity中是x和z轴
在Player.cs添加如下函数:
void RotateTurret(Vector2 direction = default(Vector2)) // 防空出错
{
    if (direction == Vector2.zero) return;
    // 因为炮台的z轴要指向射线和平面的交点,所以用LookRotation函数,只需要用到forward参数即可(z轴指向forward),
    // 最后返回一个表示旋转的四元数,并得到它转换后欧拉角中绕y轴旋转的角度
    int newRotation = (int) (Quaternion.LookRotation(new Vector3(direction.x, 0, direction.y)).eulerAngles.y);  
    //因为旋转,实际上是绕y轴旋转的,所以将y轴旋转的角度封装成四元数赋值给炮台的rotation即可
    turret.rotation = Quaternion.Euler(-90, newRotation, 0); //因为炮台本来就旋转了-90,所以这里要不变
}
将炮台加入变量中

测试

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

~Lomiss~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值