初学Unity,c#也是刚刚开始用,如果有大佬能指出不对的地方我会非常感谢。
创建地图网格
在3d编辑器中制作的所以自己划分网格。
效果大概是这样的绿色是点击过的部分,数字和debugt线目前只是用来方便观察分割效果
首先按照这种关系创建一个EmptyObject(Map)和一个有碰撞机的平面。通过在Hierarchy窗口右键中创建出来的plane默认是带有碰撞的,我们需要他来做射线命中检测。
创建的时候最好把Plane的左下角对准Map的中心,因为网格是从Map的中心开始往右上角建立。
如果是不太熟悉unity的同学,最好把这个物体放在0 0 0 的世界坐标上,避免因为文章中漏贴一些不是很重要的代码而出现奇怪的问题。
创建一个脚本Map, 在Update函数中做鼠标左键输入检测
public class Map : MonoBehaviour
{
//都设置为公开,需要在编辑器中修改
public int Height;
public int Width;
public int CellSize;
void Update()
{
if(Input.GetMouseButtonUp(0))
{
//获取鼠标点击位置的屏幕坐标。
Vector3 ScreenPositon = Input.mousePosition;
/*官方解释:产生的光线位于世界空间中,从摄像机的近平面开始,
并通过屏幕上 位置的 (x,y) 像素坐标(忽略 position.z)。
这里我的理解是起点是摄像机的位置,而穿过的点是摄像机近平面上的点,也就是我们点击的位置。
*/
Ray Mray = Camera.main.ScreenPointToRay(ScreenPositon);
RaycastHit Mhit;
//查看下射线的效果
Debug.DrawRay(Mray.origin, Mray.direction * 1000, Color.white, 100, true);
if(Physics.Raycast(Mray, out Mhit))
{
//Map是前面plane的tag
if(Mhit.collider.gameObject.tag == "Map")
TargetPos = Mhit.point;
//获取命中点的世界坐标
}
}
}
}
把Map脚本挂到Map这个Object上就可以看效果了,点击游戏窗口中的plane object能够出现类似的白线就对了
获取到点击的位置的世界坐标以后我们需要将它转化为网格中的坐标,也就是两个整数。
为此,我们再创建一个脚本CreateGrid,设置要生产的网格属性
public class CreateGrid : MonoBehaviour
{
private int width;
private int height;
//保存网格中的数值,便于以后的操作
private int [,] gridArry;
private int CellSize;
public void Create(int hig, int wid, int cellsize)
{
// CreatePlane();
// Debug.Log(transform.localPosition);
height = hig;
width = wid;
CellSize = cellsize;
gridArry = new int [height, width];
}
}
在前一个脚本Map 中加入一个start方法,调用CreateGrid。
void Start()
{
CreateGrid grid = gameObject.AddComponent<CreateGrid>() as CreateGrid;
grid.Create(Height, Width, CellSize);
//顺便调整一下主相机的位置,以便在任何位置放置地图都可以大致看到
Transform camtrans = Camera.main.transform;
float x = Width>>1;
float z = Height>>1;
float y = 150 + transform.localPosition.y;
x *= CellSize;
z *= CellSize;
x += transform.localPosition.x;
z += transform.localPosition.z;
camtrans.localPosition = new Vector3(x, y, z);
}
接着写转化坐标的
public Vector3 WorldToGridIndex(Vector3 Position)
{
Vector3 GridPosition = Position;
GridPosition -= transform.localPosition;
GridPosition.x = Mathf.FloorToInt(GridPosition.x / CellSize);
GridPosition.y = 0;
GridPosition.z = Mathf.FloorToInt(GridPosition.z / CellSize);
return GridPosition;
}
然后再Map脚本中加上
if(Physics.Raycast(Mray, out Mhit))
{
//Map是前面plane的tag
if(Mhit.collider.gameObject.tag == "Map")
TargetPos = Mhit.point;
//获取命中点的世界坐标
AimPoint = gameObject.GetComponent<CreateGrid>().WorldToGridIndex(TargetPos);
}
这样一来就得到了点击位置对应的网格中的下标类似(1,0)(3,4)这样的坐标
最后一步我们只需要创建一个object来显示被我们点击的格子。
效果是这样的一个平面
这个方法之后可能还有用所以我们写到另一个脚本中以便调用。
其实这里应该也可以用
GameObject plane = GameObject.CreatePrimitive(PrimitiveType.Plane);
来创建比我这样写的方便多了
using UnityEngine;
namespace Utils
{
// 创建网格需要 MeshFilter和MeshRenderer组件
// 然后通过MeshRenderer中的Material来设置材质
// 网格中要保存的顶点放在vertices中
class UtilsTool
{
//LeftBottomPosition:要生成object的网格的下标
//length:生成的object的边长
//
public static GameObject CreatePlane(Vector3 LeftBottomPosition, int length, Transform MyParent, Material Mymaterial = null)
{
GameObject ClickMark = new GameObject();
MeshFilter Mymeshfilter = ClickMark.AddComponent<MeshFilter>();
MeshRenderer Mymeshrenderer = ClickMark.AddComponent<MeshRenderer>();
float x = LeftBottomPosition.x;
float y = MyParent.localPosition.y;
float z = LeftBottomPosition.z;
//因为要再世界坐标中绘制,所以把(0,0)这种先转回世界坐标
x *= length;
z *= length;
//transform.localPosition是Map这个Object左下角的世界坐标
x += MyParent.transform.localPosition.x;
z += MyParent.transform.localPosition.z;
if(Mymaterial == null)
{
Mymeshrenderer.material = new Material(Shader.Find("Diffuse"));
Mymeshrenderer.material.color = Color.blue;
}
else
Mymeshrenderer.material = Mymaterial;
//创建绘制平面需要的顶点
Mymeshfilter.mesh.vertices = new Vector3 []
{
new Vector3(x, y, z),
new Vector3(x + length, y, z),
new Vector3(x, y, z + length),
new Vector3(x + length, y, z + length)
};
//顶点索引,一定要按照顺时针去绘制每个三角形,unity似乎是左手系坐标
Mymeshfilter.mesh.triangles = new int[]{3, 1, 2, 2, 1, 0};
Mymeshfilter.mesh.RecalculateNormals();
//设置Map为这个新平面的Parent
ClickMark.transform.SetParent(MyParent);
return ClickMark;
}
}
继续修改Map脚本
//添加一个用于保存目前已经产生的高亮平面的数组,在下次点击的时候销毁所有目前已经有的平面
private List<GameObject> ClickMark;
//设置平面的材质
public Material SelectBlockColor;
if(Physics.Raycast(Mray, out Mhit))
{
//----------new-------------------
if(ClickMark.Count != 0)
{
foreach(GameObject c in ClickMark)
{
Object.Destroy(c);
}
ClickMark.Clear();
}
//--------------------------------
//Map是前面plane的tag
if(Mhit.collider.gameObject.tag == "Map")
TargetPos = Mhit.point;
//获取命中点的世界坐标
AimPoint = gameObject.GetComponent<CreateGrid>().WorldToGridIndex(TargetPos);
//----------new-------------------
ClickMark.Add(Utils.UtilsTool.CreatePlane(AimPoint, CellSize, transform, SelectBlockColor));
//--------------------------------
}
所有的东西到这里就全部完成了,设置一下网格大小。
就能看到类似这样的效果了。这么多0是我另外添加的text,目前没有什么用我就没有放出代码,免得让文章更加混乱(已经很乱了XD)。