注意,本文的算法是建立在格子坐标都是整数的前提下.
因为做的游戏中有个攻击阻挡的需求(战棋类),就需要判断两个单位的连线上经过的格子中是否有障碍物的.那么就需要知道这个连线(线段)到底经过了哪些格子.本文则是针对此类问题的一个解决方案.
算法到底对没对,看看这张动态图演示就一目了然了,红色是起点,鼠标所在处是终点,黑色的格子则代表图中经过的格子.(Unity中的Demo效果)
几张静态图
任何线段都可以通过平移方式,将起点平移到原点处,而陡峭的线段可以通过延y=x翻转(x,y进行交换),得到不陡峭的线段,所以无论参与计算是线段是哪种,都可以先通过平移,翻转等操作得到类型于下面这样的线段(在第一象限,起点在原点,且不陡峭),最终在通过逆变换推算出正常的结果即可,简化计算,只用考虑下面这一种情况的处理即可.
采用x方向每0.5为递增梯度的思路,初略的以1为单位递增的话不够精确,比如这样的:
第一种情况, 如下需要检测C,D,E三点,对于C和E在格子的相交点,则需要排除掉,既视为与周围四个格子都不接触,而D在格子中,则当前格子则视为接触点.
第二种情况, 如下需要检测CDEFG五个点,C,E,G处于边界上,所以C的左右两侧格子则判断为接触.
第三种情况,检测点在上下边界上,如下的C点,C在横向边界上,所以C的上下两个格子则视为接触.
using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// Created by Vitens on 2020/7/25 16:27:02/// /// Description : /// 涉及格子间的一些数学计算/// </summary>public class GridHelper{ /// <summary> /// 计算两点间经过的格子 /// </summary> public static List<Vector2Int> GetTouchedPosBetweenTwoPoints(Vector2Int from, Vector2Int to) { List<Vector2Int> touchedGrids = GetTouch车辆报废年限edPosBetweenOrigin2Target(to - from); touchedGrids.Offt(from); return touchedGrids; } /// <summary> /// 计算目标位置到原点所经过的格子 /// </summary> static List<Vector2Int> GetTouchedPosBetweenOrigin2Target(Vector2Int target) { List<Vector2Int> touched = new List<Vector2Int>(); bool steep = Mathf.Abs(target.y) > Mathf.Abs(target.x); int x = steep ? target.y : target.x; int y = steep ? target.x : target.y; //斜率 float tangent = (float)y / x; float delta = x > 0 ? 0.5f : -0.5f; for (int i = 1; i < 2 * Mathf.Abs(x); i++) { float tempX = i * delta; float tempY = tangent * tempX; bool isOnEdge = Mathf.Abs(tempY - Mathf.FloorToInt(tempY)) == 0.5f; //偶数 格子内部判断 if ((i & 1) == 0) { //在边缘,则上下两个格子都满足条件 if (isOnEdge) { touched.AddUnique(new Vector2Int(Mathf.RoundToInt(tempX), Mathf.CeilToInt(tempY))); touched.AddUnique(new Vector2Int(Mathf.RoundToInt(tempX), Mathf.FloorToInt(tempY))); } 姐姐好饿第二季 //不在边缘就所处格子满足条件 电脑自动关机命令 el { touched.AddUnique(new Vector2Int(Mathf.RoundToInt(tempX), Mathf.RoundToInt(tempY))); } } //奇数 格子边缘判断 el { //在格子交点处,不视为阻挡,忽略 if (isOnEdge) { continue; } //否则左右两个格子满足 el { touched.AddUnique(new Vector2Int(Mathf.CeilToInt(tempX), Mathf.RoundToInt(tempY))); touched.AddUnique(new Vector2Int(Mathf.FloorToInt(tempX), Mathf.RoundToInt(tempY))); } } } if (steep) { //镜像翻转 交换 X Y 伊通实验中学 for (int i = 0; i < touched.Count; i++) { Vector2Int v = touched[i]; v.x = v.x ^ v.y; v.y = v.x ^ v.y; v.x = v.x ^ v.y; touched[i] = v; } } touched.Except(new List<Vector2Int>() { Vector2Int.zero, target }); return touched; }}
因为是在Unity环境运行的,所以用的是Vector2Int记录点的位置,这里有几个对List运算的扩展,为了使代码看起来更简洁,少些很多是否Contains的校验.
using System.Collections;using System.Collections.Generic;using UnityEngine;/// <summary>/// Created by Vitens on 2020/7/25 16:31:43/// /// Description : /// 格子相关计算扩展/// </summary>public static class GridsExtension{ //添加元素(如果已经有了则不需要重复中风治疗添加) public static void AddUnique(this List<Vector2Int> lf, Vector2Int other) { if (!lf.Contains(other)) { lf.Add(other); } } //添加元素(如果已经有了则不需要重复添加) public static void AddUnique(this List<Vector2Int> lf, List<Vector2Int> others) { if (others == null) return; for (int i = 0; i < others.Count; i++) { if (!lf.Contains(others[i])) { lf.Add(others[i]); } } } //偏移 public static void Offt(this List<Vector2Int> lf, Vector2Int offt) { for (int i = 0; i < lf.Count; i++) { lf[i] += offt; } } //移除操作 public static void Except(this List<Vector2Int> lf, List<Vector2Int> other) { if (other == null) return; for (int i = 0; i < other.Count; i++) { if (lf.Contains(other[i])) { lf.Remove(other[i]); } } }}
如果本文对您有帮助,不妨动动小指给个赞 O(∩_∩)O
本文地址:https://blog.csdn.net/Vitens/article/details/107588013
本文发布于:2023-04-03 19:11:07,感谢您对本站的认可!
本文链接:https://www.wtabcd.cn/fanwen/zuowen/6a136f0c9922c5f2a137176796404afa.html
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。
本文word下载地址:【图形学】计算网格中两点连成的直线所经过的格子.doc
本文 PDF 下载地址:【图形学】计算网格中两点连成的直线所经过的格子.pdf
留言与评论(共有 0 条评论) |