本文永久地址:http://www.omuying.com/article/34.aspx,
【文章转载请注明出处!】
在做一个策略类的游戏时,需要实现一个基地的功能,功能并不是太复杂,默认只能显示场景(45度视角)的一部分,然后通过移动场景(地形)查看场景中的其他部分,当点击建筑时可以拖动场景中的建筑到一定地方!
最终效果如下:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014202212_5468.jpg)
第一步:先布局好场景界面,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014202326_0156.jpg)
下面我们先把地表的网格显示出来,这儿用的是 Unity3D 自带的透明顶点 Shader,暂时没有想到好的解决办法,如下图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014202511_6562.jpg)
接着我们设置主摄像机的旋转视角为45度,我们现在可以看到场景里面有两个摄像机,另一个摄像机的目的是为了当我们拖动对象时可以始终保持被拖动的对象被优先渲染,如下图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014202742_1718.jpg)
另外我们需要保证另一个摄像机(BuildingCamera)的旋转、位置、缩放要与主摄像机相同,并且保证 Depth 要大于主摄像机的 Depth:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014203018_3593.jpg)
下面我们新建立一个层,主要用于显示被拖动的对象,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014203307_1718.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014203350_4531.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014203707_7500.jpg)
我们再添加一个 Tag 为 Drag 的标记,主要用来检测拖动的对象,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014203947_6406.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014204011_7031.jpg)
到现在,另外我们还需要确保 Grids 与 Buildings 的位置、旋转、缩放保持一致,主要目的是为了计算单位统一:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014204312_1250.jpg)
下面我们设置 Plane 与 Small、Large、Middle 对象的 Tag 为 Drag,因为 Plane 是放置,Small、Large、Middle 对象是可拖动对象,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014204542_6250.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014204602_2812.jpg)
到这儿场景基本上布置完毕,现在我们需要编写代码来实现了,首先我们给建立一个 C# 类,取名 SceneGrid.cs 文件,代码如下:
02 | using System.Collections.Generic; |
04 | public class SceneGrid : MonoBehaviour |
09 | public Dictionary<Vector3, int > gridList; |
13 | this .gridList = new Dictionary<Vector3, int > (); |
15 | float beginRow = - this .gridRows * 0.5f + 0.5f; |
16 | float beginCol = - this .gridCols * 0.5f + 0.5f; |
18 | Vector3 position = Vector3.zero; |
20 | for ( int rowIndex = 0; rowIndex < this .gridRows; rowIndex ++) |
22 | for ( int colIndex = 0; colIndex < this .gridCols; colIndex ++) |
24 | position = new Vector3(beginRow + rowIndex, 0f, beginCol + colIndex); |
25 | this .gridList.Add(position, 0); |
33 | /// <param name="positionList">Position list.</param> |
34 | /// <param name="status">If set to <c>true</c> status.</param> |
35 | public void SetGrid(Vector3[] positionList, bool status) |
37 | foreach (Vector3 position in positionList) |
39 | if ( this .gridList.ContainsKey(position)) |
41 | this .gridList[position] = (status == true ? 1 : 0); |
49 | /// <returns><c>true</c> if this instance can drop the specified positionList; otherwise, <c>false</c>.</returns> |
50 | /// <param name="positionList">Position list.</param> |
51 | public bool CanDrop(Vector3[] positionList) |
53 | foreach (Vector3 position in positionList) |
55 | if (! this .gridList.ContainsKey(position)) return false ; |
56 | if ( this .gridList[position] != 0) return false ; |
然后我们把 SceneGrid.cs 挂载到 Plane 对象上,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014204926_0625.jpg)
然后我们继续创建一个 C# 类,取名:SceneBuilding.cs,代码如下:
2 | using System.Collections; |
4 | public class SceneBuilding : MonoBehaviour |
6 | public int buildingWidth = 1; |
7 | public int buildingHeight = 1; |
接着我们把 SceneBuilding.cs 依次添加到 Small、Large、Middle 对象上,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014205145_4375.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014205207_0000.jpg)
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014205223_1250.jpg)
我们再添加一个 C# 类,取名:SceneController.cs,这个类比较重要,场景的主要逻辑都在这个类当中,代码如下:
002 | using System.Collections; |
004 | public class SceneController : MonoBehaviour |
013 | enum BuildingLayerEnum |
021 | public float horizontalSpeed = 10f; |
026 | public float verticalSpeed = 10f; |
031 | public float mouseScrollSpeed = 10f; |
036 | public float moveOffsetX = 1f; |
041 | public float moveOffsetY = 1f; |
046 | public Camera mainCamera; |
051 | public Camera buildingCamera; |
054 | /// 建筑容器对象,要跟 表格容器对象在相同位置,缩放、旋转都要相同 |
056 | public Transform buildingsObject; |
061 | public SceneGrid sceneGrid; |
066 | private bool mousePressStatus = false ; |
071 | private float mouseX; |
076 | private float mouseY; |
081 | private float mouseScroll; |
086 | private SceneBuilding sceneBuilding; |
091 | private GameObject moveObject; |
096 | private Vector3 movePosition; |
101 | private Vector3 moveOffset; |
106 | private Vector3[] prevPositionList; |
111 | private Vector3 hitPosition; |
116 | if (Input.GetMouseButtonDown(( int )MouseTypeEnum.LEFT)) |
118 | this .mousePressStatus = true ; |
121 | if ( this .sceneBuilding != null ) |
124 | this .sceneBuilding = null ; |
128 | this .sceneBuilding = PhysisUtils.GetTByMousePoint<SceneBuilding> ( this .mainCamera); |
130 | if ( this .sceneBuilding != null ) |
132 | this .prevPositionList = GridUtils.GetPostionList( this .sceneBuilding.transform.localPosition, this .sceneBuilding.buildingWidth, this .sceneBuilding.buildingHeight); |
133 | this .sceneGrid.SetGrid( this .prevPositionList, false ); |
137 | if (Input.GetMouseButtonUp (( int )MouseTypeEnum.LEFT)) |
139 | bool dropStatus = false ; |
140 | this .mousePressStatus = false ; |
142 | if ( this .moveObject != null && this .sceneBuilding != null ) |
144 | Vector3 targetPosition = this .moveObject.transform.localPosition; |
146 | Destroy( this .moveObject); |
147 | this .moveObject = null ; |
149 | if ( this .CanDrop( ref this .hitPosition)) |
151 | Vector3[] positionList = GridUtils.GetPostionList(targetPosition, this .sceneBuilding.buildingWidth, this .sceneBuilding.buildingHeight); |
152 | if ( this .sceneGrid.CanDrop(positionList)) |
155 | this .sceneGrid.SetGrid(positionList, true ); |
156 | this .sceneBuilding.transform.localPosition = targetPosition; |
162 | if (!dropStatus) if ( this .prevPositionList != null ) this .sceneGrid.SetGrid(prevPositionList, true ); |
164 | this .prevPositionList = null ; |
167 | if ( this .mousePressStatus) |
169 | this .mouseY = this .horizontalSpeed * Input.GetAxis ( "Mouse X" ); |
170 | this .mouseX = this .verticalSpeed * Input.GetAxis ( "Mouse Y" ); |
172 | if ((Mathf.Abs( this .mouseX) >= this .moveOffsetX || Mathf.Abs( this .mouseY) >= this .moveOffsetY) && this .sceneBuilding != null ) |
175 | if ( this .moveObject == null ) |
178 | this .movePosition = this .mainCamera.WorldToScreenPoint( this .sceneBuilding.transform.position); |
180 | this .moveOffset = this .sceneBuilding.transform.position - this .mainCamera.ScreenToWorldPoint( new Vector3(Input.mousePosition.x, Input.mousePosition.y, this .movePosition.z)); |
182 | this .moveObject = (GameObject)Instantiate( this .sceneBuilding.gameObject); |
184 | this .moveObject.name = this .sceneBuilding.gameObject.name; |
185 | this .moveObject.tag = null ; |
186 | this .moveObject.layer = ( int )BuildingLayerEnum.BUILDING; |
187 | this .moveObject.transform.parent = this .buildingsObject; |
189 | Destroy( this .moveObject.GetComponent<SceneBuilding>()); |
190 | Destroy( this .moveObject.GetComponent<BoxCollider>()); |
192 | this .moveObject.transform.localPosition = this .sceneBuilding.gameObject.transform.localPosition; |
196 | if ( this .sceneBuilding == null ) |
198 | Vector3 rotationVector = Quaternion.Euler( this .mainCamera.transform.eulerAngles) * new Vector3( this .mouseY, 0f, this .mouseX); |
199 | rotationVector.y = 0f; |
200 | this .mainCamera.transform.localPosition -= rotationVector * Time.deltaTime; |
204 | if ( this .moveObject != null ) |
206 | if ( this .CanDrop( ref this .hitPosition)) |
208 | this .hitPosition -= this .moveOffset; |
210 | Vector3 currentLocalPosition = this .buildingsObject.transform.InverseTransformPoint( this .hitPosition); |
212 | currentLocalPosition.x = ( int )currentLocalPosition.x - 0.5f; |
213 | currentLocalPosition.z = ( int )currentLocalPosition.z - 0.5f; |
215 | if ( this .sceneBuilding.buildingWidth % 2 == 0) |
217 | currentLocalPosition.x += 0.5f; |
219 | if ( this .sceneBuilding.buildingHeight % 2 == 0) |
221 | currentLocalPosition.z += 0.5f; |
224 | currentLocalPosition.y = 0f; |
227 | this .moveObject.transform.localPosition = currentLocalPosition; |
233 | this .mouseScroll = this .mouseScrollSpeed * Input.GetAxis ( "Mouse ScrollWheel" ); |
234 | if ( this .mouseScroll != 0f) |
236 | this .mainCamera.transform.localPosition -= new Vector3(0f, mouseScroll, 0f) * Time.deltaTime; |
239 | this .buildingCamera.transform.localPosition = this .mainCamera.transform.localPosition; |
245 | /// <returns><c>true</c> if this instance can drop the specified position; otherwise, <c>false</c>.</returns> |
246 | /// <param name="position">Position.</param> |
247 | private bool CanDrop( ref Vector3 position) |
249 | Ray ray = this .mainCamera.ScreenPointToRay(Input.mousePosition); |
250 | RaycastHit raycastHit = new RaycastHit(); |
252 | if (Physics.Raycast(ray, out raycastHit)) |
254 | if (raycastHit.collider.tag == "Drag" ) |
256 | position = raycastHit.point; |
然后把 SceneController.cs 挂载到 SceneController 对象上,并且设置好相关属性,如图:
![](http://www.omuying.com/Images/Upload/Article/image/20141014/20141014205541_9687.jpg)
代码中,提取出了一个计算表格所占格子的类,取名 :GridUtils.cs,代码如下:
02 | using System.Collections; |
04 | public class GridUtils |
09 | /// <returns>The postion list.</returns> |
10 | /// <param name="transformPosition">Transform position.</param> |
11 | /// <param name="buildingWidth">Building width.</param> |
12 | /// <param name="buildingHeight">Building height.</param> |
13 | public static Vector3[] GetPostionList(Vector3 transformPosition, int buildingWidth, int buildingHeight, int gridRows = 10, int gridWidth = 10) |
15 | Vector3 localPosition = new Vector3 (transformPosition.x, 0f, transformPosition.z); |
16 | localPosition.x -= buildingWidth * 0.5f; |
17 | localPosition.z += buildingHeight * 0.5f; |
19 | Vector3[] positionList = new Vector3[buildingWidth * buildingHeight]; |
21 | for ( int rowIndex = 0; rowIndex < buildingWidth; rowIndex ++) |
23 | for ( int colIndex = 0; colIndex < buildingHeight; colIndex ++) |
25 | positionList[rowIndex * buildingHeight + colIndex] = new Vector3(localPosition.x + rowIndex + 1f - 0.5f, 0f, localPosition.z - colIndex - 1f + 0.5f); |
到这儿一切都完成了,现在我们运行一下看看效果吧,用到的 PhysisUtils.cs(物理检测相关的类在另外的文章有提到)