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. jenkins slave Windows 2008 R2

    布置jenkins,添加节点(win2008R2) 配置节点参考: http://www.cnblogs.com/juddhu/archive/2013/07/18/3198191.html 生效la ...

  2. asp.net winform 界面传值

    第一种 //form1 //静态传值 public static string Chuanzhi; string Chuanzhi = textbox.text; //form2 string Chu ...

  3. DOM--sql server

    public List<LianHeData> select(int ID) { List<LianHeData> list = new List<LianHeData& ...

  4. WinForm中TabControl的使用

    TabControl和TabPage之间有一个默认颜色的边框,很难去除,需要重写TabControl控件重绘区域 public class FullTabControl : TabControl { ...

  5. 八、Node.js-http模块

    JS代码如下: /* 如果我们使用PHP来编写后端的代码时,需要Apache 或者 Nginx 的HTTP 服务器,并配上 mod_php5 模块和php-cgi,来处理客户端的请求相应. 不过对 N ...

  6. C# LINQ(3)

    我们还是接着讨论一下group by 这一章节讨论group的本质:分组. 分组之后进行存储或者查询. 这个时候就要用一个新的关键字:into 这个之后就group就不作为结尾了. 必须重写另起sel ...

  7. 857. Minimum Cost to Hire K Workers

    There are N workers.  The i-th worker has a quality[i] and a minimum wage expectation wage[i]. Now w ...

  8. [SinGuLaRiTy] 2017 百度之星程序设计大赛 初赛A

    [SinGuLaRiTy-1036] Copyright (c) SinGuLaRiTy 2017. All Rights Reserved. 小C的倍数问题 Time Limit: 2000/100 ...

  9. django 重写User表增加字段设置

    models中: from django.contrib.auth.models import AbstractUser lass User(AbstractUser): mobile = model ...

  10. Pycharm使用的一些问题!!!

    1.pycharm如何更改主题.如何更改字体的大小? 2.pycharm如何import第三方库? 1.更改主题和字体大小 主题变暗,字体变大! 2.如何导入第三方库?