@作者 : SYFStrive
@博客首页 : HomePage
📌:个人社区(欢迎大佬们加入) 👉:社区链接🔗
📌:觉得文章不错可以点点关注 👉:专栏连接🔗
💃:程序员每天坚持锻炼💪
👉 飞机大战专栏(🔥)
目录
游戏单例脚本
单例模式是1种设计模式:👉(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
单例使用说明:“单例模式是指在内存中只会创建一次对象的设计模式,并且确保一个类只有实例,而且会自行实例化,并向整个系统提供这个实例。
非持久化泛型单例
using UnityEngine;
//摘要:Base class for everything attached to GameObjects.
//Component中文说明:所有能挂载到游戏对象上的类型基类
public class Singleton<T> : MonoBehaviour where T :Component
{
public static T Instance { get; private set; }
protected virtual void Awake()
{
Instance = this as T;
}
}
游戏基类
子弹基类实现子弹移动
实现:子弹生成是就开始移动
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Projectile : MonoBehaviour
{
//子弹的移动速度
[SerializeField] float moveSpeed;
//子弹的移动方向
[SerializeField] protected Vector3 moveDirection;
//子弹移动的Obj
protected GameObject targer;
protected virtual void OnEnable()
{
StartCoroutine(ProjectileMoveIE());
}
IEnumerator ProjectileMoveIE()
{
while (true)
{
//子弹移动
transform.position += moveSpeed * moveDirection * Time.deltaTime;
yield return null;
}
}
}
生命系统的基类
实现:储存人物的血量参数(继承这个脚本的简直爽歪歪)……
代码如 👇
using System;
using System.Collections;
using System.Collections.Generic;
using System.Security.Principal;
using UnityEngine;
public class Characters : MonoBehaviour
{
[Header("---Header---")]
//最大生命值
[SerializeField] protected float maxHp;
//当前生命值
protected float currentHp;
//死亡时生成特效
[SerializeField] GameObject dieSpecialEffects;
protected virtual void OnEnable()
{
currentHp = maxHp;
}
/// <summary>
/// 玩家受伤
/// </summary>
/// <param name="injuredValue">伤害值</param>
protected virtual void Injured(float injuredValue)
{
currentHp -= injuredValue;
if (currentHp <= 0)
Die();
}
/// <summary>
/// 玩家死亡
/// </summary>
public void Die()
{
//血量归0
currentHp=0;
//调用对象池
PoolManager.Release(dieSpecialEffects,transform.position);
隐藏该对象
this.gameObject.SetActive(false);
}
/// <summary>
/// 恢复生命值
/// </summary>
protected virtual void RecoverHP(float value)
{
currentHp = Mathf.Clamp(currentHp + value, 0, maxHp);
}
/// <summary>
/// 自动恢复生命值携程
/// </summary>
/// <param name="waitForSeconds">恢复的间隔</param>
/// <param name="value">恢复值</param>
/// <returns></returns>
protected virtual IEnumerator SelfRecoverHpIE(WaitForSeconds waitForSeconds,float value)
{
while (currentHp < maxHp)
{
yield return waitForSeconds;
RecoverHP(currentHp * value);
}
}
/// <summary>
/// 持续受伤
/// </summary>
/// <param name="waitForSeconds">受伤的间隔</param>
/// <param name="value">受伤值</param>
/// <returns></returns>
protected virtual IEnumerator SelfInjuredIE(WaitForSeconds waitForSeconds, float value)
{
while (currentHp >= 0f)
{
yield return waitForSeconds;
Die(currentHp * value);
}
}
}
对象池管理器
说明:这里已经添加了这个项目所有的对象池容器
using System.Collections.Generic;
using UnityEngine;
public class PoolManager : MonoBehaviour
{
//储存不同类准备的对象池
[SerializeField] Pool[] playerPoolProjectile; //玩家子弹
[SerializeField] Pool[] enemyPoolProjectile; //敌人子弹
[SerializeField] Pool[] poolVFX; //特效
[SerializeField] Pool[] randomCreateEnemy; //随机敌人
[SerializeField] Pool[] createProp; 敌人掉落的道具
//使用字典来存储不同的装备
public static Dictionary<GameObject, Pool> dictionary;
private void Awake()
{
//实例化字典
dictionary = new Dictionary<GameObject, Pool>();
//初始化对象池
InitializeObj(playerPoolProjectile);
InitializeObj(enemyPoolProjectile);
InitializeObj(poolVFX);
InitializeObj(randomCreateEnemy);
InitializeObj(createProp);
}
#region 测试函数
#if UNITY_EDITOR
//停止游戏时执行
private void OnDestroy()
{
CheckPoolSize(playerPoolProjectile);
CheckPoolSize(enemyPoolProjectile);
CheckPoolSize(poolVFX);
CheckPoolSize(randomCreateEnemy);
CheckPoolSize(createProp);
}
#endif
#endregion
#region 测试需要对象池的容量
private void CheckPoolSize(Pool[] pools)
{
foreach (Pool pool in pools)
{
if (pool.sumSize > pool.initializeSize)
{
Debug.LogWarning(string.Format("Pool:{0}初始大小为{1},需要的大小为{2}",
pool.prefabeObjProperty.name,
pool.initializeSize,
pool.sumSize));
}
}
}
#endregion
/// <summary>
/// 初始化子弹
/// </summary>
private void InitializeObj(Pool[] pools)
{
foreach (var pool in pools)
{
#region //条件编译操作 只有在Unity引起运行
#if UNITY_EDITOR
if (dictionary.ContainsKey(pool.prefabeObjProperty))
{
Debug.Log("字典有相同的名字!"+pool.prefabeObjProperty.name);
continue;
}
#endif
#endregion
//添加到字典
dictionary.Add(pool.prefabeObjProperty, pool);
//给创建的Obj命名
Transform poolPatent = new GameObject("对象池Poll" + pool.prefabeObjProperty.name).transform;
//设置父位置
poolPatent.parent = transform;
//初始化对象池
pool.Initialize(poolPatent);
}
}
#region 释放子弹&&重载
/// <summary>
/// 释放子弹
/// </summary>
/// <param name="prefabe">指定游戏的预制体</param>
/// <returns></returns>
public static GameObject Release(GameObject prefabe)
{
#region 条件编译操作 只有在Unity引起运行
#if UNITY_EDITOR
if (!dictionary.ContainsKey(prefabe))
{
Debug.Log("找不到对应的Key");
return null;
}
#endif
#endregion
return dictionary[prefabe].PrepareQuene();
}
/// <summary>
/// 释放子弹
/// </summary>
/// <param name="prefabe">指定游戏的预制体</param>
/// <param name="position">指定游戏的位置</param>
/// <returns></returns>
public static GameObject Release(GameObject prefabe, Vector3 position)
{
#region 条件编译操作 只有在Unity引起运行
#if UNITY_EDITOR
if (!dictionary.ContainsKey(prefabe))
{
Debug.Log("找不到对应的Key");
return null;
}
#endif
#endregion
return dictionary[prefabe].PrepareQuene(position);
}
/// <summary>
/// 释放子弹
/// </summary>
/// <param name="prefabe">指定游戏的预制体</param>
/// <param name="position">指定游戏的位置</param>
/// <param name="quaternion">指定游戏的旋转位置</param>
/// <returns></returns>
public static GameObject Release(GameObject prefabe, Vector3 position, Quaternion quaternion)
{
#region 条件编译操作 只有在Unity引起运行
#if UNITY_EDITOR
if (!dictionary.ContainsKey(prefabe))
{
Debug.Log("找不到对应的Key");
return null;
}
#endif
#endregion
return dictionary[prefabe].PrepareQuene(position, quaternion);
}
/// <summary>
/// 释放子弹
/// </summary>
/// <param name="prefabe">指定游戏的预制体</param>
/// <param name="position">指定游戏的位置</param>
/// <param name="quaternion">指定游戏的旋转位置</param>
/// <param name="localscale">指定游戏的旋转缩放</param>
/// <returns></returns>
public static GameObject Release(GameObject prefabe, Vector3 position, Quaternion quaternion, Vector3 localscale)
{
#region 条件编译操作 只有在Unity引起运行
#if UNITY_EDITOR
if (!dictionary.ContainsKey(prefabe))
{
Debug.Log("找不到对应的Key");
return null;
}
#endif
#endregion
return dictionary[prefabe].PrepareQuene(position, quaternion, localscale);
}
#endregion
}
能量系统
功能实现:获取能量 👉 使用能量 👉判断能量是否够用
代码框架
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerEnergy : Singleton<PlayerEnergy>
{
//------值不会变可以使用常量------
//------常量命名规范;字母大写、不同字母之间用下划线链接------
//最大能量 与 获取能量值
public const int MAX_ENERGY=100;
public const int PERCENTAGE=1;
//能量值
private int energy;
//UI脚本组件
[SerializeField] PlayerWorldEnergyHUD playerWorldEnergyHUD;
[Header("---能量系统---")]
//使用能量技能时能量条每秒所消耗的值(协程)
[SerializeField] float useSkillTimeIE = 0.1f;
private WaitForSeconds useSkillWaitForSeconds;
//是否使用能量技能
private bool isAvailable=false;
private void Start()
{
//初始化能量
playerWorldEnergyHUD.Initialize(energy, MAX_ENERGY);
//获取能量
OptainEnergy(MAX_ENERGY);
}
private void OnEnable()
{
PlayerEnergySkill.on += PlayerEnergyProjectileOn;
PlayerEnergySkill.off += PlayerEnergyProjectileOff;
}
private void OnDestroy()
{
PlayerEnergySkill.on -= PlayerEnergyProjectileOn;
PlayerEnergySkill.off -= PlayerEnergyProjectileOff;
}
/// <summary>
/// 获取能量
/// </summary>
/// <param name="value">获取的值</param>
public void OptainEnergy(int value)
{
if (energy == MAX_ENERGY || isAvailable || !gameObject.activeSelf) return;
energy = Mathf.Clamp(energy + value, 0, MAX_ENERGY);
playerWorldEnergyHUD.UpdateUIState(energy, MAX_ENERGY);
}
/// <summary>
/// 使用技能
/// </summary>
/// <param name="value">所需能量</param>
public void UseEnergy(int value)
{
energy -= value;
playerWorldEnergyHUD.UpdateUIState(energy, MAX_ENERGY);
//判断是否正在能量爆发
if (energy == 0 && isAvailable)
{
PlayerEnergySkill.off?.Invoke();
}
}
/// <summary>
/// 判断能量是否够用
/// </summary>
/// <param name="value">所消耗的能量</param>
/// => energy >= value; 类似前端的ES箭头函数
/// <returns></returns>
public bool IsEnergyEnough(int value) => energy >= value;
#region 委托技能委托与能量持续减少
private void PlayerEnergyProjectileOn()
{
isAvailable = true;
StartCoroutine(nameof(SustionMinus));
}
private void PlayerEnergyProjectileOff()
{
isAvailable = false;
StopCoroutine(nameof(SustionMinus));
}
IEnumerator SustionMinus()
{
while (gameObject.activeSelf&&energy>0)
{
yield return useSkillWaitForSeconds;
//每个一秒消耗一点能量
UseEnergy(PERCENTAGE);
}
}
#endregion
}
1、获取能量
/// <summary>
/// 获取能量
/// </summary>
/// <param name="value">获取的值</param>
public void OptainEnergy(int value)
{
if (energy == MAX_ENERGY) return;
energy = Mathf.Clamp(energy + value, 0, MAX_ENERGY);
//更新UI的值
playerWorldEnergyHUD.UpdateUIState(energy, MAX_ENERGY);
}
2、使用能量
/// <summary>
/// 使用技能
/// </summary>
/// <param name="value">所需能量</param>
public void UseEnergy(int value)
{
energy -= value;
playerWorldEnergyHUD.UpdateUIState(energy, MAX_ENERGY);
}
3、判断能量是否够用
/// <summary>
/// 判断能量是否够用
/// </summary>
/// <param name="value">所消耗的能量</param>
/// => energy >= value; 类似前端的ES箭头函数
/// <returns></returns>
public bool EnergyEnough(int value) => energy >= value;
4、敌人脚本
using UnityEngine;
public class Enemy : Characters
{
[Header("---死亡获得的能量值---")]
[SerializeField] int destroyEnergyValue=3;
protected override void Die()
{
//敌人死亡时执行能量更新
PlayerEnergy.Instance.OptainEnergy(destroyEnergyValue);
base.Die();
}
}
5、关联玩家子弹脚本
PlayerProjectile脚本修改如 👇
protected override void OnCollisionEnter2D(Collision2D collision)
{
//子弹碰撞时触发能量条
PlayerEnergy.Instance.OptainEnergy(PlayerEnergy.PERCENTAGE);
base.OnCollisionEnter2D(collision);
}
效果
实现翻滚闪避功能
功能说明:添加InputSyster输入键 👉 利用携程完成翻转闪避的效果
1、InputSystem相关设置
添加空格键闪避动作
2、Player代码修改
实现共能:利用碰撞器的开关来实现闪避功能
代码如 👇
[Header("---Dodege---"), Range(0, 100)]
[SerializeField] int DodegeEnergyValue = 25;
//碰撞器
new private Collider2D collider2D;
//最大旋转角度
[SerializeField] float maxRoll = 720f;
//旋转的速度 如👇一秒旋转360度
[SerializeField] float rollSpeed = 360f;
//旋转时旁随缩放
[SerializeField] Vector3 minScale = new Vector3(0.5f, 0.5f, 0.5f);
//繁殖频发触发GC(垃圾回收机制)
private float currentRoll; //但钱的缩放值
private float scaleSpeed; //缩放的速度
private bool isDodge; //是否缩放
private void Start()
{
//更新PlayerUI状态
playerWorldHUD.Initialize(currentHp, maxHp);
//缩放的事件
scaleSpeed = maxRoll / rollSpeed;
}
#region 闪避
private void Dodege()
{
if (isDodge || !PlayerEnergy.Instance.IsEnergyEnough(DodegeEnergyValue)) return;
//玩家按下闪避
StartCoroutine(nameof(DodegeIE));
}
IEnumerator DodegeIE()
{
isDodge = true;
AudioManager.Instance.RandomPitchPlaySFX(DodegeAudioData);
//1、消耗能量
PlayerEnergy.Instance.UseEnergy(DodegeEnergyValue);
//2、闪避时无敌状态
collider2D.isTrigger = true;
currentRoll = 0;
//初始化的缩放
var scale = transform.localScale;
//3、闪避时人玩家翻转缩放
while (currentRoll < maxRoll)
{
currentRoll += rollSpeed * Time.deltaTime;
//完成翻转
transform.rotation = Quaternion.AngleAxis(currentRoll, Vector3.right);
if (currentRoll <= maxRoll / 2f)
{
scale.x = Mathf.Clamp(scale.x - Time.deltaTime / scaleSpeed, minScale.x, 1);
scale.y = Mathf.Clamp(scale.y - Time.deltaTime / scaleSpeed, minScale.y, 1);
scale.z = Mathf.Clamp(scale.z - Time.deltaTime / scaleSpeed, minScale.z, 1);
}
else
{
scale.x = Mathf.Clamp(scale.x + Time.deltaTime / scaleSpeed, minScale.x, 1);
scale.y = Mathf.Clamp(scale.y + Time.deltaTime / scaleSpeed, minScale.y, 1);
scale.z = Mathf.Clamp(scale.z + Time.deltaTime / scaleSpeed, minScale.z, 1);
}
transform.localScale = scale;
yield return null;
}
collider2D.isTrigger = false;
isDodge = false;
}
#endregion
3、效果
最后
本文到这里就结束了,大佬们的支持是我持续更新的最大动力,希望这篇文章能帮到大家💪
下篇文章再见ヾ( ̄▽ ̄)ByeBye