fix:1、添加项目。2、基本箭头生成
This commit is contained in:
@@ -0,0 +1,19 @@
|
||||
namespace ChillConnect
|
||||
{
|
||||
public class ArrowGameCtrl : BaseCtrl
|
||||
{
|
||||
public static ArrowGameCtrl Instance { get; private set; }
|
||||
|
||||
private ArrowGameModel model;
|
||||
|
||||
protected override void OnInit()
|
||||
{
|
||||
Instance = this;
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
Instance = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0dfe8f46fa1e414b944056bbe769fa67
|
||||
timeCreated: 1781178068
|
||||
@@ -0,0 +1,13 @@
|
||||
namespace ChillConnect
|
||||
{
|
||||
public class ArrowGameModel : BaseModel
|
||||
{
|
||||
protected override void OnInit()
|
||||
{
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6e908da0a114c6aab92325f6c42619d
|
||||
timeCreated: 1781178068
|
||||
@@ -0,0 +1,489 @@
|
||||
using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using FairyGUI;
|
||||
using FGUI.Arrow_game;
|
||||
using UnityEngine;
|
||||
|
||||
namespace ChillConnect
|
||||
{
|
||||
public class ArrowGameUI : BaseUI
|
||||
{
|
||||
private ArrowGameUICtrl ctrl;
|
||||
private com_arrow_game ui;
|
||||
|
||||
// FGUI界面根容器
|
||||
private GComponent _viewContainer;
|
||||
|
||||
#region FGUI 模板引用
|
||||
private ArrorPoint _dotTemplate;
|
||||
private ArrowEnd _arrowTemplate;
|
||||
private LineTile _lineUnitTemplate;
|
||||
#endregion
|
||||
|
||||
// 关卡总配置数据
|
||||
private LevelConfig _levelConfig;
|
||||
|
||||
// 全局固定参数
|
||||
private const float _pointSpacing = 60f; // 点位间距 60px
|
||||
private const float _moveSpeed = 120f; // 移动速度
|
||||
|
||||
public ArrowGameUI(ArrowGameUICtrl ctrl) : base(ctrl)
|
||||
{
|
||||
uiName = UIConst.ArrowGameUI;
|
||||
this.ctrl = ctrl;
|
||||
}
|
||||
|
||||
protected override void SetUIInfo(UIInfo uiInfo)
|
||||
{
|
||||
uiInfo.packageName = "Arrow_game";
|
||||
uiInfo.assetName = "com_arrow_game";
|
||||
uiInfo.layerType = UILayerType.Popup;
|
||||
uiInfo.isNeedOpenAnim = false;
|
||||
uiInfo.isNeedCloseAnim = false;
|
||||
uiInfo.isNeedUIMask = true;
|
||||
}
|
||||
|
||||
#region 生命周期
|
||||
protected override void OnInit()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnClose()
|
||||
{
|
||||
// 销毁所有动态FGUI对象
|
||||
ClearAllDynamicObj();
|
||||
|
||||
// 释放模板资源
|
||||
if (_dotTemplate != null) _dotTemplate.Dispose();
|
||||
if (_arrowTemplate != null) _arrowTemplate.Dispose();
|
||||
if (_lineUnitTemplate != null) _lineUnitTemplate.Dispose();
|
||||
|
||||
_dotTemplate = null;
|
||||
_arrowTemplate = null;
|
||||
_lineUnitTemplate = null;
|
||||
_levelConfig = null;
|
||||
}
|
||||
|
||||
protected override void OnBind()
|
||||
{
|
||||
ui = baseUI as com_arrow_game;
|
||||
}
|
||||
|
||||
public override void OnUpdate()
|
||||
{
|
||||
if (_levelConfig == null) return;
|
||||
foreach (var arrow in _levelConfig.arrows)
|
||||
{
|
||||
UpdateArrowMove(arrow);
|
||||
}
|
||||
}
|
||||
|
||||
protected override void OnOpenBefore(object args)
|
||||
{
|
||||
_viewContainer = ui.view_container;
|
||||
// 初始化FGUI模板
|
||||
_dotTemplate = (ArrorPoint)UIPackage.CreateObject("Arrow_game", "ArrorPoint");
|
||||
_arrowTemplate = (ArrowEnd)UIPackage.CreateObject("Arrow_game", "ArrowEnd");
|
||||
_lineUnitTemplate = (LineTile)UIPackage.CreateObject("Arrow_game", "LineTile");
|
||||
LoadLevelConfig();
|
||||
InitView();
|
||||
}
|
||||
|
||||
protected override void OnOpen(object args) { }
|
||||
protected override void OnHide() { }
|
||||
protected override void OnDisplay(object args) { }
|
||||
#endregion
|
||||
|
||||
#region 页面初始化 & 关闭按钮
|
||||
private void InitView()
|
||||
{
|
||||
// 关闭按钮
|
||||
ui.btn_close.SetClick(() =>
|
||||
{
|
||||
CtrlCloseUI();
|
||||
});
|
||||
|
||||
if (_levelConfig == null) return;
|
||||
|
||||
// 1. 以 ui.point 为原点生成整张网格圆点
|
||||
DrawGridDots(_levelConfig);
|
||||
|
||||
// 2. 初始化所有箭头 + 路径线条
|
||||
foreach (var arrow in _levelConfig.arrows)
|
||||
{
|
||||
BuildArrowPath(arrow, _levelConfig);
|
||||
CreateArrowAndLines(arrow, _levelConfig);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 配置加载
|
||||
private void LoadLevelConfig()
|
||||
{
|
||||
_levelConfig = JsonHelper.LoadLevel("level_1001.json");
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 【核心】网格坐标工具(基于 ui.point 为原点)
|
||||
/// <summary>点位ID 转 行列索引</summary>
|
||||
private void PointIdToRowCol(int pointId, int totalCol, out int row, out int col)
|
||||
{
|
||||
row = pointId / totalCol;
|
||||
col = pointId % totalCol;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 行列 → 实际像素坐标
|
||||
/// 原点 = ui.point 组件坐标,向右+X,向下+Y,间距固定60
|
||||
/// </summary>
|
||||
private Vector2 RowColToPos(int row, int col)
|
||||
{
|
||||
// 取UI上point组件的坐标作为网格原点
|
||||
float originX = ui.point.x;
|
||||
float originY = ui.point.y;
|
||||
|
||||
float x = originX + col * _levelConfig.pointSpacing;
|
||||
float y = originY + row * _levelConfig.pointSpacing;
|
||||
return new Vector2(x, y);
|
||||
}
|
||||
|
||||
/// <summary>点位ID → 最终像素坐标</summary>
|
||||
private Vector2 PointIdToPos(int pointId, int totalCol)
|
||||
{
|
||||
PointIdToRowCol(pointId, totalCol, out int r, out int c);
|
||||
return RowColToPos(r, c);
|
||||
}
|
||||
|
||||
/// <summary>方向转旋转角度(箭头默认朝上)</summary>
|
||||
private float DirToRotation(string dir)
|
||||
{
|
||||
return dir switch
|
||||
{
|
||||
"up" => 0f,
|
||||
"right" => 90f,
|
||||
"down" => 180f,
|
||||
"left" => 270f,
|
||||
_ => 0f
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 绘制网格圆点(以 ui.point 为起点)
|
||||
private void DrawGridDots(LevelConfig cfg)
|
||||
{
|
||||
if (_dotTemplate == null || _viewContainer == null) return;
|
||||
_dotTemplate.visible = false;
|
||||
|
||||
var rows = cfg.gridRows;
|
||||
var cols = cfg.gridCols;
|
||||
|
||||
for (var r = 0; r < rows; r++)
|
||||
{
|
||||
for (var c = 0; c < cols; c++)
|
||||
{
|
||||
var dot = ArrorPoint.CreateInstance();
|
||||
var pos = RowColToPos(r, c);
|
||||
dot.SetPosition(pos.x, pos.y,0);
|
||||
_viewContainer.AddChild(dot);
|
||||
}
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 构建箭头路径点位
|
||||
private void BuildArrowPath(ArrowConfig arrow, LevelConfig cfg)
|
||||
{
|
||||
arrow.pathPointList = new List<Vector2>();
|
||||
int totalCol = cfg.gridCols;
|
||||
|
||||
int curPointId = arrow.startPoint;
|
||||
Vector2 curPos = PointIdToPos(curPointId, totalCol);
|
||||
arrow.pathPointList.Add(curPos);
|
||||
|
||||
foreach (string dir in arrow.path)
|
||||
{
|
||||
PointIdToRowCol(curPointId, totalCol, out int r, out int c);
|
||||
switch (dir)
|
||||
{
|
||||
case "up": r--; break;
|
||||
case "down": r++; break;
|
||||
case "left": c--; break;
|
||||
case "right": c++; break;
|
||||
}
|
||||
curPointId = r * totalCol + c;
|
||||
curPos = PointIdToPos(curPointId, totalCol);
|
||||
arrow.pathPointList.Add(curPos);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 创建箭头 + 线条方块
|
||||
private void CreateArrowAndLines(ArrowConfig arrow, LevelConfig cfg)
|
||||
{
|
||||
arrow.lineUnits = new List<GComponent>();
|
||||
List<Vector2> path = arrow.pathPointList;
|
||||
|
||||
if (path.Count < 2)
|
||||
{
|
||||
CreateArrowOnly(arrow, path[0]);
|
||||
return;
|
||||
}
|
||||
|
||||
ColorUtility.TryParseHtmlString(arrow.color, out Color lineColor);
|
||||
List<Vector2> allTilePositions = new List<Vector2>();
|
||||
|
||||
// 关键:直接用 path 数组判断方向,和配置表完全一致
|
||||
for (int i = 0; i < path.Count - 1; i++)
|
||||
{
|
||||
Vector2 pStart = path[i];
|
||||
Vector2 pEnd = path[i + 1];
|
||||
string dir = arrow.path[i]; // 直接从配置里取方向
|
||||
|
||||
float offsetX = 0f;
|
||||
float offsetY = 0f;
|
||||
|
||||
// 严格按 path 方向设置步长
|
||||
switch (dir)
|
||||
{
|
||||
case "right":
|
||||
offsetX = 10f;
|
||||
break;
|
||||
case "left":
|
||||
offsetX = -10f;
|
||||
break;
|
||||
case "down":
|
||||
offsetY = 10f;
|
||||
break;
|
||||
case "up":
|
||||
offsetY = -10f;
|
||||
break;
|
||||
}
|
||||
|
||||
Vector2 curPos = pStart;
|
||||
// 生成6个方块,从pStart一直覆盖到pEnd
|
||||
for (int k = 0; k < 6; k++)
|
||||
{
|
||||
allTilePositions.Add(curPos);
|
||||
curPos.x += offsetX;
|
||||
curPos.y += offsetY;
|
||||
}
|
||||
}
|
||||
|
||||
// 移除最后一个点(终点位置的方块,避免挡住箭头)
|
||||
if (allTilePositions.Count > 0)
|
||||
{
|
||||
allTilePositions.RemoveAt(allTilePositions.Count - 1);
|
||||
}
|
||||
|
||||
// 创建所有方块
|
||||
foreach (var pos in allTilePositions)
|
||||
{
|
||||
LineTile unit = LineTile.CreateInstance();
|
||||
unit.SetPosition(pos.x, pos.y, 0);
|
||||
unit.GetChild("line").asGraph.color = lineColor;
|
||||
unit.visible = true;
|
||||
|
||||
_viewContainer.AddChild(unit);
|
||||
arrow.lineUnits.Add(unit);
|
||||
}
|
||||
|
||||
// 创建箭头
|
||||
CreateArrowOnly(arrow, path[path.Count - 1]);
|
||||
}
|
||||
|
||||
private void CreateArrowOnly(ArrowConfig arrow, Vector2 arrowPos)
|
||||
{
|
||||
ArrowEnd arrowIns = ArrowEnd.CreateInstance();
|
||||
arrowIns.SetPosition(arrowPos.x, arrowPos.y, 0);
|
||||
|
||||
if (arrow.path.Count > 0)
|
||||
{
|
||||
string lastDir = arrow.path[arrow.path.Count - 1];
|
||||
arrowIns.rotation = DirToRotation(lastDir);
|
||||
}
|
||||
|
||||
if (ColorUtility.TryParseHtmlString(arrow.color, out Color arrowColor))
|
||||
{
|
||||
GImage icon = arrowIns.GetChild("icon").asImage;
|
||||
if (icon != null) icon.color = arrowColor;
|
||||
}
|
||||
|
||||
arrowIns.visible = true;
|
||||
_viewContainer.AddChild(arrowIns);
|
||||
arrowIns.onClick.Add(() => OnArrowClick(arrow));
|
||||
arrow.arrowObj = arrowIns;
|
||||
|
||||
arrow.isMoving = false;
|
||||
arrow.targetIndex = arrow.pathPointList.Count - 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 两点间距60px,使用10x10方块,生成6个,完整铺满从pStart到pEnd的线段。
|
||||
/// 关键:覆盖完整区间,保证拐点无缝衔接。
|
||||
/// </summary>
|
||||
private void CreateLineUnitBetweenTwoPoint(Vector2 pStart, Vector2 pEnd, Color lineColor, List<GComponent> lineList)
|
||||
{
|
||||
if (_lineUnitTemplate == null) return;
|
||||
|
||||
float offsetX = 0f;
|
||||
float offsetY = 0f;
|
||||
|
||||
// 判断方向
|
||||
if (Mathf.Abs(pEnd.x - pStart.x) > 1f)
|
||||
{
|
||||
offsetX = 10f;
|
||||
}
|
||||
else
|
||||
{
|
||||
offsetY = 10f;
|
||||
}
|
||||
|
||||
Vector2 curPos = pStart;
|
||||
// 循环6次,生成6个方块,从pStart一直覆盖到pEnd
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
LineTile unit = LineTile.CreateInstance();
|
||||
unit.SetPosition(curPos.x, curPos.y, 0);
|
||||
unit.GetChild("line").asGraph.color = lineColor;
|
||||
unit.visible = true;
|
||||
|
||||
_viewContainer.AddChild(unit);
|
||||
lineList.Add(unit);
|
||||
|
||||
curPos.x += offsetX;
|
||||
curPos.y += offsetY;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 点击 & 移动逻辑
|
||||
private void OnArrowClick(ArrowConfig arrow)
|
||||
{
|
||||
if (arrow.isMoving) return;
|
||||
arrow.isMoving = true;
|
||||
}
|
||||
|
||||
private void UpdateArrowMove(ArrowConfig arrow)
|
||||
{
|
||||
if (!arrow.isMoving) return;
|
||||
List<Vector2> path = arrow.pathPointList;
|
||||
|
||||
// 全部走完,隐藏对象
|
||||
if (arrow.targetIndex < 0)
|
||||
{
|
||||
arrow.isMoving = false;
|
||||
arrow.arrowObj.visible = false;
|
||||
foreach (var line in arrow.lineUnits) line.visible = false;
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 targetPos = path[arrow.targetIndex];
|
||||
Vector2 curPos = new Vector2(arrow.arrowObj.x, arrow.arrowObj.y);
|
||||
float distance = Vector2.Distance(curPos, targetPos);
|
||||
|
||||
// 到达当前点位,切换下一个目标
|
||||
if (distance < 2f)
|
||||
{
|
||||
arrow.targetIndex--;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 向目标移动
|
||||
Vector2 dir = (targetPos - curPos).normalized;
|
||||
Vector2 newPos = curPos + dir * _moveSpeed * Time.deltaTime;
|
||||
arrow.arrowObj.SetPosition(newPos.x, newPos.y,0);
|
||||
}
|
||||
|
||||
// 刷新线条显隐
|
||||
RefreshLineVisible(arrow);
|
||||
}
|
||||
|
||||
/// <summary>逐段控制显隐,每段固定6个方块</summary>
|
||||
private void RefreshLineVisible(ArrowConfig arrow)
|
||||
{
|
||||
int segIndex = 0;
|
||||
int totalSeg = arrow.pathPointList.Count - 1;
|
||||
|
||||
for (int s = 0; s < totalSeg; s++)
|
||||
{
|
||||
bool isShow = s < arrow.targetIndex;
|
||||
for (int i = 0; i < 6; i++)
|
||||
{
|
||||
arrow.lineUnits[segIndex + i].visible = isShow;
|
||||
}
|
||||
segIndex += 6;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 资源清理
|
||||
private void ClearAllDynamicObj()
|
||||
{
|
||||
if (_levelConfig == null || _viewContainer == null) return;
|
||||
foreach (var arrow in _levelConfig.arrows)
|
||||
{
|
||||
if (arrow.arrowObj != null)
|
||||
{
|
||||
_viewContainer.RemoveChild(arrow.arrowObj);
|
||||
arrow.arrowObj.Dispose();
|
||||
arrow.arrowObj = null;
|
||||
}
|
||||
|
||||
foreach (var line in arrow.lineUnits)
|
||||
{
|
||||
_viewContainer.RemoveChild(line);
|
||||
line.Dispose();
|
||||
}
|
||||
arrow.lineUnits.Clear();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 消息监听
|
||||
protected override void AddListener() { }
|
||||
protected override void RemoveListener() { }
|
||||
#endregion
|
||||
}
|
||||
|
||||
#region 配置解析 & 数据结构(完全保留不变)
|
||||
public static class JsonHelper
|
||||
{
|
||||
public static LevelConfig LoadLevel(string fileName)
|
||||
{
|
||||
string path = Path.Combine(Application.streamingAssetsPath, fileName);
|
||||
return JsonUtility.FromJson<LevelConfig>(File.ReadAllText(path));
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 配置解析 & 数据结构(保留原有定义)
|
||||
[System.Serializable]
|
||||
public class LevelConfig
|
||||
{
|
||||
public int levelId;
|
||||
public string levelName;
|
||||
public int gridRows;
|
||||
public int gridCols;
|
||||
public int pointSpacing; // 配置内此字段仅保留兼容,实际间距以代码 60px 为准
|
||||
public List<ArrowConfig> arrows;
|
||||
}
|
||||
|
||||
[System.Serializable]
|
||||
public class ArrowConfig
|
||||
{
|
||||
public int id;
|
||||
public int startPoint;
|
||||
public int endPoint;
|
||||
public string color;
|
||||
public List<string> path;
|
||||
|
||||
// 运行时数据
|
||||
public List<Vector2> pathPointList; // 整条路径所有点位坐标
|
||||
public GComponent arrowObj;
|
||||
public List<GComponent> lineUnits; // 所有线条方块预制体
|
||||
public bool isMoving;
|
||||
public int targetIndex;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1a4af5e7d0ee45ed84096f4ea23331c2
|
||||
timeCreated: 1781178068
|
||||
@@ -0,0 +1,71 @@
|
||||
namespace ChillConnect
|
||||
{
|
||||
public class ArrowGameUICtrl : BaseUICtrl
|
||||
{
|
||||
private ArrowGameUI ui;
|
||||
private ArrowGameModel model;
|
||||
|
||||
private uint openUIMsg = UICtrlMsg.ArrowGameUI_Open;
|
||||
private uint closeUIMsg = UICtrlMsg.ArrowGameUI_Close;
|
||||
|
||||
#region 生命周期
|
||||
protected override void OnInit()
|
||||
{
|
||||
//model = ModuleManager.Instance.GetModel(ModelConst.ArrowGameModel) as ArrowGameModel;
|
||||
}
|
||||
|
||||
protected override void OnDispose()
|
||||
{
|
||||
}
|
||||
|
||||
public override void OpenUI(object args = null)
|
||||
{
|
||||
if (ui == null)
|
||||
{
|
||||
ui = new ArrowGameUI(this);
|
||||
ui.Open(args);
|
||||
}
|
||||
}
|
||||
|
||||
public override void CloseUI(object args = null)
|
||||
{
|
||||
if (ui != null && !ui.isClose)
|
||||
{
|
||||
ui.Close();
|
||||
}
|
||||
ui = null;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 消息
|
||||
public override uint GetOpenUIMsg(string uiName)
|
||||
{
|
||||
return openUIMsg;
|
||||
}
|
||||
public override uint GetCloseUIMsg(string uiName)
|
||||
{
|
||||
return closeUIMsg;
|
||||
}
|
||||
|
||||
protected override void AddListener()
|
||||
{
|
||||
uiCtrlDispatcher.AddListener(openUIMsg, OpenUI);
|
||||
uiCtrlDispatcher.AddListener(closeUIMsg, CloseUI);
|
||||
}
|
||||
protected override void RemoveListener()
|
||||
{
|
||||
uiCtrlDispatcher.RemoveListener(openUIMsg, OpenUI);
|
||||
uiCtrlDispatcher.RemoveListener(closeUIMsg, CloseUI);
|
||||
}
|
||||
|
||||
protected override void AddServerListener()
|
||||
{
|
||||
|
||||
}
|
||||
protected override void RemoveServerListener()
|
||||
{
|
||||
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 74f557b8baf9421bb8cc5c01adcd09b3
|
||||
timeCreated: 1781178068
|
||||
Reference in New Issue
Block a user