在Unity中创建攻击Slot系统

『技巧 | 经验』 版权:禁止匿名转载;禁止商业使用。

1908 0 0 2019-04-17 举报

CG素材

未设置职业

在Unity中创建攻击Slot系统







如果您打算在3D(或自顶向下的)游戏中有多个攻击玩家的敌人,那么您将需要一个攻击槽系统。 在我们的最后一篇科技文章中,我们开始构建一个航点路径系统。 我们继续使用AI教程并构建攻击槽系统! 这里的一个重要细节是,该系统实际上并不适用于基于路基的系统,因此我们将使用Unity内置的navmesh支持跟踪slot效果




什么是Attack Slots


攻击槽系统通常相当简单,可以使战斗看起来更好。 基本上,他们的做法是为每个攻击者分配一个攻击位置,以便攻击者不受束缚,位置可以是围绕攻击者,或者正面排列面对玩家。 如果您一次只希望有一个攻击者,那这个系统就不会有任何帮助,但是有两个以上的攻击者这可能是一个非常有用的工具。


没有Attack Slots







带有Attack Slots







应该很清楚的是,在Attack Slots系统中,AI控制的实体看起来更加聪明,行为更有条理。




创建场景






首先要做的是与玩家和敌人一起创建一个简单的场景。 我刚刚添加了一个plane和一些立方体和气缸的障碍物。 然后为玩家以及敌人创建了一个胶囊。



确保您已将导航窗口打开并对焦。 然后创建NavMesh,选择plane,立方体和圆柱体(但不是播放器和敌人),并在导航窗口的对象选项卡上选择导航静态:






现在你可以去转到烘焙选项卡,点击烘焙按钮:







你应该看到这样的东西:








下一步是将Nav Mesh Agent组件添加到Enemy和Player中:








不要担心任何设置,如果你不想要的。




player控制器


让我们制作一个简单的PlayerController脚本,以便我们可以移动播放器:


[C#] 纯文本查看 复制代码
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AI;public class PlayerController : MonoBehaviour { // Use this for initialization void Start () { } // Update is called once per frame void Update () { if (Input.GetMouseButtonDown (0)) { var mpos = Input.mousePosition; mpos.z = 10; var ray = Camera.main.ScreenPointToRay (mpos); RaycastHit hit; if (Physics.Raycast (ray, out hit)) { GetComponent ().destination = hit.point; } } }}



在更新中,我们只是检查鼠标左键是否按下此框。 如果是这样,通过屏幕将光线投射到鼠标的位置。 如果它碰到任何东西,指导玩家移动到那一点。 所以现在我们可以点击左键移动player。 将此组件附加到player。




初始敌军控制器


现在来简单的创建一下EnemyController:




[C#] 纯文本查看 复制代码
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AI;public class EnemyController : MonoBehaviour { GameObject target = null; float pathTime = 0f; // Use this for initialization void Start () { target = GameObject.Find ("Player"); } // Update is called once per frame void Update () { pathTime += Time.deltaTime; if (pathTime > 0.5f) { pathTime = 0f; var tpos = target.transform.position; var offset = (transform.position - tpos).normalized * 1.5f; GetComponent ().destination = tpos + offset; } }}



首先,在开始,我们只是缓存播放器作为我们的目标。 然后在更新中,每0.5秒,我们得到玩家的位置,从我们的方向计算出1.5个单位的偏移量,然后将其设置为路径目的地。 将此组件附加到敌人。 这就是没有攻击槽系统的东西。








这只是一个敌人。 很多敌人看起来不太好:








这通常会使你的敌人看起来不智能,如果你是在根据目标的距离进行攻击,那么背后的敌人可能不会被攻击。 如果他们都可以在玩家身上摆动,这样他们可以更快地杀死他们会更好! 这是我们的AttackSlots引进的原因。




创建Slot Manager


我们可以创建一个新的文件 SlotManager并添加一些初始代码:




[C#] 纯文本查看 复制代码
using System.Collections;using System.Collections.Generic;using UnityEngine;public class SlotManager : MonoBehaviour{ private List slots; public int count = 6; public float distance = 2f; void Start() { slots = new List (); for (int index = 0; index < count; ++index) { slots.Add (null); } }}



SlotManager真的只包含一个用于GameObjects的插槽列表,然后是参数来定义要创建多少个插槽以及它们距离防御器的距离。 在开始,我们只是将这些插槽初始化为null。 该系统的工作方式,如果一个插槽为空,那么它是空的。 当它设置为一个GameObject时,它被认为在使用或者满了。





获得Slots的位置



我们需要一个函数来返回Slots的位置。 我们希望这些slot围绕GameObject排列成一个圆圈:






每个线框球代表一个槽,第一个槽是顶部的。 之后插槽顺时针旋转。 以下是GetSlotPosition的代码:


public Vector3 GetSlotPosition(int index)

{

float degreesPerIndex = 360f / count;

var pos = transform.position;

var offset = new Vector3 (0f, 0f, distance);

return pos + (Quaternion.Euler(new Vector3(0f, degreesPerIndex * index, 0f)) * offset);

}


因此,首先,我们计算每个索引的度数,以确定每个插槽有多远(角度方向)。 然后,我们旋转一个向量指向z方向的新向量,该插槽的度数,并将其添加到我们的位置以获得插槽位置。




预留槽位


现在要做一个方法来为攻击者预留一个插槽:


public int Reserve(GameObject attacker)

{

var bestPosition = transform.position;

var offset = (attacker.transform.position - bestPosition).normalized * distance;

bestPosition += offset;

int bestSlot = -1;

float bestDist = 99999f;

for (int index = 0; index < slots.Count; ++index)

{

if (slots [index] != null)

continue;

var dist = (GetSlotPosition (index) - bestPosition).sqrMagnitude;

if (dist < bestDist)

{

bestSlot = index;

bestDist = dist;

}

}

if (bestSlot != -1)

slots [bestSlot] = attacker;

return bestSlot;

}


这会变得更复杂一些。 您可能会从EnemyController的初始代码中识别前3行:


// EnemyController.cs

var tpos = target.transform.position;

var offset = (transform.position - tpos).normalized * 1.5f;

GetComponent ().destination = tpos + offset;


// SlotManager.cs

var bestPosition = transform.position;

var offset = (attacker.transform.position - bestPosition).normalized * distance;

bestPosition += offset;


如前所述,我们发现一个靠近防守者的位置在攻击者的方向。 这将是我们想要的槽的最佳位置,因为它理想地意味着不必走到通常看起来不好的防守者的另一边。 接下来,我们通过所有插槽,找到当前没有使用的最接近的插槽。 如果存在的话,我们将它与攻击者进行填充,所以没有人可以接受攻击。就是这样!




释放Slot



[C#] 纯文本查看 复制代码
public void Release(int slot) { slots [slot] = null; }




如果你紧跟我的步骤,下面要做的并不奇怪。 所有需要做的是将哪个信号保留为空的插槽设置为空,以供下一个攻击者使用。





添加一个调试演示


还有一件事我们可以做,如果需要,这是添加一些Gizmos像在上面的图像。 为此,我们可以定义OnDrawGizmosSelected:




[C#] 纯文本查看 复制代码
void OnDrawGizmosSelected() { for (int index = 0; index < count; ++index) { if (slots == null || slots.Count  0.5f) { pathTime = 0f; var slotManager = target.GetComponent (); if (slotManager != null) { if (slot == -1) slot = slotManager.Reserve (gameObject); if (slot == -1) return; var agent = GetComponent (); if (agent == null) return; agent.destination = slotManager.GetSlotPosition (slot); } } }}



现在,Update只是每0.5秒执行一次,但现在它在目标上得到了SlotManager。然后,如果没有分配槽,它会尝试预留一个槽。如果失败了,我们还没有别的办法,所以我们回来了。如果它有一个插槽,那么它只是使用GetSlotPosition将导航目标引导到插槽的位置。这就是它的一切!




打包和未来的改进


在此之前,您必须将SlotManager组件附加到player,但是一旦这样做,这里就是几个敌人的样子:




需要注意的一件事是使用一对攻击对手的攻击插槽。如果他们都有攻击槽,他们可能会尝试永远相互围绕。相反,您需要检查这种情况,并且让任何想要攻击的人首先获得攻击槽。另一个参与者应该瞄准自己的位置,使攻击者的位置与攻击者的位置匹配。




我们还可以添加一些改进,例如禁用在navmesh或墙壁另一侧的插槽,自动增加插槽数以适应攻击者的数量,或者我们可以根据不同的攻击范围添加多个攻击槽环。现在,我会把这些留给你来实现!
















原文标题:Building an Attack Slot System in Unity

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有帐号?立即注册

x

让资源更有价值

  • Archiver|
  • 手机版|
  • 小黑屋|
  • CG素材网
  • 蜀ICP备18003526号-3
  • Powered by Discuz! X3.4
  • © 2001-2017 Comsenz Inc.
  • GMT+8, 2024-4-27 03:20 , Processed in 0.330648 second(s), 34 queries .

 关注CG资源素材

快速回复 返回顶部 返回列表