http://www.manew.com/thread-109310-1-1.html

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

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

x

在Unity中创建攻击Slot系统
<ignore_js_op> 
如果您打算在3D(或自顶向下的)游戏中有多个攻击玩家的敌人,那么您将需要一个攻击槽系统。 在我们的最后一篇科技文章中,我们开始构建一个航点路径系统。 我们继续使用AI教程并构建攻击槽系统! 这里的一个重要细节是,该系统实际上并不适用于基于路基的系统,因此我们将使用Unity内置的navmesh支持跟踪slot效果
 
什么是Attack Slots
攻击槽系统通常相当简单,可以使战斗看起来更好。 基本上,他们的做法是为每个攻击者分配一个攻击位置,以便攻击者不受束缚,位置可以是围绕攻击者,或者正面排列面对玩家。 如果您一次只希望有一个攻击者,那这个系统就不会有任何帮助,但是有两个以上的攻击者这可能是一个非常有用的工具。
没有Attack Slots
<ignore_js_op> 
带有Attack Slots
<ignore_js_op> 
应该很清楚的是,在Attack Slots系统中,AI控制的实体看起来更加聪明,行为更有条理。
 
创建场景
<ignore_js_op> 
首先要做的是与玩家和敌人一起创建一个简单的场景。 我刚刚添加了一个plane和一些立方体和气缸的障碍物。 然后为玩家以及敌人创建了一个胶囊。
确保您已将导航窗口打开并对焦。 然后创建NavMesh,选择plane,立方体和圆柱体(但不是播放器和敌人),并在导航窗口的对象选项卡上选择导航静态:
<ignore_js_op> 
现在你可以去转到烘焙选项卡,点击烘焙按钮:
<ignore_js_op> 
你应该看到这样的东西:
<ignore_js_op> 
下一步是将Nav Mesh Agent组件添加到Enemy和Player中:
<ignore_js_op> 
不要担心任何设置,如果你不想要的。
 
player控制器
让我们制作一个简单的PlayerController脚本,以便我们可以移动播放器:
[C#] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
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#] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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个单位的偏移量,然后将其设置为路径目的地。 将此组件附加到敌人。 这就是没有攻击槽系统的东西。
<ignore_js_op> 
这只是一个敌人。 很多敌人看起来不太好:
<ignore_js_op> 
这通常会使你的敌人看起来不智能,如果你是在根据目标的距离进行攻击,那么背后的敌人可能不会被攻击。 如果他们都可以在玩家身上摆动,这样他们可以更快地杀死他们会更好! 这是我们的AttackSlots引进的原因。
 
创建Slot Manager
我们可以创建一个新的文件 SlotManager并添加一些初始代码:
 
[C#] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
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排列成一个圆圈:
<ignore_js_op> 
每个线框球代表一个槽,第一个槽是顶部的。 之后插槽顺时针旋转。 以下是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#] 纯文本查看 复制代码
 
1
2
3
public void Release(int slot)
{
        slots [slot] = null;
}
如果你紧跟我的步骤,下面要做的并不奇怪。 所有需要做的是将哪个信号保留为空的插槽设置为空,以供下一个攻击者使用。
 
添加一个调试演示
还有一件事我们可以做,如果需要,这是添加一些Gizmos像在上面的图像。 为此,我们可以定义OnDrawGizmosSelected:
 

[C#] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
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#] 纯文本查看 复制代码
 
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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

在Unity中创建攻击Slot系统的更多相关文章

  1. 在Unity中创建可远程加载的.unity3d包

    在一个Unity项目中,发布包本身不一定要包括所有的Asset(译为资产或组件),其它的部分可以单独发布为.unity3d,再由程序从本地/远程加载执行,这部分不在本文讨论范围.虽然Unity并没有直 ...

  2. Unity中创建二维码

    在网络上发现了一个可以把字符串转换成二维码的dll,但是我们要怎么使用他呢.不废话,直接进入主题. 用到的引用 using UnityEngine;using ZXing;using ZXing.Qr ...

  3. 在Unity中创建VR游戏

    添加VR插件为了为您选择的平台创建VR游戏,我们需要下载几个插件.出于本教程的目的,我将向您展示如何上传到Android平台.要上传到iOS,您需要下载 Xcode. 现在让我们下载Unity的Goo ...

  4. 关于Unity中旧版动画系统的使用

    Unity在5.X以后,有一个旧版的动画系统和新版的动画系统. 新版的动画系统是使用Unity动画编辑器来调的,调动画和控制动画 旧版的动画系统是用其他的第三方软件调好后导出到一个FBX文件里面,就是 ...

  5. unity 中UGUI制作滚动条视图效果(按钮)

    1.在unity中创建一个Image作为滚动条视图的背景: 2.在Image下创建一个空物体,在空物体下创建unity自带的Scroll View组件: 3.对滑动条视图的子物体进行调整: 4.添加滚 ...

  6. Unity中通过深度优先算法和广度优先算法打印游戏物体名

    前言:又是一个月没写博客了,每次下班都懒得写,觉得浪费时间.... 深度优先搜索和广度优先搜索的定义,网络上已经说的很清楚了,我也是看了网上的才懂的,所以就不在这里赘述了.今天讲解的实例,主要是通过自 ...

  7. 【Unity Shaders】Reflecting Your World —— 在Unity3D中创建Cubemaps

    本系列主要参考<Unity Shaders and Effects Cookbook>一书(感谢原书作者),同时会加上一点个人理解或拓展. 这里是本书所有的插图.这里是本书所需的代码和资源 ...

  8. Unity 4.0 中的新动画系统——MecAnim

    分享一个文档资料,关于动画系统的,版本应该很老了,但是有借鉴意义的: Unity 4.0 已于 2012 年 11 月 15 日正式发布,Unity 每一次版本的提升,都给游戏开发者带来惊喜,这一次也 ...

  9. springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑。为了区别不同的异常通常根据异常类型自定义异常类,这里我们创建一个自定义系统异常,如果controller、service、dao抛出此类异常说明是系统预期处理的异常信息。

    springmvc在处理请求过程中出现异常信息交由异常处理器进行处理,自定义异常处理器可以实现一个系统的异常处理逻辑. 1.1 异常处理思路 系统中异常包括两类:预期异常和运行时异常RuntimeEx ...

随机推荐

  1. 微信运动数据抓取(Python)

    "微信运动"能够向朋友分享一个包含有运动数据的网页,网页中就有我们需要的数据.url类似于:http://hw.weixin.qq.com/steprank/step/person ...

  2. Echarts+WPF

    C# using System; using System.Collections.Generic; using System.IO; using System.Linq; using System. ...

  3. C语言中无符号与有符号问题

    unsigned char a[5] = { 12,36,96,128,182 }; a[]范围为0~256. 数组中数都有效. char a[5] = { 12,36,96,128,182 }; a ...

  4. Android学习笔记 - 开始

    因为项目需求,要在Android上开发一个证件识别软件,项目时间 9/10- 9/30 工作内容: (1)修改证件识别库 (2)移植证件识别库至Android (3)开发一个Android应用程序 学 ...

  5. C# 由范式编程==运算符引发对string内存分配的思考

    今天在看C#编程指南时(类型参数的约束http://msdn.microsoft.com/zh-cn/library/d5x73970.aspx)看到一段描述: 在应用 where T : class ...

  6. WPF的Image控件图片不能显示出来的问题探究

    在wpf项目中,用Image来显示资源图片,在界面是可以显示,但是在运行的时候却显示不出来. <Image Source=" HorizontalAlignment="Lef ...

  7. 原码、反码、补码及位操作符,C语言位操作

    计算机中的所有数据均是以二进制形式存储和处理的.所谓位操作就是直接把计算机中的二进制数进行操作,无须进行数据形式的转换,故处理速度较快. 1.原码.反码和补码 位(bit) 是计算机中处理数据的最小单 ...

  8. 20165219 学习基础与C语言基础调查

    学习基础与C语言基础调查 你有什么技能比大多数人要好? 因为不知道其他人的具体情况,我只能说,我比较擅长钢琴,素描,国画,这也是小时候掌握的比较好的技能. 针对这个技能的获取有什么成功的经验 小时候学 ...

  9. python-webdriver库之Keys

    在使用webdriver时,有些时候我们需要做一些键盘上特殊键的操作,例如backspace,ctrl,shift等,这个时候就需要用到webdriver.common.keys.Keys方法来进行 ...

  10. 题解 P1534 【不高兴的津津(升级版)】

    题目链接 不算太难.就是题目有歧义. wa了好几次才发现.上一天要是小于8的话.结算是昨天一个负值在加上今天课时数.再减去8.233.... 而不是上一天小于8个小时.就清零了..大家要注意(ps:题 ...