提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
前言
Hello大家好久不见,隔了一天没发文章是因为这期的工程也挺复杂的,警告:此篇文章难度偏高,非常不适合刚刚入门的或者没看过我前几期的读者,之所以做了这么久是因为一堆奇奇怪怪的bug真的差点给我整崩溃了然后有些素材我又找不到只能对着Texture2D去PS一张张扣下来都给我整笑了,请读者如果在阅读后感到身体不适请立刻退出这篇文章。
这期我们的主题是:制作法术系统的回血机制和法球机制,友情提示我盲猜会有一堆图片,所以用流量的请检查自己流量是否够用。
一、制作法术系统的回血机制
1.制作动画以及使用UNITY编辑器编辑
还是老规矩,我们来为小骑士添加几个新的tk2dSprite和tk2dSpriteAnimation:
我们把Knight文件夹中所有和Foucs有关的文件夹里面的Sprite全部拖进去,然后点击Apply:
然后就是创建动画了:
然后还有特效效果SpellEffect的tk2dSprite和tk2dSpriteAnimation:这些都是我在PS
为我们的小骑士生成一个新的子对象叫Focus Effect,为里面添加几个新的子对象:
Lines Anim:
Dust R:
Dust L只需要改变Transform的值即可:
其中的PlayerMaker就是上一期我们讲的延迟游戏对象SetActive = false
2.使用代码和PlayMakerFSM制作法术系统
接下来就是用代码来制作法术系统了,首先到PlayerData.cs中我们来添加几个新的变量都是和法术有关的,以及一些方法主要是获得和设置Int和Bool类型的变量:
using System;
using System.Collections.Generic;
using System.Reflection;
using GlobalEnums;
using UnityEngine;
[Serializable]
public class PlayerData
{
private static PlayerData _instance;
public static PlayerData instance
{
get
{
if(_instance == null)
{
_instance = new PlayerData();
}
return _instance;
}
set
{
_instance = value;
}
}
public bool disablePause;
public int health;
public int maxHealth;
public int maxHealthBase;
public int prevHealth;
public int nailDamage;
public int maxMP; //最大MP容量
public int MPCharge; //当前MP存储量
public int MPReverse; //花费的MP
public int MPReverseMax; //花费的最大MP
public bool soulLimited; //是否灵魂容量被限制了
public int focusMP_amount; //聚集法术所需要的MP
public bool hasDash;
public bool canDash;
public bool hasBackDash;
public bool canBackDash;
public bool hasSpell; //是否有法术
public int fireballLevel;//火球法术等级,0表示没有
public int quakeLevel;//地震法术等级,0表示没有
public int screamLevel;//狂啸法术等级,0表示没有
public bool overcharmed;
public bool gotCharm_7; //快速聚集
public bool equippedCharm_7;
public bool gotCharm_10; //英勇者勋章
public bool equippedCharm_10;
public bool gotCharm_11; //吸虫之巢
public bool equippedCharm_11;
public bool gotCharm_17;
public bool equippedCharm_17;
public bool gotCharm_19; //萨满之石
public bool equippedCharm_19;
public bool gotCharm_28; //乌恩符咒
public bool equippedCharm_28;
public bool gotCharm_31; //冲刺大师
public bool equippedCharm_31;
public bool gotCharm_34; //深度聚集
public bool equippedCharm_34;
public int CurrentMaxHealth
{
get
{
return maxHealth;
}
}
protected PlayerData()
{
SetupNewPlayerData();
}
public void Reset()
{
SetupNewPlayerData();
}
private void SetupNewPlayerData()
{
disablePause = false;
health = 5;
maxHealth = 5;
maxHealthBase = 5;
prevHealth = health;
nailDamage = 5;
maxMP = 99;
MPCharge = 0;
MPReverse = 0;
MPReverseMax = 0;
soulLimited = false;
focusMP_amount = 33;
hasDash = true; //测试阶段先设置为true方便测试
canDash = true;
hasBackDash = false;
canBackDash = false;
hasSpell = false;
fireballLevel = 0;
quakeLevel = 0;
screamLevel = 0;
overcharmed = false;
gotCharm_7 = false;
equippedCharm_7 = false;
gotCharm_10 = false;
equippedCharm_10 = false;
gotCharm_11 = false;
equippedCharm_11 = false;
gotCharm_17 = false;
equippedCharm_17 = false;
gotCharm_19 = false;
equippedCharm_19 = false;
gotCharm_28 = false;
equippedCharm_28 = false;
gotCharm_31 = true;
equippedCharm_31 = true;
gotCharm_34 = false;
equippedCharm_34 = false;
}
public void AddHealth(int amount)
{
if (health + amount >= maxHealth)
{
health = maxHealth;
}
else
{
health += amount;
}
if (health >= CurrentMaxHealth)
{
health = maxHealth;
}
}
public void TakeHealth(int amount)
{
if(amount > 0 && health == maxHealth && health != CurrentMaxHealth)
{
health = CurrentMaxHealth;
}
if(health - amount < 0)
{
health = 0;
return;
}
health -= amount;
}
public void MaxHealth()
{
health = CurrentMaxHealth;
}
public bool AddMPCharge(int amount)
{
bool result = false;
if(soulLimited && maxMP != 66)
{
maxMP = 66;
}
if (!soulLimited && maxMP != 99)
{
maxMP = 99;
}
if(MPCharge + amount > maxMP)
{
if(MPReverse < MPReverseMax)
{
MPReverse += amount - (maxMP - MPCharge);
result = true;
if(MPReverse > MPReverseMax)
{
MPReverse = MPReverseMax;
}
}
MPCharge = maxMP;
}
else
{
MPCharge += amount;
result = true;
}
return result;
}
public void TakeMP(int amount)
{
if(amount < MPCharge)
{
MPCharge -= amount;
if(MPCharge < 0)
{
MPCharge = 0;
return;
}
}
else
{
MPCharge = 0;
}
}
public int GetInt(string intName)
{
if (string.IsNullOrEmpty(intName))
{
Debug.LogError("PlayerData: Int with an EMPTY name requested.");
return -9999;
}
FieldInfo fieldInfo = GetType().GetField(intName);
if(fieldInfo != null)
{
return (int)fieldInfo.GetValue(instance);
}
Debug.LogError("PlayerData: Could not find int named " + intName + " in PlayerData");
return -9999;
}
public bool GetBool(string boolName)
{
if (string.IsNullOrEmpty(boolName))
{
return false;
}
FieldInfo field = GetType().GetField(boolName);
if (field != null)
{
return (bool)field.GetValue(instance);
}
Debug.Log("PlayerData: Could not find bool named " + boolName + " in PlayerData");
return false;
}
}
回到我们的HeroActions.cs中,我们来给法术系统添加几个新行为,一个是回血机制行为的focus,一个是快速法术的quickcast:
using System;
using InControl;
public class HeroActions : PlayerActionSet
{
public PlayerAction left;
public PlayerAction right;
public PlayerAction up;
public PlayerAction down;
public PlayerTwoAxisAction moveVector;
public PlayerAction attack;
public PlayerAction jump;
public PlayerAction dash;
public PlayerAction cast;
public PlayerAction focus;
public PlayerAction quickCast;
public HeroActions()
{
left = CreatePlayerAction("Left");
left.StateThreshold = 0.3f;
right = CreatePlayerAction("Right");
right.StateThreshold = 0.3f;
up = CreatePlayerAction("Up");
up.StateThreshold = 0.3f;
down = CreatePlayerAction("Down");
down.StateThreshold = 0.3f;
moveVector = CreateTwoAxisPlayerAction(left, right, down, up);
moveVector.LowerDeadZone = 0.15f;
moveVector.UpperDeadZone = 0.95f;
attack = CreatePlayerAction("Attack");
jump = CreatePlayerAction("Jump");
dash = CreatePlayerAction("Dash");
cast = CreatePlayerAction("Cast");
focus = CreatePlayerAction("Focus");
quickCast = CreatePlayerAction("QuickCast");
}
}
来到InputHandler.cs中把刚刚创建的变量添加到方法中:
using System;
using System.Collections;
using System.Collections.Generic;
using GlobalEnums;
using InControl;
using UnityEngine;
public class InputHandler : MonoBehaviour
{
public InputDevice gameController;
public HeroActions inputActions;
public void Awake()
{
inputActions = new HeroActions();
}
public void Start()
{
MapKeyboardLayoutFromGameSettings();
if(InputManager.ActiveDevice != null && InputManager.ActiveDevice.IsAttached)
{
}
else
{
gameController = InputDevice.Null;
}
Debug.LogFormat("Input Device set to {0}.", new object[]
{
gameController.Name
});
}
private void MapKeyboardLayoutFromGameSettings()
{
AddKeyBinding(inputActions.up, "UpArrow");
AddKeyBinding(inputActions.down, "DownArrow");
AddKeyBinding(inputActions.left, "LeftArrow");
AddKeyBinding(inputActions.right, "RightArrow");
AddKeyBinding(inputActions.attack, "Z");
AddKeyBinding(inputActions.jump, "X");
AddKeyBinding(inputActions.dash, "D");
AddKeyBinding(inputActions.cast, "F");
AddKeyBinding(inputActions.quickCast, "Q");
}
private static void AddKeyBinding(PlayerAction action, string savedBinding)
{
Mouse mouse = Mouse.None;
Key key;
if (!Enum.TryParse(savedBinding, out key) && !Enum.TryParse(savedBinding, out mouse))
{
return;
}
if (mouse != Mouse.None)
{
action.AddBinding(new MouseBindingSource(mouse));
return;
}
action.AddBinding(new KeyBindingSource(new Key[]
{
key
}));
}
}
接下来就是到HeroAnimationController.cs我们添加几个新的方法,因为在空洞骑士这个游戏中,回血的时候不能移动和播放其它动画,所以我们得创建一个属性来记录是否可以播放动画还有一个ActorStates记录失去控制之前的动画。
public ActorStates stateBeforeControl { get; private set; }
public bool controlEnabled { get; private set; }
public void StartControl()
{
actorStates = heroCtrl.hero_state;
controlEnabled = true;
PlayIdle();
}
public void StopControl()
{
if(controlEnabled)
{
controlEnabled = false;
stateBeforeControl = actorStates;
}
}
using System;
using GlobalEnums;
using UnityEngine;
public class HeroAnimationController : MonoBehaviour
{
private HeroController heroCtrl;
private HeroControllerStates cState;
private tk2dSpriteAnimator animator;
private PlayerData pd;
private bool wasFacingRight;
private bool playLanding;
private bool playRunToIdle;//播放"Run To Idle"动画片段
private bool playDashToIdle; //播放"Dash To Idle"动画片段
private bool playBackDashToIdleEnd; //播放"Back Dash To Idle"动画片段(其实并不会播放)
private bool changedClipFromLastFrame;
public ActorStates actorStates { get; private set; }
public ActorStates prevActorStates { get; private set; }
public ActorStates stateBeforeControl { get; private set; }
public bool controlEnabled { get; private set; }
private void Awake()
{
heroCtrl = HeroController.instance;
cState = heroCtrl.cState;
animator = GetComponent<tk2dSpriteAnimator>();
}
private void Start()
{
pd = PlayerData.instance;
ResetAll();
actorStates = heroCtrl.hero_state;
if (!controlEnabled)
{
animator.Stop();
}
if(heroCtrl.hero_state == ActorStates.airborne)
{
animator.PlayFromFrame("Airborne", 7);
return;
}
PlayIdle();
}
private void Update()
{
if(controlEnabled)
{
UpdateAnimation();
}
else if (cState.facingRight)
{
wasFacingRight = true;
}
else
{
wasFacingRight = false;
}
}
private void UpdateAnimation()
{
changedClipFromLastFrame = false;
if (playLanding)
{
Play("Land");
animator.AnimationCompleted = new Action<tk2dSpriteAnimator, tk2dSpriteAnimationClip>(AnimationCompleteDelegate);
playLanding = false;
}
if (playRunToIdle)
{
Play("Run To Idle");
animator.AnimationCompleted = new Action<tk2dSpriteAnimator, tk2dSpriteAnimationClip>(AnimationCompleteDelegate);
playRunToIdle = false;
}
if (playBackDashToIdleEnd)
{
Play("Backdash Land 2");
//处理animation播放完成后的事件(其实并不会播放)
animator.AnimationCompleted = new Action<tk2dSpriteAnimator, tk2dSpriteAnimationClip>(AnimationCompleteDelegate);
playDashToIdle = false;
}
if (playDashToIdle)
{
Play("Dash To Idle");
//处理animation播放完成后的事件
animator.AnimationCompleted = new Action<tk2dSpriteAnimator, tk2dSpriteAnimationClip>(AnimationCompleteDelegate);
playDashToIdle = false;
}
if (actorStates == ActorStates.no_input)
{
//TODO:
if (cState.recoilFrozen)
{
Play("Stun");
}
else if (cState.recoiling)
{
Play("Recoil");
}
}
else if (cState.dashing)
{
if (heroCtrl.dashingDown)
{
Play("Dash Down");
}
else
{
Play("Dash"); //通过cState.dashing判断是否播放Dash动画片段
}
}
else if (cState.backDashing)
{
Play("Back Dash");
}
else if(cState.attacking)
{
if (cState.upAttacking)
{
Play("UpSlash");
}
else if (cState.downAttacking)
{
Play("DownSlash");
}
else if (!cState.altAttack)
{
Play("Slash");
}
else
{
Play("SlashAlt");
}
}
else if (actorStates == ActorStates.idle)
{
//TODO:
if (CanPlayIdle())
{
PlayIdle();
}
}
else if (actorStates == ActorStates.running)
{
if (!animator.IsPlaying("Turn"))
{
if (cState.inWalkZone)
{
if (!animator.IsPlaying("Walk"))
{
Play("Walk");
}
}
else
{
PlayRun();
}
}
}
else if (actorStates == ActorStates.airborne)
{
if (cState.jumping)
{
if (!animator.IsPlaying("Airborne"))
{
animator.PlayFromFrame("Airborne", 0);
}
}
else if (cState.falling)
{
if (!animator.IsPlaying("Airborne"))
{
animator.PlayFromFrame("Airborne", 7);
}
}
else if (!animator.IsPlaying("Airborne"))
{
animator.PlayFromFrame("Airborne", 3);
}
}
//(其实并不会播放)
else if (actorStates == ActorStates.dash_landing)
{
animator.Play("Dash Down Land");
}
else if(actorStates == ActorStates.hard_landing)
{
animator.Play("HardLand");
}
if (cState.facingRight)
{
if(!wasFacingRight && cState.onGround && CanPlayTurn())
{
Play("Turn");
}
wasFacingRight = true;
}
else
{
if (wasFacingRight && cState.onGround && CanPlayTurn())
{
Play("Turn");
}
wasFacingRight = false;
}
ResetPlays();
}
private void AnimationCompleteDelegate(tk2dSpriteAnimator anim, tk2dSpriteAnimationClip clip)
{
if(clip.name == "Land")
{
PlayIdle();
}
if(clip.name == "Run To Idle")
{
PlayIdle();
}
if(clip.name == "Backdash To Idle")//(其实并不会播放)
{
PlayIdle();
}
if(clip.name == "Dash To Idle")
{
PlayIdle();
}
}
private void Play(string clipName)
{
if(clipName != animator.CurrentClip.name)
{
changedClipFromLastFrame = true;
}
animator.Play(clipName);
}
private void PlayRun()
{
animator.Play("Run");
}
public void PlayIdle()
{
animator.Play("Idle");
}
public void StopAttack()
{
if(animator.IsPlaying("UpSlash") || animator.IsPlaying("DownSlash"))
{
animator.Stop();
}
}
public void FinishedDash()
{
playDashToIdle = true;
}
private void ResetAll()
{
playLanding = false;
playRunToIdle = false;
playDashToIdle = false;
wasFacingRight = false;
controlEnabled = true;
}
private void ResetPlays()
{
playLanding = false;
playRunToIdle = false;
playDashToIdle = false;
}
public void UpdateState(ActorStates newState)
{
if(controlEnabled && newState != actorStates)
{
if(actorStates == ActorStates.airborne && newState == ActorStates.idle && !playLanding)
{
playLanding = true;
}
if(actorStates == ActorStates.running && newState == ActorStates.idle && !playRunToIdle && !cState.inWalkZone)
{
playRunToIdle = true;
}
prevActorStates = actorStates;
actorStates = newState;
}
}
public void PlayClip(string clipName)
{
if (controlEnabled)
{
Play(clipName);
}
}
public void StartControl()
{
actorStates = heroCtrl.hero_state;
controlEnabled = true;
PlayIdle();
}
public void StopControl()
{
if(controlEnabled)
{
controlEnabled = false;
stateBeforeControl = actorStates;
}
}
public void StartControlWithoutSettingState()
{
controlEnabled = true;
if (stateBeforeControl == ActorStates.running && actorStates == ActorStates.running)
{
actorStates = ActorStates.idle;
}
}
private bool CanPlayIdle()
{
return !animator.IsPlaying("Land") && !animator.IsPlaying("Run To Idle") && !animator.IsPlaying("Dash To Idle") && !animator.IsPlaying("Backdash Land") && !animator.IsPlaying("Backdash Land 2") && !animator.IsPlaying("LookUpEnd") && !animator.IsPlaying("LookDownEnd") && !animator.IsPlaying("Exit Door To Idle") && !animator.IsPlaying("Wake Up Ground") && !animator.IsPlaying("Hazard Respawn");
}
private bool CanPlayTurn()
{
return !animator.IsPlaying("Wake Up Ground") && !animator.IsPlaying("Hazard Respawn"); ;
}
}
来到HeroControllerStates.cs,除了我们要创建几个和法术有关的变量外,还需要获得某个状态的方法和设置某个状态的方法:
[Serializable]
public class HeroControllerStates
{
public bool facingRight;
public bool onGround;
public bool wasOnGround;
public bool attacking;
public bool altAttack;
public bool upAttacking;
public bool downAttacking;
public bool inWalkZone;
public bool jumping;
public bool falling;
public bool dashing;
public bool backDashing;
public bool touchingWall;
public bool wallSliding;
public bool willHardLand;
public bool recoilFrozen;
public bool recoiling;
public bool recoilingLeft;
public bool recoilingRight;
public bool freezeCharge;
public bool focusing;
public bool dead;
public bool hazardDeath;
public bool invulnerable;
public bool preventDash;
public bool preventBackDash;
public bool dashCooldown;
public bool backDashCooldown;
public bool isPaused;
public HeroControllerStates()
{
facingRight = false;
onGround = false;
wasOnGround = false;
attacking = false;
altAttack = false;
upAttacking = false;
downAttacking = false;
inWalkZone = false;
jumping = false;
falling = false;
dashing = false;
backDashing = false;
touchingWall = false;
wallSliding = false;
willHardLand = false;
recoilFrozen = false;
recoiling = false;
recoilingLeft = false;
recoilingRight = false;
freezeCharge = false;
focusing = false;
dead = false;
hazardDeath = false;
invulnerable = false;
preventDash = false;
preventBackDash = false;
dashCooldown = false;
backDashCooldown = false;
isPaused = false;
}
/// <summary>
/// 设置一个新的状态(通常用在playmakerFSM上)
/// </summary>
/// <param name="stateName"></param>
/// <param name="value"></param>
public void SetState(string stateName, bool value)
{
FieldInfo field = GetType().GetField(stateName);
if (field != null)
{
try
{
field.SetValue(HeroController.instance.cState, value);
return;
}
catch (Exception ex)
{
string str = "Failed to set cState: ";
Exception ex2 = ex;
Debug.LogError(str + ((ex2 != null) ? ex2.ToString() : null));
return;
}
}
Debug.LogError("HeroControllerStates: Could not find bool named" + stateName + "in cState");
}
/// <summary>
/// 获取一个新的状态(通常用在playmakerFSM上)
/// </summary>
/// <param name="stateName"></param>
/// <returns></returns>
public bool GetState(string stateName)
{
FieldInfo field = GetType().GetField(stateName);
if (field != null)
{
return (bool)field.GetValue(HeroController.instance.cState);
}
Debug.LogError("HeroControllerStates: Could not find bool named" + stateName + "in cState");
return false;
}
}
终于来到HeroController.cs中,我们来设置回血机制所需要的变量:
private bool drainMP; //是否正在流走MP
private float drainMP_timer; //流走MP的计时器
private float drainMP_time; //流走MP花费的时间
private float MP_drained; //已经流走的MP数量
private float focusMP_amount; //使用focus回血所需要的MP数量
private float preventCastByDialogueEndTimer;
public PlayMakerFSM spellControl;
在Update函数中:
if (drainMP)
{
drainMP_timer += Time.deltaTime;
while (drainMP_timer >= drainMP_time) //drainMP_time在无护符下等于0.027
{
MP_drained += 1f;
drainMP_timer -= drainMP_time;
TakeMp(1);
if(MP_drained == focusMP_amount)
{
MP_drained -= drainMP_time;
proxyFSM.SendEvent("HeroCtrl-FocusCompleted");
}
}
}
preventCastByDialogueEndTimer -= Time.deltaTime;
设置获取MP和消耗MP的方法,增加血量和消耗血量的方法,是否能聚集,是否能释放法术,能否输入和忽略输入,失去控制和重新获得控制:
public void AddMPCharge(int amount)
{
int mpreverse = playerData.MPReverse;
playerData.AddMPCharge(amount);
if(playerData.MPReverse != mpreverse && gm)
{
}
}
public void SetMPCharge(int amount)
{
playerData.MPCharge = amount;
//TODO:
}
public void SoulGain()
{
int num;
if(playerData.MPCharge < playerData.maxMP)
{
num = 11;
}
else
{
num = 6;
}
int mpreverse = playerData.MPReverse;
playerData.AddMPCharge(num);
if(playerData.MPReverse != mpreverse)
{
}
}
public void TakeMp(int amount)
{
if(playerData.MPCharge > 0)
{
playerData.TakeMP(amount);
if(amount > 1)
{
}
}
}
public void AddHealth(int amount)
{
playerData.AddHealth(amount);
proxyFSM.SendEvent("HeroCtrl-Healed");
}
public void TakeHealth(int amount)
{
playerData.TakeHealth(amount);
proxyFSM.SendEvent("HeroCtrl-HeroDamaged");
}
public void MaxHealth()
{
proxyFSM.SendEvent("HeroCtrl-MaxHealth");
playerData.MaxHealth();
}
public void StartMPDrain(float time)
{
Debug.LogFormat("Start MP Drain");
drainMP = true;
drainMP_timer = 0f;
MP_drained = 0f;
drainMP_time = time; //
focusMP_amount = (float)playerData.GetInt("focusMP_amount");
}
public void StopMPDrain()
{
drainMP = false;
}
public bool CanFocus()
{
return !gm.isPaused && hero_state != ActorStates.no_input && !cState.dashing && !cState.backDashing && (!cState.attacking || attack_time > ATTACK_RECOVERY_TIME) && !cState.recoiling && cState.onGround && !cState.recoilFrozen && !cState.hazardDeath && CanInput();
}
public bool CanCast()
{
return !gm.isPaused && !cState.dashing && hero_state != ActorStates.no_input && !cState.backDashing && (!cState.attacking || attack_time >= ATTACK_RECOVERY_TIME) && !cState.recoiling && !cState.recoilFrozen && CanInput() && preventCastByDialogueEndTimer <= 0f;
}
public bool CanInput()
{
return acceptingInput;
}
public void IgnoreInput()
{
if (acceptingInput)
{
acceptingInput = false;
ResetInput();
}
}
public void AcceptInput()
{
acceptingInput = true;
}
/// <summary>
/// 放弃控制
/// </summary>
public void RelinquishControl()
{
if(!controlReqlinquished && !cState.dead)
{
ResetInput();
ResetMotion();
IgnoreInput();
controlReqlinquished = true;
ResetAttacks();
touchingWallL = false;
touchingWallR = false;
}
}
/// <summary>
/// 重新获得控制
/// </summary>
public void RegainControl()
{
AcceptInput();
hero_state = ActorStates.idle;
if(controlReqlinquished && !cState.dead)
{
AffectedByGravity(true);
SetStartingMotionState();
controlReqlinquished = false;
if (startWithWallslide)
{
cState.willHardLand = false;
cState.touchingWall = true;
if(transform.localScale.x< 0f)
{
touchingWallR = true;
return;
}
touchingWallL = true;
}
else
{
if (startWithJump)
{
HeroJumpNoEffect();
startWithJump = false;
return;
}
if (startWithFullJump)
{
HeroJump();
startWithFullJump = false;
return;
}
if (startWithDash)
{
HeroDash();
startWithDash = false;
return;
}
if (startWithAttack)
{
DoAttack();
startWithAttack = false;
return;
}
cState.touchingWall = false;
touchingWallL = false;
touchingWallR = false;
}
}
}
这个SoulGain()我们要在HealthManager.cs中来调用:
public void TakeDamage(HitInstance hitInstance)
{
Debug.LogFormat("Enemy Take Damage");
int cardinalDirection = DirectionUtils.GetCardinalDirection(hitInstance.GetActualDirection(transform));
directionOfLastAttack = cardinalDirection;
FSMUtility.SendEventToGameObject(gameObject, "HIT", false);
FSMUtility.SendEventToGameObject(hitInstance.Source, "HIT LANDED", false);
FSMUtility.SendEventToGameObject(gameObject, "TOOK DAMAGE", false);
if(recoil != null)
{
recoil.RecoilByDirection(cardinalDirection,hitInstance.MagnitudeMultiplier);
}
switch (hitInstance.AttackType)
{
case AttackTypes.Nail:
if(hitInstance.AttackType == AttackTypes.Nail && enemyType !=3 && enemyType != 6)
{
HeroController.instance.SoulGain();
}
Vector3 position = (hitInstance.Source.transform.position + transform.position) * 0.5f + effectOrigin;
break;
case AttackTypes.Generic:
break;
case AttackTypes.Spell:
break;
}
if(hitEffectReceiver != null)
{
hitEffectReceiver.ReceiverHitEffect(hitInstance.GetActualDirection(transform));
}
int num = Mathf.RoundToInt((float)hitInstance.DamageDealt * hitInstance.Multiplier);
hp = Mathf.Max(hp - num, -50);
if(hp > 0)
{
NonFatalHit(hitInstance.IgnoreInvulnerable);
}
else
{
Die(new float?(hitInstance.GetActualDirection(transform)), hitInstance.AttackType, hitInstance.IgnoreInvulnerable);
}
}
这里我们添加了一个新的AttackType类型就叫Spell:
public enum AttackTypes
{
Nail,
Generic,
Spell
}
完整的HeroController.cs如下所示:
using System;
using System.Collections;
using System.Collections.Generic;
using HutongGames.PlayMaker;
using GlobalEnums;
using UnityEngine;
using System.Reflection;
public class HeroController : MonoBehaviour
{
public ActorStates hero_state;
public ActorStates prev_hero_state;
public bool acceptingInput = true;
public bool controlReqlinquished; //控制是否被放弃
public float move_input;
public float vertical_input;
private Vector2 current_velocity;
public float WALK_SPEED = 3.1f;//走路速度
public float RUN_SPEED = 5f;//跑步速度
public float JUMP_SPEED = 5f;//跳跃的食欲
private NailSlash slashComponent; //决定使用哪种攻击的NailSlash
private PlayMakerFSM slashFsm;//决定使用哪种攻击的PlayMakerFSM
public NailSlash normalSlash;
public NailSlash altetnateSlash;
public NailSlash upSlash;
public NailSlash downSlash;
public PlayMakerFSM normalSlashFsm;
public PlayMakerFSM altetnateSlashFsm;
public PlayMakerFSM upSlashFsm;
public PlayMakerFSM downSlashFsm;
private bool attackQueuing; //是否开始攻击计数步骤
private int attackQueueSteps; //攻击计数步骤
private float attack_time;
private float attackDuration; //攻击状态持续时间,根据有无护符来决定
private float attack_cooldown;
private float altAttackTime; //当时间超出可按二段攻击的时间后,cstate.altattack就会为false
public float ATTACK_DURATION; //无护符时攻击状态持续时间
public float ATTACK_COOLDOWN_TIME; //攻击后冷却时间
public float ATTACK_RECOVERY_TIME; //攻击恢复时间,一旦超出这个时间就退出攻击状态
public float ALT_ATTACK_RESET; //二段攻击重置时间
private int ATTACK_QUEUE_STEPS = 5; //超过5步即可开始攻击
private float NAIL_TERRAIN_CHECK_TIME = 0.12f;
private bool drainMP; //是否正在流走MP
private float drainMP_timer; //流走MP的计时器
private float drainMP_time; //流走MP花费的时间
private float MP_drained; //已经流走的MP数量
private float focusMP_amount; //使用focus回