开启辅助访问
 找回密码
 注册帐号

扫一扫,访问微社区

蛮牛译馆

关注:590

当前位置:游戏蛮牛 技术专区 蛮牛译馆

查看: 2834|回复: 46

[Unity教程] 在Unity中创建攻击Slot系统

[复制链接]  [移动端链接]
排名
8076
昨日变化
2

263

主题

301

帖子

1525

积分

Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15Rank: 15

UID
159865
好友
9
蛮牛币
3979
威望
0
注册时间
2016-8-1
在线时间
626 小时
最后登录
2017-11-19

蛮牛译员

发表于 2017-10-15 13:21:55 | 显示全部楼层 |阅读模式

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

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

x
在Unity中创建攻击Slot系统
finished (1).gif


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


什么是Attack Slots

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

没有Attack Slots
manywithoutslots.gif


带有Attack Slots
finished.gif


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


创建场景
InitialBeforeNavmesh.png

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


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

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


你应该看到这样的东西:
InitialSetup.png



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



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


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<NavMeshAgent> ().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<NavMeshAgent> ().destination = tpos + offset;
                }
        }
}


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



这只是一个敌人。 很多敌人看起来不太好:
manywithoutslots (1).gif



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


创建Slot Manager

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


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


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



获得Slots的位置


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

每个线框球代表一个槽,第一个槽是顶部的。 之后插槽顺时针旋转。 以下是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<NavMeshAgent> ().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 <= index || slots [index] == null)
                                Gizmos.color = Color.black;
                        else
                                Gizmos.color = Color.red;
                        Gizmos.DrawWireSphere (GetSlotPosition (index), 0.5f);
                }
        }


基本上,在编辑器中这样做是为了显示每个插槽。 在播放模式下,如果它们已被保留,它会将颜色显示为红色。


更新敌方控制器

最后一件事是向EnemyController添加一些将使用这个新系统的代码。 我们需要添加一个新的私有变量来保存当前保留的插槽,然后更改更新功能:


[C#] 纯文本查看 复制代码
public class EnemyController : MonoBehaviour {
        GameObject target = null;
        float pathTime = 0f;
        int slot = -1;
        // 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 slotManager = target.GetComponent<SlotManager> ();
                        if (slotManager != null)
                        {
                                if (slot == -1)
                                        slot = slotManager.Reserve (gameObject);
                                if (slot == -1)
                                        return;
                                var agent = GetComponent<NavMeshAgent> ();
                                if (agent == null)
                                        return;
                                agent.destination = slotManager.GetSlotPosition (slot);
                        }
                }
        }
}


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


打包和未来的改进

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



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


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







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


回复

使用道具 举报

6蛮牛粉丝
1174/1500
排名
6764
昨日变化
72

2

主题

858

帖子

1174

积分

Rank: 6Rank: 6Rank: 6

UID
241666
好友
0
蛮牛币
4574
威望
0
注册时间
2017-9-6
在线时间
164 小时
最后登录
2017-11-20
发表于 2017-10-16 07:45:39 来自Mobile--- | 显示全部楼层
感谢分享

回复

使用道具 举报

5熟悉之中
570/1000
排名
5195
昨日变化
2

6

主题

236

帖子

570

积分

Rank: 5Rank: 5

UID
176358
好友
0
蛮牛币
182
威望
0
注册时间
2016-11-4
在线时间
118 小时
最后登录
2017-11-16
发表于 2017-10-16 08:57:21 | 显示全部楼层

回复

使用道具 举报

5熟悉之中
959/1000
排名
2808
昨日变化
1

3

主题

128

帖子

959

积分

Rank: 5Rank: 5

UID
45560
好友
1
蛮牛币
14
威望
0
注册时间
2014-9-18
在线时间
418 小时
最后登录
2017-11-17
发表于 2017-10-16 09:00:03 | 显示全部楼层
感谢分享

回复

使用道具 举报

3偶尔光临
187/300
排名
10073
昨日变化
136

0

主题

70

帖子

187

积分

Rank: 3Rank: 3Rank: 3

UID
229748
好友
0
蛮牛币
230
威望
0
注册时间
2017-7-1
在线时间
41 小时
最后登录
2017-11-19
发表于 2017-10-16 09:02:42 | 显示全部楼层
6666 收藏了 支持了!!!

回复 支持 反对

使用道具 举报

7日久生情
2636/5000
排名
5558
昨日变化
3

4

主题

2186

帖子

2636

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
209186
好友
4
蛮牛币
3155
威望
0
注册时间
2017-3-1
在线时间
252 小时
最后登录
2017-11-17
发表于 2017-10-16 09:20:10 | 显示全部楼层
感谢分享

回复

使用道具 举报

排名
2545
昨日变化
2

5

主题

58

帖子

814

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
146655
好友
1
蛮牛币
1233
威望
0
注册时间
2016-4-25
在线时间
299 小时
最后登录
2017-11-15
发表于 2017-10-16 09:27:00 | 显示全部楼层
player

回复

使用道具 举报

6蛮牛粉丝
1223/1500
排名
1245
昨日变化

0

主题

19

帖子

1223

积分

Rank: 6Rank: 6Rank: 6

UID
64643
好友
2
蛮牛币
2437
威望
0
注册时间
2014-12-30
在线时间
438 小时
最后登录
2017-11-18
发表于 2017-10-16 09:30:44 | 显示全部楼层
很厉害啊

回复

使用道具 举报

0

主题

43

帖子

78

积分

Rank: 2Rank: 2

UID
202756
好友
0
蛮牛币
12
威望
0
注册时间
2017-1-17
在线时间
35 小时
最后登录
2017-11-17
发表于 2017-10-16 09:37:51 | 显示全部楼层
很厉害啊

回复

使用道具 举报

7日久生情
2186/5000
排名
546
昨日变化

1

主题

285

帖子

2186

积分

Rank: 7Rank: 7Rank: 7Rank: 7

UID
2219
好友
1
蛮牛币
5496
威望
0
注册时间
2013-8-19
在线时间
698 小时
最后登录
2017-11-18
发表于 2017-10-16 09:44:04 | 显示全部楼层
这机器翻译得令人感动

回复 支持 反对

使用道具 举报

3偶尔光临
178/300
排名
16039
昨日变化
5

0

主题

108

帖子

178

积分

Rank: 3Rank: 3Rank: 3

UID
211096
好友
1
蛮牛币
81
威望
0
注册时间
2017-3-10
在线时间
41 小时
最后登录
2017-10-24
发表于 2017-10-16 09:52:33 | 显示全部楼层
6666666666666666666

回复 支持 反对

使用道具 举报

5熟悉之中
750/1000
排名
2584
昨日变化

4

主题

78

帖子

750

积分

Rank: 5Rank: 5

UID
127539
好友
0
蛮牛币
3464
威望
0
注册时间
2015-11-1
在线时间
222 小时
最后登录
2017-11-8
QQ
发表于 2017-10-16 09:54:26 | 显示全部楼层
6666666666666666

回复 支持 反对

使用道具 举报

4四处流浪
452/500
排名
5411
昨日变化
1

0

主题

154

帖子

452

积分

Rank: 4

UID
214896
好友
0
蛮牛币
143
威望
0
注册时间
2017-3-28
在线时间
98 小时
最后登录
2017-11-17
发表于 2017-10-16 10:35:04 | 显示全部楼层
感谢分析

回复

使用道具 举报

5熟悉之中
639/1000
排名
4546
昨日变化

0

主题

241

帖子

639

积分

Rank: 5Rank: 5

UID
122160
好友
0
蛮牛币
1271
威望
0
注册时间
2015-9-10
在线时间
150 小时
最后登录
2017-11-17
发表于 2017-10-16 10:49:23 | 显示全部楼层
感谢分享
[发帖际遇]: Vincher 发帖时在路边捡到 2 蛮牛币,偷偷放进了口袋. 幸运榜 / 衰神榜

回复

使用道具 举报

3偶尔光临
151/300
排名
9813
昨日变化
4

0

主题

27

帖子

151

积分

Rank: 3Rank: 3Rank: 3

UID
245707
好友
0
蛮牛币
180
威望
0
注册时间
2017-9-24
在线时间
44 小时
最后登录
2017-11-18
发表于 2017-10-16 10:49:26 | 显示全部楼层
感谢分享

回复

使用道具 举报

您需要登录后才可以回帖 登录 | 注册帐号

本版积分规则

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