Files
HX_MapEditor/Assets/Scripts/Map/GridSelector.cs
2025-07-25 15:36:10 +08:00

567 lines
21 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

using UnityEngine;
using UnityEngine.EventSystems;
using System.Runtime.InteropServices;
using System.Collections.Generic;
using System;
//[ExecuteAlways]
public class GridSelector : MonoBehaviour
{
public struct RenderData
{
public int barrier; //阻隔点,高度值
public float isSelected;
public float r;
public float g;
public float b;
}
[Space]
[Range(0f, 1f)]
[Header("调整透明度")]
public float gridAlpha = 1;
private MeshRenderer mapRenderer;
private MeshCollider mapCollider;
private Map map;
public int horizontalNumber { get { return Mathf.CeilToInt(map.width / map.sideWidth); } }
public int verticalNumber { get { return Mathf.CeilToInt(map.height / map.sideHeight); } }
public int totalNumber { get { return horizontalNumber * verticalNumber; } }
public int moveNum;
private ComputeBuffer inputbuffer;
public RenderData[] dataArray;
public List<int> selectedGridIndex = new List<int>();
private int shiftBeginIndex;
private List<Vector2> lassoPos = new List<Vector2>();
private LineRenderer lassoLine;
public CellType getCellType(int index)
{
return (CellType)dataArray[index].barrier;
}
public CellType CellType { set; get; }
public void OnMapCreated(Map map)
{
this.map = map;
moveNum = 0;
mapRenderer.material.SetVector("_Size", new Vector4(horizontalNumber, verticalNumber));
mapRenderer.material.SetFloat("_UserInput", 1);
inputbuffer = new ComputeBuffer(totalNumber, Marshal.SizeOf(typeof(RenderData)));
dataArray = new RenderData[totalNumber];
//创建新的地图默认为全部是可行走区域
for (int i = 0; i < totalNumber; i++)
{
dataArray[i].barrier = (int)CellType.Move;
}
RefreshPlaneRender();
}
public RenderData[] GetGridData()
{
return dataArray;
}
public void setdataArrayIndex(int index,float cellheight, int barrier)
{
if (index >= dataArray.Length)
return;
int height = (int)(cellheight * 100);
height += 10000;
int mask = dataArray[index].barrier & 0xFFFF;
//去除之前的8-16位
//mask &= 0xFF;
//移除之前的信息值再加新的
mask &= ~(int)CellType.Move;
mask &= ~(int)CellType.Obstacle;
mask |= barrier;
//先取出之前的高度值
dataArray[index].barrier = height << 16;
dataArray[index].barrier |= mask;
setDataColor(index);
}
public int getdataByPos(Vector3 pos)
{
int hitIndex = Mathf.FloorToInt(pos.x / map.sideWidth) + Mathf.FloorToInt(pos.z / map.sideHeight) * horizontalNumber;
return hitIndex;
}
public Vector2Int getMapGrid(Vector3 pos)
{
return getMapGrid(new Vector2(pos.x,pos.y));
}
public Vector2Int getMapGrid(Vector2 pos)
{
var grid = new Vector2Int();
grid.x = Mathf.FloorToInt(pos.x / map.sideWidth);
grid.y = Mathf.FloorToInt(pos.y / map.sideHeight);
return grid;
}
public Vector2 getPosByGrid(Vector2Int grid)
{
return new Vector2(grid.x * map.sideWidth + map.sideWidth/2, grid.y * map.sideHeight + map.sideHeight / 2);
}
private void Awake()
{
mapRenderer = GetComponent<MeshRenderer>();
mapCollider = GetComponent<MeshCollider>();
GameObject go = GameObject.Instantiate(MapManager.Instance.line.gameObject);
go.transform.SetParent(transform);
lassoLine = go.GetComponent<LineRenderer>();
lassoLine.positionCount = 0;
lassoLine.startWidth = 0.1f;
lassoLine.endWidth = 0.1f;
lassoLine.gameObject.SetActive(false);
}
private void OnDestroy()
{
if (inputbuffer != null)
{
inputbuffer.Release();
}
}
private int GetIndexByXY(int x, int y) {
int index = x + y * horizontalNumber;
return index;
}
/// <summary>
/// 这里采用的算法是每行从左到右的算法,但是过滤了已经重复过的格子
/// *************
/// *****678*****
/// *****4*5*****
/// *****123*****
/// *************
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
private int FindMapHeight(int index) {
GetXyByIndex(index, out int oldx, out int oldy);
//像外扩展,找到一个邻近的高度值
int maxR = 1000;
int stepI;
for (int r = 1; r < maxR; r++)
{
for (int j = -r; j <= r; j++)
{
if (j == -r || j == r)
{
stepI = 1;
}
else
{
stepI = r * 2;
}
for (int i = -r; i <= r; i += stepI)
{
int x = oldx + i;
int y = oldy + j;
if (x < 0 || x >= horizontalNumber || y < 0 || y >= verticalNumber) {
continue;
}
int neighborIndex = x + y * horizontalNumber;
int neighborValue = dataArray[neighborIndex].barrier;
if (neighborValue != 0)
{
return neighborValue;
}
}
}
}
return 1;
}
float lastAlpha = 1;
private void Update()
{
if (lastAlpha != gridAlpha)
{
map.ChangeGridAlpha(gridAlpha);
lastAlpha = gridAlpha;
}
//先处理移动区域,要先处理之前的区域数据
if (Input.GetKeyDown(KeyCode.C))
{
for (int i = 0, length = selectedGridIndex.Count; i < length; i++)
{
int index = selectedGridIndex[i];
//if ((dataArray[index].barrier & (int)CellType.Obstacle) != 0)
//{
// dataArray[index].barrier &= ~(int)CellType.Obstacle;
//}
//dataArray[index].barrier |= (int)CellType.Move;
setDataCellType(index, true);
}
RefreshPlaneRender();
}
if (Input.GetKeyDown(KeyCode.X))
{
for (int i = 0, length = selectedGridIndex.Count; i < length; i++)
{
int index = selectedGridIndex[i];
//if ((dataArray[index].barrier & (int)CellType.Move) != 0)
//{
// dataArray[index].barrier &= ~(int)CellType.Move;
//}
//dataArray[index].barrier |= (int)CellType.Obstacle;
setDataCellType(index, false);
}
RefreshPlaneRender();
}
if (Input.GetKeyUp(KeyCode.Z))
{
lassoPos.Clear();
lassoLine.gameObject.SetActive(false);
lassoLine.positionCount = 0;
}
if (Input.GetMouseButtonDown(0))
{
if (mapCollider.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit, float.PositiveInfinity))
{
Vector2 hitUv = hit.textureCoord;
int hitIndex = Mathf.FloorToInt(hitUv.x * horizontalNumber) + Mathf.FloorToInt(hitUv.y * verticalNumber) * horizontalNumber;
if (!Input.GetKey(KeyCode.LeftShift))
{
if (Input.GetKey(KeyCode.LeftControl))
{
shiftBeginIndex = hitIndex;
SetGridIndexSelected(hitIndex, dataArray[hitIndex].isSelected != 1f);
RefreshPlaneRender();
}
else if (Input.GetKey(KeyCode.Z))
{
Vector2 hitPos = GetCenterPosByIndex(hitIndex);
if (lassoPos.Count == 0 || lassoPos[0] != hitPos)
{
if (lassoPos.Count == 0)
{
SetAllUnselected();
}
SetGridIndexSelected(hitIndex, true);
lassoLine.gameObject.SetActive(true);
lassoLine.positionCount++;
lassoLine.SetPosition(lassoLine.positionCount - 1, new Vector3(hitPos.x,transform.position.y + 0.1f, hitPos.y));
lassoPos.Add(hitPos);
}
else
{
lassoLine.positionCount++;
lassoLine.SetPosition(lassoLine.positionCount - 1, new Vector3(hitPos.x,transform.position.y + 0.1f, hitPos.y));
lassoPos.Add(hitPos);
for (int i = 0; i < totalNumber; i++)
{
Vector2 gridPos = GetCenterPosByIndex(i);
bool isSelected = false;
for (int j = 0, length = lassoPos.Count - 1; j < length; j++)
{
Vector2 pos0 = lassoPos[j];
Vector2 pos1 = lassoPos[j + 1];
if (Pnpoly(gridPos, pos0, pos1))
{
isSelected = !isSelected;
}
}
SetGridIndexSelected(i, isSelected);
}
}
if (lassoLine.positionCount == 2)
{
//CSSMoveMgr.Instance.FindPath(lassoPos[0], lassoPos[1]);
//在这里寻找路径
}
RefreshPlaneRender();
}
else if (!Input.GetKey(KeyCode.LeftAlt)) //左alt键涉及到镜头操作所以屏蔽掉
{
shiftBeginIndex = hitIndex;
SetAllUnselected();
SetGridIndexSelected(hitIndex, true);
RefreshPlaneRender();
}
}
}
else
{
SetAllUnselected();
RefreshPlaneRender();
}
}
else if (Input.GetMouseButton(0))
{
if (mapCollider.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit, float.PositiveInfinity))
{
if (Input.GetKey(KeyCode.LeftShift))
{
Vector2 hitUv = hit.textureCoord;
int hitIndex = Mathf.FloorToInt(hitUv.x * horizontalNumber) + Mathf.FloorToInt(hitUv.y * verticalNumber) * horizontalNumber;
int beginX, beginY;
GetXyByIndex(shiftBeginIndex, out beginX, out beginY);
int hitX, hitY;
GetXyByIndex(hitIndex, out hitX, out hitY);
beginPos.x = beginX;
beginPos.z = beginY;
endPos.x = hitX;
endPos.z = hitY;
//CSSMoveMgr.Instance.FindPath(new Vector3(beginX,0,beginY),new Vector3(hitX,0,hitY));
ShiftKeySelect(hitIndex);
RefreshPlaneRender();
}
}
}
}
private Vector3 beginPos = new Vector3();
private Vector3 endPos = new Vector3();
private void OnGUI()
{
GUIStyle style = new GUIStyle() { fontSize = 30 };
style.normal.textColor = Color.red;
int width = Screen.width;
int height = Screen.height;
GUI.Label(new Rect(width - 320, height - 150, 400, 50), "按住Shift按钮多选", style);
GUI.Label(new Rect(width - 320, height - 100, 400, 50), "选中后C键取消格子信息", style);
GUI.Label(new Rect(width - 320, height - 50, 400, 50), "选中后X键确定格子信息", style);
style.normal.textColor = Color.green;
if (selectedGridIndex.Count == 0)
{
return;
}
else if (selectedGridIndex.Count == 1)
{
int beginIndex = selectedGridIndex[0];
GetXyByIndex(beginIndex, out int x, out int y);
string labelText = string.Format(
"所选点XY序列({0},{1})",
x, y);
Vector2 labelSize = style.CalcSize(new GUIContent(labelText));
GUI.Label(
new Rect(Screen.width - labelSize.x - 20, 20, labelSize.x, labelSize.y),
labelText,
style
);
//GUI.Label(new Rect(width - 820, 20, 400, 50), string.Format("所选点XY序列({0},{1}) 高度{2} 点类型 {3}", x, y, ((dataArray[beginIndex].barrier >> 16) - 10000)/100.0f, CellTypeColors.GetAreaStr((dataArray[beginIndex].barrier)), style));
}
else
{
int beginIndex = selectedGridIndex[0];
int endIndex = selectedGridIndex[selectedGridIndex.Count - 1];
GetXyByIndex(beginIndex, out int x0, out int y0);
GetXyByIndex(endIndex, out int x1, out int y1);
GUI.Label(new Rect(width - 420, 20, 400, 50), string.Format("所选起点XY序列({0},{1})", x0, y0), style);
GUI.Label(new Rect(width - 420, 90, 400, 50), string.Format("所选终点XY序列({0},{1})", x1, y1), style);
}
}
private void SetGridIndexSelected(int index, bool isSelected)
{
if (isSelected)
{
if (!selectedGridIndex.Contains(index))
{
selectedGridIndex.Add(index);
dataArray[index].isSelected = 1f;
}
}
else
{
selectedGridIndex.Remove(index);
dataArray[index].isSelected = 0f;
}
}
private void setDataColor(int index) {
Color color = CellTypeColors.GetColor((CellType)dataArray[index].barrier);
dataArray[index].r = color.r;
dataArray[index].g = color.g;
dataArray[index].b = color.b;
}
private void setDataCellType(int index,bool IsCancel)
{
if (UICellEditor.Instance == null) return;
if (UICellEditor.Instance.editorGrid == CellType.Move ||
UICellEditor.Instance.editorGrid == CellType.Obstacle)
{
if (IsCancel)
{
if ((dataArray[index].barrier & (int)CellType.Obstacle) != 0)
{
dataArray[index].barrier &= ~(int)CellType.Obstacle;
}
dataArray[index].barrier |= (int)CellType.Move;
}
else
{
if ((dataArray[index].barrier & (int)CellType.Move) != 0)
{
dataArray[index].barrier &= ~(int)CellType.Move;
}
dataArray[index].barrier |= (int)CellType.Obstacle;
}
}
else if (UICellEditor.Instance.editorGrid == CellType.Safe)
{
if (IsCancel)
{
if ((dataArray[index].barrier & (int)CellType.Safe) != 0)
{
dataArray[index].barrier &= ~(int)CellType.Safe;
}
}
else
{
dataArray[index].barrier |= (int)CellType.Safe;
}
}
else if (UICellEditor.Instance.editorGrid == CellType.Stall)
{
if (IsCancel)
{
if ((dataArray[index].barrier & (int)CellType.Stall) != 0)
{
dataArray[index].barrier &= ~(int)CellType.Stall;
}
}
else
{
dataArray[index].barrier |= (int)CellType.Stall;
}
}
else if (UICellEditor.Instance.editorGrid == CellType.Hide)
{
if (IsCancel)
{
if ((dataArray[index].barrier & (int)CellType.Hide) != 0)
{
dataArray[index].barrier &= ~(int)CellType.Hide;
}
}
else
{
dataArray[index].barrier |= (int)CellType.Hide;
}
}
}
public void FullAllArea()
{
for (int i = 0; i < dataArray.Length; i++)
{
CellType cell = (CellType)dataArray[i].barrier;
if (cell.HasFlag(CellType.Move))
{
dataArray[i].barrier &= ~(int)CellType.Move;
}
dataArray[i].barrier |= (int)CellType.Obstacle;
}
RefreshPlaneRender();
}
public void ClearSelectArea()
{
for (int i = 0; i < dataArray.Length; i++)
{
CellType cell = (CellType)dataArray[i].barrier;
if (cell.HasFlag(CellType.Obstacle))
{
dataArray[i].barrier &= ~(int)CellType.Obstacle;
}
dataArray[i].barrier |= (int)CellType.Move;
}
RefreshPlaneRender();
}
public void RefreshPlaneRender()
{
moveNum = 0;
for (int i = 0; i < dataArray.Length; i++) {
setDataColor(i);
CellType cell = (CellType)dataArray[i].barrier;
if (cell.HasFlag(CellType.Move))
{
moveNum++;
}
}
inputbuffer.SetData(dataArray);
mapRenderer.material.SetBuffer("_InputData", inputbuffer);
}
public void GetXyByIndex(int index,out int x,out int y)
{
x = index % horizontalNumber;
y = index / horizontalNumber;
}
//TODO 这里有改动,应该是对的
private Vector2 GetCenterPosByIndex(int index)
{
GetXyByIndex(index, out int x, out int y);
return new Vector2((x + 0.5f)*map.sideWidth, (y + 0.5f) * map.sideHeight);
}
private bool Pnpoly(Vector2 gridPos, Vector2 pos0, Vector2 pos1)
{
bool tmp0 = (gridPos.y < pos1.y) != (gridPos.y < pos0.y);
bool tmp1 = gridPos.x <= (pos0.x - pos1.x) * (gridPos.y - pos1.y) / (pos0.y - pos1.y) + pos1.x;
return tmp0 && tmp1;
}
private void SetAllUnselected()
{
for (int i = selectedGridIndex.Count - 1; i >= 0; i--)
{
SetGridIndexSelected(selectedGridIndex[i], false);
}
}
private void ShiftKeySelect(int hitIndex)
{
int beginX, beginY;
GetXyByIndex(shiftBeginIndex, out beginX, out beginY);
int hitX, hitY;
GetXyByIndex(hitIndex, out hitX, out hitY);
int minX = Mathf.Min(beginX, hitX);
int maxX = Mathf.Max(beginX, hitX);
int minY = Mathf.Min(beginY, hitY);
int maxY = Mathf.Max(beginY, hitY);
shiftBeginIndex = hitIndex;
for (int y = 0; y < verticalNumber; y++)
{
for (int x = 0; x < horizontalNumber; x++)
{
bool isWithin = x >= minX && x <= maxX && y >= minY && y <= maxY;
if (isWithin)
{
int index = x + y * horizontalNumber;
SetGridIndexSelected(index, true);
}
}
}
}
public Vector2Int GetMouseByCell()
{
Vector2Int cell = new Vector2Int();
if (mapCollider.Raycast(Camera.main.ScreenPointToRay(Input.mousePosition), out RaycastHit hit, float.PositiveInfinity))
{
Vector2 hitUv = hit.textureCoord;
cell.x = Mathf.FloorToInt(hitUv.x * horizontalNumber);
cell.y = Mathf.FloorToInt(hitUv.y * verticalNumber);
}
return cell;
}
public Vector2Int GetMouseByCell(Vector3 mousePositon)
{
Vector2Int cell = new Vector2Int();
if (mapCollider.Raycast(Camera.main.ScreenPointToRay(mousePositon), out RaycastHit hit, float.PositiveInfinity))
{
Vector2 hitUv = hit.textureCoord;
cell.x = Mathf.FloorToInt(hitUv.x * horizontalNumber);
cell.y = Mathf.FloorToInt(hitUv.y * verticalNumber);
}
return cell;
}
}