[Unity]创建攻击Slot系统
来源:互联网 发布:淘宝达人后台在哪里 编辑:程序博客网 时间:2024/06/07 23:49
一、背景
在游戏中,如果有多个敌人攻击玩家,那么就需要slot系统。在这个小教程中,我们将创建一个waypoint pathing系统,系统中的每个小攻击者都是个AI。
什么是Attack Slots:在这个系统中,我们给每一个攻击者分配一个位置,这个位置可以在攻击者周围的任何地方。如果没有Attack Slots,攻击者AI可能会挤在玩家正面。Attack Slots系统使得AI更聪明一点。
二、创建场景
1. 创建场景物体
首先我们来创建我们的场景。
添加一个Plane作为地面,一些Cube、Cylinder作为障碍物。然后添加一个Capsule作为玩家,另一个Capsule作为敌人。
2. 创建NavMesh
在windows-Navigation里打开Navigation窗口。
选择地面、障碍物(注意不要选择Player和Enemy),在Navigation-Object子窗口里勾选Navigation Static
然后可以在Bake子窗口里点击“Bake”按钮进行烘培了。烘培出结果如下(可适当调整Bake窗口里的参数)。
3. 为Player和Enemy添加组件
1) 为Player和Enemy添加Nav Mesh Agent组件
2) 为Player添加一个控制脚本PlayerController,来控制Player在场景中的移动。
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)) { Vector3 mpos = Input.mousePosition; mpos.z = 10; Ray ray = Camera.main.ScreenPointToRay(mpos); RaycastHit hit; if(Physics.Raycast(ray, out hit)) { this.GetComponent<NavMeshAgent>().destination = hit.point; } } }}
这个脚本的作用就是,当鼠标点击某物体时,Player移动到鼠标点击的位置。
3)为Enemy添加脚本EnemyController
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AI;public class EnemyController : MonoBehaviour { public GameObject target = null; // 攻击目标 float pathTime = 0f; // Use this for initialization void Start () { } // Update is called once per frame void Update () { pathTime += Time.deltaTime; if(pathTime > 0.5f) { pathTime = 0f; Vector3 targetPos = target.transform.position; // 攻击目标的世界坐标 Vector3 offset = (this.transform.position - targetPos).normalized * 1.5f; this.GetComponent<NavMeshAgent>().destination = targetPos + offset; } }}
首先,我们把Player设为EnemyController的target,而后每0.5f获取target的位置。targetPos+offset就是Enemy的目标位置。这就是一个基本的没有Attack Slot的系统。但这样的问题是,当有很多个Enemy时,这些Enemy AI就会朝着同样的位置移动,它们会挤在一起。
四、创建Slot Manager
1) 创建SlotManager脚本
using System.Collections;using System.Collections.Generic;using UnityEngine;public class SlotManager : MonoBehaviour { List<GameObject> slots; public int count = 6; public float distance = 2f; // Use this for initialization void Start () { slots = new List<GameObject>(); for(int i = 0; i < count; i++) { slots.Add(null); } } // Update is called once per frame void Update () { }}
在SlotManager中维护一个GameObject列表,上述代码对slots进行初始化,初始化后包含了6个空物体。
2) 在slots中,每个列表元素(即一个slot)代表一个Enemy,我们需要获取每个slot的坐标,使得slot围绕着攻击目标,而不是挤在一起。因此在上述脚本中添加一个函数GetSlotPosition,以获得Player周围一圈的坐标。
public Vector3 GetSlotPosition(int index) { float degrresPerIndex = 360f / count; Vector3 pos = transform.position; Vector3 offset = new Vector3(0, 0, distance); return pos + (Quaternion.Euler(new Vector3(0, degrresPerIndex * index, 0f)) * offset); }
3) 在SlotManager.cs里添加Reserve函数,为某个Enemy挑选一个最佳slot
// 为攻击者设置一个slot public int Reserve(GameObject attacker) { Vector3 bestPosition = transform.position; Vector3 offset = (attacker.transform.position - bestPosition).normalized; bestPosition += offset*distance; // 不用slot时的位置 int bestSlot = -1; float bestDist = 9999f; for(int i=0;i<slots.Count; i++) { if (slots[i] != null) continue; float dist = (GetSlotPosition(i) - bestPosition).sqrMagnitude; if(dist < bestDist) // 在所有slots里找出离bestPosition最近的那个空闲slot { bestSlot = i; bestDist = dist; } } if (bestSlot != -1) slots[bestSlot] = attacker; return bestSlot; }
4) 释放slot
public void Release(int slot) { slots[slot] = null; }
5) 添加一些可视化效果,方便Debug
Gizmos: Gizmos用于可视化调试,所有的Gizmos绘制都必须在脚本下的OnDrawGizmos或OnDrawGizmosSelected函数中完成。OnDrawGizmos在每一帧都被调用,所有在OnDrawGizmos内部渲染的Gizmos都是可见的。OnDrawGizmosSelected仅在脚本所附加的物体被选中时调用。
void OnDrawGizmosSelected() { for(int i=0;i<count;i++) { if (slots == null || slots.Count <= i || slots[i] == null) Gizmos.color = Color.black; else Gizmos.color = Color.red; Gizmos.DrawWireSphere(GetSlotPosition(i), 0.5f); } }
当某个slot空闲时,该slot的位置上会画一个黑色的球。当某个slot被一个enemy占据时,该位置会画上一个红色的球。
五、修改EnemyController
EnemyController中控制Enemy的移动,现在要把Enemy移动的目标位置修改为slot的位置。修改后的EnemyController.cs如下
using System.Collections;using System.Collections.Generic;using UnityEngine;using UnityEngine.AI;public class EnemyController : MonoBehaviour { public GameObject target = null; // 攻击目标 float pathTime = 0f; int slot = -1; // Use this for initialization void Start () { } // Update is called once per frame void Update () { pathTime += Time.deltaTime; if(pathTime > 0.5f) { pathTime = 0f; /* Vector3 targetPos = target.transform.position; // 攻击目标的世界坐标 Vector3 offset = (this.transform.position - targetPos).normalized * keepGap; this.GetComponent<NavMeshAgent>().destination = targetPos + offset; */ SlotManager slotManager = target.GetComponent<SlotManager>(); if(slotManager != null) { if (slot == -1) slot = slotManager.Reserve(gameObject); if (slot == -1) return; NavMeshAgent agent = this.GetComponent<NavMeshAgent>(); if (agent == null) return; agent.destination = slotManager.GetSlotPosition(slot); } } }}
修改为以上的移动方式后,对于某个enemy,会为其保留一个slot,而后每0.5s更新enemy的位置,向这个slot移动。
六、为Player添加Component
把SlotManager.cs添加给Player
七、可改进的地方
- slot数量可根据游戏情况自动增加,或者根据不同的攻击范围来添加多个slot。
- [Unity]创建攻击Slot系统
- Unity中创建攻击Slot系统
- Slot
- slot
- slot
- slot
- Unity攻击判定
- unity人物攻击类
- UGUI--背包系统之三--------Slot
- Unity中实现高效Signal-slot模式--高效消息分发
- Unity 用户手册用户指南创建游戏旧动画系统
- 【unity&文章】攻击敌人判定
- 【Unity】unity 系统图
- 门控系统的攻击
- Unity一个智能火力点,根据攻击范围自动旋转攻击
- Unity一个智能火力点,根据攻击范围自动旋转攻击
- [Unity基础]arpg中的攻击判定
- [Unity实战]攻击范围的绘制
- SQL语句
- BZOJ1045(HAOI2008)[糖果传递]&&BZOJ3293(Cqoi2011)[分金币]--中位数
- TotoiseSVN的上手教程
- python基础知识三——try与except处理异常语句
- 不同的10月英语
- [Unity]创建攻击Slot系统
- LeetCode--350. Intersection of Two Arrays II
- python .extend() and append()函数
- Spring web Mvc
- [枚举+三分] Codeforces #626E. Simple Skewness
- 对函数与结构体的浅薄认识
- linux中的软件管理
- BZOJ 3622 DP+容斥
- 匿名内部类 使用外部的变量为什么要声明成final