本文记录TPS Documentation阅读过程,本篇讲述了此插件的基本配置与几个代码相关的系统与相关的代码案例.本篇基本上是翻译全文,有任何问题可留言,有兴趣的话推荐你阅读原文

项目导入设置

Layers and tags

添加程会使用到的 Layers & tag.

场景管理

当前有3个场景在游戏中,其中两个是包含玩家的,WeaponTest场景是测试可用物件的。有这几个prefab(Player Remy, CamRig, 3Dcanvas01Follow, EnvFx, 2Dcanvas)

  • Camrig & EnvFx 必须要在场景中,否则会报错。为了让 3Dcanvas01Follow 与 2Dcanvas 正常工作,需要有一个可用的player在场景中。
  • 当前场景中只能又一个player处于激活状态。有多个则取消其激活。
  • 如果你拖动一个 player prefa b到场景中,但是这个 player prefab 不能处于平台之上,意味着这个平台的layer并不实代码中支持的那个layer。
  • 你需要在player身上的 SetUpAndUserInput 组建中添加 CamRig 的引用(直接拖)
  • 所有的玩家设置完毕之后使用 player prefab 身上的组件 PlayerAtts ,指派新武器,指派的武器需要在场景中处于可用状态,不是prefabs,是要你在指派之前放到场景里。
  • 如果你用Tps这个project,在导入之后会自动进行上面的设置

控制

  • 所有的控制键位与逻辑都在 SetUpAndUserInput 脚本中可以找到。项目的默认设置没有进行过更改;

a. Keyboard and mouse 1: Keyboard and mouse
a. Locomotion: W/A/S/D
b. Sprint: Shift
c. Collect: E
d. Jump: Space
e. Pullout/Holster weapon: F
f. Throw grenade: G
g. Drop weapon: H
h. Crouch/Stand: C
i. Toggle Walk/Run: LeftAlt
j. Fire: Mouse0
k. Sight/Cover Aim: Mouse1
l. Hipfire Aim: Mouse0
m. Reload: R
n. SecondaryFire: Mouse2
o. Camera/Player rotation: Mouse
p. Modify weapon: V
q. Weapon Flashlight: T
r. Change weapon/throwable: Keyboard arrows
b. Touchscreen

  • 为了触摸屏的控制,MobileControlRig 这个prefab在监视面板中需要处于可用状态另外 Mobile Input也需要在unity editor中开启。移动端控制器用来拾取,移动相机,换武器… 记住移动控制的某些特性只在特定的支持设备上可用。
    触摸屏

更替角色(Character)

如果你的 Character 是 humanoid 的话(这里指Animation Type),你可以很轻易的设置成你的新player。你可以检查下你的 Rig -> configure ,如果所有的节点配置都是绿色的那么就没问题了。

角色快速配置

  • 检查下你的导入设置是否如图所示
    触摸屏
  • 创建个新场景或者直接用 WeaponTest 场景。把玩家prefab拖上来。复制一个然后重命名。把之前不用的旧 prefab 给关掉。如果导入的prefab太小了,对照之前的旧prefab 进行相应的调整。
  • 把你的角色模型拖到复制出来的player prefab里。移除模型身上的 Animator 然后我们使用 player上的Animator.把角色的 Avatar 拖到 player 的 Animator 组件中。
  • 为角色创建一个 Ragdoll(unity布娃娃)。GameObject -> 3D Object -> Ragdoll .然后把几个身体的部位设置下。

  • 在你的项目页签中 IntenseTPS/Prefabs/Player/SettingUp 。你可以看到一些prefab。在监视面板中

  1. 拖放 LeftHandHold prefab 到模型的 left hand 节点下。
  2. 拖放 RightHandHold prefab 到模型的 right hand 节点下。
  3. 拖放 WeaponIK prefab 到模型的 Head 节点下。
    最后重置这些组件的位置与旋转信息。
  • 把上一步加入的三个组件的位置进行一下修正 +/-90, +/-180 。如下图所示
    注意方向

  • 选中新 player 找到 Health 组件,我们使用这个脚本快速关闭所有 rigidbodies & ragdoll colliders。拖放 角色模型骨骼到这里。点击下(Get ragdoll colliders & rigidbodys)按钮。

  • 上一步点击按钮之后你会看见下图所示。
    顺序不重要

  • 选中角色模型,在layer中选择 PlayerBody 。

如果你的模型能正确的握住武器了那就证明没问题了。都搞定之后接着来阅读 Weapon System 部分吧。

玩家系统

在这个项目中玩家逻辑被分为多个部分,我称这些部分为 player systems.大多系统都有自己的被序列化的类。你可以直接在监视面板中更改。如果你想定义一个新系统,我非常建议你先阅读编码部分。

武器系统

这个系统几乎控制所有的武器,从拔枪到射击 等等。

为一个新的武器配置一个新player Animations

  1. Weapon System在一个Animator中使用了4个动画层(Animator Layers),RightArm, LeftArm, RightHand, LeftHand.LeftArm层模仿RightArm层。这个状态及名叫 Weapon 你看 LeftArm 层是copyRightArm层。如果你定义一个新的武器,只需要拷贝这个 Weapon 状态机然后改改就行了。
  2. 现在来看看 Weapon System 的动画融合树(animation blend trees) 如下图(RightArmLayer)
    动画融合树结构1
  3. 融合树对player使用的任何武器都适用
    1- PullOutWeapon
    2- IdleWithWeapon
    3- ReloadWeapon
    4- AimingWithWeapon
    你需要像下图那样修改融合树,我将会使用PullOutWeapon (1) 的blend trees来描述定义的动画,因为blend trees (2,3,4) 都是用的同样的blend tree。
    动画融合树结构2

  4. 如果你仔细看了上面的配置,PullOutWeapon blend tree 使用了Weapon Style参数来切换单个武器的动画。事实上这里并没有用到融合。Weapon Style的参数并不能定义为1.5或者2.3之类的。Blend tree在这里只是简单的应用。你可以在这里看到Threshold的准确信息。
    现在假说我有武器了,并且在握住武器的时候想用Weapon Style No:4 动画。我就只能添加一个motion到PullOut blend tree同时设置Threshold时间点为4。
    如果我们添加好了所有动画并且设置好了所有的在GunAtt组件上的 weaponStyle 字段,player在拿起武器的时候就会使用这些动画了。
    另外两个 animation/blend trees 都可以修改,方法同上。
    动画融合树结构3

定义一个新武器

你需要一个武器模型,在倒入模型之后,照着教程做。我将设置一个名为 Colt 的模型,让其在项目中可用。

  1. 将名为 AK47的武器拖入场景,设置成合适的大小。
    设置prefab
  2. 把新武器模型朝向/大小改的和旧武器模型差不多。加入AK47下作为子节点,把之前的旧模型干掉。
  3. 重命名AK47然后保存一个新的prefab。
    设置新prefab
  4. 如果武器动画是分离的,就需要创建Rigidbody, Box Collider,有必要重命名
  5. 设置Clip 的 collider box呈灰态,Rigidbody设置成 Non kinematic .
    设置新prefab
  6. 设置上一步的 clip 节点作为武器的一个子节点。
  7. 选择武器并且设置一些必须字段。CurClipObject 就是上一步提到的 Clip节点。CurClipPrefab 就是你导入的模型。
  8. 请给这个武器创建一个新的prefab
  9. 给这个武器添加 GunAtt,你在这个脚本里这个定义武器各方面的行为。我将在这里该一些基本的特性 武器名字,clip capacity,武器风格等。选择一个子弹模型到 CurrentProjectilePrefab 字段。
  10. 你可以看到武器的一些子节点,武器系统使用GunAtt来控制这些节点。EmptyShellPosRot 节点用来控制子弹初始化的位置/角度。TrailExitPos 节点用来控制射线或物体的位置。(这段话有问题 具体在项目中看)。勿要修改 FixHolderforCharacters 节点。
  • 武器可以射出特定口径的子弹。 这意味着如果玩家的武器剪辑为空,武器系统将尝试使用PlayerAtts ammoBag字段中相同的机芯子弹进行搜索。 口径号用于区分不同武器使用的子弹。
  • 如果武器没有使用射线投射,一些GunAtt字段将不会被使用,这意味着它们将被像RPG火箭弹那样的投射物覆盖
11. 现在指派这个新武器到 player 上。点击开始按钮你可以看到角色拿着武器站在那里


12. 如果你没有倒入新角色可以跳过 13 步骤。停止游戏,在监视面板中选中武器并且找到 GunAtt 之上的 CharacterFixHolders 字段。对于单个角色需要绑定(拖放)6个 Transfrom 。

13. 这段翻译有些长,你可以对照着下图看。

点击播放,抽出武器,首先修正右手的动画。你需要选中RightHandHold的子节点 PosRotFixer 。调整到一个合适的角度位置,然后拷贝这个节点(PosRotFixer)的ttransform信息,粘贴到 AnimRightHand 之上。
做完了这些点击 AnimRightHand 组件上的 按钮,这样的话即使你停止的游戏,也不会丢失刚才的更改。
修正其他节点的位置, AimSight, AimHipFire, AimCover, LeftHandClip 使用 PosRotFixers 节点 就上上面操作的那样。注意,只有 LeftHandle 会被直接修改。
注意:你可以使用player的 PressFire2Button 当你修正瞄准动画的时候,(你不要用 Fire1Button 来修复AimHipFirePosition)和修正瞄准时候的位置。
当你都做完之后,保存到了 temporary ,停止游戏然后点击 Load from temporary .

当角色(玩家或射手Ai)拔出武器时,将按您输入characterName字段的名称进行定位。 例如,当名为Player Remy的玩家拔出武器或瞄准它时,他将在GunAtt中使用具有匹配的字符名称的信息。

投掷系统

这个系统能够投掷,在游戏中会实例化prefab。 在项目中有一个Frag Grenade示例。
为了让玩家投掷预制,预制需要有3个主要组件。 Rigidbody, Collider,Exploder。 您可以修改投掷的Exploder组件以定义不同的可抛出行为。 Rigidbody和Collider有些可选,
但投掷系统将需要 Exploder组件在prefab上才能工作。

  1. 定义一个New Player的投掷动画
    1.1 你需要为投掷定义不同的玩家动画。作为一个例子,我已经添加了一个更多的throw动画,目前没有使用。投掷是在Player的animator的第四层完成的。
    如果你想为一个throwable添加一个不同的Player动画,你将需要遵循一个类似的方式,在武器部分示例的那样。
    我添加了一个混合树到ReadyIdle混合树导入player animation。 如果你不想使用Far/Short 只是创建一个motion。 我设置了Threshold为2,这样,当玩家拉出这个可抛物,他会使用这些动画。 修改像PullOut / ToReady这个例子类似 其他混合树。
    注意此图右边

相机系统

它的工作原理类似于一些Player系统,如Look At system.Generally,这是由其他系统,如武器,封面等使用。
例如武器系统想要相机聚焦到一个位置,它会告诉相机系统这个请求,如果相机没有更重要的工作,它会执行这个请求。
目前,该系统完全可以覆盖第三人称,第一人称,其他系统聚焦。

运动系统

Locomotion系统,正如其名称所解释的那样负责运动。 该系统有4个状态,可被其他系统覆盖。

  1. 用键自由移动
  2. 带键的静态运动(通常是转向一类的位置变换)
  3. 使用navmeshagent移动
  4. 停用状态

掩护系统

该系统负责玩家掩护(防护)的的每个方面。
掩护检测由玩家的子节点CoverChecker完成。 要修改或微调掩护检测,请使用此GameObject的CoverTargetLogic组件。 其他掩护参数可以通过Player的PlayerAtts组件修改。

观察系统

算法和代码部分

Player的基础

Player系统在理论上是完全独立的。 但在真实或游戏世界中,一般来说,这是不可能的。 因此,系统将总是尝试与其他系统通信。

我将尝试用几个例子来解释Player系统的工作流程,希望它能帮助你以后定义你的系统。

想象一下,武器被武器系统拉出,玩家击中目标按钮,然后再次武器系统正在播放瞄准动画,但是玩家也想在瞄准时向后移动,因此他/她按下移动返回按钮, S键。

如果两个系统工作而没有彼此通信,则player可能瞄准相机本身,因为Locomotion System通过转玩家来响应向后移动按钮。 然后你会得到一些有趣的行为。

现在让我们看看 IntenseTPS 在这样的情况如何工作。在我们的例子中,当按钮事件时触发时武器系统开始瞄准,Locomotion系统将上从武器系统得到一个消息,转向一个总是在Player和摄像机前面的对象。之后,他将始终瞄准并转向相机正在看的位置。
之后,当瞄准完成武器系统会告诉Locomotion系统,它不再需要转到一个位置。然后运动系统将返回其默认行为。

让我们假设玩家靠近墙壁,想要被掩护,他/她通过按掩护按钮进入掩体。掩护系统将进入掩体,它会要求相机覆盖自己的第三人相机状态与“掩体的特定参数”。假设它没有来自任何其他系统的其他请求,并根据Cover System的要求设置它自己。然后玩家拉出一个武器,想要瞄准,武器系统也会要求相机系统重写自己的第三人称相机,但与武器系统的参数。相机会选择哪一个?在这样的情况下,答案是最重要的。

一般的想法是,一个系统不能在一个参数都没有的情况下要求另一个系统改变它的状态。 该系统将如何使用该特殊参数的? 那么,这是一个系统本身需要解决的问题。

通过遵循所有Player系统的这个逻辑,当你添加一个新的系统,你不必担心,如果它将与其他系统工作。

除此之外,系统的关键触发器不会在没有特殊参数的情况下被调用。 例如投掷系统的按钮触发开始投掷,但有时我们不希望玩家投掷手榴弹时,当进掩体面时。

因此,掩体系统将修改投掷系统的触发器以在短时间内禁用其部分或全部触发器,使得我们不会错误地混淆动画。

代码例子

如果你已经阅读了上一部分(玩家系统基础),你应该知道玩家系统背后的基本逻辑。现在让我们来看看一些代码示例。
我将尝试解释我如何集成一些功能与代码示例。看看下面的代码。

Code Sample

这段代码取自Locomotion脚本,它的工作是进入第一人称照相机并退出。它也可以写成摄像机系统的一部分,因为在摄像机系统中没有其他按钮触发的事件,它被写为Locomotion System的一部分。
首先if语句检查第一人称相机按钮是否被按下,然后检查它是否已经处于第一人称相机模式,然后检查是否使用

如果你已经阅读了上一部分(玩家系统基础),你应该知道玩家系统背后的基本逻辑。现在让我们来看看一些代码示例。

我将尝试解释我如何集成一些功能与代码示例。看看下面的代码。

这段代码取自Locomotion脚本,它的工作是进入第一人称照相机并退出。它也可以写成摄像机系统的一部分,因为在摄像机系统中没有其他按钮触发的事件,它被写为Locomotion System的一部分。
首先if语句检查第一人称相机按钮是否被按下,然后检查它是否已经处于第一人称相机模式,然后用TriggS.LastValue.GetTrigger(LocomotionSystemTriggers.ct_FreeLook)代码检查是否使用启用了第一人称相机触发。
因此,如果名为Triggs的属性没有被任何其他系统播放器重写,将进入第一人称观察模式。

当第一人称观看模式被激活时,locomotion系统将以短整数(在这种情况下为-3)的方式告诉相机重写自身到第一人称相机状态。这意味着相机将重写自身到这个状态,即使它通过另一个事件被重写和改变它的状态。

然后这个触发器将尝试重写武器系统的触发器,这些触发器基本上是按钮触发的事件,例如拉出武器,瞄准等…
相同的请求也被发送到Cover System和Throw系统,最后它将发送请求到Look At系统 来设置其自身为激活状态。
我们实现了什么?
那么,在进入第一人称观看模式之后,即使他/她按下相关按钮,玩家将不能与武器交互,他/她将不能进入掩体面等。
所以如果你检查else if语句(退出第一人称看模式的条件),如果玩家按下按钮或者如果!TriggS.LastValue.GetTrigger(LocomotionSystemTriggers.ct_FreeLook)条件为真,玩家将退出第一人称模式 ,这意味着Locomotion系统触发器属性被另一个系统重写。