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笔记-12-练习项目武器模块-第二版 -> 正文阅读

[游戏开发]Unity笔记-12-练习项目武器模块-第二版

Unity笔记-12-练习项目武器模块-第二版

策划-武器模块

如果弹匣内装有子弹,可以发射;否则,等待更换弹匣;

发射子弹时,播放音效,动画,显示火花;

玩家的枪可以单发也可以连发;

玩家子弹

  1. 击中敌人后减少敌人HP,后续将加入根据击中敌人的位置而减少不同程度的HP
  2. 子弹飞行到目标点,销毁,并创建相应特效

敌人子弹

  1. 击中玩家后,玩家HP减少
  2. 子弹飞行到目标点,销毁,并创建相应特效
  3. 朝玩家头部发射,飞行速度较慢,方便玩家躲避

需求分析

创建脚本:玩家枪Gun,提供开火,更换弹匣功能

创建脚本:单发模式SingleGun,继承自Gun,根据玩家输入调用相应开火方式

创建脚本:连发模式AutomaticGun,继承自Gun,根据玩家输入调用相应开火方式

创建脚本:敌人枪EnemyGun,提供自动开火功能,无限弹药

——————————————————————————————————————

创建脚本:子弹Bullet,计算攻击的目标点,执行移动,创建接触特效;

玩家检测环境和敌人;敌人检测环境;

创建脚本:玩家子弹PlayerBullet,继承自Bullet,击中敌人后扣除HP

创建脚本:敌人子弹EnemyBullet,击中玩家后扣除HP

注:以上所有特效,声音相关暂不实现

脚本实现与解释说明

玩家部分

玩家状态

并提供玩家这一唯一对象的引用,以便其他所有对象都能获得玩家对象

提供受伤,死亡等方法

public class PlayerDemo : MonoBehaviour
{
    //提供当前的对象引用
    public static PlayerDemo Instance { get; private set; }
    public float HP;
    private void Start()
    {
        Instance = this;
        HP = 100;
    }
    /// <summary>
    /// 受伤
    /// </summary>
    /// <param name="attackNumber">伤害数字</param>
    public void Damage(int attackNumber)
    {
        HP -= attackNumber;
        if (HP <= 0)
        {
            Death();
        }
    }
    /// <summary>
    /// 死亡
    /// </summary>
    private void Death()
    {
        Destroy(this.gameObject);
    }
}

BoxMagezine-弹匣

弹匣类,提供弹匣容量,子弹总数,当前子弹数等数据,挂在枪模型对象的父空对象上

public class BoxMagezineDemo : MonoBehaviour
{
    /// <summary>
    /// 弹匣容量
    /// </summary>
    public int boxMagazine = 50;
    /// <summary>
    /// 当前弹匣内的子弹数
    /// </summary>
    public int currentBullet;
    /// <summary>
    /// 总子弹数
    /// </summary>
    public int allBullet;
}
GunAnimation-枪动画

枪动画类,提供枪的一系列动画播放功能

AnimationTool类,为动画与动画工具类,后续会说明,挂在枪模型的父空对象上

public class GunAnimation : MonoBehaviour
{
    /// <summary>
    /// 开火动画
    /// </summary>
    public string fireAnimation="Gunfire";
    /// <summary>
    /// 更换弹匣动画
    /// </summary>
    public string updateAmmoAnimation="GunUpdateAmmo";
    /// <summary>
    /// 缺少子弹动画
    /// </summary>
    public string lackBulletAnimation="GunLackBullet";
    /// <summary>
    /// 播放工具
    /// </summary>
    public AnimationTool action;

    private void Awake()
    {
        action = new AnimationTool(GetComponentInChildren<Animation>());
    }
}
BulletLight-子弹火光

枪口火光对象脚本,提供闪烁火光特效功能,挂在火光对象上,火光对象为枪模型父空对象的孩子,也就是枪模型对象的兄弟

public class BulletLightDemo : MonoBehaviour
{
    private GameObject bulletLight;
    private void Start()
    {
        bulletLight = GetResource();
        
    }
    /// <summary>
    /// 火光闪烁
    /// </summary>
    public void DisplayLight()
    {
        Destroy(Instantiate(bulletLight, transform.position, Quaternion.identity),0.20F);
    }
    private GameObject GetResource()
    {
        return Resources.Load<GameObject>("GunTest/Bullet/BulletLight");
    }
}
Gun-枪

玩家射击模式的父类,提供射击,填充弹匣等功能,注意:此脚本不挂在对象上,挂在对象上的是继承此类的射击模式:SingleGunAutomaticGun,射击模式脚本挂在枪模型对象的父空对象上

—————————————————————————————————————————————————————

射击

分以下两个个步骤:1.准备阶段:是否能够射击;2.创建子弹;

准备阶段:

  1. 弹匣里是否有子弹
  2. 当前是否在播放换弹匣动画
    /// <summary>
    /// 子弹准备
    /// </summary>
    /// <returns></returns>
    private bool Ready()
    {
        if (boxMagezine.currentBullet <= 0)
        {
            anim.action.Play(anim.lackBulletAnimation);//如果没有子弹则播放动画提示,并返回false
            return false;
        }
        if (anim.action.isPlay(anim.updateAmmoAnimation))//如果在换子弹,返回false
            return false;
        return true;
    }

创建子弹

  1. 准备完毕
  2. 创建子弹
  3. 播放射击动画以及火光对象

创建子弹对象方法 :Object.Instantiate(对象模版,创建点,初始朝向)方法

    /// <summary>
    /// 开火
    /// </summary>
    /// <param name="direction">枪口方向</param>
    protected virtual void Firing(Vector3 direction)
    {
        //准备子弹
        //判断是否做好发射准备
        if (!Ready()) return;
        //玩家枪发射,枪口方向
        //创建子弹
        //这里朝向可以用Quaternion.LookRotation,但是由于我这里的Z轴不是子弹朝向所以只能另外去改
        GameObject Bullet = Instantiate(bullet, transform.TransformPoint(Vector3.right), Quaternion.identity);
        //由于我这里的子弹朝向为Y轴,因此我要把枪口方向赋给子弹对象的Y轴方向
        Bullet.GetComponent<Transform>().up = direction;
        //播放射击动画
        anim.action.Play(anim.fireAnimation);
        //播放火光对象
        bulletLight.DisplayLight();
        //播放音频
        //audioSource.PlayOneShot(audio);
        //弹匣内子弹数减1
        boxMagezine.currentBullet--;
    }

—————————————————————————————————————————————————————

填充弹匣

当总子弹数不为0或者当前弹匣内子弹数小于弹匣容量时,允许填充弹匣

填满弹匣子弹数,并从总子弹数里减去对应的数量,如果无法填满,那么把剩余所有的子弹数都填充给弹匣,并将总子弹数归零

    /// <summary>
    /// 填充弹匣
    /// </summary>
    protected void UpdateAmmo()
    {
        if (boxMagezine.allBullet > 0&& boxMagezine.currentBullet < boxMagezine.boxMagazine)
        {
            anim.action.Play(anim.updateAmmoAnimation);//播放填充弹匣动画
            int add = boxMagezine.boxMagazine - boxMagezine.currentBullet;
            if (add <= boxMagezine.allBullet)
            {
                boxMagezine.allBullet -= add;
                boxMagezine.currentBullet += add;
            }
            else
            {
                boxMagezine.currentBullet += boxMagezine.allBullet;
                boxMagezine.allBullet = 0;
            }
        }
        else
        {
            Debug.Log("FullorNone");
            //提示已经没有子弹了或弹匣已满
        }
    }

—————————————————————————————————————————————————————

更换开火模式
    /// <summary>
    /// 切换开火模式
    /// </summary>
    private void ChangeFiringMethod()
    {
        if (Input.GetKeyDown(KeyCode.B))
        {
            //切换模式
            GetComponent<SingleGunDemo>().enabled = !GetComponent<SingleGunDemo>().enabled;
            GetComponent<AutomaticGunDemo>().enabled = !GetComponent<AutomaticGunDemo>().enabled;
        }
    }

—————————————————————————————————————————————————————

初始化Gun的各类资源与说明

主要字段

    /// <summary>
    /// 子弹类型
    /// </summary>
    protected GameObject bullet;
    /// <summary>
    /// 弹匣
    /// </summary>
    private BoxMagezineDemo boxMagezine;
    /// <summary>
    /// 枪动画脚本组件
    /// </summary>
    private GunAnimation anim;
    /// <summary>
    /// 枪口火光对象
    /// </summary>
    private BulletLightDemo bulletLight;

初始化

    protected virtual void Start()
    {
        //初始化弹匣
        boxMagezine = GetComponent<BoxMagezineDemo>();
        //初始化火光对象
        bulletLight = transform.GetChild(1).GetComponent<BulletLightDemo>();
        //初始化动画对象
        anim = GetComponent<GunAnimation>();
        //初始化总子弹数
        boxMagezine.allBullet = 1000;
        //获得子弹资源
        bullet = GetResource();
        //初始默认单点模式
        GetComponent<SingleGunDemo>().enabled = true;
        GetComponent<AutomaticGunDemo>().enabled = false;
    }

从资源文件里读取资源

需要通过资源路径获得,这里要写相对路径,自己在Assets里创建一个Resources文件,注意R要大写

具体方法: Resources.Load<GameObject>("GunTest/Bullet/PlayerBullet");

注意,这里的路径是相对路径,要从自己创建的Resources里的文件夹开始写,例如我这里的玩家子弹预制件的路径为:

Assets/Resources/GunTest/Bullet/PlayerBullet,于是我应该在这个方法里写GunTest/Bullet/PlayerBullet路径。

    /// <summary>
    /// 获取子弹资源文件
    /// </summary>
    /// <returns>资源对象</returns>
    private GameObject GetResource()
    {
              return Resources.Load<GameObject>("GunTest/Bullet/PlayerBullet");
    }
SingleGun-单发模式

挂到枪模型的父空对象上

/// <summary>
/// 单发模式
/// </summary>
public class SingleGunDemo : GunDemo
{
    /// <summary>
    /// 初始化
    /// </summary>
    protected override void Start()
    {
        //初始化父类资源
        base.Start();
    }
    private void Update()
    {
        //调用父类更新
        base.Update();
        //射击
        Fire();
    }
    /// <summary>
    /// 射击
    /// </summary>
    private void Fire()
    {
        if (Input.GetKeyDown(KeyCode.O))
        {
            base.Firing(transform.right);
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            base.UpdateAmmo();
        }
    }
}
AutomaticGun-连发模式

挂到枪模型的父空对象上

与单发模式类似,唯一的不同在于GetKey方法的选择上

/// <summary>
/// 连发模式
/// </summary>
public class AutomaticGunDemo :GunDemo
{
    /// <summary>
    /// 初始化
    /// </summary>
    protected override void Start()
    {
        //初始化父类资源
        base.Start();
    }
    private void Update()
    {
        //调用父类更新
        base.Update();
        //射击
        Fire();
    }
    /// <summary>
    /// 射击
    /// </summary>
    private void Fire()
    {
        if (Input.GetKey(KeyCode.O))
        {
            base.Firing(transform.right);
        }
        if (Input.GetKeyDown(KeyCode.R))
        {
            base.UpdateAmmo();
        }
    }
}

子弹

Bullet-子弹

所有玩家子弹类型的父类,提供射线检测等功能

各类参数定义
    /// <summary>
    /// 层
    /// </summary>
    public LayerMask mask;
    /// <summary>
    /// 射线检测的物体
    /// </summary>
    protected RaycastHit hit;
    /// <summary>
    /// 击到的目标位置
    /// </summary>
    public Vector3 targetPos;
射线检测

射线检测方法:Physics.Raycast(起点,方向向量, out 输出检测的物体信息, 距离,层)

这里要将方向向量设置为子弹朝向,我这里子弹建模的时候方向为y轴所以为transform.up,一般为transform.forward

检测到物体后,将对应位置赋值给到targetPos,储存目标位置,若没有检测到,则位置朝向移动100个单位

    /// <summary>
    /// 计算目标点
    /// </summary>
    public void BulletHit()
    {
        if (Physics.Raycast(this.transform.position,transform.up, out hit, 100, mask))
        {
            //检测到了
            targetPos = hit.point;
        }
        else
        {
            targetPos = transform.position +transform.up* 100;
        }
        //在目标点创建特效
    }
PlayerBullet-玩家子弹

继承Bullet,提供子弹飞向目标点,判断是否击中等功能

初始化子弹目标点

调用父类的BulletHit方法,获得目标点

    private void Start()
    {
        BulletHit();
    }
判断是否命中敌人

通过对象身上的标签判断是否为敌人,如果是则调用对方的受伤方法扣除HP

    /// <summary>
    /// 接触判断
    /// </summary>
    private void Touch()
    {
        if ((targetPos - transform.position).sqrMagnitude < 0.1f)
        {
            if (hit.collider.tag == "Enemy")//如果是敌人
            {
                hit.collider.GetComponent<EnemyDemo>().Damage(10);//扣血
                DestroyImmediate(this.gameObject);
            }
            else
            {
                Destroy(this.gameObject);
            }
        }
    }
子弹飞往目标点

注意,如果没有检测到任何单位,hitnull,因此要加入空判断,防止异常,子弹飞到目标点后即刻摧毁对象

    /// <summary>
    /// 子弹飞往目标点
    /// </summary>
    private void Move()
    {
        transform.position = Vector3.MoveTowards(transform.position, targetPos, 50 * Time.deltaTime);
        if (hit.collider != null)
        {
            Touch();
        }
        else
        {
            if ((transform.position - targetPos).sqrMagnitude < 0.1f)
            {
                Destroy(this.gameObject);
            }
        }
    }

敌人部分

敌人状态

提供敌人受伤,死亡等方法

public class EnemyDemo : MonoBehaviour
{
    [HideInInspector]
    public EnemyProduce produce;
    public float HP;
    private void Start()
    {
        HP = 100;
    }
    /// <summary>
    /// 受伤
    /// </summary>
    /// <param name="attackNumber">伤害数字</param>
    public void Damage(int attackNumber)
    {
        HP -= attackNumber;
        if (HP <= 0)
        {
            Death();
        }
    }
    /// <summary>
    /// 死亡
    /// </summary>
    private void Death()
    {
        Destroy(this.gameObject);
        //设置路线变为可用
        this.GetComponent<EnemyAI>().wayLine.IsUsable = true;
        //告诉生成器,该单位已经死亡,当前存活数量-1
        produce.aliveCount--;
    }
}

EnemyGun-敌人枪

敌人弹药无限,子弹速度较慢,便于玩家躲避

各类参数定义与初始化
    /// <summary>
    /// 子弹资源
    /// </summary>
    private GameObject bullet;
    /// <summary>
    /// 攻击时机
    /// </summary>
    private float firingTime;
    /// <summary>
    /// 攻击间隔
    /// </summary>
    private float attackTime;
    /// <summary>
    /// 初始化
    /// </summary>
    private void Start()
    {
        firingTime = 0;
        attackTime = 1.5F;
        bullet = GetResources();
    }
获得子弹资源
    /// <summary>
    /// 获得子弹资源
    /// </summary>
    /// <returns></returns>
    private GameObject GetResources()
    {
        return Resources.Load<GameObject>("GunTest/Bullet/EnemyBullet");
    }
攻击时机判定

有两种方法:

  1. 每次判定攻击时机大于攻击间隔,把渲染帧时间累加到攻击时机,如果大于攻击间隔则进行攻击,将攻击时机归零
  2. 每次判定攻击时机小于游戏时间,每次攻击后,把攻击时机增加一个攻击间隔
    /// <summary>
    /// 攻击时机
    /// </summary>
    private void AttackTime()
    {
        if (firingTime > attackTime)
        {
            Firing();
            firingTime = 0;
        }
        else
        {
            firingTime += Time.deltaTime;
        }
    }
射击
/// <summary>
/// 射击
/// </summary>
private void Firing()
{
    GameObject Bullet = Object.Instantiate(bullet, transform.TransformPoint(Vector3.right), Quaternion.identity);
    Bullet.GetComponent<Transform>().up = transform.right;//基于子弹朝向
}

子弹

EnemyBullet-敌人子弹

提供子弹飞行,触发等方法,这里的敌人子弹不使用射线检测,并且控制低速

public class EnemyBulletDemo : MonoBehaviour
{
    //敌人子弹为了使得玩家能够避开,不使用射线,而使用触发器,设置较慢的速度以让玩家能够有机会避开
    private void OnTriggerEnter(Collider other)
    {
        if (other.tag == "Player")//如果是玩家
        {
            //如果与玩家接触
            other.GetComponent<PlayerDemo>().Damage(1);
        }
        Destroy(this.gameObject);
    }
    private void Update()
    {
        transform.Translate(0, Time.deltaTime * 5, 0);
        Destroy(this.gameObject, 5);
    }
}
  游戏开发 最新文章
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-10-02 15:10:28  更:2021-10-02 15:11:20 
 
开发: 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/28 2:35:56-

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