前言:实现了武器的基本发射功能,但是我们弹夹数量是有限,之前并没有做装弹和弹夹显示的功能。本篇实现装弹和弹夹显示。
装弹
目标
Unity官方的项目中,如果不是满弹夹的情况,子弹的数量会随时间自动增加。
思路和实现
如果子弹没有满,且在没有开枪的情况下,子弹会自动装填。
由于子弹的自动装填是随着时间慢慢增加的,可以通过协程来实现。
public float reloadBulletTime = 2F;// 每次装弹的时间
public int reloadBulletNum = 10;// 每次装弹的数量
IEnumerator ReloadBullet()
{
while (!isFire&¤tBulletNum<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&¤tBulletNum<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&¤tBulletNum<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的大小时候,里面的显示条也会跟着拉伸。
本站资源均来自互联网,仅供研究学习,禁止违法使用和商用,产生法律纠纷本站概不负责!如果侵犯了您的权益请与我们联系!
转载请注明出处: 免费源码网-免费的源码资源网站 » Unity3D学习FPS游戏(8)装弹和弹夹UI显示
发表评论 取消回复