Unity3D 基地实现(摄像机移动、拖动建筑等)
来源:互联网 发布:重庆双成网络 编辑:程序博客网 时间:2024/04/29 19:36
在做一个策略类的游戏时,需要实现一个基地的功能,功能并不是太复杂,默认只能显示场景(45度视角)的一部分,然后通过移动场景(地形)查看场景中的其他部分,当点击建筑时可以拖动场景中的建筑到一定地方!
最终效果如下:
第一步:先布局好场景界面,如图:
下面我们先把地表的网格显示出来,这儿用的是 Unity3D 自带的透明顶点 Shader,暂时没有想到好的解决办法,如下图:
接着我们设置主摄像机的旋转视角为45度,我们现在可以看到场景里面有两个摄像机,另一个摄像机的目的是为了当我们拖动对象时可以始终保持被拖动的对象被优先渲染,如下图:
另外我们需要保证另一个摄像机(BuildingCamera)的旋转、位置、缩放要与主摄像机相同,并且保证 Depth 要大于主摄像机的 Depth:
下面我们新建立一个层,主要用于显示被拖动的对象,如图:
我们再添加一个 Tag 为 Drag 的标记,主要用来检测拖动的对象,如图:
到现在,另外我们还需要确保 Grids 与 Buildings 的位置、旋转、缩放保持一致,主要目的是为了计算单位统一:
下面我们设置 Plane 与 Small、Large、Middle 对象的 Tag 为 Drag,因为 Plane 是放置,Small、Large、Middle 对象是可拖动对象,如图:
到这儿场景基本上布置完毕,现在我们需要编写代码来实现了,首先我们给建立一个 C# 类,取名 SceneGrid.cs 文件,代码如下:
01
using
UnityEngine;
02
using
System.Collections.Generic;
03
04
public
class
SceneGrid : MonoBehaviour
05
{
06
public
int
gridRows;
07
public
int
gridCols;
08
09
public
Dictionary<Vector3,
int
> gridList;
10
11
void
Awake()
12
{
13
this
.gridList =
new
Dictionary<Vector3,
int
> ();
14
15
float
beginRow = -
this
.gridRows * 0.5f + 0.5f;
16
float
beginCol = -
this
.gridCols * 0.5f + 0.5f;
17
18
Vector3 position = Vector3.zero;
19
20
for
(
int
rowIndex = 0; rowIndex <
this
.gridRows; rowIndex ++)
21
{
22
for
(
int
colIndex = 0; colIndex <
this
.gridCols; colIndex ++)
23
{
24
position =
new
Vector3(beginRow + rowIndex, 0f, beginCol + colIndex);
25
this
.gridList.Add(position, 0);
26
}
27
}
28
}
29
30
/// <summary>
31
/// 更新格子状态
32
/// </summary>
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)
36
{
37
foreach
(Vector3 position
in
positionList)
38
{
39
if
(
this
.gridList.ContainsKey(position))
40
{
41
this
.gridList[position] = (status ==
true
? 1 : 0);
42
}
43
}
44
}
45
46
/// <summary>
47
/// 能否可以放置
48
/// </summary>
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)
52
{
53
foreach
(Vector3 position
in
positionList)
54
{
55
if
(!
this
.gridList.ContainsKey(position))
return
false
;
56
if
(
this
.gridList[position] != 0)
return
false
;
57
}
58
return
true
;
59
}
60
}
然后我们把 SceneGrid.cs 挂载到 Plane 对象上,如图:
然后我们继续创建一个 C# 类,取名:SceneBuilding.cs,代码如下:
1
using
UnityEngine;
2
using
System.Collections;
3
4
public
class
SceneBuilding : MonoBehaviour
5
{
6
public
int
buildingWidth = 1;
7
public
int
buildingHeight = 1;
8
}
接着我们把 SceneBuilding.cs 依次添加到 Small、Large、Middle 对象上,如图:
我们再添加一个 C# 类,取名:SceneController.cs,这个类比较重要,场景的主要逻辑都在这个类当中,代码如下:
001
using
UnityEngine;
002
using
System.Collections;
003
004
public
class
SceneController : MonoBehaviour
005
{
006
/// 鼠标枚举
007
enum
MouseTypeEnum
008
{
009
LEFT = 0
010
}
011
012
// 拖动建筑枚举
013
enum
BuildingLayerEnum
014
{
015
BUILDING = 8
016
}
017
018
/// <summary>
019
/// 水平移动速度
020
/// </summary>
021
public
float
horizontalSpeed = 10f;
022
023
/// <summary>
024
/// 垂直移动速度
025
/// </summary>
026
public
float
verticalSpeed = 10f;
027
028
/// <summary>
029
/// 滚轮速度
030
/// </summary>
031
public
float
mouseScrollSpeed = 10f;
032
033
/// <summary>
034
/// 拖动状态判断 X 坐标
035
/// </summary>
036
public
float
moveOffsetX = 1f;
037
038
/// <summary>
039
/// 拖动状态判断 Y 坐标
040
/// </summary>
041
public
float
moveOffsetY = 1f;
042
043
/// <summary>
044
/// 主摄像机
045
/// </summary>
046
public
Camera mainCamera;
047
048
/// <summary>
049
/// 拖动建筑显示层
050
/// </summary>
051
public
Camera buildingCamera;
052
053
/// <summary>
054
/// 建筑容器对象,要跟 表格容器对象在相同位置,缩放、旋转都要相同
055
/// </summary>
056
public
Transform buildingsObject;
057
058
/// <summary>
059
/// 场景格子
060
/// </summary>
061
public
SceneGrid sceneGrid;
062
063
/// <summary>
064
/// 鼠标状态
065
/// </summary>
066
private
bool
mousePressStatus =
false
;
067
068
/// <summary>
069
/// 鼠标 X 坐标
070
/// </summary>
071
private
float
mouseX;
072
073
/// <summary>
074
/// 鼠标 Y 坐标
075
/// </summary>
076
private
float
mouseY;
077
078
/// <summary>
079
/// 滚轮数据
080
/// </summary>
081
private
float
mouseScroll;
082
083
/// <summary>
084
/// 建筑信息
085
/// </summary>
086
private
SceneBuilding sceneBuilding;
087
088
/// <summary>
089
/// 拖动的建筑对象
090
/// </summary>
091
private
GameObject moveObject;
092
093
/// <summary>
094
/// 移动对象的位置信息
095
/// </summary>
096
private
Vector3 movePosition;
097
098
/// <summary>
099
/// 移动偏移数据
100
/// </summary>
101
private
Vector3 moveOffset;
102
103
/// <summary>
104
/// 最后一次对象位置列表
105
/// </summary>
106
private
Vector3[] prevPositionList;
107
108
/// <summary>
109
/// 射线碰撞位置
110
/// </summary>
111
private
Vector3 hitPosition;
112
113
void
Update()
114
{
115
// 按下鼠标、轴
116
if
(Input.GetMouseButtonDown((
int
)MouseTypeEnum.LEFT))
117
{
118
this
.mousePressStatus =
true
;
119
120
// 如果有选中的建筑信息
121
if
(
this
.sceneBuilding !=
null
)
122
{
123
// 重置建筑信息对象
124
this
.sceneBuilding =
null
;
125
}
126
127
// 检测鼠标点击区域是否是建筑对象
128
this
.sceneBuilding = PhysisUtils.GetTByMousePoint<SceneBuilding> (
this
.mainCamera);
129
// 如果是建筑对象
130
if
(
this
.sceneBuilding !=
null
)
131
{
132
this
.prevPositionList = GridUtils.GetPostionList(
this
.sceneBuilding.transform.localPosition,
this
.sceneBuilding.buildingWidth,
this
.sceneBuilding.buildingHeight);
133
this
.sceneGrid.SetGrid(
this
.prevPositionList,
false
);
134
}
135
}
136
// 松开鼠标、轴
137
if
(Input.GetMouseButtonUp ((
int
)MouseTypeEnum.LEFT))
138
{
139
bool
dropStatus =
false
;
140
this
.mousePressStatus =
false
;
141
// 销毁拖动对象
142
if
(
this
.moveObject !=
null
&&
this
.sceneBuilding !=
null
)
143
{
144
Vector3 targetPosition =
this
.moveObject.transform.localPosition;
145
146
Destroy(
this
.moveObject);
147
this
.moveObject =
null
;
148
149
if
(
this
.CanDrop(
ref
this
.hitPosition))
150
{
151
Vector3[] positionList = GridUtils.GetPostionList(targetPosition,
this
.sceneBuilding.buildingWidth,
this
.sceneBuilding.buildingHeight);
152
if
(
this
.sceneGrid.CanDrop(positionList))
153
{
154
dropStatus =
true
;
155
this
.sceneGrid.SetGrid(positionList,
true
);
156
this
.sceneBuilding.transform.localPosition = targetPosition;
157
}
else
{
158
Debug.Log(
"不能放置"
);
159
}
160
}
161
}
162
if
(!dropStatus)
if
(
this
.prevPositionList !=
null
)
this
.sceneGrid.SetGrid(prevPositionList,
true
);
163
164
this
.prevPositionList =
null
;
165
}
166
// 如果鼠标在按住状态
167
if
(
this
.mousePressStatus)
168
{
169
this
.mouseY =
this
.horizontalSpeed * Input.GetAxis (
"Mouse X"
);
170
this
.mouseX =
this
.verticalSpeed * Input.GetAxis (
"Mouse Y"
);
171
// 当超过一定的偏移坐标,才视为拖动建筑
172
if
((Mathf.Abs(
this
.mouseX) >=
this
.moveOffsetX || Mathf.Abs(
this
.mouseY) >=
this
.moveOffsetY) &&
this
.sceneBuilding !=
null
)
173
{
174
// 创建一个新的建筑对象
175
if
(
this
.moveObject ==
null
)
176
{
177
// 设置建筑信息的屏幕坐标
178
this
.movePosition =
this
.mainCamera.WorldToScreenPoint(
this
.sceneBuilding.transform.position);
179
// 设置建筑信息的坐标偏移值
180
this
.moveOffset =
this
.sceneBuilding.transform.position -
this
.mainCamera.ScreenToWorldPoint(
new
Vector3(Input.mousePosition.x, Input.mousePosition.y,
this
.movePosition.z));
181
182
this
.moveObject = (GameObject)Instantiate(
this
.sceneBuilding.gameObject);
183
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;
188
189
Destroy(
this
.moveObject.GetComponent<SceneBuilding>());
190
Destroy(
this
.moveObject.GetComponent<BoxCollider>());
191
192
this
.moveObject.transform.localPosition =
this
.sceneBuilding.gameObject.transform.localPosition;
193
}
194
}
195
// 如果移动摄像机
196
if
(
this
.sceneBuilding ==
null
)
197
{
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;
201
}
else
202
{
203
// 如果移动的是建筑
204
if
(
this
.moveObject !=
null
)
205
{
206
if
(
this
.CanDrop(
ref
this
.hitPosition))
207
{
208
this
.hitPosition -=
this
.moveOffset;
209
210
Vector3 currentLocalPosition =
this
.buildingsObject.transform.InverseTransformPoint(
this
.hitPosition);
211
212
currentLocalPosition.x = (
int
)currentLocalPosition.x - 0.5f;
213
currentLocalPosition.z = (
int
)currentLocalPosition.z - 0.5f;
214
215
if
(
this
.sceneBuilding.buildingWidth % 2 == 0)
216
{
217
currentLocalPosition.x += 0.5f;
218
}
219
if
(
this
.sceneBuilding.buildingHeight % 2 == 0)
220
{
221
currentLocalPosition.z += 0.5f;
222
}
223
224
currentLocalPosition.y = 0f;
225
226
// 设置对象跟随鼠标
227
this
.moveObject.transform.localPosition = currentLocalPosition;
228
}
229
}
230
}
231
}
232
// 鼠标滚轮拉近拉远
233
this
.mouseScroll =
this
.mouseScrollSpeed * Input.GetAxis (
"Mouse ScrollWheel"
);
234
if
(
this
.mouseScroll != 0f)
235
{
236
this
.mainCamera.transform.localPosition -=
new
Vector3(0f, mouseScroll, 0f) * Time.deltaTime;
237
}
238
239
this
.buildingCamera.transform.localPosition =
this
.mainCamera.transform.localPosition;
240
}
241
242
/// <summary>
243
/// 能否放置
244
/// </summary>
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)
248
{
249
Ray ray =
this
.mainCamera.ScreenPointToRay(Input.mousePosition);
250
RaycastHit raycastHit =
new
RaycastHit();
251
252
if
(Physics.Raycast(ray,
out
raycastHit))
253
{
254
if
(raycastHit.collider.tag ==
"Drag"
)
255
{
256
position = raycastHit.point;
257
return
true
;
258
}
259
}
260
return
false
;
261
}
262
}
然后把 SceneController.cs 挂载到 SceneController 对象上,并且设置好相关属性,如图:
代码中,提取出了一个计算表格所占格子的类,取名 :GridUtils.cs,代码如下:
01
using
UnityEngine;
02
using
System.Collections;
03
04
public
class
GridUtils
05
{
06
/// <summary>
07
/// 获取格子所在的位置列表
08
/// </summary>
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)
14
{
15
Vector3 localPosition =
new
Vector3 (transformPosition.x, 0f, transformPosition.z);
16
localPosition.x -= buildingWidth * 0.5f;
17
localPosition.z += buildingHeight * 0.5f;
18
19
Vector3[] positionList =
new
Vector3[buildingWidth * buildingHeight];
20
21
for
(
int
rowIndex = 0; rowIndex < buildingWidth; rowIndex ++)
22
{
23
for
(
int
colIndex = 0; colIndex < buildingHeight; colIndex ++)
24
{
25
positionList[rowIndex * buildingHeight + colIndex] =
new
Vector3(localPosition.x + rowIndex + 1f - 0.5f, 0f, localPosition.z - colIndex - 1f + 0.5f);
26
}
27
}
28
29
return
positionList;
30
}
31
}
到这儿一切都完成了,现在我们运行一下看看效果吧,用到的 PhysisUtils.cs(物理检测相关的类在另外的文章有提到)
- Unity3D 基地实现(摄像机移动、拖动建筑等)
- Unity3D 基地实现(摄像机移动、拖动建筑等)
- Unity3D 人物移动 摄像机跟随
- NGUI拖动摄像机实现面板上下拖动
- 深入浅出游戏算法(4)-unity3d算法(3)-移动摄像机
- Unity3D 使用 A 星寻路(摄像机移动、缩放优化)
- 4.5 摄像机 拖动地图,使得摄像机跟随地图移动
- Unity3D里实现物体移动到目标点,并且摄像机跟着移动的简单方案
- Unity3D里实现物体移动到目标点,并且摄像机跟着移动的简单方案
- Unity3D里实现物体移动到目标点,并且摄像机跟着移动的简单方案
- 如何实现unity3d建筑场景中灯光效果
- unity3d移动摄像机,支持PC、手机
- Unity3D让摄像机随player移动
- Unity3d摄像机跟随角色移动脚本
- Unity3D开发之控制摄像机移动
- Unity3D三维坐标(X,Z轴)下物体拖动移动(贴地面移动)实现并实现物体碰撞检测
- Unity3d中使用鼠标拖动物体移动
- Unity3d学习记录(一)摄像机跟随人物移动,并且注视人物
- 关于用xib创建静态tableview可能会用到的一些委托函数
- 跳转到距锚点固定位移的地方
- leetcode299
- 全球最杰出的14位程序员
- UIView的autoresizingMask详解
- Unity3D 基地实现(摄像机移动、拖动建筑等)
- 分享下自己写的二次方程计算器(python)
- Linux 终端访问 FTP 及 上传下载 文件
- 废话一二
- 操作系统期末考点大纲
- 机器学习算法的归类
- 基本类型对应的包装类类型
- 基于Scala与正则表达式的Html文本分析应用
- Struts2 标签库讲解