九.Character Abilities
1.介绍
Character Abilities是将使角色能够执行操作的脚本。无论是跳跃、跑步,还是按下一个按钮,这都是它发生的地方。拥有这些分离的能力脚本将允许尽可能好的最好的体系结构。它也会让你更容易创造出你自己的abilities。
2.标准能力
1.CharacterButtonActivation : 此组件允许您的角色与按钮驱动的对象(对话区、开关……)进行交互。
2.CharacterConeOfVision : 在角色周围投射一个视觉锥,可以用来检测目标,或者纯粹用于装饰目的。
3.CharacterCrouch :当按下蹲伏按钮时,允许调整控制器的大小(和专用动画)
4.CharacterDash2D/3D : 这ability允许角色向指定的方向冲刺。您可以决定一个Dash Mode,指定要用于移动角色的曲线动画、Dash的持续时间、Dash距离等。
5.CharacterDamageDash2D/3D :这种能力类似于常规的Dash,但也会在Dash时造成损坏。为此,此功能启用/禁用一个DamageOnTouch,该对象应该创建,定位在角色下,并绑定到其面板的TargetDamageOnTouch。如果您正在查看如何扩展现有ability以添加更多ability的示例,这个ability也是一个很好的参考。
6.CharacterDirectionMarker :这种能力将让你在一个角色上的目标方向或运动方向,还有两个例子在Colonel场景角色
7.CharacterFallDownHoles2D :一个2D功能,它将使你的角色掉落“holes”,由你在TopDownController2D面板中指定的hole layer mask定义。
8.CharacterGridMovement : 让您在网格上行走(这意味着角色将始终停止以网格单元格为中心的移动)。这在2D和3D中都可以工作,需要GridManager并适当设置。您将在Minimal2DGrid、Minimal3DGrid和Explodudes场景中找到该设置的示例。请注意,这是一个不同于“常规”特征运动的系统,而且大多数与运动相关的能力(跳跃、冲刺)或AI动作不会与网格运动一起工作。
9.CharacterHandleWeapon :让你的角色装备和使用武器,无论它是否来自库存。
10.CharacterHandleSecondaryWeapon :同上,但还有一件武器。
11.CharacterInventory : 让你的角色将自己绑定到库存中,以便能够装备武器和更多。请注意,该引擎目前不支持多人游戏的库存,但它已被强烈要求,并将在未来进行更新。
12.CharacterJump2D/3D : 当你按下跳跃按钮时,你的角色跳跃。注意,3D版本实际上将移动你的角色的控制器,而2D版本将保持它的位置,一个动画负责跳跃错觉。注意,在这两种情况下,你的角色在跳跃时不再被认为是在地面上的。
13.CharacterMovement :基本的,基于地面的运动。您将可以从其检查器中指定行走速度、站立阈值、加速和减速、行走粒子等。你也可以强制free, 2, 4 or 8 方向的移动。
14.CharacterOrientation2D/3D :将旋转或翻转你的角色,让它面对运动的方向,武器的方向,或两者兼而有之。
15.CharacterPathfinder3D : 只有3D的功能,可以让你的角色在导航网格上找到路径。这个导航网格需要出现在场景中才能被使用。
16.CharacterPathfindToMouse : 只有3D的功能,让你点击地面并让你的角色移动到这个目标,以及在LoftSuspendersMouseDriven场景演示
17.CharacterPause :允许具有此能力的角色(以及控制它的玩家)使用暂停按钮来暂停游戏
18.CharacterPersistence :让角色在转换到新场景时保持其当前状态
19.CharacterRotateCamera :此能力可以让您在围绕角色的垂直轴(z2D,y3D)上旋转相机。它还提供了旋转输入以匹配相机的方向,确定旋转空间和速度,以及武器的专用瞄准选项。您将在Loft3D演示场景中找到一个它的示例(使用L和M旋转相机)。
20.CharacterRotation2D :让你的2D角色改变其模型的旋转,以匹配它的前进方向。
21.CharacterRun : 当按下跑步按钮时,让角色以指定的速度跑步
22.CharacterSwap : 此能力将允许您交换对单个场景中的多个角色的控制。关于其中的一个例子,请参阅Minimal2DCharacterSwap场景。请注意,此能力依赖于LevelManager的正常角色实例化。
23.CharacterSwitchModel :允许您切换角色的外观。
24.CharacterTimeControl : 让您的角色在按下时间控制按钮时,将当前的timescale更为面板中指定的timescale。
25.Character Ability Node Swap :此能力允许您指定一组新的能力,并在按下按钮时交换到它们。
3.能力概述
那么一个能力能做什么呢?如果您对代码的工作方式感兴趣,最简单的方法是查看CharacterAbility.cs类,所有能力都可以继承该CharacterAbility.cs类。它有一些方法,让我们快速回顾一下主要的方法:
1.Initialization : 顾名思义,这就是基类将获得在子类能力中经常有用的角色或场景的部分。比如相机,TopDownController,input manager等。子类能力通常会使用它来获取其他组件或初始化变量(跳跃次数等)。
2.Animation methods :InitializeAnimatorParameters 和 UpdateAnimator:使用第一个参数来注册动画参数,然后使用第二个参数来更新它们。这分两个步骤完成,以避免检查每帧每个参数的存在,这最终会导致性能问题。
3.HandleInput : 如果按下/释放了某个按钮,此方法将调用该功能中的其他方法。
4.Early/Process/Late Process ability : 这些方法在每次更新时都被状态机调用。
5.Reset :将在角色死亡时调用此方法。用于重置计数器等。
6.Play/Stop Sfx :用来触发能力声音的方法。默认情况下,每个功能都有3个声音(在面板中为每个功能定义):一个在启动时,一个在使用时,一个在停止时。当然,你只能使用其中一个或0个。如果您创建了自己的能力,则需要调用这些方法来触发声音。
4.组织能力
现在可以将能力分成多个游戏对象(或节点)。
它很容易设置。默认情况下,角色组件将在自己的节点上查找功能。此外,您可以将任意数量的AdditionalAbilityNodes array绑定到它的附加节点。通常你会有(比如在上面的图中)一些具有能力的空游戏对象。只需将它们拖动到该数组中,Character组件将在初始化时注册它们。
这对许多事情都很有用。它避免了在单个对象上有太多的组件,这在寻找特定的能力或字段时可能会变得令人困惑。它还允许您一次启用/禁用整个功能节点,甚至交换整个能力集。Colonel 的预制体,你会在Colonel场景中发现,就是这样搭建的。
5.状态机
Character 组件负责触发各种能力。它是使用状态机来这样做的。状态机是一种设计模式,它基本上将存储当前状态和前一个状态。默认情况下,角色使用两种状态机:
1.MovementState :通过_movement属性从任何能力中访问,它表示角色正在执行的当前操作。
2.ConditionState :通过_condition属性从任何能力中访问,它存储了角色的当前状态(正常、死亡、暂停等)。
它的工作方式是,在一个场景的开始时,角色将初始化所有的能力,然后是每一帧,调用EarlyProcess、Process 和LateProcess 方法,并最终在死亡时重置它们。其他状态机实现通常只调用当前状态能力在Update。由于许多原因,这个版本没有,但主要是为了方便地扩展系统,而不必重写所有内容或修改现有的类。这意味着每个能力都负责处理自己的输入,防止进入其方法(通过测试当前状态是否允许——例如,您不能在不在地面的情况下行走)。引擎中包含的大多数能力都不使用提前进程或最新进程,但如果您需要,仍然有可能使用它。
6.授权能力
大多数能力都定义了它们自己的基本限制,即上述状态机中不能从其转换到该能力的状态。例如,默认情况下,您在jetpacking时不能jump。这些默认限制涵盖了很多情况,但在您的游戏中,您可能需要进一步限制。这可以通过扩展更改条件检查的基本能力来实现,或者简单地从面板,通过添加状态到Blocking Movement States和Blocking Condition States,如上图所示。
7.创建你自己的能力
创建自己能力的最简单方法是扩展CharacterAbility,就像引擎中的所有功能一样。需要时覆盖方法(确保在开始时调用基本方法)。要测试您的新能力,您只需要将其添加到现有角色中,它将自动添加到状态机中,并像其他角色一样进行处理。需要记住的一件事是与其他能力的交互。您可能希望扩展其他功能来防止或授权某些状态的更改。此外,你的能力可能需要新的状态。您可以在CharacterStates.cs中声明这些(在MovementStates 或CharacterConditions 枚举中的任何地方,顺序并不重要)。
为了找灵感,你可以看看当前的角色能力,因为它们正是你试图创造的,是你可以取得成就的好例子。这里是我用来创建它们的一个很好的基础,因为我发现这些方法是我用来覆盖最多的方法。确保你用你的东西替换了所有的todo:
using UnityEngine;
using System.Collections;
using MoreMountains.Tools;
namespace MoreMountains.TopDownEngine // you might want to use your own namespace here
{
/// <summary>
/// TODO_DESCRIPTION
/// </summary>
[AddComponentMenu("TopDown Engine/Character/Abilities/TODO_REPLACE_WITH_ABILITY_NAME")]
public class TODO_NEW_ABILITY_NAME : CharacterAbility
{
/// This method is only used to display a helpbox text
/// at the beginning of the ability's inspector
public override string HelpBoxText() { return "TODO_HELPBOX_TEXT."; }
[Header("TODO_HEADER")]
/// declare your parameters here
public float randomParameter = 4f;
public bool randomBool;
protected const string _yourAbilityAnimationParameterName = "YourAnimationParameterName";
protected int _yourAbilityAnimationParameter;
/// <summary>
/// Here you should initialize our parameters
/// </summary>
protected override void Initialization()
{
base.Initialization();
randomBool = false;
}
/// <summary>
/// Every frame, we check if we're crouched and if we still should be
/// </summary>
public override void ProcessAbility()
{
base.ProcessAbility();
}
/// <summary>
/// Called at the start of the ability's cycle, this is where you'll check for input
/// </summary>
protected override void HandleInput()
{
// here as an example we check if we're pressing down
// on our main stick/direction pad/keyboard
if (_inputManager.PrimaryMovement.y < -_inputManager.Threshold.y)
{
DoSomething();
}
}
/// <summary>
/// If we're pressing down, we check for a few conditions to see if we can perform our action
/// </summary>
protected virtual void DoSomething()
{
// if the ability is not permitted
if (!AbilityPermitted
// or if we're not in our normal stance
|| (_condition.CurrentState != CharacterStates.CharacterConditions.Normal)
// or if we're grounded
|| (!_controller.Grounded))
{
// we do nothing and exit
return;
}
// if we're still here, we display a text log in the console
MMDebug.DebugLogTime("We're doing something yay!");
}
/// <summary>
/// Adds required animator parameters to the animator parameters list if they exist
/// </summary>
protected override void InitializeAnimatorParameters()
{
RegisterAnimatorParameter(_yourAbilityAnimationParameterName, AnimatorControllerParameterType.Bool, out _yourAbilityAnimationParameter);
}
/// <summary>
/// At the end of the ability's cycle,
/// we send our current crouching and crawling states to the animator
/// </summary>
public override void UpdateAnimator()
{
bool myCondition = true;
MMAnimatorExtensions.UpdateAnimatorBool(_animator, _yourAbilityAnimationParameter, myCondition, _character._animatorParameters);
}
}
}