网络游戏制作---坦克大战(2)

一、动态生成坦克

1、首先将Tank预设放置到Resources文件夹下

2、进入房间后生成坦克,主要用到的函数为:Quaternion.identity表示无旋转

  float pos = Random.Range(-100.0f, 100.0f);
  PhotonNetwork.Instantiate("Tank", new Vector3(pos, 20.0f, pos), Quaternion.identity, 0)

完整代码:

    void OnJoinedRoom()
    {
        Debug.Log("Enter Room");
        //调用生成坦克函数
        CreateTank();
    }

    //生成坦克的函数
    void CreateTank()
    {
        float pos = Random.Range(-100.0f, 100.0f);
        PhotonNetwork.Instantiate("Tank", new Vector3(pos, 20.0f, pos), Quaternion.identity, 0);
    }

 

二、动态生成坦克后摄像机跟随会产生问题

       怎样实现摄像机动态跟随?

       整体的思路是通过Photon View组件的isMine属性判断生成的预设是本地的还是远程网络的

//使用SmoothFollow脚本前添加命名空间
using UnityStandardAssets.Utility;

    //声明PhotonView组件的变量
    private PhotonView pv = null;
    //主摄像机追踪的CamPivot游戏对象
    public Transform camPivot;

    //分配PhotonView组件
    pv = GetComponent<PhotonView>();

          
    //如果PhotonView为自己本地生成的坦克
    if (pv.isMine)
    {
        //为主摄像机中的SmoothFollow脚本设置追踪对象
       Camera.main.GetComponent<SmoothFollow>().target = camPivot;
     }

 

三、控制自己的坦克

  //如果PhotonView为自己本地生成的坦克
  if (pv.isMine)
  {
     //为主摄像机中的SmoothFollow脚本设置追踪对象
     Camera.main.GetComponent<SmoothFollow>().target = camPivot;

     //将Rigidbody的重心设置为较低的值
     rbody.centerOfMass = new Vector3(0.0f, -0.5f, 0.0f);
   }
   else
   {
       //如果是远程玩家的坦克,则设置使其不受物理力的影响
       rbody.isKinematic = true;
   }

 

四、平滑移动和旋转处理

    PhotonView组件传输的周期性导致坦克的移动不够平滑,通过OnPhotonSerializeView回调函数构建数据通信部分逻辑,

    实现游戏中坦克位置和旋转信息的网络同步。

    //声明坦克位置和旋转信息的变量并设置初始值
    private Vector3 currPos = Vector3.zero;
    private Quaternion currRot = Quaternion.identity;

void Awake()
{
       //设置传送数据类型
       pv.synchronization = ViewSynchronization.UnreliableOnChange;

       //将PhotonView组件的Observed属性设置为TankMove脚本
       pv.ObservedComponents[0] = this;

         //设置坦克位置和旋转值的初始值
         currPos = tr.position;
         currRot = tr.rotation;

    }
    

  OnPhotonSerializeView函数

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        //传送本地坦克的位置和炮塔旋转信息
        if (stream.isWriting)
        {
            stream.SendNext(tr.position);
            stream.SendNext(tr.rotation);
        }
        else   //接受远程玩家坦克的位置和炮塔旋转信息
        {
            currPos = (Vector3)stream.ReceiveNext();
            currRot = (Quaternion)stream.ReceiveNext();
        }
    }

接下来是Update函数

    void Update()
    {
        //如果是自己本地的坦克则直接移动/旋转
        if (pv.isMine)
        {
            h = Input.GetAxis("Horizontal");
            v = Input.GetAxis("Vertical");

            //旋转和移动处理
            tr.Rotate(Vector3.up * rotSpeed * h * Time.deltaTime);
            tr.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
        }
        else  //如果是远程玩家的坦克
        {
            //将远程玩家的坦克平滑移动到目标位置
            tr.position = Vector3.Lerp(tr.position, currPos, Time.deltaTime * 3.0f);
            //将远程玩家的坦克炮塔平滑旋转到一定角度
            tr.rotation = Quaternion.Slerp(tr.rotation, currRot, Time.deltaTime * 3.0f);
        }
    }

位置:Vector3.Lerp()函数实现了从tr.position移动到currPos的位置,最后返回的是Vector3的位置坐标信息。

旋转:Quaternion.Slerp()函数实现了从tr.rotation旋转到CurrRot的位置,最后返回的是旋转的四元组信息。

 整体代码:

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
//使用SmoothFollow脚本前添加命名空间
using UnityStandardAssets.Utility;

public class TankMove : MonoBehaviour
{
    //表示坦克移动和旋转的变量
    public float moveSpeed = 20.0f;
    public float rotSpeed = 50.0f;

    //要分配各组件的变量
    private Rigidbody rbody;
    private Transform tr;

    //保存键盘输入值的变量
    private float h, v;

    //声明PhotonView组件的变量
    private PhotonView pv = null;
    //主摄像机追踪的CamPivot游戏对象
    public Transform camPivot;

    //声明坦克位置和旋转信息的变量并设置初始值
    private Vector3 currPos = Vector3.zero;
    private Quaternion currRot = Quaternion.identity;

    // Start is called before the first frame update
    void Awake()
    {
        //初始化各组件
        rbody = GetComponent<Rigidbody>();
        tr = GetComponent<Transform>();
        //将Rigidbody的重心设置为较低的值
        rbody.centerOfMass = new Vector3(0.0f, -0.5f, 0.0f);

        //分配PhotonView组件
        pv = GetComponent<PhotonView>();

        //设置传送数据类型
        pv.synchronization = ViewSynchronization.UnreliableOnChange;

        //将PhotonView组件的Observed属性设置为TankMove脚本
        pv.ObservedComponents[0] = this;

        //如果PhotonView为自己本地生成的坦克
        if (pv.isMine)
        {
            //为主摄像机中的SmoothFollow脚本设置追踪对象
            Camera.main.GetComponent<SmoothFollow>().target = camPivot;

            //将Rigidbody的重心设置为较低的值
            rbody.centerOfMass = new Vector3(0.0f, -0.5f, 0.0f);
        }
        else
        {
            //如果是远程玩家的坦克,则设置使其不受物理力的影响
            rbody.isKinematic = true;
        }

        //设置坦克位置和旋转值的初始值         
        currPos = tr.position;
        currRot = tr.rotation;
    }

    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        //传送本地坦克的位置和炮塔旋转信息
        if (stream.isWriting)
        {
            stream.SendNext(tr.position);
            stream.SendNext(tr.rotation);
        }
        else   //接受远程玩家坦克的位置和炮塔旋转信息
        {
            currPos = (Vector3)stream.ReceiveNext();
            currRot = (Quaternion)stream.ReceiveNext();
        }
    }

    // Update is called once per frame
    void Update()
    {
        //如果是自己本地的坦克则直接移动/旋转
        if (pv.isMine)
        {
            h = Input.GetAxis("Horizontal");
            v = Input.GetAxis("Vertical");

            //旋转和移动处理
            tr.Rotate(Vector3.up * rotSpeed * h * Time.deltaTime);
            tr.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
        }
        else  //如果是远程玩家的坦克
        {
            //将远程玩家的坦克平滑移动到目标位置
            tr.position = Vector3.Lerp(tr.position, currPos, Time.deltaTime * 3.0f);
            //将远程玩家的坦克炮塔平滑旋转到一定角度
            tr.rotation = Quaternion.Slerp(tr.rotation, currRot, Time.deltaTime * 3.0f);
        }
    }
}

 

五、同步炮塔和炮身

向Turrent和Cannon添加PhotonView组件,同时修改TurrentCtrl和CannonCtrl脚本,这个过程主要是保证玩家只能移动自己坦克的炮塔和炮身。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class TurretCtrl : MonoBehaviour
{
    private Transform tr;
    //保存光线击中地面的位置的变量
    private RaycastHit hit;

    //炮塔的旋转速度
    public float rotSpeed = 5.0f;

    //PhotonView组件
    private PhotonView pv = null;
    //保存远程网络坦克炮塔旋转值的变量
    private Quaternion currRot = Quaternion.identity;

    // Start is called before the first frame update
    void Awake()
    {
        tr = GetComponent<Transform>();
        pv = GetComponent<PhotonView>();

        //将Photon View的Observed属性设置为当前脚本
        pv.ObservedComponents[0] = this;
        //设置Photon View同步属性
        pv.synchronization = ViewSynchronization.UnreliableOnChange;

        //初始化旋转值
        currRot = tr.localRotation;
    }

    // Update is called once per frame
    void Update()
    {
        //如果是本地生成的坦克
        if (pv.isMine)
        {
            //通过主摄像机生成向鼠标光标指示位置发射的射线
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            //在场景视图中以绿色光线表示射线
            Debug.DrawRay(ray.origin, ray.direction * 100.0f, Color.green);

            if (Physics.Raycast(ray, out hit, Mathf.Infinity, 1 << 8))
            {
                //将射线击中的位置转换为本地坐标
                Vector3 relative = tr.InverseTransformPoint(hit.point);
                //用反正切函数Atan2计算炮塔要旋转的角度
                float angle = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;
                //以rotSpeed变量作为炮塔旋转角度
                tr.Rotate(0, angle * Time.deltaTime * rotSpeed, 0);
            }
        }
        else   //如果是远程网络玩家的坦克
        {
            //从当前位置平滑移动到目标角度
            tr.localRotation = Quaternion.Slerp(tr.localRotation, currRot, Time.deltaTime * 3.0f);
        }
    }

    //收发数据回调函数
    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            stream.SendNext(tr.localRotation);
        }
        else
        {
            currRot = (Quaternion)stream.ReceiveNext();
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class CannonCtrl : MonoBehaviour
{
    private Transform tr;
    public float rotSpeed = 100.0f;

    //PhotonView组件
    private PhotonView pv = null;
    //保存远程网络坦克炮身旋转角度的变量
    private Quaternion currRot = Quaternion.identity;

    // Start is called before the first frame update
    void Awake()
    {
        tr = GetComponent<Transform>();
        pv = GetComponent<PhotonView>();

        //将Photon View的Observed属性设置为当前脚本
        pv.ObservedComponents[0] = this;
        //设置Photon View的同步属性
        pv.synchronization = ViewSynchronization.UnreliableOnChange;

        //初始化旋转值
        currRot = tr.localRotation;
    }

    // Update is called once per frame
    void Update()
    {
        if (pv.isMine)
        {
            float angle = -Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * rotSpeed;
            tr.Rotate(angle, 0, 0);
        }
        else
        {
            //从当前位置平滑旋转到目标角度
            tr.localRotation = Quaternion.Slerp(tr.localRotation, currRot, Time.deltaTime * 3.0f);
        }
    }

    //收发数据回调函数
    void OnPhotonSerializeView(PhotonStream stream, PhotonMessageInfo info)
    {
        if (stream.isWriting)
        {
            stream.SendNext(tr.localRotation);
        }
        else
        {
            currRot = (Quaternion)stream.ReceiveNext();
        }
    }
}

 

posted @ 2019-06-24 21:21  彩色的梦  阅读(337)  评论(0编辑  收藏  举报