zoukankan      html  css  js  c++  java
  • 网络游戏制作---坦克大战(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();
            }
        }
    }
  • 相关阅读:
    LinqPad 1.31 提供下载
    LINQ to SQL更新数据库操作
    电脑开机进入桌面很慢的解决办法
    VS2008的Linq更新数据就那么费劲
    将图片进行base64编码,并将接受编码后的图片转换为实际图片
    C# 2.0 之 static class
    测试杀毒软件的性能代码
    VS2008 Ajax Toolkit 控件(修正后转载)
    C#发送邮件程序 及测试邮件服务器
    asp.net中的<%%>形式的详细用法实例讲解
  • 原文地址:https://www.cnblogs.com/Optimism/p/11079566.html
Copyright © 2011-2022 走看看