@作者 : SYFStrive
@博客首页 : HomePage
:个人社区(欢迎大佬们加入) :社区链接
:觉得文章不错可以点点关注 :专栏连接
:程序员每天坚持锻炼
飞机大战专栏()
实现:实现了这个案例所有的输入事件绑定
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
using UnityEngine.InputSystem;
[CreateAssetMenu(menuName = "Player Input")]
public class PlayerInput : ScriptableObject,
InputActions.IGameplayActions,
InputActions.IUnpauseActions,
InputActions.IGameOverSceneActions
{
//移动:使用系统事件
public event UnityAction onMove; //= delegate { }判空的1种方法(一开始初始化)
public event UnityAction onStopMove;
//发射子弹:使用系统事件
public event UnityAction onFire;
public event UnityAction onStopFire;
//飞机闪避
public event UnityAction onDodge;
//能源技能
public event UnityAction onEnergySkill;
//菜单事件
public event UnityAction onPause;
public event UnityAction onUnPause;
//发射导弹事件
public event UnityAction onLaunchMissile;
//游戏结束
public event UnityAction onGameOver;
//初始化实例InputAction
private InputActions inputActions;
//Play时执行
private void OnEnable()
{
//实例InputAction
inputActions = new InputActions();
//登记动作表的回调函数 如我现在只有一个动作表就登记一个
inputActions.Gameplay.SetCallbacks(this);
inputActions.Unpause.SetCallbacks(this);
inputActions.GameOverScene.SetCallbacks(this);
}
//推出游戏执行
private void OnDisable()
{
EntranceSceneInhibitoryInput();
}
///
/// 激活控制表及限制鼠标与输入设备输入
///
public void AstrictGameplayImport()
{
//理解:操作玩家 ?? 启动动作表
//inputActions.Gameplay.Enable();
//确定硬件指针是否可见。
//Cursor.visible = false;
//Cursor.lockState对应的参数如 ??三种
//1、不加锁定
//CursorLockMode.None;
//2、锁定光标
//CursorLockMode.Locked;
//3、将光标限制在屏幕内
//CursorLockMode.Confined;
//Cursor.lockState = CursorLockMode.Locked;
SwitchActionMap(inputActions.Gameplay, false);
}
public void AstrictUnpauseImport()
{
SwitchActionMap(inputActions.Unpause, true);
}
public void AstrictGameOverImport()
{
SwitchActionMap(inputActions.GameOverScene, true);
}
private void SwitchActionMap(InputActionMap inputActionMap, bool isPause)
{
inputActions.Disable();
inputActionMap.Enable();
//菜单
if (isPause)
{
Cursor.visible = true;
Cursor.lockState = CursorLockMode.None;
}
else
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
}
///
/// 场景切换时禁止输入设备一系列操作
///
public void EntranceSceneInhibitoryInput()
{
//禁止输入
//inputActions.Gameplay.Disable();
inputActions.Disable();
}
#region 切换package
public void FixedDynamicUpdate() => InputSystem.settings.updateMode = InputSettings.UpdateMode.ProcessEventsInDynamicUpdate;
public void FixedUpdate() => InputSystem.settings.updateMode = InputSettings.UpdateMode.ProcessEventsInFixedUpdate;
#endregion
#region 事件
public void OnMove(InputAction.CallbackContext context)
{
//按下及按住时执行
if (context.phase == InputActionPhase.Performed)
{
//执行事件
//判断空的第二种方法
//if(onMove!=null)
//判断空的第三种方法
//传入输入动作读取到的二维向量的值 ?? 将其作为参数
//当按下按键的时候 调用onMove方法 ?? 同时将读取到的二维向量值作为参数
onMove?.Invoke(context.ReadValue());
}
//当松开按键时执行
if (context.phase == InputActionPhase.Canceled)
{
//执行事件
onStopMove?.Invoke();
}
}
public void OnFire(InputAction.CallbackContext context)
{
//按下及按住时执行
if (context.phase == InputActionPhase.Performed)
{
onFire?.Invoke();
}
//当松开按键时执行
if (context.phase == InputActionPhase.Canceled)
{
onStopFire?.Invoke();
}
}
public void OnDodge(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onDodge?.Invoke();
}
}
public void OnEnergySkill(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onEnergySkill?.Invoke();
}
}
public void OnPause(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onPause?.Invoke();
}
}
public void OnUnpause(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onUnPause?.Invoke();
}
}
public void OnLaunchMissile(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onLaunchMissile?.Invoke();
}
}
public void OnGameOver(InputAction.CallbackContext context)
{
if (context.phase == InputActionPhase.Performed)
{
onGameOver?.Invoke();
}
}
#endregion
}
单例模式是1种设计模式:(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。
单例使用说明:“单例模式是指在内存中只会创建一次对象的设计模式,并且确保一个类只有实例,而且会自行实例化,并向整个系统提供这个实例。
using UnityEngine;
//摘要:Base class for everything attached to GameObjects.
//Component中文说明:所有能挂载到游戏对象上的类型基类
public class Singleton : 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 System.Diagnostics;
using UnityEngine;
public class PersistentSingleton : MonoBehaviour where T : Component
{
public static T Instance;
protected virtual void Awake()
{
if(Instance == null)
Instance = this as T;
else
Destroy(this.gameObject);
DontDestroyOnLoad(this);
}
}
实现:子弹生成是就开始移动
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;
}
///
/// 玩家受伤
///
/// 伤害值
protected virtual void Injured(float injuredValue)
{
currentHp -= injuredValue;
if (currentHp <= 0)
Die();
}
///
/// 玩家死亡
///
public void Die()
{
//血量归0
currentHp=0;
//调用对象池
PoolManager.Release(dieSpecialEffects,transform.position);
隐藏该对象
this.gameObject.SetActive(false);
}
///
/// 恢复生命值
///
protected virtual void RecoverHP(float value)
{
currentHp = Mathf.Clamp(currentHp + value, 0, maxHp);
}
///
/// 自动恢复生命值携程
///
/// 恢复的间隔
/// 恢复值
///
protected virtual IEnumerator SelfRecoverHpIE(WaitForSeconds waitForSeconds,float value)
{
while (currentHp < maxHp)
{
yield return waitForSeconds;
RecoverHP(currentHp * value);
}
}
///
/// 持续受伤
///
/// 受伤的间隔
/// 受伤值
///
protected virtual IEnumerator SelfInjuredIE(WaitForSeconds waitForSeconds, float value)
{
while (currentHp >= 0f)
{
yield return waitForSeconds;
Die(currentHp * value);
}
}
}
理解:对象池(Object Pool)但从字面理解就是一池子的物体,在我们需要使用的时候就拿出来一个,然后包装成我想用的样子。用完之后清空放回到池子里。
说明:这里已经添加了这个项目所有的对象池容器
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, Pool> dictionary;
private void Awake()
{
//实例化字典
dictionary = new Dictionary, 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
///
/// 初始化子弹
///
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 释放子弹&&重载
///
/// 释放子弹
///
/// 指定游戏的预制体
///
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();
}
///
/// 释放子弹
///
/// 指定游戏的预制体
/// 指定游戏的位置
///
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);
}
///
/// 释放子弹
///
/// 指定游戏的预制体
/// 指定游戏的位置
/// 指定游戏的旋转位置
///
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);
}
///
/// 释放子弹
///
/// 指定游戏的预制体
/// 指定游戏的位置
/// 指定游戏的旋转位置
/// 指定游戏的旋转缩放
///
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
}
完成使用技能逻辑 与 UI同步显示
[Header("---EnergySkill---")]
//是否开启了爆发技能
private bool isEnetgySkill;
//开启技能对应的能量发生变化
[SerializeField] int areSkillDodgeAdd = 2;
[SerializeField] float areSkillPlayerMoveSpeedAdd = 1.2f;
[SerializeField] float areSkillPlayerProjectilefFactorAdd = 1.2f;
[SerializeField] GameObject energyPrefabeVFX;
readonly float recoverTime = 1f;
private void Awake()
{
//获取刚体组件
rigidbody = GetComponent<Rigidbody2D>();
//获取碰撞组件
collider2D = GetComponent<Collider2D>();
//获取导弹的脚本
missileSystem = GetComponent<MissileSystem>();
}
private void Start()
{
//实例化子弹协程时间间隔
fireProjectileTimeIE = new WaitForSeconds(fireTime);
//使用技能下的开火时间
areSkillFireProjectileTimeIE = new WaitForSeconds(fireTime / areSkillPlayerProjectilefFactorAdd);
//生命自动恢复的间隔
selfRecoverTimeIE = new WaitForSeconds(selfRecoverTime);
//无敌时间
waitForSecondsInvincibleIE = new WaitForSeconds(invincibleTimeIE);
//更新PlayerUI状态
playerWorldHUD.Initialize(currentHp, maxHp);
//缩放的事件
scaleSpeed = maxRoll / rollSpeed;
}
#region 技能
private void EnergySkill()
{
if (!PlayerEnergy.Instance.IsEnergyEnough(PlayerEnergy.MAX_ENERGY)) return;
//执行委托
PlayerEnergySkill.on?.Invoke();
}
private void EnergyOn()
{
isEnetgySkill = true;
DodegeEnergyValue *= areSkillDodgeAdd;
moveSpeed *= areSkillPlayerMoveSpeedAdd;
}
private void EnergyOff()
{
isEnetgySkill = false;
DodegeEnergyValue /= areSkillDodgeAdd;
moveSpeed /= areSkillPlayerMoveSpeedAdd;
}
#endregion
实现:释放技能攻击时触发显示技能特效
using UnityEngine;
using UnityEngine.Events;
public class PlayerEnergySkill : MonoBehaviour
{
public static UnityAction on ;
public static UnityAction off;
//正常特效
[SerializeField] GameObject engineVFXNormal;
//目标特效
[SerializeField] GameObject targgerVFX;
//能量技能特效
[SerializeField] GameObject engineVFXEnergy;
//开/关启技能特效
[SerializeField] AudioData onSFX;
[SerializeField] AudioData offSFX;
private void Awake()
{
on += On;
off += Off;
}
private void OnDestroy()
{
on -= On;
off -= Off;
}
private void On()
{
engineVFXNormal.SetActive(false);
targgerVFX.SetActive(true);
engineVFXEnergy.SetActive(true);
AudioManager.Instance.RandomPitchPlaySFX(onSFX);
}
private void Off()
{
engineVFXNormal.SetActive(true);
engineVFXEnergy.SetActive(false);
AudioManager.Instance.RandomPitchPlaySFX(offSFX);
}
}
本文到这里就结束了,大佬们的支持是我持续更新的最大动力,希望这篇文章能帮到大家相关专栏连接
下篇文章再见ヾ( ̄▽ ̄)ByeBye