今天教大家制作王者荣耀中使用的技能范围指示器
类型包含:区域圆形、小范围圆形、矩形、扇形
参考下图:
代码已写好注释,有不懂的可以留言问我。
技能摇杆代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 |
using UnityEngine; using System.Collections; using UnityEngine.UI; using UnityEngine.EventSystems; using System; public class SkillJoystick : MonoBehaviour, IPointerDownHandler, IPointerUpHandler, IDragHandler { public float outerCircleRadius = 100; Transform innerCircleTrans; Vector2 outerCircleStartWorldPos = Vector2.zero; public Action<Vector2> onJoystickDownEvent; // 按下事件 public Action onJoystickUpEvent; // 抬起事件 public Action<Vector2> onJoystickMoveEvent; // 滑动事件 void Awake() { innerCircleTrans = transform.GetChild(0); } void Start() { outerCircleStartWorldPos = transform.position; } /// <summary> /// 按下 /// </summary> public void OnPointerDown(PointerEventData eventData) { innerCircleTrans.position = eventData.position; if (onJoystickDownEvent != null) onJoystickDownEvent(innerCircleTrans.localPosition / outerCircleRadius); } /// <summary> /// 抬起 /// </summary> public void OnPointerUp(PointerEventData eventData) { innerCircleTrans.localPosition = Vector3.zero; if (onJoystickUpEvent != null) onJoystickUpEvent(); } /// <summary> /// 滑动 /// </summary> public void OnDrag(PointerEventData eventData) { Vector2 touchPos = eventData.position - outerCircleStartWorldPos; if (Vector3.Distance(touchPos, Vector2.zero) < outerCircleRadius) innerCircleTrans.localPosition = touchPos; else innerCircleTrans.localPosition = touchPos.normalized * outerCircleRadius; if (onJoystickMoveEvent != null) onJoystickMoveEvent(innerCircleTrans.localPosition / outerCircleRadius); } } |
技能范围指示器代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 |
using UnityEngine; using System.Collections; using System.Collections.Generic; public enum SkillAreaType { OuterCircle = 0, OuterCircle_InnerCube = 1, OuterCircle_InnerSector = 2, OuterCircle_InnerCircle = 3, } public class SkillArea : MonoBehaviour { enum SKillAreaElement { OuterCircle, // 外圆 InnerCircle, // 内圆 Cube, // 矩形 Sector60, // 扇形 Sector120, // 扇形 } SkillJoystick joystick; public GameObject player; public SkillAreaType areaType; // 设置指示器类型 Vector3 deltaVec; float outerRadius = 6; // 外圆半径 float innerRadius = 2f; // 内圆半径 float cubeWidth = 2f; // 矩形宽度 (矩形长度使用的外圆半径) int angle = 60; // 扇形角度 bool isPressed = false; string path = "Effect/Prefabs/Hero_skillarea/"; // 路径 string circle = "quan_hero"; // 圆形 string cube = "chang_hero"; // 矩形 string sector60 = "shan_hero_60"; // 扇形60度 string sector120 = "shan_hero_120"; // 扇形120度 Dictionary<SKillAreaElement, string> allElementPath; Dictionary<SKillAreaElement, Transform> allElementTrans; // Use this for initialization void Start() { joystick = GetComponent<SkillJoystick>(); joystick.onJoystickDownEvent += OnJoystickDownEvent; joystick.onJoystickMoveEvent += OnJoystickMoveEvent; joystick.onJoystickUpEvent += OnJoystickUpEvent; InitSkillAreaType(); } void OnDestroy() { joystick.onJoystickDownEvent -= OnJoystickDownEvent; joystick.onJoystickMoveEvent -= OnJoystickMoveEvent; joystick.onJoystickUpEvent -= OnJoystickUpEvent; } void InitSkillAreaType() { allElementPath = new Dictionary<SKillAreaElement, string>(); allElementPath.Add(SKillAreaElement.OuterCircle, circle); allElementPath.Add(SKillAreaElement.InnerCircle, circle); allElementPath.Add(SKillAreaElement.Cube, cube); allElementPath.Add(SKillAreaElement.Sector60, sector60); allElementPath.Add(SKillAreaElement.Sector120, sector120); allElementTrans = new Dictionary<SKillAreaElement, Transform>(); allElementTrans.Add(SKillAreaElement.OuterCircle, null); allElementTrans.Add(SKillAreaElement.InnerCircle, null); allElementTrans.Add(SKillAreaElement.Cube, null); allElementTrans.Add(SKillAreaElement.Sector60, null); allElementTrans.Add(SKillAreaElement.Sector120, null); } void OnJoystickDownEvent(Vector2 deltaVec) { isPressed = true; this.deltaVec = new Vector3(deltaVec.x, 0, deltaVec.y); CreateSkillArea(); } void OnJoystickUpEvent() { isPressed = false; HideElements(); } void OnJoystickMoveEvent(Vector2 deltaVec) { this.deltaVec = new Vector3(deltaVec.x, 0, deltaVec.y); } void LateUpdate() { if(isPressed) UpdateElement(); } /// <summary> /// 创建技能区域展示 /// </summary> void CreateSkillArea() { switch (areaType) { case SkillAreaType.OuterCircle: CreateElement(SKillAreaElement.OuterCircle); break; case SkillAreaType.OuterCircle_InnerCube: CreateElement(SKillAreaElement.OuterCircle); CreateElement(SKillAreaElement.Cube); break; case SkillAreaType.OuterCircle_InnerSector: CreateElement(SKillAreaElement.OuterCircle); switch (angle) { case 60: CreateElement(SKillAreaElement.Sector60); break; case 120: CreateElement(SKillAreaElement.Sector120); break; default: break; } break; case SkillAreaType.OuterCircle_InnerCircle: CreateElement(SKillAreaElement.OuterCircle); CreateElement(SKillAreaElement.InnerCircle); break; default: break; } } /// <summary> /// 创建技能区域展示元素 /// </summary> /// <param name="element"></param> void CreateElement(SKillAreaElement element) { Transform elementTrans = GetElement(element); if (elementTrans == null) return; allElementTrans[element] = elementTrans; switch (element) { case SKillAreaElement.OuterCircle: elementTrans.localScale = new Vector3(outerRadius * 2, 1, outerRadius * 2) / player.transform.localScale.x; elementTrans.gameObject.SetActive(true); break; case SKillAreaElement.InnerCircle: elementTrans.localScale = new Vector3(innerRadius * 2, 1, innerRadius * 2) / player.transform.localScale.x; break; case SKillAreaElement.Cube: elementTrans.localScale = new Vector3(cubeWidth, 1, outerRadius) / player.transform.localScale.x; break; case SKillAreaElement.Sector60: case SKillAreaElement.Sector120: elementTrans.localScale = new Vector3(outerRadius, 1, outerRadius) / player.transform.localScale.x; break; default: break; } } Transform elementParent; /// <summary> /// 获取元素的父对象 /// </summary> /// <returns></returns> Transform GetParent() { if (elementParent == null) { elementParent = player.transform.FindChild("SkillArea"); } if (elementParent == null) { elementParent = new GameObject("SkillArea").transform; elementParent.parent = player.transform; elementParent.localEulerAngles = Vector3.zero; elementParent.localPosition = Vector3.zero; elementParent.localScale = Vector3.one; } return elementParent; } /// <summary> /// 获取元素物体 /// </summary> Transform GetElement(SKillAreaElement element) { if (player == null) return null; string name = element.ToString(); Transform parent = GetParent(); Transform elementTrans = parent.Find(name); if (elementTrans == null) { GameObject elementGo = Instantiate(Resources.Load(path + allElementPath[element])) as GameObject; elementGo.transform.parent = parent; elementGo.gameObject.SetActive(false); elementGo.name = name; elementTrans = elementGo.transform; } elementTrans.localEulerAngles = Vector3.zero; elementTrans.localPosition = Vector3.zero; elementTrans.localScale = Vector3.one; return elementTrans; } /// <summary> /// 隐藏所有元素 /// </summary> void HideElements() { if (player == null) return; Transform parent = GetParent(); for (int i = 0, length = parent.childCount; i < length; i++) { parent.GetChild(i).gameObject.SetActive(false); } } /// <summary> /// 隐藏指定元素 /// </summary> /// <param name="element"></param> void HideElement(SKillAreaElement element) { if (player == null) return; Transform parent = GetParent(); Transform elementTrans = parent.Find(element.ToString()); if (elementTrans != null) elementTrans.gameObject.SetActive(false); } /// <summary> /// 每帧更新元素 /// </summary> void UpdateElement() { switch (areaType) { case SkillAreaType.OuterCircle: break; case SkillAreaType.OuterCircle_InnerCube: UpdateElementPosition(SKillAreaElement.Cube); break; case SkillAreaType.OuterCircle_InnerSector: switch (angle) { case 60: UpdateElementPosition(SKillAreaElement.Sector60); break; case 120: UpdateElementPosition(SKillAreaElement.Sector120); break; default: break; } break; case SkillAreaType.OuterCircle_InnerCircle: UpdateElementPosition(SKillAreaElement.InnerCircle); break; default: break; } } /// <summary> /// 每帧更新元素位置 /// </summary> /// <param name="element"></param> void UpdateElementPosition(SKillAreaElement element) { if (allElementTrans[element] == null) return; switch (element) { case SKillAreaElement.OuterCircle: break; case SKillAreaElement.InnerCircle: allElementTrans[element].transform.position = GetCirclePosition(outerRadius); break; case SKillAreaElement.Cube: case SKillAreaElement.Sector60: case SKillAreaElement.Sector120: allElementTrans[element].transform.LookAt(GetCubeSectorLookAt()); break; default: break; } if (!allElementTrans[element].gameObject.activeSelf) allElementTrans[element].gameObject.SetActive(true); } /// <summary> /// 获取InnerCircle元素位置 /// </summary> /// <returns></returns> Vector3 GetCirclePosition(float dist) { if (player == null) return Vector3.zero; Vector3 targetDir = deltaVec * dist; float y = Camera.main.transform.rotation.eulerAngles.y; targetDir = Quaternion.Euler(0, y, 0) * targetDir; return targetDir + player.transform.position; } /// <summary> /// 获取Cube、Sector元素朝向 /// </summary> /// <returns></returns> Vector3 GetCubeSectorLookAt() { if (player == null) return Vector3.zero; Vector3 targetDir = deltaVec; float y = Camera.main.transform.rotation.eulerAngles.y; targetDir = Quaternion.Euler(0, y, 0) * targetDir; return targetDir + player.transform.position; } } |
效果展示:
项目使用版本:Unity5.3.4 GitHub下载地址:
https://github.com/654306663/SkillAreaDisplay.git
- 本文固定链接: http://www.u3d8.com/?p=1256
- 转载请注明: 网虫虫 在 u3d8.com 发表过
非常好用,感谢网虫虫
git下载的文件坏了
5.3.4f1版本,是不是你版本太高了?
CreateElement方法内,缩放需要除以Player.localScale.x,这个是有什么用。
因为elementTrans是player的子物体,但又不能受player的scale的影响,所以除以x
你好,如果我想做一个人物突进移动,就像王者荣耀里宫本的2技能那样的突进效果应该怎么做呢,能不能给个思路?
不清楚宫本2技能是啥样的哈~ 你可以Q我详细说
为什么我下了代码,运行时人物不能动啊??
Animator has not been initialized.
检查一下Animator组件是否开启、Unity版本是否一致。
我是一个初学者,也想做类似的功能,看了大神你的代码发现深受启发,不过看完你的代码我有几个疑问:1.你的这两个脚本是绑定到摇杆下面还是直接 绑定到player 下面?2.你的prefab 里面放的是什么?是那些圆圈,扇形的特效么?如果是那些特效,那些特效我没有,能麻烦分享一份么?3.你的player 和areaType 好像没有初始化?这两个不初始化也能正常运行么?还是要我手动在Start 方法里面进行手动初始化?真诚的在这里求助,我是真心想学习的,再次谢谢你的分享。
上面有源码分享地址,可以自己下载运行看下~
技能是不可以自定义角度和长度的吗???
可以的。你可以看下SkillArea.cs第32-35行。除了扇形是需要每个角度都要有对应模型,其它形状都是直接拉伸就可以
谢谢,真的很谢谢。。
大佬,DEMO里面的技能怎样能变换形状
你可以看下SkillArea.cs第32-35 40-44行
已解决,谢谢大佬
你这个扇形区域支持指定任意角度吗
为什么矩形要用两张图拼起来
支持任意角度。但需要对应角度的模型,并且需要在代码扩展下,可以参考代码里用到60、120的地方