当前位置: 首页 > article >正文

Unity3D学习FPS游戏(8)装弹和弹夹UI显示

前言:实现了武器的基本发射功能,但是我们弹夹数量是有限,之前并没有做装弹和弹夹显示的功能。本篇实现装弹和弹夹显示。

装弹和弹夹UI显示

  • 装弹
    • 目标
    • 思路和实现
  • 弹夹UI显示
    • 目标
    • 弹夹UI的思路和实现
    • UI代码的思路和实现
  • 武器控制的完整代码
  • 效果
  • 补充知识
    • 锚点

装弹

目标

Unity官方的项目中,如果不是满弹夹的情况,子弹的数量会随时间自动增加。

思路和实现

如果子弹没有满,且在没有开枪的情况下,子弹会自动装填。

由于子弹的自动装填是随着时间慢慢增加的,可以通过协程来实现。

public float reloadBulletTime = 2F;// 每次装弹的时间
public int reloadBulletNum = 10;// 每次装弹的数量
IEnumerator ReloadBullet()
{
    while (!isFire&&currentBulletNum<bulletNum)// 达到弹夹容量,协程自己会停止运行
    {
    	yield return new WaitForSeconds(reloadBulletTime);
        currentBulletNum= (reloadBulletNum+currentBulletNum)>bulletNum?bulletNum:(reloadBulletNum + currentBulletNum);// 判断一下会不会超过弹夹容量
    }
}

当子弹左键按下的时候,就表示要开枪,这时候就应该停止装弹协程。
当子弹左键松开的时候,就表示停止开枪,这时候就应该开启装弹协程。

private void OpenFire()
{
    if (Input.GetMouseButtonDown(0))
    {
        StopCoroutine("ReloadBullet");
        isFire = true;
        StartCoroutine("Shoot");
    }
    if (Input.GetMouseButtonUp(0))
    {
        isFire = false;
        StopCoroutine("Shoot");
        StartCoroutine("ReloadBullet");
    }
}

弹夹UI显示

目标

子弹数量发生变化的时候,弹夹会有一个UI进度条提醒当前子弹的情况。

弹夹UI的思路和实现

UI毫无疑问得在Canvas上进行。

在之前显示做准星的Canvas上,右键UI-Slider,添加一个滑动条。
默认的Slider中三个部分分别对应界面内容如下,Backgroud是滑动条底色,Fill Area是滑动区域,Handle Slider Area是滑动区域末尾小圆球。
在这里插入图片描述
其中Handle Slider Area小圆球,我们可以删掉,因为我们并不用。
把Fill Area中的Fill拖到和Backgroud平级,并调整strech(伸展)左右铺满,Fill Area作用不大可以删掉只是更好约束了Fill的strech。
在这里插入图片描述
接下来调整颜色和样式,给Backgroud和Fill选择方形的背景图片TEX_Black和TEX_White。下面演示了是如何找到素材和切换背景图片。
在这里插入图片描述
然后修改Fill的颜色,模仿Unity官方案例的蓝色,然后调整Slider大小。
位置为常在的右下角,这个Slider直接拖动到右下角是没有用的,可以通过锚点来常驻右下角。锚点调到右下角后,再拖动到合适位置。这样屏幕无论怎么样变化,Slider位置都是相对右下角进行变化的。
在这里插入图片描述
在这里插入图片描述
添加弹夹的UI,Slider下面右键新增UI-Image,然后切换Source Image为武器的图标。切换图片后,可以Set Native Size,可将图像框的尺寸设置为纹理的原始像素大小,不会变形太厉害。
在这里插入图片描述
调整武器的图标大小到合适。
在这里插入图片描述

UI代码的思路和实现

代码控制Slider,先在代码中添加Slider组件,添加后Unity把组件拖过来就行了。

public Slider bulletSlider;// 弹夹Slider UI

在Start函数中初始化Slider,最大值和当前值。

void Start()
{
    if (bulletSlider)
    {
        bulletSlider.maxValue = bulletNum;
        bulletSlider.value = currentBulletNum;
    }
}

在子弹数量会发生变化的地方,进行Slider更新,会发生变化的地方只有发射子弹(Shoot)和装弹(ReloadBullet)两个协程中。

IEnumerator Shoot()
{
    while (isFire)
    {
        if (currentBulletNum >0)
        {
            GameObject newBullet = bulletPool.Get();
            currentBulletNum--;
            // 更新弹夹数量
            if (bulletSlider)
                bulletSlider.value = currentBulletNum;
        }
        yield return new WaitForSeconds(shootInterval);
    }
}
IEnumerator ReloadBullet()
{
    while (!isFire&&currentBulletNum<bulletNum)
    {
        yield return new WaitForSeconds(reloadBulletTime);
        currentBulletNum = (reloadBulletNum+currentBulletNum)>bulletNum?bulletNum:(reloadBulletNum + currentBulletNum);
        // 更新弹夹数量
        if (bulletSlider)
            bulletSlider.value = currentBulletNum;
    }
}

武器控制的完整代码

这次主要修改的WeaponController的代码,下面是修改后WeaponController的完整代码。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Pool;
using UnityEngine.UI;

public class WeaponController : MonoBehaviour
{
    [Header("武器数值")]
    public Vector3 defaultPosition= new Vector3(0.4F, -0.6F, 1.15F);// 默认位置
    public Vector3 centerPosition = new Vector3(0F, -0.6F, 0.807F);// 中心位置
    public float positionLerpRatio = 0.5f;// 线性插值参数

    [Header("子弹数值")]
    public Transform shootPoint;// 子弹发射位置
    public GameObject bullet;// 子弹预制体
    public float shootInterval = 1;// 子弹间隔时间
    private bool isFire;// 发射状态
    public int bulletNum = 100;// 弹夹
    public int currentBulletNum;// 当前子弹的数量
    public float reloadBulletTime = 2F;// 每次装弹的时间
    public int reloadBulletNum = 10;// 每次装弹的数量
    public Slider bulletSlider;// 弹夹Slider UI

    private ObjectPool<GameObject> bulletPool;// 子弹对象池

    private void Awake()
    {
        currentBulletNum = bulletNum;
        bulletPool = new ObjectPool<GameObject>(CreateBullet,BulletOnGet, BulletOnRelease, BulletOnDestory,true,10,bulletNum);
    }

    GameObject CreateBullet()
    {
        GameObject obj = Instantiate(bullet, shootPoint);
        obj.GetComponent<BulletController>().bulletPool = bulletPool;
        return obj;
    }

    void BulletOnGet(GameObject obj)
    {
        obj.GetComponent<BulletController>().BulletReset();
        obj.gameObject.SetActive(true);
    }

    void BulletOnRelease(GameObject obj)
    {
        obj.gameObject.SetActive(false);
    }

    void BulletOnDestory(GameObject obj)
    {
        Destroy(obj);
    }

    void Start()
    {
    	// 弹夹UI初始化
        if (bulletSlider)
        {
            bulletSlider.maxValue = bulletNum;
            bulletSlider.value = currentBulletNum;
        }
    }

    void Update()
    {
        ChangePosition();
        OpenFire();
    }

    private void OpenFire()
    {
        if (Input.GetMouseButtonDown(0))
        {
            StopCoroutine("ReloadBullet");
            isFire = true;
            StartCoroutine("Shoot");
        }
        if (Input.GetMouseButtonUp(0))
        {
            isFire = false;
            StopCoroutine("Shoot");
            StartCoroutine("ReloadBullet");
        }
    }
    IEnumerator Shoot()
    {
        while (isFire)
        {
            if (currentBulletNum >0)
            {
                //GameObject newBullet = Instantiate(bullet, shootPoint);
                GameObject newBullet = bulletPool.Get();
                currentBulletNum--;
                // 弹夹UI更新
                if (bulletSlider)
                    bulletSlider.value = currentBulletNum;
            }
            yield return new WaitForSeconds(shootInterval);
        }
    }
    private void ChangePosition()
    {
        // 按下左键
        if (Input.GetMouseButtonDown(1))
        {
            StopCoroutine("ToDefault");
            StartCoroutine("ToCenter");
        }
        // 松开左键
        if (Input.GetMouseButtonUp(1))
        {
            StopCoroutine("ToCenter");
            StartCoroutine("ToDefault");
        }
    }
    IEnumerator ToCenter() {
        while (transform.localPosition!=centerPosition)
        {
            transform.localPosition = Vector3.Lerp(transform.localPosition, centerPosition, positionLerpRatio);
            yield return null;// 等待一帧
        }
    }
    IEnumerator ToDefault()
    {
        while (transform.localPosition != defaultPosition)
        {
            transform.localPosition = Vector3.Lerp(transform.localPosition, defaultPosition, positionLerpRatio);
            yield return null;// 等待一帧
        }
    }
    IEnumerator ReloadBullet()
    {
        while (!isFire&&currentBulletNum<bulletNum)
        {
            yield return new WaitForSeconds(reloadBulletTime);
            currentBulletNum = (reloadBulletNum+currentBulletNum)>bulletNum?bulletNum:(reloadBulletNum + currentBulletNum);
            // 弹夹UI更新
            if (bulletSlider)
                bulletSlider.value = currentBulletNum;
        }
    }
}

效果

在这里插入图片描述

补充知识

锚点

Canvas下创建UI会自带四个△为锚点,如下图。
在这里插入图片描述
锚点可以分开,可以构成矩形。
在这里插入图片描述
Unity中UI属性可以设置锚点,红色框部分是锚点在一起,黄色框部分是锚点分开。这两个属性在本篇弹夹显示的UI中都用到的了。
在这里插入图片描述
锚点在一起的时候,图片的大小不会随着父对象的大小改变而改变,但是图片定位是相对锚点进行定位的。例如本文实现弹夹显示在右下角的时候,锚点就被设置在了右下角。

锚点分开的时候,用作与拉伸,锚点的位置会随着父物体的大小进行变动。例如本文实现弹夹显示条,调整父物体Slider的大小时候,里面的显示条也会跟着拉伸。


http://www.kler.cn/a/377449.html

相关文章:

  • 为什么越来越多人开始用云电脑?网友道出了真相
  • 2024年【汽车修理工(高级)】考试总结及汽车修理工(高级)试题及解析
  • ffmpeg+vue2
  • D59【python 接口自动化学习】- python基础之异常
  • Python基于TensorFlow实现双向循环神经网络GRU加注意力机制分类模型(BiGRU-Attention分类算法)项目实战
  • 注意力机制的实现
  • Android开发之——SOLID基础设计原则(掌握23种设计模式)其实开发之路如此简单
  • 苹果转向 Apple Silicon,Intel Mac 的支持时限倒计时
  • 通用型函数——冒泡排序
  • 商务英语学习柯桥学外语到泓畅-老外说“go easy on me”是什么意思?
  • spring-解析Scope注解
  • golang switch v := data.(type)
  • Flarum:简洁而强大的开源论坛软件
  • 活动回顾丨艾体宝《开源软件供应链安全的最佳实践》线下研讨会圆满落幕!
  • 五、SpringBoot3实战(1)
  • docker对nginx.conf进行修改后页面无变化或页面报错
  • 【运动的&足球】足球场地区域图像分割系统源码&数据集全套:改进yolo11-RFAConv
  • 提高交换网络可靠性之端口安全配置
  • 项目自动化构建工具——make与Makefile详解
  • 高效实现SCRM用户管理的最佳实践与策略
  • DB-GPT系列(三):底层大模型设置(开源模型、在线模型)
  • 景联文科技医疗数据处理平台:强化医疗数据标注与管理,推动医疗数字化新篇章
  • Waymo的EMMA给多模态端到端自驾指引了方向
  • 软件(2)
  • Rust 力扣 - 73. 矩阵置零
  • gazebo仿真初学者可以试试这个ros小车