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


    这个游戏可能有点大,我们一步步来实现。

    一、模型的导入和坦克的移动逻辑

    首先给坦克模型添加Rigidbody组件,设置mass=2000;添加Box Collider组件,调整触发器的大小。

    编写模型移动的脚本:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TankMove : MonoBehaviour
    {
        //表示坦克移动和旋转的变量
        public float moveSpeed = 20.0f;
        public float rotSpeed = 50.0f;
    
        //要分配各组件的变量
        private Rigidbody rbody;
        private Transform tr;
    
        //保存键盘输入值的变量
        private float h, v;
    
        // Start is called before the first frame update
        void Start()
        {
            //初始化各组件
            rbody = GetComponent<Rigidbody>();
            tr = GetComponent<Transform>();
            //将Rigidbody的重心设置为较低的值
            rbody.centerOfMass = new Vector3(0.0f, -0.5f, 0.0f);
        }
    
        // Update is called once per frame
        void Update()
        {
            h = Input.GetAxis("Horizontal");
            v = Input.GetAxis("Vertical");
    
            //旋转和移动处理
            tr.Rotate(Vector3.up * rotSpeed * h * Time.deltaTime);
            tr.Translate(Vector3.forward * v * moveSpeed * Time.deltaTime);
        }
    }

    以上脚本的编写和普通游戏物体的移动相似,通过获取X和Y轴进行旋转和移动。

    ***这里要注意一点是坦克的重心相对较低,较低重心的方式为:

    rbody.centerOfMass = new Vector3(0.0f, -0.5f, 0.0f);

    将获取到的刚体组件进行调用centerOfMass函数进行实现。

    二、实现坦克的履带动画

    整体思路我们是通过改变履带纹理的偏移量实现履带旋转。

    纹理的理解是如果现在有一个物体,给它增加一个材质(Matiral),其实这个材质就是一个纹理,我们可以设置其的shader方式,包括纹理贴图Diffuse、Normal map等

    具体的代码:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TrackAnim : MonoBehaviour
    {
        //纹理旋转速度
        private float scrollSpeed = 1000.0f;
        private Renderer _renderer;
    
        // Start is called before the first frame update
        void Start()
        {
            _renderer = GetComponent<Renderer>();
        }
    
        // Update is called once per frame
        void Update()
        {
            var offset = Time.time * scrollSpeed * Input.GetAxisRaw("Vertical");
    
            //更改默认纹理的Y偏移量值
            _renderer.material.SetTextureOffset("_MainTex", new Vector2(0, offset));
            //更改常规纹理的Y偏移量值
            _renderer.material.SetTextureOffset("_BumpMap", new Vector2(0, offset));
        }
    }

    这里需要说明的是Renderer是渲染组件,如果需要对其材质改变,需要先获取渲染组件,在调用材质,再设置材质的偏移量。

    这里的所有操作都是在update函数中进行,而时间的改变主要在于Time.time:其表示总共花费的时间。

    这里的函数重点再说一句:

    void SetTextureOffset(string propertyName, Vecter2 offset)

    纹理属性名称“_MainTex”--Diffuse类型、“_BimpMap”--Normal map类型、"_Cube"--Cubemap类型。

    单词:Renderer---渲染、offset---偏移量。

    --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

    三、摄像机追随

    主要用到是Unity内置的资源包SmoothFollow-------路径Standard Assets/Utility/SmoothFollow

    将脚本SmoothFollow添加到Main Camera上,并且设置追随目标(Target)

    四、旋转炮塔

     炮塔旋转这一部分可能有点复杂,我们慢慢来看。

    我们主要根据鼠标的旋转方向进行旋转,如何实现这个过程:

    1、首先创建一条又摄像机到鼠标位置的一条射线

     //通过主摄像机生成向鼠标光标指示位置发射的射线
     Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

    2、接下来对射线进行处理

    先通过下面函数进行判断,具体而言ray是我们生成的射线,hit是碰撞体信息,Infinity是射线距离,1<<8是过滤层

    ***1<<8这个参数相对较为重要,它决定了射线碰撞的对象是第8层,我们将Terrain设置为了第8层,碰撞对象就是Terrain。

    Physics.Raycast(ray, out hit, Mathf.Infinity, 1 << 8)

    3、将射线的位置转化为本地坐标

      //将射线击中的位置转换为本地坐标
      Vector3 relative = tr.InverseTransformPoint(hit.point);

    4、根据本地坐标计算炮塔旋转的角度

      //用反正切函数Atan2计算炮塔要旋转的角度
      float angle = Mathf.Atan2(relative.x, relative.z) * Mathf.Rad2Deg;

     上面是对x,z的坐标进行了反正切运算,求出一个弧度值,在乘以Mathf.Rad2Deg转化为了角度值。

    5、对炮塔进行旋转

       //以rotSpeed变量作为炮塔旋转角度
       tr.Rotate(0, angle * Time.deltaTime * rotSpeed, 0);

    整体代码:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class TurretCtrl : MonoBehaviour
    {
        private Transform tr;
        //保存光线击中地面的位置的变量
        private RaycastHit hit;
    
        //炮塔的旋转速度
        public float rotSpeed = 5.0f;
    
        // Start is called before the first frame update
        void Start()
        {
            tr = GetComponent<Transform>();
        }
    
        // Update is called once per frame
        void Update()
        {
            //通过主摄像机生成向鼠标光标指示位置发射的射线
            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);
            }
        }
    }

    将脚本添加到Turret上,就实现了移动鼠标光标进行炮塔旋转。

    五、调整炮身角度

    使用鼠标滚轮实现炮弹的发射角度,就是上下移动

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class CannonCtrl : MonoBehaviour
    {
        private Transform tr;
        public float rotSpeed = 100.0f;
    
        // Start is called before the first frame update
        void Start()
        {
            tr = GetComponent<Transform>();
        }
    
        // Update is called once per frame
        void Update()
        {
            float angle = -Input.GetAxis("Mouse ScrollWheel") * Time.deltaTime * rotSpeed;
            tr.Rotate(angle, 0, 0);
        }
    }

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------

    六、建立炮弹预设和发射逻辑

    1、建立炮弹预设

      1) 创建一个空对象,添加Capsule Collider组件和Rigidbody组件,

            另外还需要添加一个Trail Renderer组件,主要实现炮弹发射后的视觉效果,其需要添加一个纹理。

      2)添加脚本,实现子弹的向前发射

    //炮弹速度
    public float speed = 6000.0f;
    
    GetComponent<Rigidbody>().AddForce(transform.forward * speed);

      3)完成预设之后,我们将其放置在Resources文件夹下,用以脚本中调动预设资源。

    2、设置炮弹的起始位置

           Tank--->Turrent(炮台)--->Cannon(大炮)--->FirePos,在Cannon下创建空子对象FirePos,将其移动到合适的位置。

    3、点击鼠标左键实现发射子弹

      1)定义炮弹预设和炮弹初始点的变量

        //炮弹预设
        public GameObject cannon = null;
    
        //炮弹发射初始点
        public Transform firePos;

      2)加载预设资源到变量

        void Awake()
        {
            //加载Resources文件夹中的Cannon预设
            cannon = (GameObject)Resources.Load("Cannon");
        }

      3)点击鼠标左键实现发射子弹

        void Update()
        {
            //点击鼠标左键时发射逻辑
            if (Input.GetMouseButtonDown(0))
            {
                Fire();
            }
        }
    
        void Fire()
        {
            Instantiate(cannon, firePos.position, firePos.rotation);
        }
    }

      克隆,首先应该克隆预设、克隆发射位置、克隆旋转。

    整体代码

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FireCannon : MonoBehaviour
    {
        //炮弹预设
        public GameObject cannon = null;
    
        //炮弹发射初始点
        public Transform firePos;
    
        void Awake()
        {
            //加载Resources文件夹中的Cannon预设
            cannon = (GameObject)Resources.Load("Cannon");
        }
    
        void Update()
        {
            //点击鼠标左键时发射逻辑
            if (Input.GetMouseButtonDown(0))
            {
                Fire();
            }
        }
    void Fire() { Instantiate(cannon, firePos.position, firePos.rotation); } }

    4、子弹和物体发生碰撞时产生爆炸效果

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class Cannon : MonoBehaviour
    {
        //炮弹速度
        public float speed = 6000.0f;
    
        //爆炸效果预设
        public GameObject expEffect;
    
        private CapsuleCollider _collider;
        private Rigidbody _rigidbody;
    
        // Start is called before the first frame update
        void Start()
        {
            _collider = GetComponent<CapsuleCollider>();
            _rigidbody = GetComponent<Rigidbody>();
    
            GetComponent<Rigidbody>().AddForce(transform.forward * speed);
    
            //3秒后执行自动爆炸的协程函数
            StartCoroutine(this.ExplosionCannon(3.0f));
        }
    
        void OnTriggerEnter()
        {
            //撞击地面或坦克时立即爆炸
            StartCoroutine(this.ExplosionCannon(0.0f));
        }
    
        IEnumerator ExplosionCannon(float tm)
        {
            yield return new WaitForSeconds(tm);
    
            //禁用Collider组件
            _collider.enabled = false;
            //无需再受物理引擎影响
            _rigidbody.isKinematic = true;
    
            //动态生成爆炸预设
            GameObject obj = (GameObject)Instantiate(expEffect, transform.position, Quaternion.identity);
    
            Destroy(obj, 1.0f);
    
            //Trail Renderer消失并等待一段时间后删除炮弹
            Destroy(this.gameObject, 1.0f);
        }
    
        // Update is called once per frame
        void Update()
        {
            
        }
    }

    七、炮弹发射音效

    首先为Tank添加AudioSource组件

    具体过程:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class FireCannon : MonoBehaviour
    {
        //炮弹预设
        private GameObject cannon = null;
    
        //炮弹发射声音
        private AudioClip fireSfx = null;
        //AudioSource组件
        private AudioSource sfx = null;
    
        //炮弹发射初始点
        public Transform firePos;
    
        void Awake()
        {
            //加载Resources文件夹中的Cannon预设
            cannon = (GameObject)Resources.Load("Cannon");
    
            //从Resources文件夹中加载炮弹声音文件
            fireSfx = Resources.Load<AudioClip>("CannonFire");
            //声明AudioSource变量
            sfx = GetComponent<AudioSource>();
        }
    
        // Start is called before the first frame update
        void Start()
        {
            
        }
    
        // Update is called once per frame
        void Update()
        {
            //点击鼠标左键时发射逻辑
            if (Input.GetMouseButtonDown(0))
            {
                Fire();
            }
        }
    
        void Fire()
        {
            sfx.PlayOneShot(fireSfx, 1.0f);
            Instantiate(cannon, firePos.position, firePos.rotation);
        }
    }

    -----------------------------------------------------------------------------------------------------------------------------------------------------------------------

    八、安装Photon Unity Networking插件

    1、在资源商店搜索PUN---Photon Unity Networking Free,下载安装

    2、需要在网站www.exitgames.com注册会员得到Application ID

    3、下载好PUN后,将AppId填入,选择地区,协议选择TCP协议稳定一点

    4、选择Auto-Join Lobby(自动加入大厅)

    单词:Photon(光子)、Lobby(游戏大厅)

    这里先介绍一下网络游戏的基本过程:

    玩家--->首先连接光子云(Photon Cloud)---->连接成功后会进入到游戏大厅(Lobby)--->接着光子云创建游戏房间(room)--->玩家进入房间就可以开始游戏了

    这里需要说明的是第一个创建房间的玩家会成为主客户端,如果玩家退出,其他玩家成为主客户端。

    8.1连接Photon Cloud

    层次视图创建空对象(PhotonInit)

    主要的代码如下:

        public string version = "v1.0";
    
        void Awake()
        {
            //连接Photo Cloud
            PhotonNetwork.ConnectUsingSettings(version);
        }
    PhotonNetwork.ConnectUsingSettings(),通过这个函数就可连接到光子云,参数是版本号,实现同一版本号的玩家进行游戏。

    完整代码:
        public string version = "v1.0";
    
        void Awake()
        {
            //连接Photo Cloud
            PhotonNetwork.ConnectUsingSettings(version);
        }
    
        //正常连接Photo Cloud并进入大厅后调用回调函数
        void OnJoinedLobby()
        {
            Debug.Log("Entered Lobby!");
        }

    8.2随机配对

    随机加入房间

       //正常连接Photo Cloud并进入大厅后调用回调函数
        void OnJoinedLobby()
        {
            Debug.Log("Entered Lobby!");
            PhotonNetwork.JoinRandomRoom();
        }

    随机连接房间失败

     //随机连接房间失败时调用回调函数
        void OnPhotonRandomJoinFailed()
        {
            Debug.Log("No rooms!");
        }

    8.3制作房间

       //随机连接房间失败时调用回调函数
        void OnPhotonRandomJoinFailed()
        {
            Debug.Log("No rooms!");
            //建立房间
            PhotonNetwork.CreateRoom("MyRoom");
        }
    
        void OnJoinedRoom()
        {
            Debug.Log("Enter Room");
        }

    完整代码:

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class PhotonInit : MonoBehaviour
    {
        //App版本信息
        public string version = "v1.0";
    
        void Awake()
        {
            //连接Photo Cloud
            PhotonNetwork.ConnectUsingSettings(version);
        }
    
        //正常连接Photo Cloud并进入大厅后调用回调函数
        void OnJoinedLobby()
        {
            Debug.Log("Entered Lobby!");
            PhotonNetwork.JoinRandomRoom();
        }
    
        //随机连接房间失败时调用回调函数
        void OnPhotonRandomJoinFailed()
        {
            Debug.Log("No rooms!");
            //建立房间
            PhotonNetwork.CreateRoom("MyRoom");
        }
    
        void OnJoinedRoom()
        {
            Debug.Log("Enter Room");
        }
    
        void OnGUI()
        {
            //画面左上角出现连接过程日志
            GUILayout.Label(PhotonNetwork.connectionStateDetailed.ToString());
        }
    }
  • 相关阅读:
    设计模式目录
    垃圾收集器
    598. Range Addition II
    Java中的四种引用
    垃圾回收算法
    645. Set Mismatch
    java 8中撤销永久代,引入元空间
    L2-013. 红色警报 (并查集)
    POJ 1127 Jack Straws (线段相交)
    L2-014. 列车调度 (DP)
  • 原文地址:https://www.cnblogs.com/Optimism/p/11042383.html
Copyright © 2020-2023  润新知