IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> 【学Unity】尝试自制一个坦克大战 -> 正文阅读

[游戏开发]【学Unity】尝试自制一个坦克大战


项目场景:

? ?Unity API查询:Unity - Scripting API:

? 素材 : 百度搜图片:地面纹理,找到喜欢的贴图导入Import资源即可

? ?自制一个坦克大战:需要的GameObjects有:Plane,Bullets,EnemyTank,PlaytTank,Wall空气墙

? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? Main Camera,Directional Light

?由于音频视频导入和UI制作和3D建模都不会,只能搞搞基础的了

以下是实机展示:

?

?

?

?

?

?空气墙的制作:先在Plane边缘建立一个墙,尽量将墙边缘贴近Plane后用v键使重合,然后Crtl+D复制三份,通过移动和旋转角度和Plane重合,最后关闭Inspector面板的Mesh Mender组件,游戏运行时就会生成

?己方坦克的要实现的功能有;移动,旋转,发射炮弹,创建一个PlayerControl.cs的脚本

移动,旋转通过虚拟轴GexAxis()获取正负方向Horizontal,Vertical

创建一个炮弹发射点的空对象

发射炮弹用transform.Find("目录")找到这个空对象

同时创建一个球体即炮弹的预设体

用圆柱体和长方体创建一个坦克并给它脚本和刚体,把坦克的GameObject全放在空对象中

再创建一个Bullets.cs的组件,负责控制炮弹的速度和方向

**创建敌方坦克**

因为要生成多个敌方坦克,需要将敌方坦克设置为一个预设体,颜色要和玩家的有所区分,还有坦克生成的最大数量,生成的时间间隔。首先他得是个刚体,其次得有计数器counter,计时器timer和时间间隔interval控制它生成的时间

需要创建一个TankControl.cs的脚本负责坦克的基本生成,移动等等。。。同时还需要刚体,计时器

最重要的还是随机生成,使用Random.Range()来随机生成的位置,角度等等。。

Example:对于旋转的角度来说,生成的是欧拉角,但要转成四元数,因为你要使用

Instantiate(GameObject,Vector3,Quaternion)这个方法;这时候我们就要使用Quaternion.Euler()这个方法来转化为四元数了。

? 既然生成了坦克,就不得不考虑模型重叠的问题了,添加刚体的情况下坦克容易被搞飞。

这时候就要用Physcis.CheckSphere(Vector3,float,int layerMask),球形检测,参数分别代表的意思是中心位置,半径,检测的Layer层,

第三个参数:layerMask涉及位或运算,由于我不会就简单介绍一下。(1 << 8),前面的“1 <<”是固定的,后面的8是只检测第八层,如果反过来只想检测除了八层的其它层就要加括号前加“~”号。

**设置敌方坦克参数**

综上我们已经设计好了坦克的生成,需要设置敌方坦克AI了。

对于敌方坦克,我们需要创建他们三种状态的方法,开火Fire(),朝玩家移动Move(),巡逻Patrol()

敌方坦克要攻击就得有炮弹,发射前先瞄准玩家的坦克,但首先判断自己前方的多少米是玩家还是自己的友军,用物理射线判断标签,给敌军加一个“Enemy”标签,玩家"Player"标签

private bool CheckForwardFriend(float dis)
? ? {
? ? ? ? //发射物理射线
? ? ? ? if(Physics.Raycast(firePoint.position,
? ? ? ? ? ? transform.forward ,out hit,dis))
? ? ? ? {
? ? ? ? ? ? if(hit.collider.transform.root.tag == "Enemy")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }

返回的是true,则不可以开火return,false则可以

同样,发射炮弹也需要时间间隔和计时器(计时器用完要归零)

if (timer > fireInterval)
? ? ? ? {
? ? ? ? ? ? //生成炮弹
? ? ? ? ? ? GameObject blt =?
? ? ? ? ? ? ? ? Instantiate(bullerPrefab,?
? ? ? ? ? ? ? ? firePoint.position, Quaternion.identity);
? ? ? ? ? ? //给炮弹一个速度
? ? ? ? ? ? blt.GetComponent<Rigidbody>().velocity =
? ? ? ? ? ? ? ? transform.forward * bltSpeed;
? ? ? ? ? ? blt.GetComponent<Bullets>().moveDir = transform.forward;
? ? ? ? ? ? Destroy(blt, 5f);
? ? ? ? ? ? //计时器归零
? ? ? ? ? ? timer = 0;

**小扩展**摄像机跟随

[Header("要跟随的目标")]
? ? public GameObject followTarget;

? ? [Header("摄像机跟随的速度")]
? ? public float moveSpeed = 0.1f;
? ? private Vector3 dir;
? ? private void Start()
? ? {
? ? ? ? //计算方向向量
? ? ? ? dir = followTarget.transform.position - transform.position ;
? ? }
? ? private void Update()
? ? {
? ? ? ? //计算摄像机跟随移动的向量
? ? ? ? transform.position = Vector3.Lerp(transform.position,followTarget.transform.position -dir,moveSpeed) ;
? ? }

**小扩展2:炮弹的方向**

[HideInInspector]
? ? //炮弹的飞行方向
? ? public Vector3 moveDir;
? ? [Header("炮弹飞行速度")]
? ? public float moveSpeed = 3f;

? ? private void Update()
? ? {
? ? ? ? transform.position += moveDir * moveSpeed * Time.deltaTime;
? ? }

以下是代码展示

一丶敌方AI坦克

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

public class EnemysTankControll : MonoBehaviour
{
? ? //实现坦克的三大功能:开火,转向敌人,巡逻
? ? [Header("炮弹预设体")]
? ? public GameObject bulletPrefab;
? ? [Header("敌方坦克移动速度")]
? ? public float moveSpeed = 3f;
? ? [Header("敌方坦克转身速度")]
? ? public float turnSpeed = 3f;
? ? [Header("炮弹发生点")]
? ? private Transform firePoint;

? ? //炮弹发射时间
? ? private float timer = 0;
? ? [Header("时间间隔")]
? ? public float timeInterval = 3f;

? ? //敌方坦克与玩家之间的向量
? ? private Vector3 dir;

? ? //敌方坦克与玩家之间的距离
? ? private float distance;

? ? //巡逻监视的时候Partol的y坐标
? ? private float yPartol = 0;

? ? [Header("炮弹飞行速度")]
? ? public float bltSpeed = 3f;

? ? private Transform playerTank;
? ? //射线碰撞检测器
? ? private RaycastHit hit;

? ? //创建的方法有:Awake():负责找到炮弹发射点,以及玩家坦克的组件
? ? /*Update():负责加上计时器,以及判断玩家和敌人的距离
? ? ?* ? ? ? ? 当达到某种距离时会执行相应的方法
? ? ?* ? ? ? ??
? ? ?* Rotate(Quaternion quaTarget):负责执行Lerp()插值函数从而旋转
? ? ?* RotateTo(Vector3 Pos) :负责将向量转化为四元数从而执行Rotate(Quaternion quaTarget)方法
? ? ?* RotateToPlayer() :负责计算玩家坐标减去本坐标的向量,将向量传给RotateTo()
? ? ?*?
? ? ?* CheckForwardFriend(float distance):负责判断前方是否是友军,用射线和标签来判断
? ? ?*?
? ? ?* Fire():开火,负责生成炮弹并速度,销毁
? ? ?* Move():负责转向玩家,并朝着玩家的方向移动,移动到某某米时就会转为Fire()
? ? ?* Partol():巡逻,负责先朝着一个方向移动,并在某时间范围内随机转向
? ? ?* ? ? ? ? ?Rotate(Quaternion.Euler(Vector3.up * yPartol));
? ? ?
? ? ?*/
? ? private void Awake()
? ? {
? ? ? ? firePoint = transform.Find("Top/Gun/FirePoint");
? ? ? ? //找到玩家坦克的组件
? ? ? ? playerTank = GameObject.FindWithTag("Player").transform;
? ? }
? ? private void Update()
? ? {
? ? ? ? distance = Vector3.Distance(transform.position, playerTank.position);
? ? ? ? timer += Time.deltaTime;
? ? ? ? if (distance < 15)
? ? ? ? {
? ? ? ? ? ? Fire();
? ? ? ? }
? ? ? ? if ( distance < 25)
? ? ? ? {
? ? ? ? ? ? Move();
? ? ? ? }
? ? ? ? else
? ? ? ? {
? ? ? ? ? ? Partol();
? ? ? ? }
? ? }

? ? /// <summary>
? ? /// 开火
? ? /// </summary>
? ? private void Fire()
? ? {
? ? ? ? if (!CheckForwardFriend(20))
? ? ? ? {
? ? ? ? ? ? //生成炮弹
? ? ? ? ? ? if (timer > timeInterval)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? GameObject blt = Instantiate(bulletPrefab,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? firePoint.position, Quaternion.identity);
? ? ? ? ? ? ? ? blt.GetComponent<MyBullets>().moveDir = transform.forward;
? ? ? ? ? ? ? ? blt.GetComponent<Rigidbody>().velocity = transform.forward * bltSpeed * Time.deltaTime;
? ? ? ? ? ? ? ? Destroy(blt, 4f);
? ? ? ? ? ? ? ? //计时器归零
? ? ? ? ? ? ? ? timer = 0;
? ? ? ? ? ? }
? ? ? ? }
? ? }
? ? private void Move()
? ? {
? ? ? ? //先转向玩家
? ? ? ? RotatoPlayer();
? ? ? ? //判断前方有没有友军
? ? ? ? if (CheckForwardFriend(10))
? ? ? ? {
? ? ? ? ? ? transform.position +=
? ? ? ? ? ? ? ? Vector3.forward * moveSpeed * Time.deltaTime;
? ? ? ? }
? ? }
? ? private bool CheckForwardFriend(float dis)
? ? {
? ? ? ? //是否有物体
? ? ? ? if (Physics.Raycast(firePoint.position, transform.forward, out hit, dis))
? ? ? ? {
? ? ? ? ? ? //是敌军还是友军
? ? ? ? ? ? if (hit.collider.transform.root.tag == "Enemy")
? ? ? ? ? ? {
? ? ? ? ? ? ? ? //说明是友军
? ? ? ? ? ? ? ? return true;
? ? ? ? ? ? }
? ? ? ? }
? ? ? ? return false;
? ? }
? ? /// <summary>
? ? /// 转向玩家
? ? /// </summary>
? ? private void RotatoPlayer()
? ? {
? ? ? ? dir = playerTank.position - transform.position;
? ? ? ? RotateTo(dir);
? ? }
? ? /// <summary>
? ? /// 巡逻
? ? /// </summary>
? ? private void Partol()
? ? {
? ? ? ? transform.position +=
? ? ? ? ? ? ? ? transform.forward * moveSpeed * Time.deltaTime;
? ? ? ? if (timer > timeInterval)
? ? ? ? {
? ? ? ? ? ? yPartol = Random.Range(0, 360);

? ? ? ? ? ? //计时器归零
? ? ? ? ? ? timer = 0;
? ? ? ? }
? ? ? ? //新生成一个角度来旋转
? ? ? ? Rotate(Quaternion.Euler(Vector3.up * yPartol));
? ? ? ? //Quaternion quaRotation = Quaternion.LookRotation
? ? ? ? // (new Vector3(0, yPartol, 0));

? ? ? ? // transform.rotation = Quaternion.Lerp(transform.rotation,
? ? ? ? // ? quaRotation, turnSpeed * Time.deltaTime);
? ? }
? ? private void Rotate(Quaternion quaTarget)
? ? {
? ? ? ? //再用插值函数
? ? ? ? transform.rotation = Quaternion.Lerp(transform.rotation,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? quaTarget, turnSpeed * Time.deltaTime);

? ? }
? ? private void RotateTo(Vector3 pos)
? ? {
? ? ? ? //先将方向向量转化为四元数
? ? ? ? Quaternion quaTarget = Quaternion.LookRotation(pos);
? ? ? ? Rotate(quaTarget);

? ? }
}

?

其次是敌方坦克管理(如生成)

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

public class MyEnemyTankManager : MonoBehaviour
{
? ? //创建敌方坦克的生成
? ? //以及敌方坦克的基本属性:随机生成的位置,旋转的角度
? ? //还需要一些限制:最大生成数量,定时生产,生成的时候不能和其它坦克重合

? ? [Header("敌方坦克的预设体")]
? ? public GameObject enemyTankPrefab;

? ? private float counter;
? ? [Range(30, 100)]
? ? public int maxEnemy = 50;

? ? private float timer = 0;
? ? [Header("生成的时间间隔")]
? ? public float timeInterval = 5f;

? ? private float radius = 5f;
? ? private void Start()
? ? {
? ? ? ??
? ? }

? ? private void Update()
? ? {
? ? ? ? timer += Time.deltaTime;
? ? ? ? if (timer > timeInterval)
? ? ? ? {
? ? ? ? ? ? if(counter<maxEnemy)
? ? ? ? ? ? {
? ? ? ? ? ? ? ? CreateTank();
? ? ? ? ? ? }
? ? ? ? ? ? timer = 0;
? ? ? ? }
? ? }
? ? /// <summary>
? ? /// 创建坦克
? ? /// </summary>
? ? private void CreateTank()
? ? {
? ? ? ? float x = 0, z = 0;
? ? ? ? int y = 0;
? ? ? ? Vector3 pos = Vector3.zero;
? ? ? ? do
? ? ? ? {
? ? ? ? ? ? x = Random.Range(-40f, 40f);
? ? ? ? ? ? z = Random.Range(-40f, 40f);
? ? ? ? ? ? pos = new Vector3(x, 0, z);

? ? ? ? ? ? y = Random.Range(0, 360);
? ? ? ? } while (!CanCreateTank(pos));
? ? ? ? ? ? //将欧拉角转化为四元数
? ? ? ? ? ? Quaternion quaTarget = Quaternion.Euler(new Vector3(0, y, 0));
? ? ? ? ? ? Instantiate(enemyTankPrefab, pos,quaTarget);
? ? ? ? ? ? counter++;
? ? }
? ? /// <summary>
? ? /// 用来表示这个位置可不可用的
? ? /// </summary>
? ? /// <param name="pos"></param>
? ? /// <returns></returns>
? ? private bool CanCreateTank(Vector3 pos)
? ? {
? ? ? ? //检测除了第八层以外其它碰撞体,如果有则返回false
? ? ? ? return !Physics.CheckSphere(pos, radius, ~(1 << 8));
? ? }
}

?

然后是:玩家的坦克(移动旋转发射)

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

public class MyTankMove : MonoBehaviour
{
? ? //实现坦克的旋转,移动,炮弹的发生
? ? [Header("玩家坦克移动速度")]
? ? public float moveSpeed = 3f;
? ? [Header("玩家坦克旋转速度")]
? ? public float turnSpeed = 3f;
? ? [Header("炮弹的预设体")]
? ? public GameObject bulletprefab;
? ? [Header("炮弹的飞行速度")]
? ? public float bltSpeed = 5f;

? ? private Transform firePoint;

? ? float hor, ver; //虚拟x,y轴
? ? bool fire; //是否开火
? ? private void Awake()
? ? {
? ? ? ? firePoint = transform.Find("Top/Gun/FirePoint");
? ? }

? ? private void Update()
? ? {
? ? ? ? //获取虚拟x,y轴
? ? ? ? hor = Input.GetAxis("Horizontal");
? ? ? ? ver = Input.GetAxis("Vertical");

? ? ? ? transform.position += transform.forward?
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? * ver * moveSpeed *Time.deltaTime;
? ? ? ? transform.eulerAngles += transform.up * hor * turnSpeed;

? ? ? ? fire = Input.GetButtonDown("Fire1");
? ? ? ? if (fire)
? ? ? ? {
? ? ? ? ? ? //生成炮弹
? ? ? ? ? ? GameObject blt = Instantiate(bulletprefab,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? firePoint.position, Quaternion.identity);
? ? ? ? ? ? //给炮弹一个速度
? ? ? ? ? ? blt.GetComponent<MyBullets>().moveDir = transform.forward;
? ? ? ? ? ? blt.GetComponent<Rigidbody>().velocity = transform.forward * bltSpeed;

? ? ? ? ? ? //定期销毁炮弹
? ? ? ? ? ? Destroy(blt, 4f);
? ? ? ? }
? ? }
}


以及炮弹方向设置

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

public class MyBullets : MonoBehaviour
{
? ? //用来控制子弹的飞行方向的.cs
? ? public Vector3 moveDir;

? ? public float moveSpeed =1f;

? ? private void Update()
? ? {
? ? ? ? transform.position += moveDir * Time.deltaTime * moveSpeed;
? ? }
}

控制镜头移动:

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

public class MyCameraMove : MonoBehaviour
{
? ? public Vector3 dir;

? ? public Transform followTarget;

? ? [Header("摄像机的移动速度")]
? ? public float moveSpeed = 0.1f;
? ? private void Start()
? ? {
? ? ? ? //方向向量
? ? ? ? dir = followTarget.position - transform.position;
? ? }
? ? private void Update()
? ? {
? ? ? ? //摄像机的跟随移动
? ? ? ? transform.position = Vector3.Lerp(transform.position,
? ? ? ? ? ? ? ? ? ? ? ? ? ? ? followTarget.position - dir, moveSpeed);
? ? }
}

当代码写完时,还需要将相应的脚本挂在相应的对象上

以及贴标签和设置层Layer,如下所示

设置空对象PlayerTank将坦克的所有对象放进去,空气墙四个Wall,以及空对象EnemyTankManager可以控制生成地方坦克

注意要给坦克添加子弹的预设体

给空对象添加敌方坦克的预设体

(不然999+个Error)Unity直接死机

!!注意!!

一定要给Plane对象加入Layer层Plane(第八层,要自己去设置)

不然也是999+个Error死机了


?

?

?

?

?


介绍下插值函数Lerp,生成函数Instantiate():

?百度上的定义:在离散数据的基础上补插连续函数,使得这条连续曲线通过全部给定的离散数据点。?[1]

插值是离散函数逼近的重要方法,利用它可通过函数在有限个点处的取值状况,估算出函数在其他点处的近似值

插值:用来填充图像变换时像素之间的空隙。

作用是可以使场景的,物体的移动平滑,常用两个类的插值函数有

①Vector3.Lerp()

public static?Vector3?Lerp(Vector3?a,?Vector3?b, float?t);

②Quaternion.Lerp()

public static?Quaternion?Lerp(Quaternion?a,?Quaternion?b, float?t);

最后一个参数是控制变化速度的,

对于Instantiate()

只介绍一种重载:即(GameObject,Vector3,Quaternion);

先尝试如何移动自己的坦克!

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

public class MyTankMove : MonoBehaviour
{
    //实现坦克的旋转,移动,炮弹的发生
    [Header("玩家坦克移动速度")]
    public float moveSpeed = 3f;
    [Header("玩家坦克旋转速度")]
    public float turnSpeed = 3f;
    [Header("炮弹的预设体")]
    public GameObject bulletprefab;
    [Header("炮弹的飞行速度")]
    public float bltSpeed = 5f;

    private Transform firePoint;

    float hor, ver; //虚拟x,y轴
    bool fire; //是否开火
    private void Awake()
    {
        firePoint = transform.Find("Top/Gun/FirePoint");
    }

    private void Update()
    {
        //获取虚拟x,y轴
        hor = Input.GetAxis("Horizontal");
        ver = Input.GetAxis("Vertical");

        transform.position += transform.forward 
                              * ver * moveSpeed *Time.deltaTime;
        transform.eulerAngles += transform.up * hor * turnSpeed;

        fire = Input.GetButtonDown("Fire1");
        if (fire)
        {
            //生成炮弹
            GameObject blt = Instantiate(bulletprefab,
                              firePoint.position, Quaternion.identity);
            //给炮弹一个速度
            blt.GetComponent<MyBullets>().moveDir = transform.forward;
            blt.GetComponent<Rigidbody>().velocity = transform.forward * bltSpeed;

            //定期销毁炮弹
            Destroy(blt, 4f);
        }
    }
}


常见的方法补充

把方向向量转化为四元数
? ? ? ? Quaternion targetQua = Quaternion.LookRotation(dir)

多少秒后销毁该游戏对象

? ? ? ? Destroy(blt, 5f);

实现坦克的旋转
? ? ? ? int y = Random.Range(0, 360); ? ? ? ?
将欧拉角转化为四元数
? ? ? ? //Quaternion.Euler(欧拉角)
? ? ? ? Quaternion qua =Quaternion.Euler( new Vector3(0, y, 0));

检测第八层(1 << 8)前面的~号相当于!号
? ? ? ? //检测除了8,9层以外的其它层~(1 << 8|1 <<9)
? ? ? ? //~(1 << 8 | 1 << 9);
? ? ? ? //~(1 << 8)表示抛出第八层
? ? ? ? //如果检测到其它膨胀体表示该点不能用
? ? ? ? //如果没检测到则返回true表示能用
? ? ? ?Physics.CheckSphere(pos, 5, ~(1 << 8))


Ending:

?

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-26 09:10:11  更:2021-11-26 09:12:09 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 22:28:09-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码