Files
ft/Client/Assets/Scripts/EventCubeMelt/BoardRenderer.cs
2026-06-29 21:18:33 +08:00

1413 lines
51 KiB
C#
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
// 渲染游戏板
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Threading.Tasks;
using asap.core;
using asap.core.common;
using DG.Tweening;
using EventBlocks.UI;
using EventCubeMelt.Data;
using EventCubeMelt.UI;
using EventCubeMelt.Utils;
using Game;
using UnityEngine;
using UnityEngine.Serialization;
using UnityEngine.UI;
using Debug = UnityEngine.Debug;
using Quaternion = UnityEngine.Quaternion;
using Vector2 = UnityEngine.Vector2;
using Vector3 = UnityEngine.Vector3;
namespace EventCubeMelt
{
/// <summary>
/// 新手引导「落子判定」在棋盘上的 Ghost 格(行/列与 <see cref="TetrisBoardData"/> 一致)。
/// </summary>
[Serializable]
public struct NewGuideGhostCheckCell
{
public int row;
public int col;
}
public class BoardRenderer : MonoBehaviour
{
#region
[Header("预制体")] [SerializeField] private GameObject _cellPrefab;
[SerializeField] private GameObject _ghostCellPrefab;
[SerializeField] private GameObject _emptyCellPrefab;
//-
[SerializeField] private RectTransform bgTransform;
[SerializeField] private float boardPadding = 35f;
// [SerializeField] private float cellSpacing = 0.1f;
[SerializeField] private Transform _boardContainer;
[SerializeField] private Transform _ghostContainer;
[SerializeField] private PieceRenderer[] cellPiece; // 当前的Piece
[SerializeField]
private float putBeforeDisappearDuration = 0.2f;
//- 消除特效
[SerializeField] private Transform fxUIEventCubemeltEliminatable;
// 整行消除特效
[SerializeField] private Transform fxUIEventCubeMeltDisappear;
[SerializeField] private Transform fxUIEventCubeMeltColDisappear;
[SerializeField] private Transform fxUIEventCubeMeltDisappear6;
[SerializeField] private Transform fxUIEventCubeMeltColDisappear6;
[SerializeField] private Transform fxUIEventCubeMeltDisappear7;
[SerializeField] private Transform fxUIEventCubeMeltColDisappear7;
[Header("三行和三列消除特效")]
[SerializeField] private Transform fxUIEventCubemeltDisappearColorful;
[SerializeField] private Transform fxUIEventCubemeltDisappearColorfulVertical;
[SerializeField] private Transform fxUIEventCubemeltDisappearColorful6;
[SerializeField] private Transform fxUIEventCubemeltDisappearColorfulVertical6;
[SerializeField] private Transform fxUIEventCubemeltDisappearColorful7;
[SerializeField] private Transform fxUIEventCubemeltDisappearColorfulVertical7;
[Header("跳字特效")]
[SerializeField] private Transform fxInfoPiercing;
// 飞行的对象Controller
[Header("飞行特效")]
[SerializeField]
private RewardFlyBatchController _rewardFlyBatchCtrl;
//
[SerializeField]
private BatchedRewardFly _rewardFly;
[Header("延时")]
[SerializeField]
private float bombStartDelay = 0.2f;
[SerializeField]
private float bombFlyDelay = 0.2f;
// [SerializeField] private Transform fxUIEventCubemeltBroke;
[Header("新手引导")]
[Tooltip("NewGuide1放手时需被 Ghost 覆盖的棋盘格;在 Inspector 中填写行/列,长度 0 则判定始终失败")]
[SerializeField]
private NewGuideGhostCheckCell[] _newGuide1GhostCheckCells =
{
new NewGuideGhostCheckCell { row = 0, col = 3 },
new NewGuideGhostCheckCell { row = 1, col = 3 },
new NewGuideGhostCheckCell { row = 1, col = 2 },
new NewGuideGhostCheckCell { row = 0, col = 2 },
};
#endregion
#region
private TetrisBoardData _boardData;
private CellRenderer[,] _cellRenderers;
private GameObject[,] _emptyCellObjs;
private Dictionary<CellType, Material> _materialCache = new();
// 特效Trans
private Transform effectTrans;
// 对象池
//
// private readonly Queue<CellRenderer> _cellPool = new();
// 当前活跃的Cell
private readonly List<CellRenderer> _activeCells = new();
// 特效对象池
private IObjectPoolService _objectPoolService;
private IObjectPool _lineEffectPool;
// 特效消除池
private IObjectPool _disappearEffectRowPool;
private IObjectPool _disappearEffectColPool;
// 当前渲染隐藏
private readonly List<CellRenderer> _ghostCells = new();
private EventCubeMeltManager _thisEventManager;
private EventCubeMeltData _thisEventData;
public float TargetCellSize { get; set; } = 100;
// private Vector2 _cellSize;
// private Vector2 _cellSpace;
private float _contentWidth;
private float _contentHeight;
private Vector2 _contentZero;
// private float TargetCellSpace { get; set; } = 3f;
#endregion
#region
private void Awake()
{
effectTrans = transform.Find("EffectPanel");
}
#endregion
#region
//
public void Initialize(int height, int width, List<int> boardData)
{
ELog($"Initialize -> {height} {width}");
_objectPoolService = GContext.container.Resolve<IObjectPoolService>();
_lineEffectPool = _objectPoolService.CreatePool(fxUIEventCubemeltEliminatable, 0, 32);
_disappearEffectRowPool = _objectPoolService.CreatePool(fxUIEventCubeMeltDisappear, 0, 32);
_disappearEffectColPool = _objectPoolService.CreatePool(fxUIEventCubeMeltColDisappear, 0, 32);
_thisEventManager = GContext.container.Resolve<EventCubeMeltManager>();
_thisEventData = _thisEventManager.EventData;
_boardData = new TetrisBoardData(height, width);
_boardData.LoadData(boardData);
CreateGrid();
UpdateBgSize(height, width);
}
private void UpdateBgSize(int height, int width)
{
ELog("UpdateBgSize -> ");
var layoutGroup = _boardContainer.GetComponent<GridLayoutGroup>();
var cellSize = layoutGroup.cellSize;
var spacing = layoutGroup.spacing;
// _cellSize = cellSize;
// _cellSpace = spacing;
// var topLeft = -(cellSize.x + spacing.x) * width * 0.5;
_contentWidth = cellSize.x * width + spacing.x * (width - 1);
_contentHeight = cellSize.y * height + spacing.y * (height - 1);
_contentZero = new Vector2(-_contentWidth * 0.5f, _contentHeight* 0.5f);
bgTransform.sizeDelta = new Vector2(width * (cellSize.x + spacing.x) + boardPadding * 2 , height * (cellSize.y + spacing.y) + boardPadding * 2);
}
private void CreateGrid()
{
ELog("CreateGrid -> ");
// 清空现有网格
foreach (Transform child in _boardContainer)
Destroy(child.gameObject);
foreach (Transform child in _ghostContainer)
Destroy(child.gameObject);
// 创建网格单元
_cellRenderers = new CellRenderer[_boardData.Height, _boardData.Width];
_emptyCellObjs = new GameObject[_boardData.Height, _boardData.Width];
for (var row = 0; row < _boardData.Height; row++)
{
for (var col = 0; col < _boardData.Width; col++)
{
var position = Vector3.zero;
CreateCell(row, col, position);
// 创建影子单元格
CreateEmptyCell(row, col, position);
}
}
}
private void CreateCell(int row, int col, Vector3 position)
{
// var parent = isGhost ? _ghostContainer : _boardContainer;
// var cellName = isGhost ? $"GhostCell_{row}_{col}" : $"Cell_{row}_{col}";
var parent = _boardContainer;
var cellName = $"Cell_{row}_{col}";
GameObject cellObj;
// if (_cellPool.Count > 0)
// {
// cellObj = _cellPool.Dequeue().gameObject;
// cellObj.name = cellName;
// cellObj.transform.position = position;
// cellObj.transform.parent = parent;
// cellObj.SetActive(true);
// }
// else
{
cellObj = Instantiate(_cellPrefab, position, Quaternion.identity, parent);
cellObj.name = cellName;
}
var cellRenderer = cellObj.GetComponent<CellRenderer>();
if (!cellRenderer)
cellRenderer = cellObj.AddComponent<CellRenderer>();
// 没什么用
// 初始化单元格大小
// cellRenderer.SetSize(_cellSize);
// if (isGhost)
// {
// _ghostRenderers[row, col] = cellRenderer;
// // cellRenderer.SetMaterial(_materialCache[CellType.Ghost]);
// }
// else
// {
// _cellRenderers[row, col] = cellRenderer;
// // cellRenderer.SetMaterial(_gridMaterial);
// // cellRenderer.SetColor(_gridColor);
// }
_cellRenderers[row, col] = cellRenderer;
cellRenderer.Row = row;
cellRenderer.Col = col;
// _activeCells.Add(cellRenderer);
}
private void CreateEmptyCell(int row, int col, Vector3 position)
{
var parent = _ghostContainer;
var cellName = $"Empty_{row}_{col}";
var cellObj = Instantiate(_emptyCellPrefab, position, Quaternion.identity, parent);
cellObj.name = cellName;
_emptyCellObjs[row, col] = cellObj;
}
#endregion
#region
public void UpdateBoard()
{
ELog("UpdateBoard-> ");
if (_boardData.ReSizeWithData(_thisEventData.BoardViewSize.y , _thisEventData.BoardViewSize.x , _thisEventData.BoardData))
{
RemakeBoard();
}
UpdateGridCells();
}
private void RemakeBoard()
{
ELog("ReMakeBoard-> ");
UpdateBgSize(_boardData.Height, _boardData.Width);
CreateGrid();
}
public void ClearBoard()
{
for (var row = 0; row < _boardData.Height; row++)
{
for (var col = 0; col < _boardData.Width; col++)
{
var cellRenderer = _cellRenderers[row, col];
cellRenderer.UpdateVisual(CellType.Empty);
}
}
}
//-
public void UpdateBoardWithAnim(Sequence sequence,float scaleDuration,float delayBetweenCells,out float delay)
{
ELog("UpdateBoardWithAnim");
UpdateBoard();
// _boardData.SetSize();
// _boardData.LoadData(_thisEventData?.BoardData);
// UpdateGridCells();
delay = 0;
var animationSequence = sequence;
var hasIndex = 0;
for (var row = 0; row < _boardData.Height; row++)
{
var has = false;
for (var col = 0; col < _boardData.Width; col++)
{
var cellType = _boardData[row, col];
var cell = _cellRenderers[row, col];
if (cellType != CellType.Empty)
{
cell.transform.localScale = Vector3.zero;
animationSequence.Insert(delay, cell.transform.DOScale(1.0f, scaleDuration)
.SetEase(Ease.OutBack)); // 使用弹性缓动效果,看起来更自然
has = true;
}
}
if (has)
{
hasIndex++;
delay = (hasIndex)* delayBetweenCells;
}
}
}
private void UpdateGridCells()
{
ELog("UpdateGridCells-> ");
// _activeCells.Clear();
for (var row = 0; row < _boardData.Height; row++)
{
for (var col = 0; col < _boardData.Width; col++)
{
var cellType = _boardData[row, col];
var cellRenderer = _cellRenderers[row, col];
cellRenderer.UpdateVisual(cellType,_boardData.GetGridValue(row,col-1),_boardData.GetGridValue(row +1,col - 1) ,_boardData.GetGridValue(row +1,col ));
}
}
}
//
[Header("清除特效时长")]
[SerializeField]
private float disappearTimeDuration = 1f;
private async Task DoGridClearAndAnims(List<int> rows,
List<int> cols,
List<(int row,int col,int value,int newRow,int newCol)> listBombs)
{
ELog("DoGridClearAndAnims-> ");
var list = new List<Task>();
var countClear = rows.Count + cols.Count;
foreach (var row in rows)
{
list.Add(DoRowDisappearEffect(row,countClear,listBombs));
}
//ClearCol
foreach (var col in cols)
{
list.Add(DoColDisappearEffect(col,countClear,listBombs));
}
if (rows.Count + cols.Count > 0)
{
GContext.Publish(new EventUISound("audio_ui_block_cube_clear"));
}
await Task.WhenAll(list);
RemoveDisappearEffect();
}
private async Task DoColDisappearEffect(int col,int count,List<(int row,int col,int value,int newRow,int newCol)> listBombs)
{
await Awaiters.NextFrame;
var list = new List<Task>();
list.Add(AddColDisappearEffect(col, count ));
var ls = listBombs.Select(item => (item.row, item.col)).ToList();
for (var i = 0; i< _boardData.Height; i++)
{
var cellRenderer = _cellRenderers[i,col];
// _ = cellRenderer.RunClearAnimation(_boardData.Height - i ,count >= 3);
if (!ls.Contains((i, col)))
{
// list.Add(cellRenderer.RunBombAnimation(_boardData.Height - i,true,2f));
// }
// else
// {
list.Add(cellRenderer.RunClearAnimation(_boardData.Height - i ,count >= 3));
}
}
await Task.WhenAll(list);
}
private async Task DoRowDisappearEffect(int row,int count,List<(int row,int col,int value,int newRow,int newCol)> listBombs)
{
await Awaiters.NextFrame;
var list = new List<Task>();
list.Add(AddRowDisappearEffect(row, count));
var ls = listBombs.Select(item => (item.row, item.col)).ToList();
for (var i = 0; i< _boardData.Width; i++)
{
var cellRenderer = _cellRenderers[row, i];
if (!ls.Contains((row, i)))
{
list.Add(cellRenderer.RunClearAnimation(i, count >= 3));
}
}
await Task.WhenAll(list);
}
private bool TryScreenToGrid(Vector2 screenPos, out Vector2Int grid)
{
var canvas = GetComponentInParent<Canvas>();
var boardRT = (RectTransform)_boardContainer;
var cam = canvas.worldCamera;
grid = default;
if (!RectTransformUtility.ScreenPointToLocalPointInRectangle(boardRT, screenPos, cam, out var local))
{
#if UNITY_EDITOR
if (visualCell)
visualCell.SetActive(false);
#endif
return false;
}
// RectTransformUtility.ScreenPointToLocalPointInRectangle(boardRT, new Vector2(0,0), cam,out var p2);
// RectTransformUtility.ScreenPointToLocalPointInRectangle(boardRT, new Vector2(100,100), cam,out var p3);
ELog($"TryScreenToGrid -> {screenPos} {local}");
// var rect = boardRT.rect;
// ELog($"rect -> {rect.width}x{rect.height}");
// /var topLeft = new Vector2(-rect.width * 0.5f, rect.height * 0.5f);
var topLeft = _contentZero;
// 将 local 改为以左上为原点、向右向下为正的坐标
var p = new Vector2(local.x - topLeft.x, topLeft.y - local.y);
#if UNITY_EDITOR
if (visualCell != null)
{
// var localFromP = new Vector2(topLeft.x + p.x, topLeft.y - p.y);
// visualCell.transform.localPosition = local;
// visualCell.SetActive(true);
}
#endif
// var px = rect.width / _boardData.Width;
// var py = rect.height / _boardData.Height;
// var cellSize = new Vector2(100,100);
var cellSize = new Vector2(_contentWidth / _boardData.Width, _contentHeight / _boardData.Height);
var px = p.x / cellSize.x ;
var py = p.y / cellSize.y;
var x = Mathf.FloorToInt(px);
var y = Mathf.FloorToInt(py);
// ELog($"TryScreenToGrid -> zero = {p.x } {p.y} : {px} {py} : {x} {y}");
grid = new Vector2Int(x, y);
// return x >= 0 && x < _boardData.Width && y >= 0 && y < _boardData.Height;
return true;
}
public void UpdateVisual(PieceRenderer pieceRenderer, Vector2 screenPos)
{
// ELog($"UpdateVisual -> {pieceRenderer}");
var bRet = CanGetNewGhostGrid(pieceRenderer);
// ELog($"UpdateVisual ->{bRet}");
if (!bRet)
{
return;
}
// 显示
RemoveLineEffect();
RemoveDisappearEffect();
if (IsCanPlaceDown(pieceRenderer))
{
foreach (var cell in _ghostCells)
{
cell.UpdateGhostVisual();
}
AddClearEffects();
}
ELog($"UpdateVisual ->End");
}
public void ClearGhostGrid(bool placed = false)
{
if (_ghostCells is not { Count: > 0 })
{
return;
}
foreach (var cell in _ghostCells)
{
cell.UpdateVisual(CellType.Empty);
}
_ghostCells.Clear();
_boardData.ClearGhostGrid();
}
public GameObject visualCell;
private bool CanGetNewGhostGrid(PieceRenderer pieceRenderer)
{
//
var zeroPos = pieceRenderer.GetPiecePosition();
if (!TryScreenToGrid(zeroPos, out var grid))
{
ClearGhostGrid();
RemoveLineEffect();
RemoveDisappearEffect();
SavePieceGridPos(Vector2Int.zero);
ELog("CanGetNewGhostGrid TryScreenToGrid Failed");
return false;
}
if (IsPieceGridNotChange(grid))
{
return false;
}
this.SetCurrentPos($"row:{grid.y },col:{grid.x}");
ELog($"CanGetNewGhostGrid-> {_lastGrid} => {grid} ");
SavePieceGridPos(grid);
ClearGhostGrid();
var tetrominoData = pieceRenderer.Data;
// 计算影子方块在网格中的位置
for (var row = 0; row < tetrominoData.Height; row++)
{
for (var col = 0; col < tetrominoData.Width; col++)
{
if (tetrominoData[row, col] != CellType.Empty)
{
var gridRow = grid.y + row;
var gridCol = grid.x + col;
if (gridCol >= 0 && gridCol < _boardData.Width && gridRow >= 0 &&
gridRow < _boardData.Height)
{
if (_boardData[gridRow, gridCol] == CellType.Empty)
{
_cellRenderers[gridRow, gridCol].CellGhostTypeValue = tetrominoData[row, col];
_ghostCells.Add(_cellRenderers[gridRow, gridCol]);
_boardData.SetGhostValue(gridRow, gridCol, tetrominoData[row, col]);
}
}
}
}
}
return true;
}
private void SavePieceGridPos(Vector2Int grid)
{
_lastGrid = grid;
}
private Vector2Int _lastGrid = Vector2Int.zero;
private bool IsPieceGridNotChange(Vector2Int grid)
{
return _lastGrid.Equals(grid);
}
// 检测是否显示可消除的行列
public void AddClearEffects()
{
var rows = _boardData.GetClearRows(withGhostCheck: true);
if (rows.Count > 0)
{
foreach (var row in rows)
{
AddRowEffect(row);
}
}
var cols = _boardData.GetClearCols(withGhostCheck: true);
if (cols.Count > 0)
{
foreach (var col in cols)
{
AddColEffect(col);
}
}
}
public void ClearGridAndCollectItems(int pieceId,out List<int> rows ,out List<int> cols,
out List<(int,int,int,int)> list,
out List<(int row,int col,int value,int newR,int newC)> listBomb)
{
rows = _boardData.GetClearRows();
cols = _boardData.GetClearCols();
// 清空和收集道具
ClearAddCollectItems(pieceId,rows, cols, out list,out listBomb);
if (ShouldBuildELog())
{
var valList = list.Select(item => $"({item.Item1} : {item.Item2}) => {item.Item3}").ToList();
if (valList is { Count: > 0 } || rows is { Count: > 0 } || cols is { Count: > 0 })
ELog(
$"LCM-> CheckLineClears -> rows : {string.Join(", ", rows)} cols {string.Join(", ", cols)} {string.Join(", ", valList)}");
}
}
private void ClearAddCollectItems(int pieceId, List<int> rows, List<int> cols,
out List<(int row ,int col ,int value ,int count)> list,
out List<(int row,int col,int value,int newR,int newC)> listBomb2)
{
_boardData.ClearRowsAndCols(rows, cols, out var myList );
list = DoCollectTransFilter(myList,out var listBomb);
var bombValues =_thisEventManager.GetBombValues(listBomb.Select(item => item.value).ToList());
listBomb2 = _boardData.UpdateBombValue(listBomb,bombValues);
var items = list
.GroupBy(x => x.Item3) // 按第三个值分组
.ToDictionary(
g => g.Key, // Key是第三个值
g => g.Sum(x => x.count) // 取每个组最后一个的第四个值
);
var itemCount = items.Sum(item => item.Value);
switch (itemCount)
{
case > 3:
GContext.Publish(new VibrationData(HapticTypes.Success));
break;
case > 0:
GContext.Publish(new VibrationData(HapticTypes.MediumImpact));
break;
default:
GContext.Publish(new VibrationData(HapticTypes.LightImpact));
break;
}
#if AGG
using (var e = GEvent.GameEvent("event_cubemelt"))
{
e.AddContent("piece_id",pieceId);
e.AddContent("clear_count", rows.Count + cols.Count);
e.AddContent("collection_count", itemCount);
e.AddContent("stage_id",_thisEventData.CurrentStagetId);
}
#else
ELog($"event_cubemelt -> {pieceId} {rows.Count + cols.Count} {itemCount} {_thisEventData.CurrentStagetId}");
#endif
_thisEventManager.AddCollect(items);
}
private string IconNameFromCellType(int cellType)
{
var items = _thisEventManager.GetCollectionItems();
if (items != null && items.TryGetValue(cellType, out var info))
return _thisEventManager.GetCollectIconById(info.TrueId);
return _thisEventManager.GetCollectIconByType(cellType);
}
//- 清理
// private async Task RunCollectItems(List<(int,int,int,int)> listItems)
// {
// ELog("RunCollectItems");
//
// if (listItems.Count <= 0)
// return;
//
// PlayFxInfoPiercing(listItems.Count);
// var tasks = new List<Task>();
// if (ShouldBuildELog())
// {
// var valList = listItems.Select(item => $"({item.Item1} : {item.Item2}) => {item.Item3} {item.Item4}").ToList();
// ELog($"LCM-> RunCollectItems ->newListItems {string.Join(", ", valList)}");
// }
// foreach (var item in listItems)
// {
// var cell = _cellRenderers[item.Item1, item.Item2];
// var req = new BatchedRewardFlyRequest
// {
// Icon = new BatchedRewardFlyRequestIcon { iconName = IconNameFromCellType(item.Item3) },
// StartPoint = new BatchedRewardFlyStartPoint((RectTransform)cell.transform),
// EndPoint = new BatchedRewardFlyEndPoint(GetComponentInParent<EventBlockPanel>().GetEndRectTransform(item.Item3)),
// IsDestinationRewardStash = false,
// AnimationParamIndex = 0,
// Quantity = item.Item4
// };
// tasks.Add(_rewardFlyBatchCtrl.OnRewardFlyRequestAsync(req));
// }
// await Task.WhenAll(tasks);
//
// var list = new HashSet<int>(listItems.Select(item => item.Item3)).ToList();
// var bRet = _thisEventManager.CheckCollectFinish(list);
// GContext.Publish(new EventUISound("audio_ui_cubemelt_cube_collect"));
// GContext.Publish(bRet ? new VibrationData(HapticTypes.Success) : new VibrationData(HapticTypes.SoftImpact));
// }
private List<(int row ,int col ,int value ,int count )> DoCollectTransFilter(
List<(int row , int col, int value )> listItems,
out List<(int row,int col,int value)> listBomb)
{
// var list = _thisEventManager.GetRepValue();
var collectionItems = _thisEventManager.GetCollectionItems();
var list = new List<(int, int, int )>();
listBomb = new List<(int row, int col, int value)>();
foreach (var (row, col, value) in listItems)
{
if (_thisEventManager.IsUnCollectValue(value))
{
listBomb.Add((row,col,value));
}
else
{
var newValues = _thisEventManager.ReplaceValue(value);
list.AddRange(newValues
.Where(nv => collectionItems.ContainsKey(nv)) // 过滤出被集合包含的值
.Select(nv => (row, col, nv)));
}
}
return list.GroupBy(t => t)
.Select(g => (g.Key.Item1, g.Key.Item2, g.Key.Item3, g.Count()))
.ToList();
// return list;
}
private bool IsCanPlaceDown(PieceRenderer pieceRenderer)
{
if (!pieceRenderer)
return false;
var num = _ghostCells?.Count;
return num == pieceRenderer.Data.UsableCount;
}
#region
// private Stopwatch sw;
// private string scopeName;
// private static Dictionary<string,long> s_accum = new Dictionary<string,long>();
// private ProfileScope ps = new ();
// public void L_ProfileScope(string sectionName)
// {
// scopeName = sectionName;
// sw = Stopwatch.StartNew();
// }
// public void L_ProfileScopeEnd()
// {
// sw.Stop();
// long el = sw.ElapsedMilliseconds;
// s_accum.TryGetValue(scopeName, out long old);
// s_accum[scopeName] = old + el;
// Log($"{scopeName} 本次 {el} ms 累计 {s_accum[scopeName]} ms");
// }
#endregion
public bool TryPlaceDown(PieceRenderer pieceRenderer)
{
//-
if (_thisEventData.IsFinished )
{
ClearGhostGrid(true);
RemoveLineEffect();
RemoveDisappearEffect();
return false;
}
if (_thisEventData.TicketCount <= 0)
{
// GContext.Publish(new EventCubeMeltShowNeoNormalPack());
ClearGhostGrid(true);
RemoveLineEffect();
RemoveDisappearEffect();
return false;
}
//
if (!IsCanPlaceDown(pieceRenderer))
return false;
#if UNITY_EDITOR
_thisEventManager.SaveDataForPrev();
#endif
// ps.L_ProfileScope("ClearGhostGrid");
// 移除Ghost
ClearGhostGrid(true);
// ps.L_ProfileScopeEnd();
// 删除特效
// ps.L_ProfileScope("RemoveLineEffect");
RemoveLineEffect();
RemoveDisappearEffect();
// ps.L_ProfileScopeEnd();
// ps.L_ProfileScope("UpdateCellData");
// 更新数值
UpdateCellData(pieceRenderer);
// 更新显示
UpdateGridCells();
//
// ps.L_ProfileScopeEnd();
return true;
}
public async Task OnGridAnimation(int pieceId, List<int> rows,
List<int> cols,
List<(int, int, int, int)> list,
List<(int row, int col, int value, int newRow, int newCol)> listBomb,
bool ignoreReqDelay)
{
// ELog($"OnGridAnimation-> {elapsedMilliseconds}");
// ps.L_ProfileScope("UpdateGridClearData");
UpdateGridClearData(pieceId);
// ps.L_ProfileScopeEnd();
// ps.L_ProfileScope("Awaiters");
if (!ignoreReqDelay)
{
ELog("OnGridAnimation awaitPutBeforeDisappear");
await Awaiters.Seconds(putBeforeDisappearDuration );
}
// ps.L_ProfileScopeEnd();
// ps.L_ProfileScope("RunGridClearAnimation");
await RunGridClearAnimation(rows,cols,list,listBomb);
// ps.L_ProfileScopeEnd();
// ps.L_ProfileScope("UpdateGridCells");
UpdateGridCells();
// ps.L_ProfileScopeEnd();
_thisEventManager.SaveDataForNow();
}
//更新格子数据
public void UpdateGridClearData(int pieceId
// ,out List<int> rows,out List<int> cols,
// out List<(int,int,int,int)> list,
// out List<(int row,int col,int value,int newRow,int newCol)> listBomb
)
{
// ClearGridAndCollectItems( pieceId,out rows,out cols, out list,out listBomb);
_thisEventManager.UpdateGridData(_boardData.GetBoardData());
if (_thisEventManager.IsThisCollectFinished())
{
// GetComponentInParent<CanvasGroup>().blocksRaycasts = false;
GContext.Publish(new EventCubeMeltStageFinish());
}
}
// 执行格子清理动画
private async Task RunGridClearAnimation(List<int> rows,List<int> cols,
List<(int,int,int,int)> list,
List<(int row,int col,int value,int newRow,int newCol)> listBomb)
{
if (rows is { Count: <= 0 } && cols is { Count: <= 0 } && list is { Count: <= 0 })
return;
RemoveDisappearEffect();
// 在EmptyCell 上添加 播放特效
_ = DoGridClearAndAnims(rows,cols,listBomb);
_ = RunCollectItemsAndBombs(list, listBomb);
_ = DoGridBombAnims(listBomb,bombStartDelay);
await RunBombAnims(listBomb,bombFlyDelay);
// //TODO:LCM
// if (true)
// {
// // await RunCollectItemsAndBombs(list, listBomb);
// }
// else
// {
// await RunCollectItems(list);
// }
//
// 全部完成之后,刷新数据
if(list is {Count: > 0})
GContext.Publish(new EventForUpdateCollect() );
}
private async Task RunBombAnims(List<(int row, int col, int value, int newRow, int newCol)> listBomb,float delay)
{
await Awaiters.Seconds(delay);
var tasks = new List<Task>();
foreach (var bomb in listBomb)
{
var cell = _cellRenderers[bomb.row, bomb.col];
if (bomb.newRow == -1 || bomb.newCol == -1)
continue;
var endCell = _cellRenderers[bomb.newRow, bomb.newCol];
var req = new BatchedRewardFlyRequest
{
Icon = new BatchedRewardFlyRequestIcon { iconName = IconNameFromCellType(bomb.value) },
StartPoint = new BatchedRewardFlyStartPoint((RectTransform)cell.transform),
EndPoint = new BatchedRewardFlyEndPoint(endCell.GetAniTransform()),
IsDestinationRewardStash = false,
AnimationParamIndex = 1,
};
tasks.Add(RunBombTask(req, endCell, bomb));
}
await Task.WhenAll(tasks);
async Task RunBombTask (BatchedRewardFlyRequest req, CellRenderer endCell,
(int row, int col, int value, int endRow, int endCol) bomb)
{
await _rewardFlyBatchCtrl.OnRewardFlyRequestAsync(req);
await endCell.RunBombAni(bomb.value);
}
}
private async Task DoGridBombAnims(List<(int row, int col, int value, int newRow, int newCol)> listBombs,float delayTime)
{
await Awaiters.Seconds(delayTime);
var list2 = new List<Task>();
foreach (var bomb in listBombs)
{
var cellRenderer = _cellRenderers[bomb.row ,bomb.col];
list2.Add(cellRenderer.RunBombAnimation(0));
}
await Task.WhenAll(list2);
}
private async Task RunCollectItemsAndBombs(
List<(int, int, int, int)> listItems,
List<(int row,int col,int value,int newRow ,int newCol)> listBomb)
{
ELog("RunCollectItemsAndBombs");
var iCount = listItems.Count + listBomb.Count;
if (iCount <= 0) return;
// if (listItems.Count <= 0)
// return;
RectTransform GetEndRectTransform(int cellTypeValue)
{
var rootPanel = GetComponentInParent<EventBlockPanel>();
var root2Panel = GetComponentInParent<EventCubeMeltPanel>();
if (rootPanel)
{
return rootPanel.GetEndRectTransform(cellTypeValue);
}
return root2Panel ? root2Panel.GetEndRectTransform(cellTypeValue) : null;
}
PlayFxInfoPiercing(iCount);
var tasks = new List<Task>();
var valList = listItems.Select(item => $"({item.Item1} : {item.Item2}) => {item.Item3} {item.Item4}").ToList();
ELog($"LCM-> RunCollectItems ->newListItems {string.Join(", ",valList)}" );
foreach (var item in listItems)
{
var cell = _cellRenderers[item.Item1, item.Item2];
var req = new BatchedRewardFlyRequest
{
Icon = new BatchedRewardFlyRequestIcon { iconName = IconNameFromCellType(item.Item3) },
StartPoint = new BatchedRewardFlyStartPoint((RectTransform)cell.transform),
EndPoint = new BatchedRewardFlyEndPoint(GetEndRectTransform(item.Item3)),
IsDestinationRewardStash = false,
AnimationParamIndex = 0,
Quantity = item.Item4
};
tasks.Add(_rewardFlyBatchCtrl.OnRewardFlyRequestAsync(req));
}
// // var listBombRepValues = GetBombEndPoint(listBomb);
// foreach (var bomb in listBomb)
// {
// var cell = _cellRenderers[bomb.row, bomb.col];
// var endCell = _cellRenderers[bomb.newRow, bomb.newCol];
// var req = new BatchedRewardFlyRequest
// {
// Icon = new BatchedRewardFlyRequestIcon { iconName = IconNameFromCellType(bomb.value) },
// StartPoint = new BatchedRewardFlyStartPoint((RectTransform)cell.transform),
// EndPoint = new BatchedRewardFlyEndPoint(endCell.GetAniTransform()),
// IsDestinationRewardStash = false,
// AnimationParamIndex = 0,
// };
// tasks.Add(RunBombTask(req, endCell, bomb));
// // tasks.Add(_rewardFlyBatchCtrl.OnRewardFlyRequestAsync(req));
// }
await Task.WhenAll(tasks);
var list = new HashSet<int>(listItems.Select(item => item.Item3)).ToList();
var bRet = _thisEventManager.CheckCollectFinish(list);
// var list2 = new HashSet<int>(listBomb.Select(item => ))
// var bRet2 = _thisEventManager.CheckBombFinsh(listBomb);
// GContext.Publish(new EventUISound("audio_ui_cubemelt_cube_collect"));
GContext.Publish(bRet ? new VibrationData(HapticTypes.Success) : new VibrationData(HapticTypes.SoftImpact));
}
private List<(int row, int col,int value,int endRow,int endCol)> GetBombEndPoint(List<(int row, int col, int value)> listBomb)
{
ELog("GetBombEndPoint");
var ls = new List<(int, int, int, int, int)>();
int m = _boardData.Height;
int n = _boardData.Width;
// 1. 先扫一遍,把所有 99 的坐标收集起来
var list = new List<(int, int)>();
for (int i = 0; i < m; i++)
{
for (int j = 0; j < n; j++)
{
if (_boardData[i, j] == CellType.Ct99)
list.Add((i, j));
}
}
// 2. 如果没有 99返回无效坐标
if (list.Count == 0)
return ls;
var finder = new UniqueRandom99(_boardData.GetGrid());
foreach (var bomb in listBomb)
{
for (var i = 0; i < 4; ++i)
{
var (r, c) = finder.Next();
if (r >= 0 && r < m && c >= 0 && c < n)
{
ls.Add(new ValueTuple<int, int, int, int, int>(bomb.row,bomb.col,bomb.value,r,c));
}
}
}
return ls;
// var randomValue = UnityEngine.Random.Range(0, list.Count);
// var (newRow,newCol) = list[randomValue];
// ls.Add(new ValueTuple<int, int, int, int, int>(1,2,3,newRow,newCol))
}
private async void PlayFxInfoPiercing(int iCount)
{
if(iCount < 1)
return;
const string baseStr = "piercing_0";
var str = iCount > 5 ? $"{baseStr}5" : $"{baseStr}{iCount -1}";
foreach (Transform child in fxInfoPiercing)
{
child.gameObject.SetActive(false);
}
var p = fxInfoPiercing.Find(str);
if (p)
{
p.gameObject?.SetActive(true);
await Awaiters.Seconds(1f);
p.gameObject.SetActive(false);
}
}
//- 上一步
public void OnPrevStep()
{
_thisEventManager.LoadPrevData();
GContext.Publish(new EventForRefreshCubeMeltPanel());
}
public void OnRestoreStep()
{
#if UNITY_EDITOR
_thisEventManager.RestoreData();
GContext.Publish(new EventForRefreshCubeMeltPanel());
#endif
}
private void UpdateCellData(PieceRenderer piece)
{
var ghostPosition = piece.GetPiecePosition();
TryScreenToGrid(ghostPosition, out var ghostPos);
_boardData.UpdateDataByPiece(piece.Data,ghostPos);
_thisEventManager.UpdateGridData(_boardData.GetBoardData());
}
#endregion
#region Log
private static bool ShouldBuildELog()
{
#if UNITY_EDITOR
return EventCubeMeltLogPolicy.EnableELog;
#else
return false;
#endif
}
private static void ELog(string message)
{
#if UNITY_EDITOR
if (!EventCubeMeltLogPolicy.EnableELog) return;
Debug.Log($"<color=orange>BoardRenderer => {message}</color>");
#endif
}
#endregion
#region
private readonly List<Transform> _disappearEffectList = new();
private async Task AddRowDisappearEffect(int row,int count)
{
if (_disappearEffectRowPool == null)
return;
var fx = GetSingleDisappearFx();
if (count >= 3)
{
fx = fxUIEventCubemeltDisappearColorful;
if (_boardData.Width == 6)
fx = fxUIEventCubemeltDisappearColorful6;
else if (_boardData.Height == 7)
fx = fxUIEventCubemeltDisappearColorful7;
}
var fxClone = Instantiate(fx.gameObject, effectTrans).transform;
if (fxClone)
{
_disappearEffectList.Add(fxClone);
fxClone.name = $"disappear_effect_row";
fxClone.localScale = Vector3.one;
//* (1.05F * _boardData.Height / 8F);
var cell = _cellRenderers[row, 0];
// 默认位置 x = 0;
var position = new Vector3(0, cell.transform.position.y, cell.transform.position.z);
var localPosition = new Vector3(0, cell.transform.localPosition.y, cell.transform.localPosition.z);
fxClone.transform.localPosition = localPosition;
if (ShouldBuildELog())
ELog($"AddRowDisappearEffect -> {cell.transform.position} {position} {fxClone.transform.localPosition}");
await Awaiters.Seconds(1F);
}
}
private Transform GetSingleDisappearFx()
{
var fx = fxUIEventCubeMeltDisappear;
if (_boardData.Width == 6)
fx = fxUIEventCubeMeltDisappear6;
else if(_boardData.Width == 7)
fx = fxUIEventCubeMeltDisappear7;
return fx;
}
private async Task AddColDisappearEffect(int col,int count)
{
if (_disappearEffectColPool == null)
return;
var fx = fxUIEventCubeMeltColDisappear;
if (_boardData.Width == 6)
fx = fxUIEventCubeMeltColDisappear6;
else if(_boardData.Width == 7)
fx = fxUIEventCubeMeltColDisappear7;
if (count >= 3)
{
fx = fxUIEventCubemeltDisappearColorfulVertical;
if (_boardData.Width == 6)
fx = fxUIEventCubemeltDisappearColorfulVertical6;
else if(_boardData.Width == 7)
fx = fxUIEventCubemeltDisappearColorfulVertical7;
}
var fxClone = Instantiate(fx.gameObject, effectTrans).transform;
if (fxClone)
{
_disappearEffectList.Add(fxClone);
// fxClone.SetParent( transform);
fxClone.name = $"disappear_effect_col";
fxClone.localScale = Vector3.one;
//* (1.05F * _boardData.Height / 8F);
// 1.1f;
var cell = _cellRenderers[0,col];
var position = new Vector3(cell.transform.position.x, 0, cell.transform.position.z);
var localPosition = new Vector3(cell.transform.localPosition.x, 0, cell.transform.localPosition.z);
fxClone.transform.localPosition = localPosition;
if (ShouldBuildELog())
ELog($"AddColDisappearEffect -> {cell.transform.position} {position} {fxClone.transform.localPosition}");
await Awaiters.Seconds(1F);
}
}
public async void RemoveDisappearEffect()
{
ELog("RemoveDisappearEffect-> ");
await Awaiters.NextFrame;
foreach (var trans in _disappearEffectList)
{
Destroy(trans.gameObject);
}
_disappearEffectList.Clear();
}
private async void AddRowEffect(int row)
{
if (_lineEffectPool == null)
return;
await Awaiters.NextFrame;
// var fxClone = _lineEffectPool.SpawnObject() as Transform;
var fx = fxUIEventCubemeltEliminatable;
var fxClone = Instantiate(fx.gameObject,effectTrans).transform;
if (fxClone)
{
_lineEffectList.Add(fxClone);
fxClone.name = $"effect_row";
// fxClone.localScale = Vector3.one * 1.1f * (_boardData.Width / 8F);
fxClone.localScale = new Vector3(1.12f * (_boardData.Height / 8F),1.1f,1 );
var cell = _cellRenderers[row, 0];
// 默认位置 x = 0;
var position = new Vector3(0, cell.transform.position.y, cell.transform.position.z);
// var localPosition = new Vector3(0, cell.transform.localPosition.y, cell.transform.localPosition.z);
// fxClone.transform.localPosition = localPosition;
if (ShouldBuildELog())
ELog($"AddRowEffect -> {cell.transform.position} {position} {fxClone.transform.localPosition}");
fxClone.transform.position = position;
var pr = fxClone.transform.Find("1/fx_mat_ui_event_cubemelt_eliminatable_01")
.GetComponent<ParticleSystem>();
var main = pr.main;
main.startRotation = 0;
}
}
// 可以加个内存池子
private async void AddColEffect(int col)
{
if (_lineEffectPool == null)
return;
await Awaiters.NextFrame;
var fx = fxUIEventCubemeltEliminatable;
var fxClone = Instantiate(fx.gameObject, effectTrans).transform;
if (fxClone)
{
_lineEffectList.Add(fxClone);
// fxClone.SetParent(transform);
fxClone.name = "effect_col";
fxClone.localScale = new Vector3(1.2f,1.12f * (_boardData.Height / 8F),1f );
var cell = _cellRenderers[0, col];
// 默认位置 x = 0;
var position = new Vector3(cell.transform.position.x, 0, cell.transform.position.z);
// fxClone.transform.position = position;
// fxClone.transform.localRotation = Quaternion.Euler(0, 0, 90);
var localPosition = new Vector3(cell.transform.localPosition.x, 0, cell.transform.localPosition.z);
fxClone.transform.localPosition = localPosition;
// fxClone.transform.position = position;
if (ShouldBuildELog())
ELog($"AddColEffect -> {cell.transform.position} {position} {fxClone.transform.localPosition}");
var pr = fxClone.transform.Find("1/fx_mat_ui_event_cubemelt_eliminatable_01")
.GetComponent<ParticleSystem>();
var main = pr.main;
main.startRotation = Mathf.Deg2Rad * 90f;
// 第二组
var pr2 = fxClone.transform.Find("1/fx_mat_ui_event_cubemelt_eliminatable_02")?.GetComponent<ParticleSystem>();
if (pr2?.main != null)
{
var main2 = pr2.main;
main2.startRotation = Mathf.Deg2Rad * 90f;
}
}
}
private readonly List<Transform> _lineEffectList = new();
public async void RemoveLineEffect()
{
ELog("RemoveLineEffect-> ");
await Awaiters.NextFrame;
foreach (var trans in _lineEffectList)
{
Destroy(trans.gameObject);
}
_lineEffectList.Clear();
}
#endregion
public bool SatisfyNewGuide1(PieceRenderer dragPiece)
{
ELog($"SatisfyNewGuide1 -> GetPiecePosition -> {dragPiece.GetPiecePosition()}");
if (_newGuide1GhostCheckCells == null || _newGuide1GhostCheckCells.Length == 0)
{
ELog("SatisfyNewGuide1: _newGuide1GhostCheckCells 未配置");
return false;
}
foreach (var cell in _newGuide1GhostCheckCells)
{
var v = _boardData.GetGhostValue(cell.row, cell.col);
if (v == CellType.Empty || v == CellType.Illegal)
return false;
}
return true;
}
#if UNITY_EDITOR
private string _currentPosStr = string.Empty;
private bool isShow = false;
//
private void OnGUI()
{
if (!isShow)
{
return;
}
int fontSize = 40;
GUI.skin.label.fontSize = fontSize;
GUILayout.Space(220);
GUILayout.Label($" CurrentPosStr: {_currentPosStr}");
}
#endif
private void Update()
{
#if UNITY_EDITOR
if (Input.GetKeyDown(KeyCode.K))
{
isShow = !isShow;
}
#endif
}
public void SetCurrentPos(string posStr)
{
#if UNITY_EDITOR
_currentPosStr = posStr;
#endif
}
}
}