2026-06-24 09:24:15 +08:00
|
|
|
|
using System;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
using System.Collections;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.IO;
|
2026-06-13 19:58:55 +08:00
|
|
|
|
using System.Linq;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
using DG.Tweening;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
using FairyGUI;
|
|
|
|
|
|
using FGUI.Arrow_game;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
using FGUI.Common_01;
|
|
|
|
|
|
using IgnoreOPS;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
using UnityEngine;
|
2026-07-01 10:26:18 +08:00
|
|
|
|
using btn_saveingpot = FGUI.Common_01.btn_saveingpot;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
using Random = UnityEngine.Random;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
|
|
|
|
|
namespace ChillConnect
|
|
|
|
|
|
{
|
|
|
|
|
|
public class ArrowGameUI : BaseUI
|
|
|
|
|
|
{
|
|
|
|
|
|
private ArrowGameUICtrl ctrl;
|
|
|
|
|
|
private com_arrow_game ui;
|
|
|
|
|
|
|
|
|
|
|
|
// FGUI界面根容器
|
|
|
|
|
|
private GComponent _viewContainer;
|
|
|
|
|
|
|
|
|
|
|
|
// 关卡总配置数据
|
|
|
|
|
|
private LevelConfig _levelConfig;
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 网格基础参数
|
2026-06-24 09:24:15 +08:00
|
|
|
|
private int gridColNum = 10;
|
|
|
|
|
|
private int gridRowNum = 10;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private readonly float pointSpace = 60f;
|
|
|
|
|
|
private readonly float outOffset = 200f;
|
|
|
|
|
|
|
|
|
|
|
|
// 网格边界
|
2026-06-14 16:25:04 +08:00
|
|
|
|
private Vector2 _gridWorldCenter; // 网格世界中心点
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private float gridLeft;
|
|
|
|
|
|
private float gridRight;
|
|
|
|
|
|
private float gridTop;
|
|
|
|
|
|
private float gridBottom;
|
|
|
|
|
|
|
2026-06-14 16:25:04 +08:00
|
|
|
|
// 网格行列、单个格子尺寸(你原有网格参数)
|
|
|
|
|
|
private int _gridRow;
|
|
|
|
|
|
private int _gridCol;
|
|
|
|
|
|
private float _cellSize;
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 网格占用标记:true=该点位被占用,false=空闲
|
|
|
|
|
|
private bool[,] _gridOccupied;
|
|
|
|
|
|
// 记录每个箭头占用的所有网格点位(行,列)
|
|
|
|
|
|
private Dictionary<ArrowConfig, List<(int row, int col)>> _arrowOccupiedCells = new Dictionary<ArrowConfig, List<(int row, int col)>>();
|
|
|
|
|
|
|
|
|
|
|
|
// 爱心数量
|
|
|
|
|
|
private int _heartCount = 3;
|
|
|
|
|
|
// 记录已经发生过碰撞的箭头对象(去重:同个箭头多次碰撞只扣一次血)
|
|
|
|
|
|
private HashSet<ArrowConfig> _collidedArrows = new HashSet<ArrowConfig>();
|
|
|
|
|
|
// 统计场上还存在的箭头总数(用于判断全部移除=通关)
|
|
|
|
|
|
private int _remainArrowCount = 0;
|
|
|
|
|
|
|
2026-06-13 18:27:00 +08:00
|
|
|
|
// 记录当前UI所有由 CrazyAsyKit 启动的协程,用于统一停止
|
|
|
|
|
|
private List<Coroutine> _activeCoroutines = new List<Coroutine>();
|
|
|
|
|
|
|
|
|
|
|
|
// 是否开启删除道具模式
|
|
|
|
|
|
private bool _isDeleteMode = false;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
// 手指提示图标
|
|
|
|
|
|
private com_finger _fingerTipObj;
|
|
|
|
|
|
// 当前正在提示的箭头
|
|
|
|
|
|
private ArrowConfig _currentTipArrow;
|
|
|
|
|
|
// 手指图标偏移(微调位置)
|
2026-06-18 09:27:38 +08:00
|
|
|
|
private readonly Vector2 _fingerOffset = new Vector2(-20, -20);
|
2026-06-16 10:22:11 +08:00
|
|
|
|
// 所有网格圆点实例列表,用于批量改色/清理
|
|
|
|
|
|
private List<ArrorPoint> _gridDotList = new List<ArrorPoint>();
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
#region 箭头颜色配置
|
|
|
|
|
|
// 8种预设彩色,可按需调整色值
|
|
|
|
|
|
private readonly Color[] _colorfulPalette = new Color[]
|
|
|
|
|
|
{
|
|
|
|
|
|
new Color(1f, 0.3f, 0.3f), // 红
|
|
|
|
|
|
new Color(1f, 0.6f, 0.2f), // 橙
|
|
|
|
|
|
new Color(1f, 0.9f, 0.2f), // 黄
|
|
|
|
|
|
new Color(0.3f, 0.8f, 0.4f), // 绿
|
|
|
|
|
|
new Color(0.2f, 0.8f, 0.9f), // 青
|
|
|
|
|
|
new Color(0.3f, 0.5f, 1f), // 蓝
|
|
|
|
|
|
new Color(0.7f, 0.4f, 1f), // 紫
|
|
|
|
|
|
new Color(1f, 0.5f, 0.8f) // 粉
|
|
|
|
|
|
};
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
#region 缩放、拖拽、滑动条 全局变量
|
|
|
|
|
|
// 缩放阈值(滑动条左右边界对应这两个值)
|
2026-06-24 09:24:15 +08:00
|
|
|
|
private readonly float _minScale = 0.5f;
|
2026-06-13 19:58:55 +08:00
|
|
|
|
private readonly float _maxScale = 1.5f;
|
|
|
|
|
|
// FGUI 滑动条组件
|
|
|
|
|
|
private GSlider _zoomSlider;
|
|
|
|
|
|
// 双指缩放手势
|
|
|
|
|
|
private float _lastTwoFingerDistance;
|
|
|
|
|
|
private bool _isTwoFingerTouch;
|
|
|
|
|
|
// 单指拖拽平移
|
|
|
|
|
|
private Vector2 _lastTouchPos;
|
|
|
|
|
|
private bool _isDraging;
|
|
|
|
|
|
// 拖拽边界留边(防止完全移出屏幕)
|
2026-07-01 10:26:18 +08:00
|
|
|
|
private readonly float _dragBorder = -300f;
|
2026-06-13 19:58:55 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
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";
|
2026-06-25 14:22:52 +08:00
|
|
|
|
uiInfo.layerType = UILayerType.Bottom;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
uiInfo.isNeedOpenAnim = false;
|
|
|
|
|
|
uiInfo.isNeedCloseAnim = false;
|
|
|
|
|
|
uiInfo.isNeedUIMask = true;
|
2026-06-14 16:25:04 +08:00
|
|
|
|
uiInfo.isTickUpdate = true;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#region 生命周期
|
|
|
|
|
|
protected override void OnInit()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnClose()
|
|
|
|
|
|
{
|
2026-06-13 18:27:00 +08:00
|
|
|
|
// ========= 第一步:统一停止所有协程(核心修复) =========
|
|
|
|
|
|
foreach (var co in _activeCoroutines)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (co != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
CrazyAsyKit.StopCoroutine(co);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 清空列表
|
|
|
|
|
|
_activeCoroutines.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
// 保险========= 强制停止所有移动,终止对应协程 =========
|
|
|
|
|
|
if (_levelConfig != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var arrow in _levelConfig.arrows)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
// 销毁所有动态FGUI对象
|
|
|
|
|
|
ClearAllDynamicObj();
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
|
|
|
|
|
HideFingerTip();
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
|
|
|
|
|
_levelConfig = null;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
// 清空网格占用数据
|
|
|
|
|
|
_gridOccupied = null;
|
|
|
|
|
|
_arrowOccupiedCells.Clear();
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnBind()
|
|
|
|
|
|
{
|
|
|
|
|
|
ui = baseUI as com_arrow_game;
|
|
|
|
|
|
}
|
2026-06-14 19:25:04 +08:00
|
|
|
|
|
|
|
|
|
|
// 新增坐标转换方法
|
|
|
|
|
|
private Vector2 UnityMouseToFGUIPos(Vector2 unityPos)
|
|
|
|
|
|
{
|
|
|
|
|
|
float x = unityPos.x;
|
|
|
|
|
|
float y = Screen.height - unityPos.y;
|
|
|
|
|
|
return new Vector2(x, y);
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-24 11:22:55 +08:00
|
|
|
|
#region 全局计数式开关
|
|
|
|
|
|
// 全局开关(true: 恢复输入 false:禁用输入)
|
|
|
|
|
|
private void OpenOrStopGameInput(object obj = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (obj != null && !(bool)obj)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 禁用输入
|
|
|
|
|
|
DisableGameInput();
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 恢复输入
|
|
|
|
|
|
EnableGameInput();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private int _inputDisableCount = 0;
|
|
|
|
|
|
// 禁用输入
|
|
|
|
|
|
private void DisableGameInput()
|
|
|
|
|
|
{
|
|
|
|
|
|
_inputDisableCount++;
|
|
|
|
|
|
// 只有第一次禁用时执行清理
|
|
|
|
|
|
if (_inputDisableCount == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_viewContainer != null)
|
|
|
|
|
|
_viewContainer.touchable = false;
|
|
|
|
|
|
|
|
|
|
|
|
_isDraging = false;
|
|
|
|
|
|
_isTwoFingerTouch = false;
|
|
|
|
|
|
_lastTouchPos = Vector2.zero;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 恢复输入
|
|
|
|
|
|
private void EnableGameInput(object obj = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_inputDisableCount = Mathf.Max(0, _inputDisableCount - 1);
|
|
|
|
|
|
// 计数归0时才真正恢复
|
|
|
|
|
|
if (_inputDisableCount == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_viewContainer != null)
|
|
|
|
|
|
_viewContainer.touchable = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
public override void OnUpdate()
|
|
|
|
|
|
{
|
2026-06-24 17:59:07 +08:00
|
|
|
|
// 禁用输入时,直接跳过拖拽
|
2026-06-24 11:22:55 +08:00
|
|
|
|
if (_inputDisableCount > 0)
|
2026-06-24 17:59:07 +08:00
|
|
|
|
{
|
|
|
|
|
|
// Debug.Log($"输入已禁用,直接跳过拖拽");
|
2026-06-24 11:22:55 +08:00
|
|
|
|
return;
|
2026-06-24 17:59:07 +08:00
|
|
|
|
}
|
2026-06-24 11:22:55 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 初始化未完成,直接跳过拖拽
|
|
|
|
|
|
if (!_initComplete)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-14 16:25:04 +08:00
|
|
|
|
#if UNITY_EDITOR
|
2026-06-18 09:27:38 +08:00
|
|
|
|
float moveThreshold = 1f;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 鼠标按下:仅在拖拽区域内按下才开启拖拽
|
2026-06-14 19:25:04 +08:00
|
|
|
|
if (InputHelper.Instance.GetMouseButtonDown(0))
|
2026-06-14 16:25:04 +08:00
|
|
|
|
{
|
2026-06-14 19:25:04 +08:00
|
|
|
|
Vector2 unityPos = InputHelper.Instance.MousePosition;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
Vector2 fguiPos = UnityMouseToFGUIPos(unityPos);
|
|
|
|
|
|
|
|
|
|
|
|
if (IsInDragArea(fguiPos))
|
|
|
|
|
|
{
|
|
|
|
|
|
_lastTouchPos = fguiPos;
|
|
|
|
|
|
_isDraging = true;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// Debug.Log("鼠标按下,在拖拽区域内,开始拖拽");
|
2026-06-18 09:27:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
return;
|
2026-06-14 16:25:04 +08:00
|
|
|
|
}
|
2026-06-14 19:25:04 +08:00
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 拖拽中:原有逻辑不变
|
2026-06-14 19:25:04 +08:00
|
|
|
|
if (_isDraging && InputHelper.Instance.GetMouseButton(0))
|
2026-06-14 16:25:04 +08:00
|
|
|
|
{
|
2026-06-14 19:25:04 +08:00
|
|
|
|
Vector2 unityPos = InputHelper.Instance.MousePosition;
|
|
|
|
|
|
Vector2 curMousePos = UnityMouseToFGUIPos(unityPos);
|
2026-06-14 16:25:04 +08:00
|
|
|
|
Vector2 offset = curMousePos - _lastTouchPos;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
|
|
|
|
|
|
if (offset.magnitude > moveThreshold)
|
|
|
|
|
|
{
|
|
|
|
|
|
MoveContainer(offset);
|
2026-06-18 09:27:38 +08:00
|
|
|
|
_lastTouchPos = curMousePos;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
}
|
2026-06-14 16:25:04 +08:00
|
|
|
|
}
|
2026-06-14 19:25:04 +08:00
|
|
|
|
|
|
|
|
|
|
// 鼠标抬起,结束拖拽
|
|
|
|
|
|
if (InputHelper.Instance.GetMouseButtonUp(0))
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-14 16:25:04 +08:00
|
|
|
|
_isDraging = false;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// Debug.Log("鼠标抬起,结束拖拽");
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
2026-06-14 16:25:04 +08:00
|
|
|
|
#endif
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
|
|
|
|
|
// ========= 双指 = 缩放(优先级最高) =========
|
2026-06-14 19:25:04 +08:00
|
|
|
|
if (InputHelper.Instance.TouchCount == 2)
|
2026-06-13 19:58:55 +08:00
|
|
|
|
{
|
|
|
|
|
|
_isDraging = false;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
Touch t0 = InputHelper.Instance.GetTouch(0);
|
|
|
|
|
|
Touch t1 = InputHelper.Instance.GetTouch(1);
|
2026-06-13 19:58:55 +08:00
|
|
|
|
float currentDis = Vector2.Distance(t0.position, t1.position);
|
|
|
|
|
|
|
|
|
|
|
|
if (!_isTwoFingerTouch)
|
|
|
|
|
|
{
|
|
|
|
|
|
_lastTwoFingerDistance = currentDis;
|
|
|
|
|
|
_isTwoFingerTouch = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
float delta = currentDis - _lastTwoFingerDistance;
|
|
|
|
|
|
float curScale = _viewContainer.scaleX;
|
|
|
|
|
|
// 缩放灵敏度,按需微调
|
|
|
|
|
|
curScale += delta * 0.001f;
|
|
|
|
|
|
curScale = Mathf.Clamp(curScale, _minScale, _maxScale);
|
|
|
|
|
|
|
|
|
|
|
|
// 应用缩放
|
|
|
|
|
|
_viewContainer.scaleX = curScale;
|
|
|
|
|
|
_viewContainer.scaleY = curScale;
|
|
|
|
|
|
|
|
|
|
|
|
// 关键:双指缩放后,同步更新滑动条位置
|
|
|
|
|
|
_zoomSlider.value = GetSliderValueByScale(curScale);
|
|
|
|
|
|
|
|
|
|
|
|
_lastTwoFingerDistance = currentDis;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// ========= 单指 = 拖拽平移 =========
|
2026-06-14 19:25:04 +08:00
|
|
|
|
else if (InputHelper.Instance.TouchCount == 1)
|
2026-06-13 19:58:55 +08:00
|
|
|
|
{
|
2026-06-14 19:25:04 +08:00
|
|
|
|
Touch touch = InputHelper.Instance.GetTouch(0);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// 触摸坐标和鼠标同属Unity屏幕坐标系,统一转成FGUI坐标系
|
|
|
|
|
|
Vector2 curTouchPos = UnityMouseToFGUIPos(touch.position);
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
|
|
|
|
|
if (touch.phase == TouchPhase.Began)
|
|
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 只有在拖拽区域内按下,才开启拖拽
|
|
|
|
|
|
if (IsInDragArea(curTouchPos))
|
|
|
|
|
|
{
|
|
|
|
|
|
_lastTouchPos = curTouchPos;
|
|
|
|
|
|
_isDraging = true;
|
|
|
|
|
|
_isTwoFingerTouch = false;
|
|
|
|
|
|
}
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 只有点击在网格范围内才触发震动
|
|
|
|
|
|
// if (IsInGridArea(curTouchPos))
|
|
|
|
|
|
// {
|
|
|
|
|
|
// TriggerVibration();
|
|
|
|
|
|
// }
|
2026-06-13 19:58:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
else if (touch.phase == TouchPhase.Moved && _isDraging)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector2 offset = curTouchPos - _lastTouchPos;
|
|
|
|
|
|
MoveContainer(offset);
|
|
|
|
|
|
_lastTouchPos = curTouchPos;
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (touch.phase == TouchPhase.Ended || touch.phase == TouchPhase.Canceled)
|
|
|
|
|
|
{
|
|
|
|
|
|
_isDraging = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 无触摸,重置状态
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
_isTwoFingerTouch = false;
|
|
|
|
|
|
_isDraging = false;
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnOpenBefore(object args)
|
|
|
|
|
|
{
|
2026-07-01 10:26:18 +08:00
|
|
|
|
if (Screen.safeArea.y != 0)
|
|
|
|
|
|
{//刘海屏
|
|
|
|
|
|
ui.panel.y += Screen.safeArea.y - 25;
|
|
|
|
|
|
ui.com_money.y += Screen.safeArea.y- 15;
|
|
|
|
|
|
ui.com_gem.y += Screen.safeArea.y - 15;
|
|
|
|
|
|
ui.btn_signin.y += Screen.safeArea.y - 25;
|
|
|
|
|
|
ui.com_gift.y += Screen.safeArea.y - 15;
|
|
|
|
|
|
ui.btn_wv.y += Screen.safeArea.y - 25;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
ui.state.selectedIndex = GameHelper.IsGiftSwitch() ? 1 : 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
//加载配置
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// InitAllLevelData();
|
2026-06-12 18:10:00 +08:00
|
|
|
|
LoadLevelConfig();
|
2026-06-24 09:24:15 +08:00
|
|
|
|
_viewContainer = ui.view_container_parent.panel;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
InitGridBounds();
|
|
|
|
|
|
|
|
|
|
|
|
// 初始化网格占用数组
|
|
|
|
|
|
InitGridOccupied();
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 新增:对局状态重置 ==========
|
|
|
|
|
|
_heartCount = 3;
|
|
|
|
|
|
_collidedArrows.Clear();
|
|
|
|
|
|
if(_levelConfig != null)
|
|
|
|
|
|
_remainArrowCount = _levelConfig.arrows.Count;
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
InitView();
|
2026-06-24 17:59:07 +08:00
|
|
|
|
|
|
|
|
|
|
((com_money)ui.com_money).btn_ch.title = GameHelper.getDesByKey("ch_out_1");
|
2026-07-01 10:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
if (GameHelper.IsGiftSwitch() && ConfigSystem.GetConfig<CommonModel>().PiggyBankSwitch == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.btn_saveingpot.visible = true;
|
|
|
|
|
|
ui.com_gift.visible = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
GameHelper.IsShowFirstReward();
|
|
|
|
|
|
|
|
|
|
|
|
GameHelper.IsShowPettyReward();
|
|
|
|
|
|
|
|
|
|
|
|
GameHelper.ShowStatementView();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void RefreshGift(object obj = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.com_gift.text_gift.text = GameHelper.Get102Str((decimal)SaveData.GetSaveObject().saveingpot_ch);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Set101(decimal coin = -1)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (coin < 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
coin = DataMgr.Coin.Value;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (ui.com_gem is com_gold btnCoin) btnCoin.text_gold.text = $"{coin:N0}";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Set102()
|
|
|
|
|
|
{
|
|
|
|
|
|
decimal coin = DataMgr.Ticket.Value;
|
|
|
|
|
|
|
|
|
|
|
|
if (ui.com_money is com_money comMoney) comMoney.text_gold.text = coin.ToString("0.00");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void OnUpdate101(object obj = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Set101();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public void OnUpdate102(object obj = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
Set102();
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
protected override void OnOpen(object args)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnHide()
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void OnDisplay(object args)
|
|
|
|
|
|
{
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
#endregion
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
#region 消息监听
|
|
|
|
|
|
protected override void AddListener()
|
|
|
|
|
|
{
|
|
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.reset_game, OnRestartGame);
|
|
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.Update102, SetTopCurr);
|
|
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.Update101, SetTopCurr);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.ThemeChange, SetModel);
|
|
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.UpdateSpeed, SetSpeed);
|
2026-06-18 09:27:38 +08:00
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.UseProps, SetUserPorp);
|
2026-06-24 11:22:55 +08:00
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.StopArrowTouch,OpenOrStopGameInput);
|
2026-07-01 10:26:18 +08:00
|
|
|
|
GameDispatcher.Instance.AddListener(GameMsg.refreshGift,RefreshGift);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
protected override void RemoveListener()
|
|
|
|
|
|
{
|
|
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.reset_game, OnRestartGame);
|
|
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.Update102, SetTopCurr);
|
|
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.Update101, SetTopCurr);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.ThemeChange, SetModel);
|
|
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.UpdateSpeed, SetSpeed);
|
2026-06-18 09:27:38 +08:00
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.UseProps, SetUserPorp);
|
2026-06-24 11:22:55 +08:00
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.StopArrowTouch, OpenOrStopGameInput);
|
2026-07-01 10:26:18 +08:00
|
|
|
|
GameDispatcher.Instance.RemoveListener(GameMsg.refreshGift,RefreshGift);
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
#region 页面初始化
|
|
|
|
|
|
private bool _initComplete = false;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
private void InitView()
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 重置手势状态
|
|
|
|
|
|
_isTwoFingerTouch = false;
|
|
|
|
|
|
_isDraging = false;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ui.text_level.SetVar("lv", GameHelper.GetLevel().ToString()).FlushVars();
|
|
|
|
|
|
|
2026-06-16 10:22:11 +08:00
|
|
|
|
// 按钮点击事件绑定
|
|
|
|
|
|
OnBtnClickBindEvent();
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
//爱心重新更新显示
|
|
|
|
|
|
for (int i = _heartCount; i > 0; i--)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.HeartsPanel.GetChild($"xin_{i}").visible = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// ui.ch_progress.value = DataMgr.ArrowChProgress.Value;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
_currentTipArrow = null;
|
|
|
|
|
|
|
2026-06-16 10:22:11 +08:00
|
|
|
|
_zoomSlider = ui.com_bottom.zoomSlide;
|
2026-06-13 19:58:55 +08:00
|
|
|
|
// 2. 绑定滑动条拖动回调
|
|
|
|
|
|
_zoomSlider.onChanged.Add(OnZoomSliderChange);
|
|
|
|
|
|
// 3. 容器初始缩放、位置
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// _viewContainer.scaleX = 1f;
|
|
|
|
|
|
// _viewContainer.scaleY = 1f;
|
|
|
|
|
|
// // 滑动条初始居中(对应 1.0 缩放)
|
|
|
|
|
|
// float initValue = GetSliderValueByScale(1f);
|
|
|
|
|
|
// _zoomSlider.value = initValue;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 先兜底重置为默认锚点
|
|
|
|
|
|
_viewContainer.pivot = new Vector2(0, 0);
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
if (_levelConfig == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_initComplete = true;
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 第一步:绘制网格 → 内部会计算网格中心、修改pivot为网格中心
|
2026-06-12 18:10:00 +08:00
|
|
|
|
DrawGridDots(_levelConfig);
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// ===== 新增:计算并应用初始缩放 =====
|
|
|
|
|
|
float initialScale = CalculateInitialScale();
|
|
|
|
|
|
_viewContainer.scaleX = initialScale;
|
|
|
|
|
|
_viewContainer.scaleY = initialScale;
|
|
|
|
|
|
|
|
|
|
|
|
// 同步滑动条位置(假设滑动条映射范围为 _minScale~_maxScale)
|
|
|
|
|
|
float sliderValue = GetSliderValueByScale(initialScale);
|
|
|
|
|
|
_zoomSlider.value = Mathf.Clamp(sliderValue, 0f, 100f); // 确保不越界
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// ========== 核心:把网格中心钉在屏幕中心 ==========
|
|
|
|
|
|
// _gridWorldCenter 是 DrawGridDots 里 CalculateGridCenter 算出的值
|
2026-06-18 09:27:38 +08:00
|
|
|
|
float screenCenterX = ui.view_container_parent.width * 0.5f;
|
|
|
|
|
|
float screenCenterY = ui.view_container_parent.height * 0.5f;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
// 计算容器需要移动多少,才能让网格中心正好对准屏幕中心
|
|
|
|
|
|
float targetContainerX = screenCenterX - _gridWorldCenter.x;
|
|
|
|
|
|
float targetContainerY = screenCenterY - _gridWorldCenter.y;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 直接设置容器位置
|
|
|
|
|
|
_viewContainer.SetPosition(targetContainerX, targetContainerY, 0);
|
|
|
|
|
|
// ==================================================
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
foreach (var arrow in _levelConfig.arrows)
|
|
|
|
|
|
{
|
|
|
|
|
|
BuildArrowPath(arrow, _levelConfig);
|
|
|
|
|
|
CreateArrowAndLines(arrow, _levelConfig);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
MarkArrowOccupiedCells(arrow, _levelConfig);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
_initComplete = true;
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
SetModel();
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
SetTopCurr();
|
2026-07-01 10:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
Set101();
|
|
|
|
|
|
|
|
|
|
|
|
RefreshGift();
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
_fingerTipObj = com_finger.CreateInstance();
|
|
|
|
|
|
_viewContainer.AddChild(_fingerTipObj);
|
|
|
|
|
|
_fingerTipObj.visible = false;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
_fingerTipObj.touchable = false;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
_fingerTipObj.sortingOrder = 9999;
|
2026-07-01 10:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (GameHelper.IsShowOpenGameUI() && HallManager.Instance.openTipsTimes >= 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.OpenGameUI_Open);
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
private float CalculateInitialScale()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_levelConfig == null) return 0.8f; // 保底
|
|
|
|
|
|
|
|
|
|
|
|
int cols = _levelConfig.gridCols;
|
|
|
|
|
|
int rows = _levelConfig.gridRows;
|
|
|
|
|
|
float spacing = _levelConfig.pointSpacing;
|
|
|
|
|
|
|
|
|
|
|
|
// 网格实际像素宽高
|
|
|
|
|
|
float gridWidth = (cols - 1) * spacing;
|
|
|
|
|
|
float gridHeight = (rows - 1) * spacing;
|
|
|
|
|
|
|
|
|
|
|
|
// 容器有效尺寸(可减去固定边距,如 40px)
|
|
|
|
|
|
float margin = 80f;
|
|
|
|
|
|
float containerWidth = ui.view_container_parent.width - margin * 2;
|
|
|
|
|
|
float containerHeight = ui.view_container_parent.height - margin * 2;
|
|
|
|
|
|
|
|
|
|
|
|
// 边界保护(网格可能只有一行/列)
|
|
|
|
|
|
if (gridWidth <= 0) gridWidth = 1f;
|
|
|
|
|
|
if (gridHeight <= 0) gridHeight = 1f;
|
|
|
|
|
|
|
|
|
|
|
|
float scaleX = containerWidth / gridWidth;
|
|
|
|
|
|
float scaleY = containerHeight / gridHeight;
|
|
|
|
|
|
|
|
|
|
|
|
float scale = Mathf.Min(scaleX, scaleY);
|
|
|
|
|
|
|
|
|
|
|
|
// 限制上限(避免网格过大时缩放太小;下限由滑动条范围决定,建议调整 _minScale)
|
|
|
|
|
|
scale = Mathf.Clamp(scale, _minScale, _maxScale); // 下限设 0.3 更合理
|
|
|
|
|
|
return scale;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
private void SetTopCurr(object a = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ui.com_money is com_money btnMoney)
|
|
|
|
|
|
{
|
|
|
|
|
|
btnMoney.text_gold.text = DataMgr.Ticket.Value.ToString("0.00");
|
|
|
|
|
|
btnMoney.SetClick(PopMakeup);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void PopMakeup()
|
|
|
|
|
|
{
|
|
|
|
|
|
var makeupTaskData = DataMgr.MakeupTaskHistory.Value.Last();
|
|
|
|
|
|
var vo = ConfigSystem.GetConfig<MakeupModel>().GetData(makeupTaskData.tableId);
|
|
|
|
|
|
if (vo == null)
|
|
|
|
|
|
{
|
2026-07-01 10:26:18 +08:00
|
|
|
|
Debug.LogError("MakeupTaskData 为空");
|
2026-06-15 17:50:56 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
GameHelper.showGameUI = false;
|
|
|
|
|
|
uiCtrlDispatcher.Dispatch(UICtrlMsg.MakeupConfirmUI_Open, makeupTaskData);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region UI相关
|
2026-06-16 10:22:11 +08:00
|
|
|
|
|
|
|
|
|
|
private void OnBtnClickBindEvent()
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.btn_close.SetClick(CtrlCloseUI);
|
|
|
|
|
|
ui.com_bottom.btn_clear.SetClick(OnClickDeleteItem);
|
|
|
|
|
|
ui.com_bottom.btn_hint.SetClick(OnClickHint);
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
ui.com_bottom.btn_setting.SetClick(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
uiCtrlDispatcher.Dispatch(UICtrlMsg.PersonViewUI_Open, _moveSpeed);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-07-01 10:26:18 +08:00
|
|
|
|
if (!GameHelper.IsGiftSwitch() && ConfigSystem.GetConfig<CommonModel>().WVswitch == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.btn_wv.visible = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
ui.btn_wv.SetClick(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
SDKOpenConfig openConfig = new SDKOpenConfig
|
|
|
|
|
|
{
|
|
|
|
|
|
normal = true,
|
|
|
|
|
|
url = ""
|
|
|
|
|
|
};
|
|
|
|
|
|
CtrlDispatcher.Instance.Dispatch(CtrlMsg.open_wb,openConfig);
|
|
|
|
|
|
});
|
|
|
|
|
|
|
2026-06-16 10:22:11 +08:00
|
|
|
|
ui.com_bottom.btn_skin.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.ArrowThemeUI_Open); });
|
|
|
|
|
|
|
|
|
|
|
|
ui.btn_petty.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.PettyAwardUI_Open); });
|
|
|
|
|
|
|
|
|
|
|
|
ui.btn_signin.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SignInUI_Open); });
|
|
|
|
|
|
|
|
|
|
|
|
ui.btn_statement.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.StatementViewUI_Open); });
|
|
|
|
|
|
|
|
|
|
|
|
ui.btn_saveingpot.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SaveingPotUI_Open); });
|
2026-07-01 10:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
ui.com_gift.SetClick(() => { uiCtrlDispatcher.Dispatch(UICtrlMsg.SaveingPotUI_Open); });
|
2026-06-16 10:22:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
// 缓存上一次箭头主题,用于判断是否需要重新随机彩色
|
|
|
|
|
|
private int _lastArrowTheme = -1;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
/// <summary>
|
2026-06-17 14:45:28 +08:00
|
|
|
|
/// 主题变更统一回调(昼夜/箭头主题切换都会触发)
|
2026-06-15 17:50:56 +08:00
|
|
|
|
/// </summary>
|
2026-06-17 14:45:28 +08:00
|
|
|
|
private void SetModel(object a = null)
|
2026-06-15 17:50:56 +08:00
|
|
|
|
{
|
2026-06-17 14:45:28 +08:00
|
|
|
|
int darkThemeIdx = DataMgr.ArrowDarkTheme.Value;
|
|
|
|
|
|
int currentArrowTheme = DataMgr.ArrowTheme.Value;
|
|
|
|
|
|
|
|
|
|
|
|
ui.mode.selectedIndex = darkThemeIdx;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 更新网格圆点昼夜配色
|
|
|
|
|
|
bool isDarkMode = darkThemeIdx == 0; // 0=黑夜
|
|
|
|
|
|
Debug.Log($"ArrowDarkTheme==={darkThemeIdx}");
|
|
|
|
|
|
UpdateGridDotColor(isDarkMode);
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// 2. 判断是否需要重新随机彩色:只有从非彩色切到彩色时才刷新
|
|
|
|
|
|
bool forceRandomColorful = currentArrowTheme == 2 && _lastArrowTheme != currentArrowTheme;
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 更新所有箭头颜色
|
|
|
|
|
|
UpdateAllArrowsColor(forceRandomColorful);
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 更新缓存,用于下次对比
|
|
|
|
|
|
_lastArrowTheme = currentArrowTheme;
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"主题刷新完成:昼夜={darkThemeIdx},箭头主题={currentArrowTheme}");
|
2026-06-16 10:22:11 +08:00
|
|
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 批量设置网格圆点颜色(随昼夜模式切换)
|
2026-06-17 14:45:28 +08:00
|
|
|
|
/// // 色值映射:白天#2c3a62,黑夜#d4e3f2
|
2026-06-16 10:22:11 +08:00
|
|
|
|
/// </summary>
|
2026-06-17 14:45:28 +08:00
|
|
|
|
/// <param name="isDay">true=黑夜模式,false=白天模式</param>
|
|
|
|
|
|
|
|
|
|
|
|
private string DarkColor = "#d4e3f2";
|
|
|
|
|
|
private string LightColor = "#2c3a62";
|
2026-06-16 10:22:11 +08:00
|
|
|
|
private void UpdateGridDotColor(bool isDay)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridDotList == null || _gridDotList.Count == 0)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
Debug.Log($"isDay=========={isDay}");
|
|
|
|
|
|
string colorHex = isDay ? DarkColor : LightColor;
|
2026-06-16 10:22:11 +08:00
|
|
|
|
ColorUtility.TryParseHtmlString(colorHex, out Color targetColor);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var dot in _gridDotList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dot == null) continue;
|
|
|
|
|
|
|
|
|
|
|
|
dot.point.color = targetColor;
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 根据当前主题,为单个箭头生成对应颜色
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="arrow">箭头配置对象</param>
|
|
|
|
|
|
/// <param name="isRandomNew">彩色模式下是否重新随机颜色</param>
|
|
|
|
|
|
private Color GetArrowRuntimeColor(ArrowConfig arrow, bool isRandomNew = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
int themeType = DataMgr.ArrowTheme.Value;
|
|
|
|
|
|
|
|
|
|
|
|
// 0 = 纯色模式(黑色按钮):白天黑、黑夜白
|
|
|
|
|
|
if (themeType == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isDark = DataMgr.ArrowDarkTheme.Value == 0; // 0=黑夜
|
|
|
|
|
|
return isDark ? Color.white : Color.black;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 2 = 彩色随机模式
|
|
|
|
|
|
else if (themeType == 2)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (isRandomNew || arrow.runtimeColor == default)
|
|
|
|
|
|
{
|
|
|
|
|
|
int randomIndex = Random.Range(0, _colorfulPalette.Length);
|
|
|
|
|
|
arrow.runtimeColor = _colorfulPalette[randomIndex];
|
|
|
|
|
|
}
|
|
|
|
|
|
return arrow.runtimeColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 兜底默认黑色
|
|
|
|
|
|
return Color.black;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// 批量更新场上所有箭头的颜色(线条+单箭头)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="forceRandomColorful">彩色模式下是否强制重新随机</param>
|
|
|
|
|
|
private void UpdateAllArrowsColor(bool forceRandomColorful = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_levelConfig == null || _levelConfig.arrows == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
int themeType = DataMgr.ArrowTheme.Value;
|
|
|
|
|
|
bool isColorfulMode = themeType == 2;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var arrow in _levelConfig.arrows)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 只有强制刷新时,彩色模式才重新随机颜色
|
|
|
|
|
|
bool isRandomNew = isColorfulMode && forceRandomColorful;
|
|
|
|
|
|
Color targetColor = GetArrowRuntimeColor(arrow, isRandomNew);
|
|
|
|
|
|
|
|
|
|
|
|
// 更新线条颜色
|
|
|
|
|
|
if (arrow.lineUnits != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var line in arrow.lineUnits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
line.GetChild("line").asGraph.color = targetColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 更新单箭头颜色
|
|
|
|
|
|
if (arrow.arrowObj != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
GImage icon = arrow.arrowObj.GetChild("icon").asImage;
|
|
|
|
|
|
if (icon != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
icon.color = targetColor;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
//反馈特效工具
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 触发震动反馈
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="isStrong">true=强震动(阻挡时用),false=轻震动(普通点击)</param>
|
|
|
|
|
|
private void TriggerVibration(bool isStrong = false)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 编辑器模式跳过震动
|
|
|
|
|
|
#if UNITY_EDITOR
|
|
|
|
|
|
return;
|
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
// 通用震动(兼容iOS/安卓,系统默认时长)
|
|
|
|
|
|
// Handheld.Vibrate();
|
|
|
|
|
|
|
|
|
|
|
|
Debug.Log($"震动反馈:{isStrong}");
|
|
|
|
|
|
// ===== 进阶:安卓自定义震动时长(可选开启)=====
|
|
|
|
|
|
if (Application.platform == RuntimePlatform.Android)
|
|
|
|
|
|
{
|
|
|
|
|
|
long duration = isStrong ? 100L : 50L;
|
|
|
|
|
|
AndroidJavaClass unityPlayer = new AndroidJavaClass("com.unity3d.player.UnityPlayer");
|
|
|
|
|
|
AndroidJavaObject activity = unityPlayer.GetStatic<AndroidJavaObject>("currentActivity");
|
|
|
|
|
|
AndroidJavaObject vibrator = activity.Call<AndroidJavaObject>("getSystemService", "vibrator");
|
|
|
|
|
|
vibrator.Call("vibrate", duration);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 触发阻挡时两侧红色闪烁效果
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void TriggerBlockFlash()
|
|
|
|
|
|
{
|
2026-06-24 17:59:07 +08:00
|
|
|
|
ui.t1.Play();
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 先终止正在播放的动画,避免叠加错乱
|
|
|
|
|
|
ui.com_ficker.visible = true;
|
|
|
|
|
|
ui.com_ficker.t0.Play(() =>
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.com_ficker.visible = false;
|
|
|
|
|
|
});
|
2026-06-24 17:59:07 +08:00
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private void InitGridBounds()
|
|
|
|
|
|
{
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 第0列、第0行的起点坐标
|
2026-06-13 15:34:00 +08:00
|
|
|
|
gridLeft = ui.point.x;
|
|
|
|
|
|
gridTop = ui.point.y;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 正确计算:(点数 - 1) × 点间距 = 总跨度
|
|
|
|
|
|
gridRight = ui.point.x + (_levelConfig.gridCols - 1) * _levelConfig.pointSpacing;
|
|
|
|
|
|
gridBottom = ui.point.y + (_levelConfig.gridRows - 1) * _levelConfig.pointSpacing;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>初始化网格占用二维数组</summary>
|
|
|
|
|
|
private void InitGridOccupied()
|
|
|
|
|
|
{
|
|
|
|
|
|
int rows = _levelConfig.gridRows;
|
|
|
|
|
|
int cols = _levelConfig.gridCols;
|
|
|
|
|
|
_gridOccupied = new bool[rows, cols];
|
|
|
|
|
|
// 初始全部置为空闲
|
|
|
|
|
|
for (int r = 0; r < rows; r++)
|
|
|
|
|
|
for (int c = 0; c < cols; c++)
|
|
|
|
|
|
_gridOccupied[r, c] = false;
|
|
|
|
|
|
|
|
|
|
|
|
_arrowOccupiedCells.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>标记单个箭头占用的所有网格点</summary>
|
|
|
|
|
|
private void MarkArrowOccupiedCells(ArrowConfig arrow, LevelConfig cfg)
|
|
|
|
|
|
{
|
|
|
|
|
|
List<(int row, int col)> cellList = new List<(int row, int col)>();
|
|
|
|
|
|
int totalCol = cfg.gridCols;
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历箭头整条路径的所有网格点位
|
|
|
|
|
|
foreach (var pos in arrow.pathPointList)
|
|
|
|
|
|
{
|
|
|
|
|
|
int pointId = GetPointIdByPos(pos, cfg);
|
|
|
|
|
|
PointIdToRowCol(pointId, totalCol, out int r, out int c);
|
|
|
|
|
|
|
|
|
|
|
|
// 边界保护
|
|
|
|
|
|
if (r >= 0 && r < cfg.gridRows && c >= 0 && c < cfg.gridCols)
|
|
|
|
|
|
{
|
|
|
|
|
|
_gridOccupied[r, c] = true;
|
|
|
|
|
|
cellList.Add((r, c));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_arrowOccupiedCells[arrow] = cellList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>释放单个箭头占用的网格(箭头消失时调用)</summary>
|
|
|
|
|
|
private void ReleaseArrowOccupied(ArrowConfig arrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!_arrowOccupiedCells.TryGetValue(arrow, out var cellList))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var (r, c) in cellList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (r >= 0 && r < _levelConfig.gridRows && c >= 0 && c < _levelConfig.gridCols)
|
|
|
|
|
|
{
|
|
|
|
|
|
_gridOccupied[r, c] = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
// 移除字典记录,避免冗余
|
|
|
|
|
|
_arrowOccupiedCells.Remove(arrow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
#region 配置加载
|
|
|
|
|
|
private void LoadLevelConfig()
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// _levelConfig = JsonHelper.LoadLevel("level_1001.json");
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 示例:加载第1关 levelId=0
|
2026-06-15 17:50:56 +08:00
|
|
|
|
int currentLevelId = GameHelper.GetLevel();
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 替换为:按ID加载单关
|
|
|
|
|
|
bool loadSuccess = LevelManager.Instance.LoadLevel(currentLevelId);
|
|
|
|
|
|
if (!loadSuccess)
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 加载失败兜底(比如回到第一关)
|
|
|
|
|
|
return;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// 直接取当前关卡配置,后续逻辑完全不变
|
|
|
|
|
|
_levelConfig = LevelManager.Instance.CurrentLevel;
|
|
|
|
|
|
|
|
|
|
|
|
// if (_allLevelData.levels.Count < currentLevelId) currentLevelId = _allLevelData.levels.Count;
|
|
|
|
|
|
// _levelConfig = LoadLevelById(currentLevelId);
|
|
|
|
|
|
// // 给 path 为空的箭头,设置默认方向
|
|
|
|
|
|
// foreach (var arrow in _levelConfig.arrows)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// if (arrow.path == null || arrow.path.Count == 0)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// arrow.defaultDir = "up";
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// arrow.defaultDir = null;
|
|
|
|
|
|
// }
|
|
|
|
|
|
// }
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 从 all_levels.json 中按 levelId 读取指定关卡
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="targetLevelId">目标关卡ID 如 1001</param>
|
|
|
|
|
|
/// <returns>找到返回关卡数据,没找到返回null</returns>
|
|
|
|
|
|
///
|
|
|
|
|
|
private static AllLevelRoot _allLevelData;
|
|
|
|
|
|
// 游戏启动时调用一次
|
|
|
|
|
|
public static void InitAllLevelData()
|
|
|
|
|
|
{
|
|
|
|
|
|
string path = Path.Combine(Application.streamingAssetsPath, "all_levels.json");
|
2026-06-17 14:45:28 +08:00
|
|
|
|
#if UNITY_ANDROID && !UNITY_EDITOR
|
|
|
|
|
|
// Android 平台需要使用 UnityWebRequest 读取 StreamingAssets
|
|
|
|
|
|
using (UnityEngine.Networking.UnityWebRequest www = UnityEngine.Networking.UnityWebRequest.Get(path))
|
|
|
|
|
|
{
|
|
|
|
|
|
www.SendWebRequest();
|
|
|
|
|
|
while (!www.isDone) { }
|
|
|
|
|
|
|
|
|
|
|
|
if (www.result == UnityEngine.Networking.UnityWebRequest.Result.Success)
|
|
|
|
|
|
{
|
|
|
|
|
|
string json = www.downloadHandler.text;
|
|
|
|
|
|
_allLevelData = JsonUtility.FromJson<AllLevelRoot>(json);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.LogError($"Failed to load all_levels.json: {www.error}");
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#else
|
|
|
|
|
|
// Editor 和其他平台直接读取
|
2026-06-15 17:50:56 +08:00
|
|
|
|
string json = File.ReadAllText(path);
|
|
|
|
|
|
_allLevelData = JsonUtility.FromJson<AllLevelRoot>(json);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
#endif
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public static LevelConfig LoadLevelById(int targetLevelId)
|
|
|
|
|
|
{
|
|
|
|
|
|
AllLevelRoot root = _allLevelData;
|
|
|
|
|
|
if (root == null || root.levels == null) return null;
|
|
|
|
|
|
|
|
|
|
|
|
// 遍历查找对应关卡
|
|
|
|
|
|
foreach (var lv in _allLevelData.levels)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (lv.levelId == targetLevelId)
|
|
|
|
|
|
{
|
|
|
|
|
|
return lv;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
Debug.LogWarning($"未找到关卡ID: {targetLevelId}");
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
#region 【核心】网格坐标工具
|
2026-06-12 18:10:00 +08:00
|
|
|
|
/// <summary>点位ID 转 行列索引</summary>
|
|
|
|
|
|
private void PointIdToRowCol(int pointId, int totalCol, out int row, out int col)
|
|
|
|
|
|
{
|
|
|
|
|
|
row = pointId / totalCol;
|
|
|
|
|
|
col = pointId % totalCol;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
/// <summary>行列 → 实际像素坐标</summary>
|
2026-06-12 18:10:00 +08:00
|
|
|
|
private Vector2 RowColToPos(int row, int col)
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
/// <summary>像素坐标反向计算点位ID</summary>
|
|
|
|
|
|
private int GetPointIdByPos(Vector2 pos, LevelConfig cfg)
|
|
|
|
|
|
{
|
|
|
|
|
|
float originX = ui.point.x;
|
|
|
|
|
|
float originY = ui.point.y;
|
|
|
|
|
|
|
|
|
|
|
|
int col = Mathf.RoundToInt((pos.x - originX) / cfg.pointSpacing);
|
|
|
|
|
|
int row = Mathf.RoundToInt((pos.y - originY) / cfg.pointSpacing);
|
|
|
|
|
|
|
|
|
|
|
|
return row * cfg.gridCols + col;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary> 根据FGUI旋转角度获取方向 </summary>
|
|
|
|
|
|
private string GetDirByRotation(float rot)
|
|
|
|
|
|
{
|
|
|
|
|
|
rot = Mathf.Round(rot);
|
|
|
|
|
|
if (rot == 0) return "right";
|
|
|
|
|
|
if (rot == 90) return "down";
|
|
|
|
|
|
if (rot == 180) return "left";
|
|
|
|
|
|
if (rot == 270) return "up";
|
|
|
|
|
|
return "right";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
/// <summary>方向转旋转角度(箭头默认朝上)</summary>
|
|
|
|
|
|
private float DirToRotation(string dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
return dir switch
|
|
|
|
|
|
{
|
|
|
|
|
|
"up" => 0f,
|
|
|
|
|
|
"right" => 90f,
|
|
|
|
|
|
"down" => 180f,
|
|
|
|
|
|
"left" => 270f,
|
|
|
|
|
|
_ => 0f
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
#region 绘制网格圆点
|
2026-06-12 18:10:00 +08:00
|
|
|
|
private void DrawGridDots(LevelConfig cfg)
|
|
|
|
|
|
{
|
|
|
|
|
|
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);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
dot.SetPosition(pos.x, pos.y, 0);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
_viewContainer.AddChild(dot);
|
2026-06-16 10:22:11 +08:00
|
|
|
|
// 加入列表统一管理
|
|
|
|
|
|
_gridDotList.Add(dot);
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-14 16:25:04 +08:00
|
|
|
|
|
2026-06-16 10:22:11 +08:00
|
|
|
|
// ========= 网格绘制完成后,设置居中缩放轴心 =========
|
2026-06-14 16:25:04 +08:00
|
|
|
|
CalculateGridCenter(cfg);
|
|
|
|
|
|
SetViewPivotToGridCenter();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>计算整个网格圆点区域的世界中心点</summary>
|
|
|
|
|
|
private void CalculateGridCenter(LevelConfig cfg)
|
|
|
|
|
|
{
|
|
|
|
|
|
int rows = cfg.gridRows;
|
|
|
|
|
|
int cols = cfg.gridCols;
|
|
|
|
|
|
|
|
|
|
|
|
// 取四个角点位,算出整体中心
|
|
|
|
|
|
Vector2 topLeft = RowColToPos(0, 0);
|
|
|
|
|
|
Vector2 bottomRight = RowColToPos(rows - 1, cols - 1);
|
|
|
|
|
|
|
|
|
|
|
|
// 几何中心
|
|
|
|
|
|
_gridWorldCenter.x = (topLeft.x + bottomRight.x) * 0.5f;
|
|
|
|
|
|
_gridWorldCenter.y = (topLeft.y + bottomRight.y) * 0.5f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>设置_viewContainer缩放轴心 = 网格中心,实现居中缩放</summary>
|
|
|
|
|
|
private void SetViewPivotToGridCenter()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_viewContainer == null) return;
|
|
|
|
|
|
|
|
|
|
|
|
// FGUI pivot 取值范围 0~1,是容器内部相对比例
|
|
|
|
|
|
float pivotX = _gridWorldCenter.x / _viewContainer.width;
|
|
|
|
|
|
float pivotY = _gridWorldCenter.y / _viewContainer.height;
|
|
|
|
|
|
|
|
|
|
|
|
// 限制在合法区间
|
|
|
|
|
|
pivotX = Mathf.Clamp01(pivotX);
|
|
|
|
|
|
pivotY = Mathf.Clamp01(pivotY);
|
|
|
|
|
|
|
|
|
|
|
|
// 设置轴心,此后所有缩放都围绕网格中心
|
|
|
|
|
|
_viewContainer.pivot = new Vector2(pivotX, pivotY);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
#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;
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
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>();
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrow.originalLinePos = new List<Vector2>();
|
|
|
|
|
|
arrow.lineTrackIndex = new List<int>();
|
|
|
|
|
|
arrow.totalTrack = new List<Vector2>();
|
|
|
|
|
|
List<Vector2> pathPoints = arrow.pathPointList;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 生成整条连续行走轨迹
|
|
|
|
|
|
if (pathPoints.Count >= 2)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
for (int i = 0; i < pathPoints.Count - 1; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector2 segStart = pathPoints[i];
|
|
|
|
|
|
string dir = arrow.path[i];
|
|
|
|
|
|
float offX = 0, offY = 0;
|
|
|
|
|
|
switch (dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "right": offX = 10; break;
|
|
|
|
|
|
case "left": offX = -10; break;
|
|
|
|
|
|
case "down": offY = 10; break;
|
|
|
|
|
|
case "up": offY = -10; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
Vector2 cur = segStart;
|
|
|
|
|
|
for (int k = 0; k < 6; k++)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrow.totalTrack.Add(cur);
|
|
|
|
|
|
cur.x += offX;
|
|
|
|
|
|
cur.y += offY;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (arrow.path.Count > 0)
|
|
|
|
|
|
arrow.finalMoveDir = arrow.path[arrow.path.Count - 1];
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 路径只有单个点(纯箭头)
|
|
|
|
|
|
if (pathPoints.Count < 2)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
CreateArrowOnly(arrow, pathPoints[0]);
|
|
|
|
|
|
// ========= 修复1:单点箭头也初始化轨迹索引 =========
|
|
|
|
|
|
arrow.arrowTrackIndex = 0;
|
|
|
|
|
|
arrow.finalMoveDir = GetDirByRotation(arrow.arrowObj.rotation);
|
|
|
|
|
|
// ========= 修复2:强制记录原始坐标(关键!)=========
|
|
|
|
|
|
arrow.originalArrowPos = new Vector2(arrow.arrowObj.x, arrow.arrowObj.y);
|
2026-06-13 18:27:00 +08:00
|
|
|
|
// 优化:给单点箭头填充一个轨迹点,和有线箭头逻辑完全一致
|
|
|
|
|
|
arrow.totalTrack.Add(pathPoints[0]);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 生成线条图块
|
|
|
|
|
|
List<Vector2> tilePositions = new List<Vector2>();
|
|
|
|
|
|
for (int i = 0; i < pathPoints.Count - 1; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
Vector2 segStart = pathPoints[i];
|
|
|
|
|
|
string dir = arrow.path[i];
|
|
|
|
|
|
float offX = 0, offY = 0;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
switch (dir)
|
|
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
case "right": offX = 10; break;
|
|
|
|
|
|
case "left": offX = -10; break;
|
|
|
|
|
|
case "down": offY = 10; break;
|
|
|
|
|
|
case "up": offY = -10; break;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
Vector2 cur = segStart;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
for (int k = 0; k < 6; k++)
|
|
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
tilePositions.Add(cur);
|
|
|
|
|
|
cur.x += offX;
|
|
|
|
|
|
cur.y += offY;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (tilePositions.Count > 0)
|
|
|
|
|
|
tilePositions.RemoveAt(tilePositions.Count - 1);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
for (int i = 0; i < tilePositions.Count; i++)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
Vector2 pos = tilePositions[i];
|
2026-06-12 18:10:00 +08:00
|
|
|
|
LineTile unit = LineTile.CreateInstance();
|
|
|
|
|
|
unit.SetPosition(pos.x, pos.y, 0);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// 运行时主题取色
|
|
|
|
|
|
Color lineColor = GetArrowRuntimeColor(arrow);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
unit.GetChild("line").asGraph.color = lineColor;
|
|
|
|
|
|
unit.visible = true;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
unit.touchable = true;
|
2026-07-01 10:26:18 +08:00
|
|
|
|
unit.name = "Arrow_" + arrow.id;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
unit.onClick.Add(() => OnPathClick(arrow));
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
|
|
|
|
|
_viewContainer.AddChild(unit);
|
|
|
|
|
|
arrow.lineUnits.Add(unit);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrow.originalLinePos.Add(pos);
|
|
|
|
|
|
arrow.lineTrackIndex.Add(i);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 创建箭头
|
2026-06-13 15:34:00 +08:00
|
|
|
|
CreateArrowOnly(arrow, pathPoints[pathPoints.Count - 1]);
|
|
|
|
|
|
arrow.originalArrowPos = new Vector2(arrow.arrowObj.x, arrow.arrowObj.y);
|
|
|
|
|
|
arrow.arrowTrackIndex = arrow.totalTrack.Count - 1;
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// ========== 初始化距离偏移 ==========
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.movedDistance = 0f;
|
|
|
|
|
|
// 轨迹总长度 = (点数-1) * 点间距
|
|
|
|
|
|
arrow.trackTotalLength = (arrow.totalTrack.Count - 1) * 10f;
|
|
|
|
|
|
// 箭头初始在轨迹末端
|
|
|
|
|
|
arrow.arrowInitOffset = arrow.trackTotalLength;
|
|
|
|
|
|
|
|
|
|
|
|
// 每个线条的初始偏移 = 索引 * 点间距
|
|
|
|
|
|
arrow.lineInitOffsets = new List<float>();
|
|
|
|
|
|
for (int i = 0; i < arrow.lineUnits.Count; i++)
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.lineInitOffsets.Add(i * 10f);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
#region 道具(删除和提示)
|
2026-06-18 09:27:38 +08:00
|
|
|
|
private void SetUserPorp(object a)
|
2026-06-17 14:45:28 +08:00
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
if (a != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
var type = (int)a;
|
|
|
|
|
|
Debug.Log($"Set User Prop: {type}");
|
|
|
|
|
|
if (type == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 提示道具
|
|
|
|
|
|
ShowRandomArrowTip();
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (type == 1)
|
|
|
|
|
|
{
|
|
|
|
|
|
_isDeleteMode = !_isDeleteMode;
|
2026-07-01 10:26:18 +08:00
|
|
|
|
GameHelper.ShowTips("You can delete any one of the arrows!");
|
2026-06-18 09:27:38 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
}
|
2026-06-13 18:27:00 +08:00
|
|
|
|
/// <summary>点击删除道具,开启/关闭删除模式</summary>
|
2026-06-13 19:58:55 +08:00
|
|
|
|
private void OnClickDeleteItem()
|
2026-06-13 18:27:00 +08:00
|
|
|
|
{
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// _isDeleteMode = !_isDeleteMode;
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.ArrowTipsUI_Open, 1);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
// // 可选:加提示,区分状态
|
|
|
|
|
|
// if (_isDeleteMode)
|
|
|
|
|
|
// {
|
|
|
|
|
|
// Debug.Log("已开启删除模式,点击箭头即可删除");
|
|
|
|
|
|
// // 可加UI提示:如图标高亮、文字提示
|
|
|
|
|
|
// }
|
|
|
|
|
|
// else
|
|
|
|
|
|
// {
|
|
|
|
|
|
// Debug.Log("已关闭删除模式");
|
|
|
|
|
|
// }
|
2026-06-13 18:27:00 +08:00
|
|
|
|
}
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
|
|
|
|
|
private void OnClickHint()
|
|
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.ArrowTipsUI_Open, 0);
|
2026-06-13 19:58:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>随机选中有效箭头,显示静态手指提示(不跟随移动)</summary>
|
|
|
|
|
|
private void ShowRandomArrowTip()
|
|
|
|
|
|
{
|
|
|
|
|
|
// 先隐藏上一次提示
|
|
|
|
|
|
HideFingerTip();
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
if (_levelConfig == null || _levelConfig.arrows == null || _levelConfig.arrows.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log("暂无可提示的箭头");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 筛选条件:对象存在 + 未销毁 + 未在移动中 + 前方无阻挡可直接消除
|
2026-06-13 19:58:55 +08:00
|
|
|
|
List<ArrowConfig> validArrows = _levelConfig.arrows
|
2026-06-18 09:27:38 +08:00
|
|
|
|
.Where(arrow =>
|
|
|
|
|
|
arrow != null &&
|
|
|
|
|
|
arrow.arrowObj != null &&
|
|
|
|
|
|
!arrow.isMoving &&
|
|
|
|
|
|
!IsArrowBlocked(arrow))
|
2026-06-13 19:58:55 +08:00
|
|
|
|
.ToList();
|
|
|
|
|
|
|
|
|
|
|
|
if (validArrows.Count == 0)
|
|
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
Debug.Log("当前没有可直接消除的箭头,无法提示");
|
2026-06-13 19:58:55 +08:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 从可消除箭头中随机选一个
|
2026-06-13 19:58:55 +08:00
|
|
|
|
int randomIndex = Random.Range(0, validArrows.Count);
|
|
|
|
|
|
_currentTipArrow = validArrows[randomIndex];
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 设置手指提示位置
|
2026-06-13 19:58:55 +08:00
|
|
|
|
float x = _currentTipArrow.arrowObj.x + _fingerOffset.x;
|
|
|
|
|
|
float y = _currentTipArrow.arrowObj.y + _fingerOffset.y;
|
|
|
|
|
|
_fingerTipObj.SetPosition(x, y, 0);
|
|
|
|
|
|
_fingerTipObj.visible = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>隐藏手指提示并清空标记</summary>
|
|
|
|
|
|
private void HideFingerTip()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_fingerTipObj != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_fingerTipObj.visible = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
_currentTipArrow = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#region 缩放
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断FGUI全局坐标是否落在拖拽有效区域(view_container_parent)内
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsInDragArea(Vector2 fguiGlobalPos)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (ui.view_container_parent == null)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 全局坐标转成父容器本地坐标(左上角为原点)
|
|
|
|
|
|
Vector2 localPos = ui.view_container_parent.GlobalToLocal(fguiGlobalPos);
|
|
|
|
|
|
|
|
|
|
|
|
// 判断是否在容器矩形范围内
|
|
|
|
|
|
return localPos.x >= 0
|
|
|
|
|
|
&& localPos.x <= ui.view_container_parent.width
|
|
|
|
|
|
&& localPos.y >= 0
|
|
|
|
|
|
&& localPos.y <= ui.view_container_parent.height;
|
|
|
|
|
|
}
|
2026-06-24 09:24:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 判断FGUI全局坐标是否落在圆点网格范围内
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private bool IsInGridArea(Vector2 fguiGlobalPos)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_viewContainer == null || _levelConfig == null)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
// 全局坐标转成 _viewContainer 本地坐标(网格坐标基于该容器)
|
|
|
|
|
|
Vector2 localPos = _viewContainer.GlobalToLocal(fguiGlobalPos);
|
|
|
|
|
|
|
|
|
|
|
|
// 和网格四边边界比对
|
|
|
|
|
|
return localPos.x >= gridLeft
|
|
|
|
|
|
&& localPos.x <= gridRight
|
|
|
|
|
|
&& localPos.y >= gridTop
|
|
|
|
|
|
&& localPos.y <= gridBottom;
|
|
|
|
|
|
}
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
/// <summary>根据缩放值,计算滑动条 value(0~1)</summary>
|
|
|
|
|
|
private float GetSliderValueByScale(float scale)
|
|
|
|
|
|
{
|
2026-06-14 16:25:04 +08:00
|
|
|
|
float validScale = Mathf.Clamp(scale, _minScale, _maxScale);
|
|
|
|
|
|
float ratio = (validScale - _minScale) / (_maxScale - _minScale);
|
|
|
|
|
|
float sliderVal = ratio * 100f;
|
|
|
|
|
|
|
|
|
|
|
|
return Mathf.Clamp(sliderVal, 0f, 100f);
|
2026-06-13 19:58:55 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>根据滑动条 value(0~1),计算实际缩放值</summary>
|
|
|
|
|
|
private float GetScaleBySliderValue(float sliderValue)
|
|
|
|
|
|
{
|
2026-06-14 16:25:04 +08:00
|
|
|
|
// 先把滑块值钳位在 0~100 内
|
|
|
|
|
|
float val = Mathf.Clamp(sliderValue, 0f, 100f);
|
|
|
|
|
|
|
|
|
|
|
|
// 线性映射:0→0.6,100→1.5
|
|
|
|
|
|
float ratio = val / 100f;
|
|
|
|
|
|
float scale = _minScale + ratio * (_maxScale - _minScale);
|
|
|
|
|
|
|
2026-06-13 19:58:55 +08:00
|
|
|
|
return Mathf.Clamp(scale, _minScale, _maxScale);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>滑动条数值变化回调</summary>
|
|
|
|
|
|
private void OnZoomSliderChange()
|
|
|
|
|
|
{
|
|
|
|
|
|
float scale = GetScaleBySliderValue((float)_zoomSlider.value);
|
|
|
|
|
|
_viewContainer.scaleX = scale;
|
|
|
|
|
|
_viewContainer.scaleY = scale;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>移动容器 + 边界限制,防止完全拖出屏幕</summary>
|
|
|
|
|
|
private void MoveContainer(Vector2 offset)
|
|
|
|
|
|
{
|
2026-06-14 19:25:04 +08:00
|
|
|
|
Debug.Log($"[arrow] 移动容器:{offset}");
|
2026-06-13 19:58:55 +08:00
|
|
|
|
float newX = _viewContainer.x + offset.x;
|
|
|
|
|
|
float newY = _viewContainer.y + offset.y;
|
|
|
|
|
|
|
|
|
|
|
|
float viewW = _viewContainer.width * _viewContainer.scaleX;
|
|
|
|
|
|
float viewH = _viewContainer.height * _viewContainer.scaleY;
|
|
|
|
|
|
|
|
|
|
|
|
// 左右边界
|
|
|
|
|
|
float minX = -viewW + _dragBorder;
|
|
|
|
|
|
float maxX = Screen.width - _dragBorder;
|
|
|
|
|
|
newX = Mathf.Clamp(newX, minX, maxX);
|
|
|
|
|
|
|
|
|
|
|
|
// 上下边界
|
|
|
|
|
|
float minY = -viewH + _dragBorder;
|
|
|
|
|
|
float maxY = Screen.height - _dragBorder;
|
|
|
|
|
|
newY = Mathf.Clamp(newY, minY, maxY);
|
|
|
|
|
|
|
|
|
|
|
|
_viewContainer.SetPosition(newX, newY,0);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
#region 点击 & 移动逻辑
|
|
|
|
|
|
|
|
|
|
|
|
// 线条/箭头 统一点击回调
|
|
|
|
|
|
private void OnPathClick(ArrowConfig clickArrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (clickArrow.isMoving)
|
|
|
|
|
|
{
|
|
|
|
|
|
Debug.Log($"[arrow] {clickArrow.id}正在移动,请勿点击其他箭头");
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
TriggerVibration(true);
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 点击的是当前提示箭头,直接隐藏手指
|
|
|
|
|
|
if (_currentTipArrow == clickArrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
HideFingerTip();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// ========= 删除模式:直接删除箭头,不走移动逻辑 =========
|
|
|
|
|
|
if (_isDeleteMode)
|
|
|
|
|
|
{
|
|
|
|
|
|
DeleteArrowCompletely(clickArrow);
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
clickArrow.isMoving = true;
|
|
|
|
|
|
bool hasBlock = CheckBlockByGrid(clickArrow);
|
2026-06-24 09:24:15 +08:00
|
|
|
|
Debug.Log("[arrow] 点击箭头 id:" + clickArrow.id +$" hasBlock=阻挡=={hasBlock}");
|
2026-06-15 17:50:56 +08:00
|
|
|
|
if (hasBlock)
|
|
|
|
|
|
{
|
|
|
|
|
|
MoveThenReset(clickArrow);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
MoveToDisappear(clickArrow);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 纯检测:箭头前方是否有阻挡(无副作用,不扣血、不改状态)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>true=前方有阻挡,false=可直接飞出消除</returns>
|
|
|
|
|
|
private bool IsArrowBlocked(ArrowConfig curArrow)
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
if (_gridOccupied == null || _levelConfig == null)
|
|
|
|
|
|
return false;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 获取箭头头部所在网格行列
|
2026-06-13 15:34:00 +08:00
|
|
|
|
Vector2 arrowPos = new Vector2(curArrow.arrowObj.x, curArrow.arrowObj.y);
|
|
|
|
|
|
int curPointId = GetPointIdByPos(arrowPos, _levelConfig);
|
|
|
|
|
|
PointIdToRowCol(curPointId, _levelConfig.gridCols, out int curRow, out int curCol);
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 箭头已超出网格,判定无阻挡
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (curRow < 0 || curRow >= _levelConfig.gridRows || curCol < 0 || curCol >= _levelConfig.gridCols)
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 获取最终射出方向
|
2026-06-13 15:34:00 +08:00
|
|
|
|
string moveDir;
|
|
|
|
|
|
if (curArrow.path != null && curArrow.path.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
moveDir = curArrow.path[curArrow.path.Count - 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (!string.IsNullOrEmpty(curArrow.defaultDir))
|
|
|
|
|
|
{
|
|
|
|
|
|
moveDir = curArrow.defaultDir;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
moveDir = "up";
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 沿射出方向遍历前方网格
|
2026-06-13 15:34:00 +08:00
|
|
|
|
switch (moveDir)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "up":
|
|
|
|
|
|
for (int r = curRow - 1; r >= 0; r--)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridOccupied[r, curCol])
|
2026-06-18 09:27:38 +08:00
|
|
|
|
return true;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "down":
|
|
|
|
|
|
for (int r = curRow + 1; r < _levelConfig.gridRows; r++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridOccupied[r, curCol])
|
2026-06-18 09:27:38 +08:00
|
|
|
|
return true;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "left":
|
|
|
|
|
|
for (int c = curCol - 1; c >= 0; c--)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridOccupied[curRow, c])
|
2026-06-18 09:27:38 +08:00
|
|
|
|
return true;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
case "right":
|
|
|
|
|
|
for (int c = curCol + 1; c < _levelConfig.gridCols; c++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridOccupied[curRow, c])
|
2026-06-18 09:27:38 +08:00
|
|
|
|
return true;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-18 09:27:38 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 基于网格行列的阻挡检测(带扣血逻辑,点击箭头时调用)
|
|
|
|
|
|
private bool CheckBlockByGrid(ArrowConfig curArrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
bool isCollide = IsArrowBlocked(curArrow);
|
|
|
|
|
|
|
|
|
|
|
|
// ========== 碰撞判重 + 扣爱心 ==========
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (isCollide)
|
|
|
|
|
|
{
|
2026-06-18 09:27:38 +08:00
|
|
|
|
// 同个箭头第一次碰撞才扣血
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (!_collidedArrows.Contains(curArrow))
|
|
|
|
|
|
{
|
|
|
|
|
|
_collidedArrows.Add(curArrow);
|
|
|
|
|
|
_heartCount--;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
if (_heartCount > 0)
|
|
|
|
|
|
ui.HeartsPanel.GetChild($"xin_{_heartCount + 1}").visible = false;
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 爱心归零 → 失败结算
|
|
|
|
|
|
if (_heartCount <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowResult(false);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
return isCollide;
|
|
|
|
|
|
}
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 计算箭头沿射出方向,到前方第一个阻挡点的前移距离(像素)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="arrow">目标箭头</param>
|
|
|
|
|
|
/// <param name="hitMargin">碰撞预留余量,离阻挡点还差多少像素就停下回弹</param>
|
|
|
|
|
|
/// <returns>实际前移的总像素距离</returns>
|
|
|
|
|
|
private float GetDistanceToFirstBlock(ArrowConfig arrow, float hitMargin = 20f)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_gridOccupied == null || _levelConfig == null)
|
|
|
|
|
|
return 20f; // 异常兜底,保持原默认值
|
|
|
|
|
|
|
|
|
|
|
|
// 计算箭头头部所在的网格坐标(路径终点)
|
|
|
|
|
|
int curRow = arrow.startPoint / _levelConfig.gridCols;
|
|
|
|
|
|
int curCol = arrow.startPoint % _levelConfig.gridCols;
|
|
|
|
|
|
|
|
|
|
|
|
// 沿路径走到终点,定位箭头头部网格位置
|
|
|
|
|
|
foreach (string dir in arrow.path)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "up": curRow--; break;
|
|
|
|
|
|
case "down": curRow++; break;
|
|
|
|
|
|
case "left": curCol--; break;
|
|
|
|
|
|
case "right": curCol++; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
string moveDir = GetFinalMoveDir(arrow);
|
|
|
|
|
|
int emptyGridCount = 0;
|
|
|
|
|
|
|
|
|
|
|
|
// 沿射出方向逐格检测第一个阻挡
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 前进一步
|
|
|
|
|
|
switch (moveDir)
|
|
|
|
|
|
{
|
|
|
|
|
|
case "up": curRow--; break;
|
|
|
|
|
|
case "down": curRow++; break;
|
|
|
|
|
|
case "left": curCol--; break;
|
|
|
|
|
|
case "right": curCol++; break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 出界无阻挡,兜底返回默认距离
|
|
|
|
|
|
if (curRow < 0 || curRow >= _levelConfig.gridRows
|
|
|
|
|
|
|| curCol < 0 || curCol >= _levelConfig.gridCols)
|
|
|
|
|
|
{
|
|
|
|
|
|
return 20f;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 找到阻挡点
|
|
|
|
|
|
if (_gridOccupied[curRow, curCol])
|
|
|
|
|
|
{
|
|
|
|
|
|
// 总距离 = 空格数*点间距 + (单格距离 - 预留余量)
|
|
|
|
|
|
float total = (emptyGridCount + 1) * _levelConfig.pointSpacing - hitMargin;
|
|
|
|
|
|
// 最小前移保护,避免阻挡太近时几乎没动画
|
|
|
|
|
|
return Mathf.Max(total, 6f);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
emptyGridCount++;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 18:27:00 +08:00
|
|
|
|
/// <summary>彻底删除指定箭头:销毁UI+清数据+停逻辑+移除列表</summary>
|
|
|
|
|
|
private void DeleteArrowCompletely(ArrowConfig arrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (arrow == null) return;
|
2026-06-13 19:58:55 +08:00
|
|
|
|
|
|
|
|
|
|
// 被删除的是提示箭头,隐藏手指
|
|
|
|
|
|
if (_currentTipArrow == arrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
HideFingerTip();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 18:27:00 +08:00
|
|
|
|
// 1. 停止移动标记,拦截Update和协程
|
|
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 销毁箭头中所有线条UI
|
|
|
|
|
|
if (arrow.lineUnits != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var line in arrow.lineUnits)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (line != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_viewContainer.RemoveChild(line);
|
|
|
|
|
|
line.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
arrow.lineUnits.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 销毁箭头本体UI
|
|
|
|
|
|
if (arrow.arrowObj != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_viewContainer.RemoveChild(arrow.arrowObj);
|
|
|
|
|
|
arrow.arrowObj.Dispose();
|
|
|
|
|
|
arrow.arrowObj = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 释放网格占用数据(和飞出边界逻辑保持一致)
|
|
|
|
|
|
ReleaseArrowOccupied(arrow);
|
|
|
|
|
|
|
|
|
|
|
|
// 5. 存活箭头计数-1
|
|
|
|
|
|
_remainArrowCount--;
|
|
|
|
|
|
|
|
|
|
|
|
// 6. 从总列表移除该箭头(核心:删除数据源)
|
|
|
|
|
|
if (_levelConfig != null && _levelConfig.arrows != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_levelConfig.arrows.Remove(arrow);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 7. 可选:判断是否全部删除完成,触发结算(按需开启)
|
|
|
|
|
|
if (_remainArrowCount <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowResult(true);
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
// 消除每条线小概率触发
|
|
|
|
|
|
DisappearLineGetReward();
|
|
|
|
|
|
}
|
2026-06-13 18:27:00 +08:00
|
|
|
|
// 8. 退出删除模式
|
|
|
|
|
|
_isDeleteMode = false;
|
|
|
|
|
|
Debug.Log("[arrow] 箭头已彻底删除");
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
// 创建箭头 + 绑定点击
|
2026-06-12 18:10:00 +08:00
|
|
|
|
private void CreateArrowOnly(ArrowConfig arrow, Vector2 arrowPos)
|
|
|
|
|
|
{
|
|
|
|
|
|
ArrowEnd arrowIns = ArrowEnd.CreateInstance();
|
|
|
|
|
|
arrowIns.SetPosition(arrowPos.x, arrowPos.y, 0);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrowIns.touchable = true;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (arrow.path != null && arrow.path.Count > 0)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
string lastDir = arrow.path[arrow.path.Count - 1];
|
|
|
|
|
|
arrowIns.rotation = DirToRotation(lastDir);
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
else if (!string.IsNullOrEmpty(arrow.defaultDir))
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 纯单点箭头 使用默认方向旋转
|
|
|
|
|
|
arrowIns.rotation = DirToRotation(arrow.defaultDir);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// 运行时主题取色
|
|
|
|
|
|
Color arrowColor = GetArrowRuntimeColor(arrow);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
GImage icon = arrowIns.GetChild("icon").asImage;
|
|
|
|
|
|
if (icon != null) icon.color = arrowColor;
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
arrowIns.visible = true;
|
2026-07-01 10:26:18 +08:00
|
|
|
|
arrowIns.name = "Arrow_" + arrow.id;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrowIns.onClick.Add(() => OnPathClick(arrow));
|
2026-06-12 18:10:00 +08:00
|
|
|
|
_viewContainer.AddChild(arrowIns);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
arrow.arrowObj = arrowIns;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 移动后复位动画(回弹,不释放占用)
|
|
|
|
|
|
private void MoveThenReset(ArrowConfig arrow)
|
|
|
|
|
|
{
|
2026-06-13 18:27:00 +08:00
|
|
|
|
RunCoroutine(MoveAndResetCoroutine(arrow));
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
private readonly float _bounceSpeed = 1200f;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private IEnumerator MoveAndResetCoroutine(ArrowConfig arrow)
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.movedDistance = 0f;
|
|
|
|
|
|
arrow.finalMoveDir = GetFinalMoveDir(arrow);
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 动态计算:前移到快碰到阻挡点的距离
|
|
|
|
|
|
float forwardTotal = GetDistanceToFirstBlock(arrow, 3f);
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float duration = forwardTotal / _bounceSpeed;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
float timer = 0f;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// ========== 第一段:向前移动到快碰到阻挡 ==========
|
2026-06-15 17:50:56 +08:00
|
|
|
|
while (timer < duration)
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
if (arrow == null || arrow.arrowObj == null) yield break;
|
|
|
|
|
|
|
|
|
|
|
|
timer += Time.deltaTime;
|
2026-06-18 09:27:38 +08:00
|
|
|
|
arrow.movedDistance = Mathf.Min(_bounceSpeed * timer, forwardTotal);
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
UpdateAllElementsPosition(arrow);
|
|
|
|
|
|
yield return null;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 核心:阻挡强震动 + 红色氛围闪烁
|
|
|
|
|
|
TriggerBlockFlash();
|
|
|
|
|
|
|
|
|
|
|
|
// 强制对齐前移终点,消除浮点误差
|
2026-06-18 09:27:38 +08:00
|
|
|
|
arrow.movedDistance = forwardTotal;
|
|
|
|
|
|
UpdateAllElementsPosition(arrow);
|
|
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// ========== 直接瞬间复位(删除原渐变回弹逻辑)==========
|
|
|
|
|
|
// 可选:加 1 帧停顿,模拟碰撞卡顿感,觉得突兀就打开注释
|
|
|
|
|
|
// yield return null;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-24 09:24:15 +08:00
|
|
|
|
// 距离归零 + 原始坐标强制校准,双保险保证精准回到初始位置
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.movedDistance = 0f;
|
|
|
|
|
|
UpdateAllElementsPosition(arrow);
|
2026-06-18 09:27:38 +08:00
|
|
|
|
|
|
|
|
|
|
if (arrow.arrowObj != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrow.arrowObj.SetPosition(arrow.originalArrowPos.x, arrow.originalArrowPos.y, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
for (int i = 0; i < arrow.lineUnits.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (arrow.lineUnits[i] != null && i < arrow.originalLinePos.Count)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrow.lineUnits[i].SetPosition(
|
|
|
|
|
|
arrow.originalLinePos[i].x,
|
|
|
|
|
|
arrow.originalLinePos[i].y, 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>统一更新箭头+所有线条的位置</summary>
|
|
|
|
|
|
private void UpdateAllElementsPosition(ArrowConfig arrow)
|
|
|
|
|
|
{
|
2026-06-13 15:34:00 +08:00
|
|
|
|
for (int i = 0; i < arrow.lineUnits.Count; i++)
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float curDist = arrow.lineInitOffsets[i] + arrow.movedDistance;
|
|
|
|
|
|
Vector2 pos = GetPositionByDistance(arrow, curDist);
|
|
|
|
|
|
arrow.lineUnits[i].SetPosition(pos.x, pos.y, 0);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float arrowCurDist = arrow.arrowInitOffset + arrow.movedDistance;
|
|
|
|
|
|
Vector2 arrowPos = GetPositionByDistance(arrow, arrowCurDist);
|
|
|
|
|
|
arrow.arrowObj.SetPosition(arrowPos.x, arrowPos.y, 0);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private void MoveToDisappear(ArrowConfig arrow)
|
|
|
|
|
|
{
|
2026-06-13 18:27:00 +08:00
|
|
|
|
RunCoroutine(MoveAndDisappearCoroutine(arrow));
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
|
|
|
|
|
|
private void SetSpeed(object a = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (a != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_moveSpeed = (float)a;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 新增:统一移动速度(像素/秒),数值越大移动越快,可自由修改
|
2026-06-17 14:45:28 +08:00
|
|
|
|
[SerializeField] private float _moveSpeed = 1500f;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private IEnumerator MoveAndDisappearCoroutine(ArrowConfig arrow)
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
arrow.movedDistance = 0f;
|
|
|
|
|
|
arrow.finalMoveDir = GetFinalMoveDir(arrow);
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
while (true)
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 空对象兜底保护
|
2026-06-13 18:27:00 +08:00
|
|
|
|
if (arrow == null || arrow.arrowObj == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 每帧累计移动距离:速度 * 时间增量
|
|
|
|
|
|
float deltaDis = _moveSpeed * Time.deltaTime;
|
|
|
|
|
|
arrow.movedDistance += deltaDis;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 更新所有线条位置
|
|
|
|
|
|
for (int i = 0; i < arrow.lineUnits.Count; i++)
|
2026-06-13 18:27:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float curDist = arrow.lineInitOffsets[i] + arrow.movedDistance;
|
|
|
|
|
|
Vector2 pos = GetPositionByDistance(arrow, curDist);
|
|
|
|
|
|
arrow.lineUnits[i].SetPosition(pos.x, pos.y, 0);
|
2026-06-13 18:27:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 更新箭头位置
|
|
|
|
|
|
float arrowCurDist = arrow.arrowInitOffset + arrow.movedDistance;
|
|
|
|
|
|
Vector2 arrowPos = GetPositionByDistance(arrow, arrowCurDist);
|
|
|
|
|
|
arrow.arrowObj.SetPosition(arrowPos.x, arrowPos.y, 0);
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 边界检测:以尾部线条为准(和原逻辑一致)
|
|
|
|
|
|
Vector2 tailPos = GetPositionByDistance(arrow, arrow.movedDistance);
|
|
|
|
|
|
if (CheckIsOutOfBounds(tailPos, arrow.finalMoveDir))
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// ========== 以下销毁逻辑和原代码保持一致 ==========
|
2026-06-13 15:34:00 +08:00
|
|
|
|
ReleaseArrowOccupied(arrow);
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var line in arrow.lineUnits)
|
2026-06-13 18:27:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
if (line != null)
|
2026-06-13 18:27:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
_viewContainer.RemoveChild(line);
|
|
|
|
|
|
line.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
arrow.lineUnits.Clear();
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
if (arrow.arrowObj != null)
|
2026-06-13 18:27:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
_viewContainer.RemoveChild(arrow.arrowObj);
|
|
|
|
|
|
arrow.arrowObj.Dispose();
|
|
|
|
|
|
arrow.arrowObj = null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
_remainArrowCount--;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
if (_remainArrowCount <= 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
ShowResult(true);
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
DisappearLineGetReward();
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
yield return null; // 每帧执行一次,平滑移动
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
#region 移动工具方法
|
|
|
|
|
|
/// <summary>根据距离,计算沿轨迹的实际坐标</summary>
|
|
|
|
|
|
private Vector2 GetPositionByDistance(ArrowConfig arrow, float distance)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
int trackCount = arrow.totalTrack.Count;
|
|
|
|
|
|
const float segmentLen = 10f; // 轨迹点固定间距
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 距离在轨迹范围内:两点插值得到精确位置
|
|
|
|
|
|
if (distance <= arrow.trackTotalLength)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float indexFloat = distance / segmentLen;
|
|
|
|
|
|
int index0 = Mathf.FloorToInt(indexFloat);
|
|
|
|
|
|
int index1 = Mathf.Min(index0 + 1, trackCount - 1);
|
|
|
|
|
|
float t = indexFloat - index0;
|
|
|
|
|
|
|
|
|
|
|
|
Vector2 p0 = arrow.totalTrack[index0];
|
|
|
|
|
|
Vector2 p1 = arrow.totalTrack[index1];
|
|
|
|
|
|
return Vector2.Lerp(p0, p1, t);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
// 距离超出轨迹:沿最终方向直线延伸
|
2026-06-12 18:10:00 +08:00
|
|
|
|
else
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
float extraDis = distance - arrow.trackTotalLength;
|
|
|
|
|
|
Vector2 endPos = arrow.totalTrack[trackCount - 1];
|
|
|
|
|
|
Vector2 dir = DirToVector2(arrow.finalMoveDir);
|
|
|
|
|
|
return endPos + dir * extraDis;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
/// <summary>方向字符串转单位向量</summary>
|
|
|
|
|
|
/// <summary>方向字符串转单位向量(适配 FGUI 坐标系:y 轴向下)</summary>
|
|
|
|
|
|
private Vector2 DirToVector2(string dir)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
return dir switch
|
|
|
|
|
|
{
|
|
|
|
|
|
"up" => new Vector2(0, -1), // FGUI 向上 = y 减小
|
|
|
|
|
|
"down" => new Vector2(0, 1), // FGUI 向下 = y 增大
|
|
|
|
|
|
"left" => new Vector2(-1, 0),
|
|
|
|
|
|
"right" => new Vector2(1, 0),
|
|
|
|
|
|
_ => new Vector2(0, -1)
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
/// <summary>统一获取箭头最终移动方向</summary>
|
|
|
|
|
|
private string GetFinalMoveDir(ArrowConfig arrow)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (arrow.path != null && arrow.path.Count > 0)
|
|
|
|
|
|
return arrow.path[arrow.path.Count - 1];
|
|
|
|
|
|
if (!string.IsNullOrEmpty(arrow.defaultDir))
|
|
|
|
|
|
return arrow.defaultDir;
|
|
|
|
|
|
return "up";
|
|
|
|
|
|
}
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
/// <summary>统一边界检测</summary>
|
|
|
|
|
|
private bool CheckIsOutOfBounds(Vector2 pos, string dir)
|
|
|
|
|
|
{
|
|
|
|
|
|
switch (dir)
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
case "right": return pos.x > gridRight + outOffset;
|
|
|
|
|
|
case "left": return pos.x < gridLeft - outOffset;
|
|
|
|
|
|
case "down": return pos.y > gridBottom + outOffset;
|
|
|
|
|
|
case "up": return pos.y < gridTop - outOffset;
|
|
|
|
|
|
default: return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 消除每条线时概率获得奖励
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void DisappearLineGetReward()
|
|
|
|
|
|
{
|
2026-07-01 10:26:18 +08:00
|
|
|
|
|
|
|
|
|
|
if (!GameHelper.IsGiftSwitch()) return;
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
bool isGet = false;
|
|
|
|
|
|
|
|
|
|
|
|
int money_rate = ConfigSystem.GetConfig<CommonModel>().rewardrate;
|
|
|
|
|
|
var randomNum = UnityEngine.Random.Range(0, 100);
|
|
|
|
|
|
// Debug.Log($"[creat] money_rate------------ {randomNum}==={money_rate}");
|
2026-07-01 10:26:18 +08:00
|
|
|
|
if (randomNum < money_rate)
|
2026-06-15 17:50:56 +08:00
|
|
|
|
{
|
|
|
|
|
|
isGet = true;
|
|
|
|
|
|
|
|
|
|
|
|
// 显示大额奖励
|
|
|
|
|
|
float[] cash_array = GameHelper.GetRewardValue(1);
|
|
|
|
|
|
var temp = new SuccessData();
|
|
|
|
|
|
temp.IsWin = true;
|
|
|
|
|
|
temp.cash_number = cash_array[0];
|
|
|
|
|
|
temp.rate = (int)cash_array[1];
|
|
|
|
|
|
temp.IsLevelSuccess = false;
|
|
|
|
|
|
temp.IsH5Reward = false;
|
|
|
|
|
|
temp.boost_array = GameHelper.GetRewardBoost(1);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
DOVirtual.DelayedCall(0.25f, () =>
|
2026-06-12 18:10:00 +08:00
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!isGet && Random.Range(0, 100) < ConfigSystem.GetConfig<CommonModel>().Smallrewardsrate)
|
|
|
|
|
|
{
|
|
|
|
|
|
var rewardData = new RewardData();
|
|
|
|
|
|
Vector2 fguiPosition = new Vector2(ui.samll_point.x, ui.samll_point.y);
|
|
|
|
|
|
var start = fguiPosition;
|
|
|
|
|
|
var end = GameHelper.GetUICenterPosition(ui.com_money.GetChild("text_gold"));
|
|
|
|
|
|
float[] cash_array = GameHelper.GetRewardValue(0);
|
|
|
|
|
|
Debug.Log($"小额奖励:{cash_array[0]}");
|
|
|
|
|
|
var rewardSingleData = new RewardSingleData(102, (decimal)cash_array[0], RewardOrigin.AdTask)
|
|
|
|
|
|
{
|
|
|
|
|
|
startPosition = start,
|
|
|
|
|
|
endPosition = new Vector2(end.x - 135, end.y - 135)
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
rewardData.AddReward(rewardSingleData);
|
|
|
|
|
|
rewardData.displayType = RewardDisplayType.RewardFly | RewardDisplayType.ValueChange;
|
|
|
|
|
|
GameDispatcher.Instance.Dispatch(GameMsg.GetReward, rewardData);
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
#endregion
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
|
|
|
|
|
#region 资源清理
|
2026-06-13 18:27:00 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>全局启动协程 + 自动记录,关闭界面时统一停止</summary>
|
|
|
|
|
|
private Coroutine RunCoroutine(IEnumerator cor)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (cor == null) return null;
|
|
|
|
|
|
Coroutine co = CrazyAsyKit.StartCoroutine(cor);
|
|
|
|
|
|
if (co != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_activeCoroutines.Add(co);
|
|
|
|
|
|
}
|
|
|
|
|
|
return co;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
private void ClearAllDynamicObj()
|
|
|
|
|
|
{
|
|
|
|
|
|
if (_levelConfig == null || _viewContainer == null) return;
|
2026-06-16 10:22:11 +08:00
|
|
|
|
|
|
|
|
|
|
// 销毁网格圆点
|
|
|
|
|
|
foreach (var dot in _gridDotList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (dot != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
_viewContainer.RemoveChild(dot);
|
|
|
|
|
|
dot.Dispose();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
_gridDotList.Clear();
|
|
|
|
|
|
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
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
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
#region 结算
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 弹出结算
|
|
|
|
|
|
/// isSuccess: true=成功,false=失败
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
private void ShowResult(bool isSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 暂停所有交互/移动(可选)
|
|
|
|
|
|
foreach (var arrow in _levelConfig.arrows)
|
|
|
|
|
|
{
|
|
|
|
|
|
arrow.isMoving = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-14 19:25:04 +08:00
|
|
|
|
// uiCtrlDispatcher.Dispatch(UICtrlMsg.LevelSuccessUI_Open, isSuccess);
|
|
|
|
|
|
|
|
|
|
|
|
if (isSuccess)
|
|
|
|
|
|
{
|
|
|
|
|
|
GameHelper.SetLevel(GameHelper.GetLevel() + 1);
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
// DataMgr.ArrowChProgress.Value += 10;
|
2026-06-14 19:25:04 +08:00
|
|
|
|
//通关成功
|
|
|
|
|
|
float[] cash_array = GameHelper.GetRewardValue(2);
|
|
|
|
|
|
|
|
|
|
|
|
var temp = new SuccessData();
|
|
|
|
|
|
temp.IsWin = true;
|
|
|
|
|
|
temp.cash_number = cash_array[0];
|
|
|
|
|
|
temp.rate = (int)cash_array[1];
|
|
|
|
|
|
temp.IsLevelSuccess = true;
|
|
|
|
|
|
temp.IsH5Reward = false;
|
|
|
|
|
|
temp.boost_array = GameHelper.GetRewardBoost(2);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
DOVirtual.DelayedCall(0.2f, () =>
|
2026-06-14 19:25:04 +08:00
|
|
|
|
{
|
|
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
//通关失败
|
|
|
|
|
|
float[] cash_array = GameHelper.GetRewardValue(2);
|
|
|
|
|
|
|
|
|
|
|
|
var temp = new SuccessData();
|
|
|
|
|
|
temp.IsWin = false;
|
|
|
|
|
|
temp.cash_number = cash_array[0];
|
|
|
|
|
|
temp.IsLevelSuccess = true;
|
|
|
|
|
|
temp.IsH5Reward = false;
|
|
|
|
|
|
temp.boost_array = GameHelper.GetRewardBoost(2);
|
2026-06-17 14:45:28 +08:00
|
|
|
|
DOVirtual.DelayedCall(0.2f, () =>
|
2026-06-14 19:25:04 +08:00
|
|
|
|
{
|
|
|
|
|
|
UICtrlDispatcher.Instance.Dispatch(UICtrlMsg.LevelSuccessUI_Open, temp);
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-15 17:50:56 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 重新开始本局
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="a">true: 重生 false:重开一局或者下一局 </param>
|
2026-06-13 15:34:00 +08:00
|
|
|
|
private void OnRestartGame(object a = null)
|
|
|
|
|
|
{
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
|
|
|
|
|
Debug.Log($"[ArrowGameUI] 重新开始本局 a:{(bool)a}");
|
|
|
|
|
|
if (a != null && (bool)a)
|
2026-06-13 15:34:00 +08:00
|
|
|
|
{
|
|
|
|
|
|
_heartCount = 3;
|
|
|
|
|
|
for (int i = _heartCount; i > 0; i--)
|
|
|
|
|
|
{
|
|
|
|
|
|
ui.HeartsPanel.GetChild($"xin_{i}").visible = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 先清空当前所有动态物体
|
|
|
|
|
|
ClearAllDynamicObj();
|
2026-06-24 09:24:15 +08:00
|
|
|
|
|
|
|
|
|
|
//获取下一关的配置数据
|
|
|
|
|
|
LoadLevelConfig();
|
2026-06-13 15:34:00 +08:00
|
|
|
|
// 重新走初始化流程
|
|
|
|
|
|
InitGridBounds();
|
|
|
|
|
|
InitGridOccupied();
|
|
|
|
|
|
_heartCount = 3;
|
|
|
|
|
|
_collidedArrows.Clear();
|
|
|
|
|
|
_remainArrowCount = _levelConfig.arrows.Count;
|
|
|
|
|
|
DataMgr.ArrowResultLevel.Value = 1;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-13 15:34:00 +08:00
|
|
|
|
InitView();
|
|
|
|
|
|
}
|
2026-06-15 17:50:56 +08:00
|
|
|
|
#endregion
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
2026-06-13 15:34:00 +08:00
|
|
|
|
|
|
|
|
|
|
#region 配置解析 & 数据结构
|
2026-06-12 18:10:00 +08:00
|
|
|
|
public static class JsonHelper
|
|
|
|
|
|
{
|
|
|
|
|
|
public static LevelConfig LoadLevel(string fileName)
|
|
|
|
|
|
{
|
|
|
|
|
|
string path = Path.Combine(Application.streamingAssetsPath, fileName);
|
|
|
|
|
|
return JsonUtility.FromJson<LevelConfig>(File.ReadAllText(path));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[System.Serializable]
|
|
|
|
|
|
public class LevelConfig
|
|
|
|
|
|
{
|
|
|
|
|
|
public int levelId;
|
|
|
|
|
|
public string levelName;
|
|
|
|
|
|
public int gridRows;
|
|
|
|
|
|
public int gridCols;
|
2026-06-13 15:34:00 +08:00
|
|
|
|
public int pointSpacing;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
public List<ArrowConfig> arrows;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[System.Serializable]
|
|
|
|
|
|
public class ArrowConfig
|
|
|
|
|
|
{
|
|
|
|
|
|
public int id;
|
|
|
|
|
|
public int startPoint;
|
|
|
|
|
|
public List<string> path;
|
2026-06-24 09:24:15 +08:00
|
|
|
|
[NonSerialized] public int endPoint;
|
|
|
|
|
|
[NonSerialized] public string color;
|
|
|
|
|
|
|
|
|
|
|
|
[NonSerialized]public string defaultDir;
|
|
|
|
|
|
[NonSerialized]public List<Vector2> pathPointList;
|
|
|
|
|
|
[NonSerialized]public List<GComponent> lineUnits;
|
|
|
|
|
|
[NonSerialized]public GComponent arrowObj;
|
|
|
|
|
|
|
|
|
|
|
|
[NonSerialized]public bool isMoving;
|
|
|
|
|
|
[NonSerialized]public List<Vector2> originalLinePos;
|
|
|
|
|
|
[NonSerialized]public Vector2 originalArrowPos;
|
|
|
|
|
|
|
|
|
|
|
|
[NonSerialized]public List<Vector2> totalTrack;
|
|
|
|
|
|
[NonSerialized]public List<int> lineTrackIndex;
|
|
|
|
|
|
[NonSerialized]public int arrowTrackIndex;
|
|
|
|
|
|
[NonSerialized]public string finalMoveDir;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// ========== 距离驱动移动所需字段 ==========
|
2026-06-24 09:24:15 +08:00
|
|
|
|
[NonSerialized]public float movedDistance; // 当前整体累计移动距离
|
|
|
|
|
|
[NonSerialized]public float trackTotalLength; // 整条轨迹的总像素长度
|
|
|
|
|
|
[NonSerialized]public List<float> lineInitOffsets; // 每个线条单元的初始距离偏移
|
|
|
|
|
|
[NonSerialized]public float arrowInitOffset; // 箭头的初始距离偏移
|
2026-06-17 14:45:28 +08:00
|
|
|
|
// ========== 运行时颜色缓存 ==========
|
2026-06-24 09:24:15 +08:00
|
|
|
|
[NonSerialized]public Color runtimeColor;
|
2026-06-15 17:50:56 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
[System.Serializable]
|
|
|
|
|
|
public class AllLevelRoot
|
|
|
|
|
|
{
|
|
|
|
|
|
public List<LevelConfig> levels;
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2026-06-24 14:26:29 +08:00
|
|
|
|
|
|
|
|
|
|
public class SuccessData
|
|
|
|
|
|
{
|
|
|
|
|
|
public bool IsWin;
|
|
|
|
|
|
public float cash_number;
|
|
|
|
|
|
public bool IsLevelSuccess;
|
|
|
|
|
|
public bool IsH5Reward;
|
|
|
|
|
|
public int rate;
|
|
|
|
|
|
public int[] boost_array;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-06-12 18:10:00 +08:00
|
|
|
|
}
|