《Unity3D技术之塔防类项目详解(一)》由会员分享,可在线阅读,更多相关《Unity3D技术之塔防类项目详解(一)(9页珍藏版)》请在金锄头文库上搜索。
1、Unity3D技术之塔防类项目详解(一)通常意义上讲,塔防游戏是指一类在地图上建造炮台或者类似建筑物来阻止敌人进攻 的策略类游戏。从这个概念中,我们可以快速地抽离出来三个元素,即地图(场景)、敌人、 炮台(防守单位)。当我们抽离出来这样三个元素后,现在塔防游戏就变成了这样的一种描 述,即敌人按照地图中设计的路径进攻,玩家利用防守单位进行防守的一类策略游戏。经 典的塔防游戏有哪些呢?比如我们最为熟悉的植物大战僵尸、保卫萝卜都是塔防 类游戏的经典游戏。如果我们将塔防游戏中的防守单位的范围扩大到玩家,那么像英雄 联盟这样的游戏同样是可以称之为塔防游戏的,因为敌我阵营的最终目的都是要摧毁敌 方的防御塔
2、,只是敌我双方都从炮台或者怪物变成了有血有肉的人物,加之角色扮演(RPG) 和即时战略(RTS)等元素的混合渗透,使得这样的游戏从单纯的塔防游戏变成了一款可玩 度极高的游戏(天啊,我居然在夸这个游戏)。好了,那么我们就来尝试着做出一个简单 的塔防游戏吧,注意是简单的塔防游戏哦,既然塔防游戏的三个要素是地图、敌人和防守 单位,那么我们就从这三个方面来着手设计这个游戏吧!在本篇文章中,我们将用到下面 的知识: Unity2D 中的 Sprite 动画 3D中的可视化辅助类Gizmos塔防游戏中敌人按路径寻路的实现 Unity3D uGUI的初步探索简单的AI算法一、地图篇地图是一个塔防游戏中玩家最
3、为关注的地方,因为地图和敌人将直接影响到玩家的策 略。如图是从保卫萝卜游戏中提取的一张游戏地图。在这张地图中我们可以清楚看到 怪物进攻的路径,怪物将沿着地图中的路径向我方防守单位发起攻击。那么,在游戏中, 我们该怎样确定怪物的攻击路径呢?首先我们可以对地图进行下分析,在地图中基本上基 本上只有两种类型的区域,即可以放置防守单位的区域和不可放置防守单位的区域两种。 由此我们可以设计出下面的结构:csharp view pla in copy 匸1. using UnityEngine;2. using System.Collections;3.3. SerializeField4. public
4、 class GridNode : MonoBehaviour5. 6. public enum NodeType7. 8. CanPlace,9. CantPlace10. 12.13. public NodeType GridNodeType=NodeType.CanPlace;14. 可以看出,我们在GridNode类中定义了一个称为NodeType的枚举类型,这个枚举类 型有两个值,Can Place表示可以放置防守单位,Ca ntPlace表示不可以放置防守单位。在 GridNode类中只有一个NodeType类型的成员变量GridNodeType,该成员变量的默认值 是Can Pl
5、ace,即可以放置防守单位。那么,现在问题来了,我们找到了一种可以用来描 述地图中不同区域的方法,可是这些区域在哪里呢?所以我们需要一种方法来生成这些区 域。这里隆重向大家介绍Gizoms类,Gizmo是Unity中一个用于在场景视图可视化调试 或辅助设置的工具类。简单的说,当我们需要在编辑器环境中实现某种可视化调试的时候,我们就可以使用Gizmo类。所以的Gizmo绘制都需要在OnDrawGizmos或On DrawGizmosSelected函数里完成。从这两个函数的名称我们就可以看出它们的区别,OnDrawGizmos在每一帧都调用,所有在Gizmos里渲染的Gizmo都将被渲染,而On
6、DrawGizmosSelected仅在脚本附加的物体被选中时渲染。好了,在了解了 Gizmos的 基本概念和用法后,我们回到我们的游戏中。我们刚刚提到,我们需要一种方法来生成区 域以便于我们可以使用GridNode类来描述每个区域的属性,那么具体怎么做呢?其实思 路就是在地图上画出网格,这样网格便可以将整个地图分割成不同的区域,然后我们就可 以使用GridNode来描述每个区域的属性啦。好了,下面我们来看具体的脚本:csharp view pla in copyC1. using UnityEngine;2. using System.Collections;3.3. public clas
7、s GridMap : MonoBehaviour 5.4. public static GridMap Instance=null;7.5. public int MapSizeX;6. public int MapSizeZ;10.7. HideInInspector8. public GameObject mNodes;9. HideInInspector10. public GameObject mPaths;15.11. void Awake()12. 13. Instance=this;14. mNodes=GameObject.FindGameObjectsWithTag(Gri
8、dNode);15. mPaths=GameObject.FindGameObjectsWithTag(PathNode);16. 22.23. void DrawGrid()24. 25. Gizmos.color=Color.blue;26. for(int i=0;i=MapSizeX;i+)27. 28. Gizmos.DrawLine(new Vector3(i,0,0),new Vector3(i,MapSizeZ,0);29. 30. for(int j=0;j=MapSizeZ;j+)31. 32. Gizmos.DrawLine(new Vector3(0,j,0),new
9、Vector3(MapSizeX,j,0);33. 34. 35.35. void DrawColor()36. 37. if(mNodes=null) return;38. foreach(GameObject go in mNodes)39. 40. Vector3 mPos=go.transform.position;41. if(go.GetComponent()!=null)42. if(go.GetComponent().GridNodeType=GridNode.NodeType.CanPlace)43. Gizmos.color=Color.green;44. elseif(g
10、o.GetComponent().GridNodeType=GridNode.NodeType.CantPlace)45. Gizmos.color=Color.red;46. 47. Gizmos.DrawCube(mPos,new Vector3(1,1,1);48. 49. 50. 52.51. void DrawPath()52. 53. Gizmos.color=Color.white;54. if(mPaths=null) return;55. foreach(GameObject go in mPaths)56. 57. if(go.GetComponent()!=null)58
11、. PathNode node=go.GetComponent();59. if(node.ThatNode!=null)60. Gizmos.DrawLine(node.transform.position,node.ThatNode.transform.position);61. 62. 63. 64. 67.65. void OnDrawGizmos()66. 67. DrawGrid();68. DrawColor();69. DrawPath();70. 74.75.文章出处【狗刨学习网】在这段脚本中,我们首先定义了两个int类型的变量MapSizeX,MapSizeZ,这两个变量
12、分别用来表示需要绘制网格的大小。下面我们来重点关注On DrawGizmos方法,在这个方 法中我们定义了 3个方法DrawGrid、DrawColor和DrawPath。其中DrawGrid方法负责 绘制地图网格,DrawColor方法负责绘制地图区域、DrawPath方法负责绘制敌人寻路路 径。我们首先来说DrawGrid,DrawGrid负责绘制地图网格,默认从原点开始绘制,要绘 制网格只需要绘制交错的横线和竖线即可,这里我们使用的Gizmos类下的DrawLine方 法我们首先在场景中创建一个MeshRoot的空物体,将GridMap脚本附加到该物体上,我 们下面来看看绘制的效果:因为
13、Gizmos为我们提供了可视化的调试功能,因此我们可以直接在编辑器窗口中看到 实际的效果,这样我们就利用Unity绘制出了地图的网格。为了让地图的左下角和场景原 点能够完全匹配,这里写了一个简单的工具类AutoPlace来实现地图的位置计算和调整:csharp view pla in copyC1. using UnityEngine;2. using System.Collections;3.3. public class AutoPlace : MonoBehaviour 6. /精灵渲染器7. private SpriteRenderer mRenderer;8. /精灵宽度9. pri
14、vate float mSpriteWidth;10. /精灵高度11. private float mSpriteHeight;12.13.12. void Start ()13. 14. mRenderer=GetComponent();15. /计算精灵的实际大小16. mSpriteWidth=mRenderer.sprite.bounds.size.x * transform.localScale.x;17. mSpriteHeight=mRenderer.sprite.bounds.size.y * transform.localScale.y;18. /自动调整精灵的位置19.
15、transform.position=new Vector3(mSpriteWidth/2,mSpriteHeight/2,0);20. 21. 好了,下面我们来继续讲解地图中区域的生成。什么是地图中的区域呢?在塔防游戏 中玩家通常情况下都只能在可以放置防守单位的区域放置防守的单位,那么可以放置防守 单位的这些地方就是我们接下来要来研究的区域。我们首先需要根据第一步绘制的网格, 为每一个网格单元创建一个空物体NodeObject,并为该物体附加GridNode脚本,如果该 物体所在的位置在地图上是可以放置防守单位,那么我们就将其GridNodeType设为 Can Place,否则就设为Ca ntPlace。其实在这里是更喜欢用动态生成的方式来为每个网格 单元添加区域属性的,不过这里我们为了将过程讲明白,索性就手动创建吧!