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 UIprivate 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.xdnf.cn/news/6555.html

如若内容造成侵权/违法违规/事实不符,请联系一条长河网进行投诉反馈,一经查实,立即删除!

相关文章

GameFramework教程☀️福利(五):关于该框架的一些意义

文章目录 📢 不同模式的意义本章探讨GF这样编写的意义和使用场景。 📢 不同模式的意义 最近在做一个app,现在在调研阶段。 代码上后期可能用华佗进行C#热更新。 在调研华佗打包完的热更代码如何和UI AB结合起来时,看到了: "> 从这一点可以延伸理解出,当我们使…

加密货币行业与2024年美国大选

加密货币行业经历了近十年的飞速发展&#xff0c;尤其是在比特币、以太坊等主要加密资产的兴起之后&#xff0c;越来越多的美国人开始将其视为一种财富积累或交易的工具。然而&#xff0c;尽管这一新兴行业的市场规模在持续扩大&#xff0c;但加密货币仍面临着重重监管难题&…

开源 AI 智能名片 2+1 链动模式 S2B2C 商城小程序与私域流量圈层

摘要&#xff1a;本文探讨了私域流量圈层的特点以及其在当今时代的重要性&#xff0c;分析了开源 AI 智能名片 21 链动模式 S2B2C 商城小程序源码在私域流量圈层构建中的作用&#xff0c;阐述了产品在圈层时代被标签化的现象&#xff0c;并以实例展示了如何利用该小程序源码打造…

tinymce扩展功能:1、行高、段落间距、格式刷;2、视频上传进度条;3、对复制的图片设置尺寸

tinymce扩展功能&#xff1a;1、行高、段落间距、格式刷&#xff1b;2、视频上传进度条&#xff1b;3、对复制的图片设置尺寸 一、需求描述二、行高、段落间距、格式刷插件三、实现视频上传的进度条、对复制的图片设置尺寸 一、需求描述 使用技术&#xff1a; vue2 tinymce5.…

【算法篇】--重温算法题

目录 前言【算 一、反转链表 二、合并两个有序链表 三、反转链表II 四、移除链表元素 前言 本篇文章基于学习了一段数据结构&#xff0c;并练习了几道算法题所做的一些笔记&#xff0c;方便日后复习时可以用到。 如果有什么不对的地方&#xff0c;欢迎大家在评论区指正&…

秋叶SD4.9最新版本,解压就能使用,Ai生图超级强大!!!

Midjourney &#xff0c;StableDiffusion&#xff0c;ComfyUI&#xff0c;在AI绘画领域&#xff0c;这3款工具非常有名&#xff0c;Midjourney 这一款对网络有要求&#xff0c;一般可能上不了。SD和ComfyUI是可以本地运行&#xff0c;只要你的电脑配置了8G及以上的独立显卡&…

Docker — 跨平台和环境部署

Docker 是一个开源的容器化平台&#xff0c;通过将应用程序和其依赖打包在一个轻量级、独立的容器中&#xff0c;能够跨平台和环境部署。 1. Docker 基本概念 镜像 (Image)&#xff1a;Docker 镜像是一个只读模板&#xff0c;包含运行应用程序所需的代码、库、依赖和环境配置。…

qt QFocusEvent详解

1、概述 QFocusEvent是Qt C框架中的一个事件类&#xff0c;它专门用于处理与焦点变化相关的事件。在图形用户界面&#xff08;GUI&#xff09;编程中&#xff0c;焦点事件是不可或缺的一部分&#xff0c;它们允许开发者在控件获取或失去焦点时执行特定的操作。QFocusEvent通常…

高性能分布式缓存Redis-高级应用篇章

一、发布订阅 Redis提供了发布订阅功能&#xff0c;可以用于消息的传输 Redis的发布订阅机制包括三个部分&#xff0c;publisher&#xff0c;subscriber和Channel 发布者和订阅者都是Redis客户端&#xff0c;Channel则为Redis服务器端。 发布者将消息发送到某个的频道&…

使用Python Flask实战构建Web应用

Python Flask是一个轻量级的Web框架&#xff0c;它简单易用、灵活性高&#xff0c;适用于构建各种规模的Web应用。本文将介绍如何使用Python Flask框架来实战构建一个简单的Web应用&#xff0c;并展示其基本功能和特性。 第一部分&#xff1a;搭建开发环境 在开始之前我们需要…

dockerfile 和 docker compose

目录 1.dockerfile和docker compose区别 主要区别 目的&#xff1a; 格式&#xff1a; 使用场景&#xff1a; 2.Dockerfile 2.1基本格式 2.2模块解析 2.3例子 3.docker compose 3.1安装 3.2格式 3.3执行 1.dockerfile和docker compose区别 Dockerfile 和…

如何安全的使用助听器?

安全使用助听器是非常重要的&#xff0c;以下是一些关键的建议和注意事项&#xff0c;以确保您或您的家人能够正确且安全地使用助听器&#xff1a; 1. 遵循专业指导 •在初次佩戴前&#xff0c;请务必咨询专业的听力师或医生。他们会根据您的听力状况和个人需求来调整助听器的…

VMWare安装以后虚拟机NAT模式时网卡down问题

安装完成VMware后&#xff0c;安装linux虚拟机&#xff0c;网络模式为NAT模式&#xff0c;用来联网&#xff0c;但是发现虚拟机的网卡状态一直是down的。 service network restart 会报错 解决办法如下&#xff1a; ctlaltdelete打开任务管理->服务->打开服务->找到…

Springcloud高校选课管理系统-计算机毕业设计源码27115

摘 要 随着信息技术的快速发展和高校信息化建设的深入推进&#xff0c;选课管理系统作为高校教育信息化建设的重要组成部分&#xff0c;其重要性和紧迫性日益凸显。传统的选课管理系统往往采用单体架构&#xff0c;存在系统耦合度高、可维护性差、扩展性不强等问题&#xff0c;…

Java项目实战II基于Spring Boot高校教师科研管理系统设计与实现(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。 一、前言 随着高等教育的快速发展和科研活动的日…

Markdown快速上手(typora)

一级标题~六级标题 可以选中文本在这里直接设置&#xff0c;后面也有快捷键&#xff0c;也可以使用其语法&#xff0c;一个#&#xff0c;对应一级标题&#xff0c;两个#&#xff0c;对应二级标题&#xff0c;等。 我这里使用Ctrl1没生效是因为快捷键冲突&#xff0c;也需要注意…

更快更强 | HP15加热台新品!Max温度350度,200度只需60秒!30~150W功率可调,恒温加热和回流焊双模式!

正点原子HP15加热台更快更强&#xff01;最高温度可达350度&#xff0c;200度只需60秒&#xff01;30~150W功率可调&#xff0c;恒温加热和回流焊双模式&#xff01; HP15是正点原子全新推出的迷你恒温加热台&#xff0c;设备支持30~150W功率可调&#xff0c;在150W功率下从室温…

【点云网络】 pointnet 和 pointnet++

这两个网络都是斯坦福大学的一个团队提出的 我先先看一下pointnet的网络架构,这个网络比较经典&#xff0c;是2016年提出的&#xff1a; PointNet 是一个专门用于点云数据处理的神经网络。它的设计目的是直接操作不规则的点云数据&#xff0c;而无需将点云数据转换为规则网格或…

分布式——BASE理论

简单来说&#xff1a; BASE&#xff08;Basically Available、Soft state、Eventual consistency&#xff09;是基于CAP理论逐步演化而来的&#xff0c;核心思想是即便不能达到强一致性&#xff08;Strong consistency&#xff09;&#xff0c;也可以根据应用特点采用适当的方…

FPGA实战篇:Moore/Mealy状态机

什么是状态机&#xff1f; 状态机是根据当前输入信号和自身当前所处状态来改变输出逻辑的一种逻辑系统&#xff0c;目前它也被抽象应用于软件设计当中&#xff0c;本文从硬件设计角度来解释状态机&#xff0c;使用Verilog语言来抽象描述并实现状态机。 状态机类型 状态分为两…