Unity 环境区域网格化
区域网格化
在使用A星算法和物体布局的过程中,常常会使用的网格的概念,即建立在网格的基础上,会使得游戏的相关编程变得简单的多。
格子的代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine; [System.Serializable]
public class Node
{
public Vector3 _worldPos;//格子中心点的位置
public int _gridX, _gridY;//在网格列表的下标 public Node(Vector3 Position, int x, int y)
{
_worldPos = Position;
_gridX = x;
_gridY = y;
}
}
网格代码:
using System.Collections;
using System.Collections.Generic;
using UnityEngine; //网格,网格的起点是在左下角,终点是右上角
public class Grid : MonoBehaviour
{
public static Grid instance; private Node[,] grid;//网格
public Vector2 gridSize;//网格横纵大小
public float nodeRadius;//格子的半径
private float nodeDiameter;//格子的直径
public int gridCntX, gridCntY;//两个方向上的网格数量 //Test
public Transform tarTrans;//目标
public Node tar;
public float dir;//射程
//目标区域
public Node zoneLeftDown;//网格的左下角
public Node zoneRightUp;//网格的右上角 Vector3 pos = new Vector3();
// Start is called before the first frame update
void Awake()
{
instance = this; nodeDiameter = nodeRadius * ;
gridCntX = Mathf.RoundToInt(gridSize.x / nodeDiameter);
gridCntY = Mathf.RoundToInt(gridSize.y / nodeDiameter);
grid = new Node[gridCntX, gridCntY];
CreateGrid();
}//创建网格,起始点在左下角
private void CreateGrid()
{
//获得网格的左下角的坐标
Vector3 startPoint = transform.position - gridSize.x / * Vector3.right - gridSize.y / * Vector3.up;
for (int i = ; i < gridCntX; i++)
{
for (int j = ; j < gridCntY; j++)
{
Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.up * (j * nodeDiameter + nodeRadius);
grid[i, j] = new Node(worldPoint, i, j);
}
}
} //获取某个坐标处的格子
public Node GetFromPosition(Vector3 position)
{
//首先获得该坐标相对于网格的宽高的百分比
float percentX = (position.x + gridSize.x / ) / gridSize.x;
float percentY = (position.y + gridSize.y / ) / gridSize.y; //保证百分比值在0到1之间
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY); int x = Mathf.RoundToInt((gridCntX - ) * percentX);
int y = Mathf.RoundToInt((gridCntY - ) * percentY); return grid[x, y];
} //获取一个正方形区域中随机点,length为区域的边长
public Vector3 GetZoneRandomPos(Vector3 center,float length)
{
//射程一定要大于等于0
//float len = Mathf.Abs(length) / 2;
//获取射程网格区域
zoneLeftDown = GetFromPosition(center - new Vector3(length, length));
zoneRightUp = GetFromPosition(center + new Vector3(length, length));
//获取并返回射程网格区域中的一个随机点
int i = Random.Range(zoneLeftDown._gridX, zoneRightUp._gridX);
int j = Random.Range(zoneLeftDown._gridY, zoneRightUp._gridY); return grid[i, j]._worldPos;
} //获取整个区域中的一个随机点
public Vector3 GetZoneRandomPos()
{
int i = Random.Range(, gridCntX);
int j = Random.Range(, gridCntY);
return grid[i, j]._worldPos;
} private void OnDrawGizmos()
{ //绘制网格边界线
Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, gridSize.y, ));
if (grid == null) return; Gizmos.color = new Color(, , , 0.2f);
//绘制网格
foreach (var node in grid)
{
Gizmos.DrawCube(node._worldPos+Vector3.forward, Vector3.one * (nodeDiameter - .1f*nodeDiameter));
}
}
}
运行结果:

一般A星算法实例一,具体算法原理略
这里修改原来的网格代码,从而符合A星算法的需求
using System.Collections;
using System.Collections.Generic;
using UnityEngine; [System.Serializable]
public class Node
{
public bool _walkable;//是否可走
public Vector3 _worldPos;//格子中心点的位置
public int _gridX, _gridY;//在网格列表的下标 public int gCost;//到起始点的曼哈顿距离
public int hCost;//到目标点的曼哈顿距离 public Node parent;//父亲网格点 public int fCost {//曼哈顿距离综合
get { return gCost +hCost; }
} public Node(bool walkable,Vector3 Position, int x, int y)
{
_walkable = walkable;
_worldPos = Position;
_gridX = x;
_gridY = y;
}
} public class Grid : MonoBehaviour
{
public static Grid instance; private Node[,] grid;//网格
public Vector2 gridSize;//网格横纵大小
public float nodeRadius;//格子的半径
private float nodeDiameter;//格子的直径
public int gridCntX, gridCntY;//两个方向上的网格数量
public Vector3 startPoint;//网格的最右下角的坐标
public LayerMask layer; //Test
public Transform tarTrans;//目标
public Node tar;
public float dir;//射程 public List<Node> path = new List<Node>(); //目标区域
public Node zoneLeftDown;//网格的左下角
public Node zoneRightUp;//网格的右上角 Vector3 pos = new Vector3();
// Start is called before the first frame update
void Awake()
{
instance = this; nodeDiameter = nodeRadius * ;
gridCntX = Mathf.RoundToInt(gridSize.x / nodeDiameter);
gridCntY = Mathf.RoundToInt(gridSize.y / nodeDiameter);
grid = new Node[gridCntX, gridCntY];
CreateGrid();
} //创建网格,起始点在左下角
private void CreateGrid()
{
//获得网格的左下角的坐标
startPoint = transform.position - gridSize.x / * Vector3.right - gridSize.y / * Vector3.up;
for (int i = ; i < gridCntX; i++)
{
for (int j = ; j < gridCntY; j++)
{
Vector3 worldPoint = startPoint + Vector3.right * (i * nodeDiameter + nodeRadius) + Vector3.up * (j * nodeDiameter + nodeRadius);
bool walkable = !Physics2D.OverlapCircle(worldPoint, nodeRadius, layer);
grid[i, j] = new Node(walkable,worldPoint, i, j);
}
}
} //获取某个坐标处的格子,利用百分比,那么在网格区域外的一个点的网格点就是离它最近的那个网格点
public Node GetFromPosition(Vector3 position)
{
//首先获得该坐标相对于网格的宽高的百分比
float percentX = (position.x + gridSize.x / ) / gridSize.x;
float percentY = (position.y + gridSize.y / ) / gridSize.y; //保证百分比值在0到1之间
percentX = Mathf.Clamp01(percentX);
percentY = Mathf.Clamp01(percentY); int x = Mathf.RoundToInt((gridCntX - ) * percentX);
int y = Mathf.RoundToInt((gridCntY - ) * percentY); return grid[x, y];
} //获取网格范围内一个正方形区域中随机点,length为区域的边长
public Vector3 GetZoneRandomPos(Vector3 center, float length)
{
//射程一定要大于等于0
//float len = Mathf.Abs(length) / 2;
//获取射程网格区域
zoneLeftDown = GetFromPosition(center - new Vector3(length, length));
zoneRightUp = GetFromPosition(center + new Vector3(length, length));
//获取并返回射程网格区域中的一个随机点
int i = Random.Range(zoneLeftDown._gridX, zoneRightUp._gridX);
int j = Random.Range(zoneLeftDown._gridY, zoneRightUp._gridY); return grid[i, j]._worldPos;
} //获取整个区域中的一个随机点
public Vector3 GetZoneRandomPos()
{
int i = Random.Range(, gridCntX);
int j = Random.Range(, gridCntY);
return grid[i, j]._worldPos;
} //获取某格子周围除了自身外的另外所有可走格子
public List<Node> GetNeibourhood(Node node)
{
List<Node> neibourhood = new List<Node>();
for(int i = -; i <= ; i++)
{
for(int j = -; j<= ; j++)
{
if (i == && j == ) continue; int tempX = node._gridX + i;
int tempY = node._gridY + j;
if (tempX > && tempX < gridCntX && tempY > && tempY < gridCntY)
{
neibourhood.Add(grid[tempX,tempY]);
}
}
}
return neibourhood;
} private void OnDrawGizmos()
{ //绘制网格边界线
Gizmos.DrawWireCube(transform.position, new Vector3(gridSize.x, gridSize.y, ));
if (grid == null) return;
//Gizmos.color = new Color(1, 1, 1, 0.2f);
//绘制网格
foreach (var node in grid)
{
Gizmos.color = node._walkable ? Color.white : Color.red;
Gizmos.DrawCube(node._worldPos + Vector3.forward, Vector3.one * (nodeDiameter - .1f * nodeDiameter));
} if (path != null)
{
foreach(var node in path)
{
Gizmos.color = Color.black;
Gizmos.DrawCube(node._worldPos + Vector3.forward, Vector3.one * (nodeDiameter - .1f * nodeDiameter));
}
} Node tarNode = GetFromPosition(tarTrans.position);
if (tarNode != null && tarNode._walkable)
{
Gizmos.color = Color.cyan;
Gizmos.DrawCube(tarNode._worldPos, Vector3.one * (nodeDiameter - .1f * nodeDiameter));
}
}
}
下面是A星寻路算法,这里使用了两种不能的路径处理结果
using System.Collections;
using System.Collections.Generic;
using UnityEngine; //FindingPath为经典的A星寻路算法,获得网格路径。
//FindingPathForFlay是在寻路结束之后,将网格路径中不需要的网格删除调,从而使得运动方向更加平滑,剪除的过程在GeneratePathForFly中进行,
//后期优化可以放在寻路过程中进行
public class FindPath : MonoBehaviour
{
public Transform hunter, tar;
Grid _grid; // Start is called before the first frame update
void Start()
{
_grid = GetComponent<Grid>();
} // Update is called once per frame
void Update()
{
//FindingPath(hunter.position, tar.position);
FindingPathForFly(hunter.position, tar.position);
} //经典A*算法到目标点的精确路径查找
void FindingPath(Vector3 startPos,Vector3 endPos)
{
Node startNode = _grid.GetFromPosition(startPos);
Node endNode = _grid.GetFromPosition(endPos); List<Node> openSet = new List<Node>();
HashSet<Node> closeSet = new HashSet<Node>();
openSet.Add(startNode); while (openSet.Count > )
{
//根据曼哈顿距离寻找新结点
Node currentNode = openSet[]; for (int i = ; i < openSet.Count; i++)
{
if (openSet[i].fCost < currentNode.fCost || openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)
{
currentNode = openSet[i];
}
} openSet.Remove(currentNode);
closeSet.Add(currentNode); //如果当前结点是最后一个结点,那么寻找结束
if (currentNode == endNode) {
GeneratePath(startNode,endNode);
return;
} //添加周围的可用结点到openSet
foreach (var node in _grid.GetNeibourhood(currentNode))
{
if (!node._walkable || closeSet.Contains(node)) continue;
int newCost = currentNode.gCost + GetDistanceNodes(currentNode, node);
if (newCost < node.gCost || !openSet.Contains(node))
{
node.gCost = newCost;
node.hCost = GetDistanceNodes(node, endNode);
node.parent = currentNode;
if (!openSet.Contains(node))
{
openSet.Add(node);
}
}
}
}
} //经典A*算法下的飞行游戏路径查找
void FindingPathForFly(Vector3 startPos, Vector3 endPos)
{
Node startNode = _grid.GetFromPosition(startPos);
Node endNode = _grid.GetFromPosition(endPos); List<Node> openSet = new List<Node>();
HashSet<Node> closeSet = new HashSet<Node>();
openSet.Add(startNode); while (openSet.Count > )
{
//根据曼哈顿距离寻找新结点
Node currentNode = openSet[]; for (int i = ; i < openSet.Count; i++)
{
if (openSet[i].fCost < currentNode.fCost || openSet[i].fCost == currentNode.fCost && openSet[i].hCost < currentNode.hCost)
{
currentNode = openSet[i];
}
} openSet.Remove(currentNode);
closeSet.Add(currentNode); //如果当前结点是最后一个结点,那么寻找结束
if (currentNode == endNode)
{
GeneratePathForFly(startNode, endNode);
return;
} //添加周围的可用结点到openSet
foreach (var node in _grid.GetNeibourhood(currentNode))
{
if (!node._walkable || closeSet.Contains(node)) continue;
int newCost = currentNode.gCost + GetDistanceNodes(currentNode, node);
if (newCost < node.gCost || !openSet.Contains(node))
{
node.gCost = newCost;
node.hCost = GetDistanceNodes(node, endNode);
node.parent = currentNode;
if (!openSet.Contains(node))
{
openSet.Add(node);
}
}
}
}
} //经典A*算法下根据父子关系回溯获得路径
void GeneratePath(Node startNode,Node endNode)
{
List<Node> path = new List<Node>();
Node temp = endNode;
while (temp != startNode)//回溯,将所有路径结点结合成路径列表
{
path.Add(temp);
temp = temp.parent;
}
path.Reverse();//将列表反转
_grid.path = path;//将列表交予Grid;
} //经典A*算法下根据父子关系回溯获得路径,并且回溯。如果两个网格点之间没有障碍,可以直达,那么剪除两个网格点之间剪除不要的网格点
void GeneratePathForFly(Node startNode, Node endNode)
{
List<Node> path = new List<Node>();
Node temp = endNode;
while (temp != startNode)//回溯,将所有路径网格结合成路径列表
{
if(path.Count==)
path.Add(temp); RaycastHit2D hit;
//如果前一个网格点与当前网格点的父亲网格之间有障碍,那么表明需要当前网格点,将其添加到路径表中
if(hit=Physics2D.Raycast(path[path.Count-]._worldPos, temp.parent._worldPos - path[path.Count - ]._worldPos,Vector3.Distance(temp.parent._worldPos, path[path.Count - ]._worldPos)))
{
path.Add(temp);
} temp = temp.parent;
}
path.Reverse();//将列表反转 _grid.path = path;//将列表交予Grid;
} //获取两个点之间的曼哈顿距离
int GetDistanceNodes(Node a,Node b)
{
int cntX = Mathf.Abs(a._gridX - b._gridX);
int cntY = Mathf.Abs(a._gridY - b._gridY);
if (cntX > cntY)
{
return * cntY + * (cntX - cntY);
}
else
{
return * cntX + * (cntY - cntX);
}
}
}
结果

剪除多余的网格点之后

对于不同体积的角色,避免碰撞的处理方案有两种:
第一种:将网格分为不同的层,每层的格子的大小不同。
第二种:寻路时,网格可以走的条件为,walkable为真,且在设定半径范围内没有其他碰撞体存在。
2D飞行游戏最简单AI设计:
1.如果自己与目标之间没有障碍,那么直接飞向目标
2.飞向目标的方式:转向目标方向,如果当前的方向与到目标方向的角度小于设定的某个角度,那么开启喷射引擎。
3.逃离目标:这里以圆形的障碍目标为例,如果检测到障碍,那么设定障碍到自身的方向为目标方向,转向该目标方向,从而逃离目标。由于这个过程中自身的位置 不断变化,目标方向也是变化的,但是获得的结果也更加真实。
Unity 环境区域网格化的更多相关文章
- ARToolKit for Unity环境搭建(初步搭建成功)
最近一段时间才开始学习使用Unity3d,AR的学习使用中,先后使用了metaio SDK.vuforia SDK,但由于这两个都属于收费的,今天开始尝试使用ARToolKit.先将ARToolKit ...
- 在Unity环境下使用抽象和接口
http://gamasutra.com/blogs/VictorBarcelo/20131217/207204/Using_abstractions_and_interfaces_with_Unit ...
- unity 环境增强
早上过来解决两个问题: (1)VS2012 配置下 VA_X 插件,现在VS在字体颜色方面已经提高很多,但是在类成员实时显示方面还是不方便,或者我没找到吧 (2)安装unityVs插件,微软把 ...
- Unity 指定区域随机实例化预制体Prefab 代码
using UnityEngine; public class NewBehaviourScript : MonoBehaviour { public GameObject prefab; void ...
- 转:Oculus Unity Development Guide开发指南(2015-7-21更新)
http://forum.exceedu.com/forum/forum.php?mod=viewthread&tid=34175 Oculus Unity Development Guide ...
- C#区域截图——调用API截图
原文:C#区域截图——调用API截图 前言:截图对于一个C++开发者来说无非是小菜一碟,也有朋友使用C#的 Graphics.CopyFromScreen 方法屏幕操作,作为一名整天想着用 C++ 开 ...
- Unity 发布到IOS,Android的各种坑
Unity 发布到IOS的注意事项1.开发环境MAC环境:Xcode环境 7.2.1Unity环境:Unity5.32.基本说明首先,我说一下,这是我在对Unity发布到IOS的实际使用中,总结出来的 ...
- 【Holograms 101D】一步步用Unity 开发 Hologram
转载请注明出处: copperface:[Holograms 101D]一步步用Unity 开发 Hologram Holograms 101 该教程将带领你走完 Hologram 创建 的全过程.整 ...
- 【原】Unity Shader VS UDK Material Editor
UDK 的材质编辑器十分好用,毕竟是所见即所得的.虽然unity也有类似第三方插件,但易用性还是差很多,下面主要是,把一些常见表达式概念对应起来. 1. UDK CameraVector (相机位向量 ...
随机推荐
- linux fedora 的备份小技巧
大家都知道,在fedora中,是没有默认安装带有GUI的备份软件的. 我们可以去软件中心搜索“备份”或者“dup”来安装deja-dup来进行备份,这个软件就是ubuntu中设置的“备份”,只不过ub ...
- spring jpetstore研究入门(zz)
spring jpetstore研究入门 分类: java2008-12-21 23:25 561人阅读 评论(2) 收藏 举报 springstrutsibatissearchweb框架servle ...
- 【转】先说IEnumerable,我们每天用的foreach你真的懂它吗?
[转]先说IEnumerable,我们每天用的foreach你真的懂它吗? 我们先思考几个问题: 为什么在foreach中不能修改item的值? 要实现foreach需要满足什么条件? 为什么Linq ...
- springMVC学习 六 跳转方式
SpringMVC的controller中的方法执行完之后,默认的跳转方式是请求转发 如果想要修改跳转方式,可以设置返回值字符串内容(1) 添加 redirect:资源路径 重定向 "red ...
- 8月的list
多校的list: 第一周的多校list: k路归并 (思想大概理解了,还没实现 莫比乌斯 树归 第三场的多校list: 斯坦纳树 第四场多校: Pollard_rho算法和Miller_Rabin ...
- ubuntu新建、删除用户
新建用户名为newuser的用户,并赋予sudo权限 adduser newuser --ingroup sudo 删除用户以及用户目录 deluser -r newuser
- 2019.01.19 codeforces915E.Physical Education Lessons(ODT)
传送门 ODT水题(当然可以上线段树) 支持区间01覆盖,询问全局1的个数. 思路:直接上ODTODTODT. 不会的点这里 代码: #include<bits/stdc++.h> #de ...
- C#的委托与Java的自定义接口的异曲同工的同步操作
C#的委托(以WinForm为例) 在子窗体(ChildFrm)中定义一个委托 this.CaptureListener(callback);//子窗体触发委托事件,以告诉调用的窗体 /// < ...
- C#程序集问题:混合模式程序集是针对“v2.0.50727”版的运行时生成的.....
今天在把以前写的代码生成工具从原来的.NET3.5升级到.NET4.0,同时准备进一步完善,将程序集都更新后,一运行程序在一处方法调用时报出了一个异常: 混合模式程序集是针对“v2.0.50727”版 ...
- 【1】ASP.NET异步(1)
图标说明了异步的基础认识. 1.如果没有Ajax,提交之后整个页会刷新(左图).右图所示的虚线范围区域加入了ajax技术,提交之后只更新了虚线区域的内容,这样看比较直白. <form>①& ...