969 lines
31 KiB
TypeScript
969 lines
31 KiB
TypeScript
/**
|
||
* 负责单个滚轮的创建、滚动、停止等逻辑
|
||
*/
|
||
import {
|
||
_decorator,
|
||
Node,
|
||
UITransform,
|
||
Vec2,
|
||
v2,
|
||
Vec3,
|
||
v3,
|
||
tween,
|
||
Tween,
|
||
} from 'cc';
|
||
import { IconFactory } from './IconFactory';
|
||
import { Icon } from './Icon';
|
||
import { BaseRoller, ROLLER_STATE } from './BaseRoller';
|
||
import { ICON_MAP, ROLLER_COMBINE_EVENT, ROLLER_EVENT } from './Define';
|
||
import { AudioManager } from 'db://assets/Loading/scripts/manager/AudioManager';
|
||
|
||
let { ccclass, property, executeInEditMode } = _decorator;
|
||
|
||
|
||
/**
|
||
* 滚轮组件类
|
||
*/
|
||
@ccclass('Roller')
|
||
@executeInEditMode
|
||
export class Roller extends BaseRoller {
|
||
// 编辑器属性
|
||
@property({ tooltip: '行数' })
|
||
row: number = 3;
|
||
|
||
// 本地格式化相关
|
||
@property
|
||
private _format = false;
|
||
@property({ tooltip: '本地格式化' })
|
||
get format(): boolean {
|
||
return this._format;
|
||
}
|
||
set format(b: boolean) {
|
||
this._format = b;
|
||
this.resizeContentSize();
|
||
|
||
if (!this.iconFactory) {
|
||
console.error('IconFactory没有设置');
|
||
return;
|
||
}
|
||
|
||
// 重新创建图标
|
||
this._content.removeAllChildren();
|
||
this._info.icons = [];
|
||
this._allIcons.clear();
|
||
this._posToIconKey.clear();
|
||
// 清除位置缓存 - 添加这一行
|
||
this._positionCache.clear();
|
||
|
||
for (let i = 0; i < this.row; i++) {
|
||
let randomIndex = Math.floor(Math.random() * this.iconFactory.getIconNum());
|
||
this.createNormalIcon(i, randomIndex);
|
||
}
|
||
}
|
||
|
||
_cachedContentHeight: number = 0;
|
||
_cachedAnchorY: number = 0;
|
||
|
||
/**
|
||
* 创建滚轮实例
|
||
* @param id 滚轮ID
|
||
* @param row 行数
|
||
* @param iconWidth 图标宽度
|
||
* @param iconHeight 图标高度
|
||
* @param iconFactory 图标工厂实例
|
||
* @param anchor 锚点位置
|
||
* @returns 新创建的滚轮实例
|
||
*/
|
||
static create(
|
||
id: number,
|
||
row: number,
|
||
iconWidth: number,
|
||
iconHeight: number,
|
||
iconFactory: IconFactory,
|
||
anchor: Vec2 = v2(0.5, 0.5)
|
||
): Roller {
|
||
let rollerNode = new Node(`Roller${id}`);
|
||
rollerNode.addComponent(UITransform);
|
||
|
||
let roller = rollerNode.addComponent(Roller);
|
||
roller._rollerId = id;
|
||
roller.row = row;
|
||
roller.iconWidth = iconWidth;
|
||
roller.iconHeight = iconHeight;
|
||
roller.iconFactory = iconFactory;
|
||
|
||
rollerNode.getComponent(UITransform).setAnchorPoint(anchor);
|
||
roller.resizeContentSize();
|
||
roller.initRoller(id);
|
||
|
||
return roller;
|
||
}
|
||
|
||
/**
|
||
* 设置节点尺寸和位置
|
||
*/
|
||
setupNodesSizeAndPosition() {
|
||
let totalHeight = this.iconHeight * this.row;
|
||
let anchorPoint = this.node.getComponent(UITransform).anchorPoint;
|
||
|
||
// 设置主节点尺寸
|
||
this.node.getComponent(UITransform).setContentSize(this.iconWidth, totalHeight);
|
||
|
||
// 设置view节点
|
||
this._view.setPosition(0, 0);
|
||
this._view.getComponent(UITransform).setContentSize(this.iconWidth, totalHeight);
|
||
this._view.getComponent(UITransform).setAnchorPoint(anchorPoint);
|
||
this._view.getComponent(UITransform).width *= 2;
|
||
|
||
// 设置content节点
|
||
this._content.setPosition(0, 0);
|
||
this._content.getComponent(UITransform).setContentSize(this.iconWidth, totalHeight);
|
||
this._content.getComponent(UITransform).setAnchorPoint(anchorPoint);
|
||
this._content.removeAllChildren();
|
||
}
|
||
|
||
/**
|
||
* 获取图标的实际坐标
|
||
* @param pos 图标位置索引
|
||
* @param size 图标大小
|
||
* @returns 图标的世界坐标
|
||
*/
|
||
getIconPosition(pos: number, size: number = 1): Vec3 {
|
||
// 创建缓存键
|
||
let cacheKey = size > 1 ? (pos + 1) * 1000 + size : pos;
|
||
|
||
if (this._positionCache.has(cacheKey)) {
|
||
return this._positionCache.get(cacheKey).clone();
|
||
}
|
||
|
||
// 确保缓存数据已初始化
|
||
if (!this._cachedContentHeight) {
|
||
this._cachedContentHeight = this._content.getComponent(UITransform).height;
|
||
this._cachedAnchorY = this._content.getComponent(UITransform).anchorY;
|
||
}
|
||
|
||
// 计算基准位置
|
||
let contentHeight = this._cachedContentHeight;
|
||
let anchorY = this._cachedAnchorY;
|
||
|
||
// 计算顶部位置
|
||
let topY = contentHeight * (1 - anchorY);
|
||
|
||
// 计算第一个位置的中心Y坐标
|
||
let firstCenterY = topY - this.iconHeight / 2;
|
||
|
||
// 计算当前位置的中心Y坐标
|
||
let centerY = firstCenterY - pos * this.iconHeight;
|
||
|
||
// 对于大图标,需要调整位置
|
||
let finalY = centerY;
|
||
if (size > 1) {
|
||
// 大图标的中心点应该下移,使其顶部对齐格子
|
||
// 对于高度为3的图标,中心点应该下移1个格子高度
|
||
finalY = centerY - (size - 1) * this.iconHeight / 2;
|
||
}
|
||
|
||
|
||
// 创建最终位置
|
||
let position = v3(0, finalY, 0);
|
||
|
||
// 缓存结果
|
||
this._positionCache.set(cacheKey, position.clone());
|
||
return position;
|
||
}
|
||
|
||
/**
|
||
* 初始化性能优化相关的缓存
|
||
*/
|
||
initCache() {
|
||
this._cachedUITransform = this._content.getComponent(UITransform);
|
||
this._cachedContentHeight = this._cachedUITransform.height;
|
||
this._cachedAnchorY = this._cachedUITransform.anchorY;
|
||
}
|
||
|
||
/**
|
||
* 收集现有图标
|
||
*/
|
||
collectExistingIcons() {
|
||
for (let i = 0; i < this.row; i++) {
|
||
let icon = this._allIcons.get(this._posToIconKey.get(i));
|
||
if (icon) {
|
||
this._allIcons.delete(this._posToIconKey.get(i));
|
||
this._posToIconKey.delete(i);
|
||
this._info.icons.push(icon);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 手动停止滚动
|
||
* @param data 停止时的图标数据
|
||
*/
|
||
async manualStopScroll(data: number[]) {
|
||
if (this._info.isManualStop ||
|
||
this._info.state === ROLLER_STATE.STOP) {
|
||
return;
|
||
}
|
||
|
||
|
||
this._info.resetLxInfo();
|
||
this._stopData = data;
|
||
this._info.isManualStop = true;
|
||
Tween.stopAllByTarget(this._info.speedNode);
|
||
|
||
// 直接回收所有动态图标
|
||
while (this._info.icons.length > 0) {
|
||
let icon = this._info.icons.pop();
|
||
this.iconFactory.recycleIcon(icon);
|
||
}
|
||
|
||
// 回收固定位置图标
|
||
for (let i = 0; i < this.row; i++) {
|
||
let icon = this._allIcons.get(this._posToIconKey.get(i));
|
||
if (icon) {
|
||
this._allIcons.delete(this._posToIconKey.get(i));
|
||
this._posToIconKey.delete(i);
|
||
this.iconFactory.recycleIcon(icon);
|
||
}
|
||
}
|
||
this._allIcons.clear();
|
||
this._posToIconKey.clear();
|
||
|
||
this.createInitIcons(data);
|
||
// 为所有创建的图标播放动画
|
||
for (let icon of this._allIcons.values()) {
|
||
icon.getComponent(Icon).playSpawnAni();
|
||
}
|
||
this.changeState(ROLLER_STATE.STOP);
|
||
}
|
||
|
||
/**
|
||
* 创建最后一页图标
|
||
*/
|
||
createLastPage() {
|
||
let data = this._stopData;
|
||
if (!data) return;
|
||
|
||
// 计算顶部基准位置
|
||
let topY = this.getIconPosition(0).y;
|
||
let icons = this._info.icons;
|
||
if (icons.length > 0) {
|
||
topY = Math.max(topY, this.findHighestIconXorY(icons));
|
||
}
|
||
|
||
// 清除已有的位置映射,准备重新创建
|
||
this._allIcons.clear();
|
||
this._posToIconKey.clear();
|
||
|
||
|
||
// 从上到下依次创建图标(从位置0开始)
|
||
for (let i = 0; i < data.length; i++) {
|
||
// 检查当前位置是否需要创建特殊图标(n*1图标)
|
||
if (this._CroSymbols && this._CroSymbols[i]) {
|
||
let iconSpecialMsg = this._CroSymbols[i];
|
||
// 检查这个特殊图标是否已经被处理过
|
||
let isProcessed = false;
|
||
for (let j = 0; j < i; j++) {
|
||
if (this._CroSymbols[j] && this._CroSymbols[j].id === iconSpecialMsg.id) {
|
||
isProcessed = true;
|
||
break;
|
||
}
|
||
}
|
||
|
||
// 如果这个特殊图标还没有被处理,创建它
|
||
if (!isProcessed) {
|
||
let startPos = iconSpecialMsg.startPos;
|
||
let endPos = iconSpecialMsg.endPos;
|
||
let height = iconSpecialMsg.lHeight;
|
||
let iconIndex = iconSpecialMsg.iconIndex;
|
||
let frameType = iconSpecialMsg.Type;
|
||
|
||
// 生成图标ID
|
||
let iconKey = this.generateIconKey(startPos, height, endPos);
|
||
|
||
// 创建图标节点
|
||
let icon = this.iconFactory.icfactoryCreateIcon(iconIndex);
|
||
icon.getComponent(Icon).initIcon(iconIndex, height, iconKey, frameType, this._rollerId);
|
||
|
||
// 计算位置
|
||
// 计算该位置普通图标应该在的Y坐标
|
||
let normalIconY = topY + (this.row - startPos) * this.iconHeight;
|
||
// 对于大图标,需要考虑其锚点在中心,向下偏移(height-1)/2个格子高度
|
||
let offsetY = (height - 1) * this.iconHeight / 2;
|
||
// 最终位置
|
||
let y = normalIconY - offsetY;
|
||
|
||
icon.setPosition(0, y, 0);
|
||
this._content.addChild(icon);
|
||
|
||
// 存储图标节点
|
||
this._allIcons.set(iconKey, icon);
|
||
// 设置位置映射
|
||
for (let i = 0; i < height; i++) {
|
||
this._posToIconKey.set(startPos + i, iconKey);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 如果当前位置已被特殊图标占用,跳过
|
||
if (this._posToIconKey.has(i)) continue;
|
||
|
||
// 生成图标ID
|
||
let pos = i;
|
||
let iconIndex = data[i];
|
||
let iconKey = this.generateIconKey(pos, 1, pos);
|
||
|
||
// 创建图标节点
|
||
let icon = this.iconFactory.icfactoryCreateIcon(iconIndex);
|
||
icon.getComponent(Icon).initIcon(iconIndex, 1, iconKey, 0, this._rollerId);
|
||
|
||
// 计算位置
|
||
let y = topY + (this.row - pos) * this.iconHeight;
|
||
icon.setPosition(0, y, 0);
|
||
this._content.addChild(icon);
|
||
|
||
// 存储图标节点
|
||
this._allIcons.set(iconKey, icon);
|
||
this._posToIconKey.set(pos, iconKey);
|
||
|
||
}
|
||
this.node.emit(ROLLER_EVENT.LAST_PAGE_CREATE, this._rollerId);
|
||
}
|
||
|
||
|
||
/**
|
||
* 检查图标是否超出显示范围
|
||
*/
|
||
checkDeadLine(icon: Node): boolean {
|
||
if (!this._cachedContentHeight) {
|
||
this._cachedContentHeight = this._content.getComponent(UITransform).height;
|
||
this._cachedAnchorY = this._content.getComponent(UITransform).anchorY;
|
||
}
|
||
|
||
let iconComponent = icon.getComponent(Icon);
|
||
let lheight = iconComponent.lHeight;
|
||
let iconBody = (lheight - 1) * this.iconHeight + this.iconHeight / 2;
|
||
let deadLine = -(this._cachedContentHeight * this._cachedAnchorY) - iconBody;
|
||
|
||
return icon.position.y <= deadLine;
|
||
}
|
||
|
||
|
||
/**
|
||
* 补充新的图标
|
||
*/
|
||
suppleIcon() {
|
||
let bornLine = this.getIconPosition(0, 1).y;
|
||
let icons = this._info.icons;
|
||
|
||
// 找到最高的图标Y坐标
|
||
let topY = this.findHighestIconXorY(icons);
|
||
|
||
// 如果最高的图标低于出生线,创建新图标
|
||
if (topY < bornLine) {
|
||
this.createRandomIcon();
|
||
}
|
||
}
|
||
|
||
|
||
/**
|
||
* 创建随机图标
|
||
*/
|
||
createRandomIcon() {
|
||
let iconIndex = this.getRandomIconIndex();
|
||
let newY = this.computeNewIconXorY();
|
||
|
||
let icon = this.iconFactory.icfactoryCreateIcon(iconIndex);
|
||
// 设置快速图标效果
|
||
if (this.shouldShowFastIcon()) {
|
||
icon.getComponent(Icon).showFastIcon(true);
|
||
}
|
||
icon.setPosition(0, newY, 0);
|
||
this._content.addChild(icon);
|
||
|
||
this._info.icons.push(icon);
|
||
this.node.emit(ROLLER_EVENT.ON_R_ICON_CREATE, this._rollerId, icon);
|
||
}
|
||
|
||
/**
|
||
* 找到最高的图标Y坐标
|
||
* @param icons 图标数组
|
||
* @returns 最高的Y坐标
|
||
*/
|
||
findHighestIconXorY(icons: Node[]): number {
|
||
return icons.reduce((maxY, icon) => {
|
||
if (!icon || !icon.active) return maxY;
|
||
|
||
let iconComponent = icon.getComponent(Icon);
|
||
if (!iconComponent) return maxY;
|
||
|
||
let lHeight = iconComponent.lHeight || 1;
|
||
let iconY = icon.position.y;
|
||
|
||
// 对于高度大于1的图标,考虑其顶部位置
|
||
if (lHeight > 1) {
|
||
// 计算图标顶部位置:当前位置 + 高度差 * 图标高度 / 2
|
||
return Math.max(maxY, iconY + (lHeight - 1) * this.iconHeight / 2);
|
||
} else {
|
||
return Math.max(maxY, iconY);
|
||
}
|
||
}, -999);
|
||
}
|
||
|
||
/**
|
||
* 计算新图标的Y坐标
|
||
*/
|
||
computeNewIconXorY(): number {
|
||
let icons = this._info.icons;
|
||
|
||
// 如果没有图标,使用初始位置
|
||
if (!icons.length) {
|
||
return this.getIconPosition(0).y + this.iconHeight;
|
||
}
|
||
|
||
// 找到最高的图标Y坐标
|
||
let highestY = this.findHighestIconXorY(icons);
|
||
|
||
// 新图标位置 = 最高图标位置 + 图标高度
|
||
return highestY + this.iconHeight;
|
||
}
|
||
|
||
/**
|
||
* 清理资源
|
||
*/
|
||
onDestroy() {
|
||
this._cachedUITransform = null;
|
||
this._cachedContentHeight = 0;
|
||
this._cachedAnchorY = 0;
|
||
this._positionCache.clear();
|
||
}
|
||
|
||
/**
|
||
* 滚轮移动
|
||
* @param dt 时间增量
|
||
*/
|
||
rollerMove(dt: number) {
|
||
// 计算移动向量
|
||
let speed = this._info.speed;
|
||
let move = speed * dt;
|
||
let moveVec = v3(0, move, 0);
|
||
|
||
|
||
|
||
// 更新动态图标位置
|
||
this._info.icons.forEach(icon => {
|
||
if (!icon || !icon.isValid) return;
|
||
|
||
if (icon.active) {
|
||
let newPosition = icon.position.clone().subtract(moveVec);
|
||
icon.setPosition(newPosition);
|
||
}
|
||
});
|
||
// 获取所有图标
|
||
let allIcons = Array.from(this._allIcons.values());
|
||
allIcons.forEach(icon => {
|
||
if (!icon || !icon.isValid) return;
|
||
let newPosition = icon.position.clone().subtract(moveVec);
|
||
icon.setPosition(newPosition);
|
||
});
|
||
}
|
||
|
||
/**
|
||
* 回收滚轮图标
|
||
*/
|
||
recycleRollerIcon() {
|
||
// 回收动态图标
|
||
let icons = this._info.icons;
|
||
for (let i = icons.length - 1; i >= 0; i--) {
|
||
let icon = icons[i];
|
||
if (!icon || !icon.isValid) {
|
||
icons.splice(i, 1);
|
||
continue;
|
||
}
|
||
|
||
if (this.checkDeadLine(icon)) {
|
||
// 从数组中移除
|
||
icons.splice(i, 1);
|
||
// 回收图标
|
||
this.iconFactory.recycleIcon(icon);
|
||
}
|
||
}
|
||
|
||
// 回收固定位置图标(非最后一页创建状态)
|
||
if (this._info.state !== ROLLER_STATE.LAST_PAGE_CREATE) {
|
||
for (let i = 0; i < this.row; i++) {
|
||
let iconKey = this._posToIconKey.get(i);
|
||
if (!iconKey) continue;
|
||
|
||
let icon = this._allIcons.get(iconKey);
|
||
if (!icon || !icon.isValid) {
|
||
this._posToIconKey.delete(i);
|
||
this._allIcons.delete(iconKey);
|
||
continue;
|
||
}
|
||
|
||
if (this.checkDeadLine(icon)) {
|
||
// 从映射中移除
|
||
this._allIcons.delete(iconKey);
|
||
this._posToIconKey.delete(i);
|
||
// 回收图标
|
||
this.iconFactory.recycleIcon(icon);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 处理特殊图标(n*1图标)
|
||
if (this._info.state !== ROLLER_STATE.LAST_PAGE_CREATE) {
|
||
// 获取所有特殊图标的key
|
||
let specialIconKeys = Array.from(this._allIcons.keys())
|
||
.filter(key => key.startsWith('large_'));
|
||
|
||
for (let iconKey of specialIconKeys) {
|
||
let icon = this._allIcons.get(iconKey);
|
||
if (!icon || !icon.isValid) {
|
||
this._allIcons.delete(iconKey);
|
||
continue;
|
||
}
|
||
|
||
if (this.checkDeadLine(icon)) {
|
||
// 从映射中移除
|
||
this._allIcons.delete(iconKey);
|
||
|
||
// 移除所有关联的位置映射
|
||
for (let [pos, key] of this._posToIconKey.entries()) {
|
||
if (key === iconKey) {
|
||
this._posToIconKey.delete(pos);
|
||
}
|
||
}
|
||
|
||
// 回收图标
|
||
this.iconFactory.recycleIcon(icon);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 停止处理
|
||
*/
|
||
stopProcess() {
|
||
let upIcon = this.getIconPosition(0);
|
||
let stopline = upIcon.y - 100; // 偏移量100
|
||
let topY = this.findHighestIconXorY(Array.from(this._allIcons.values()));
|
||
|
||
if (topY <= stopline) {
|
||
this.changeState(ROLLER_STATE.BOUNCE);
|
||
this.playBounceAnimation();
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 播放回弹动画
|
||
*/
|
||
playBounceAnimation() {
|
||
let time = 0.1;
|
||
let offset = 50;
|
||
|
||
// 获取所有图标
|
||
let allIcons = Array.from(this._allIcons.values());
|
||
|
||
allIcons.forEach(icon => {
|
||
|
||
if (!icon || !icon.isValid) return;
|
||
|
||
let iconComponent = icon.getComponent(Icon);
|
||
if (!iconComponent) return;
|
||
|
||
// 获取图标的起始位置和高度
|
||
let startPos = iconComponent.startPos;
|
||
let lHeight = iconComponent.lHeight || 1;
|
||
|
||
// 获取图标应该在的位置
|
||
let position = this.getIconPosition(startPos, lHeight);
|
||
|
||
// 设置初始位置(向下偏移)
|
||
// icon.setPosition(position.add(v3(0, -offset, 0)));
|
||
icon.setPosition(position);
|
||
iconComponent.playSpawnAni();
|
||
|
||
// // 创建回弹动画
|
||
// tween(icon)
|
||
// .by(time, { position: v3(0, offset, 0) })
|
||
// .start();
|
||
});
|
||
|
||
// 延迟切换到停止状态
|
||
this.scheduleOnce(() => {
|
||
this.changeState(ROLLER_STATE.STOP);
|
||
}, time);
|
||
}
|
||
|
||
|
||
// 添加一个集合来追踪已删除的位置
|
||
private _deletedPositions = new Set<number>();
|
||
|
||
/**
|
||
* 消除逻辑
|
||
* 一定是在静止状态消除的
|
||
* @param deleteMsg 删除信息
|
||
* */
|
||
deleteIconNode(positions: number[]) {
|
||
// 记录被处理过的图标键
|
||
let processedPos = new Set<number>();
|
||
|
||
// 处理每个位置
|
||
for (let pos of positions) {
|
||
// 如果此图标已处理过,跳过
|
||
if (processedPos.has(pos)) {
|
||
continue;
|
||
}
|
||
|
||
let iconKey = this._posToIconKey.get(pos);
|
||
// if (!iconKey) {
|
||
// console.error('deleteIconNode iconKey is null', pos);
|
||
// continue;
|
||
// }
|
||
|
||
let iconNode = this._allIcons.get(iconKey);
|
||
// if (!iconNode || !iconNode.isValid) {
|
||
// console.error('deleteIconNode iconNode is null', pos);
|
||
// continue;
|
||
// }
|
||
|
||
let iconComponent = iconNode.getComponent(Icon);
|
||
// if (!iconComponent) {
|
||
// console.error('deleteIconNode iconComponent is null', pos);
|
||
// continue;
|
||
// }
|
||
|
||
|
||
let startPos = iconComponent.startPos;
|
||
// 从allIcons中删除图标
|
||
this._allIcons.delete(iconKey);
|
||
|
||
let height = iconComponent.lHeight || 1;
|
||
// 移除所有关联的位置映射
|
||
if (height > 1) {
|
||
// 对于n*1图标,需要清除所有占用的位置
|
||
for (let i = 0; i < height; i++) {
|
||
this._posToIconKey.delete(startPos + i);
|
||
// 标记此图标已处理
|
||
processedPos.add(startPos + i);
|
||
}
|
||
} else {
|
||
this._posToIconKey.delete(pos);
|
||
// 标记此图标已处理
|
||
processedPos.add(pos);
|
||
}
|
||
|
||
iconComponent.playWinAni(true);
|
||
iconComponent.playDeleteAni();
|
||
this.scheduleOnce(() => {
|
||
this.iconFactory.recycleIcon(iconNode);
|
||
}, 1.2)
|
||
}
|
||
|
||
this.scheduleOnce(() => {
|
||
this.node.emit(ROLLER_EVENT.ICON_DELETED, this._rollerId);
|
||
}, 1.2)
|
||
|
||
}
|
||
|
||
// 播放动画
|
||
playFrameTypeChangeAni(positions: number[]) {
|
||
// 记录被处理过的图标键
|
||
let processedPos = new Set<number>();
|
||
|
||
for (let pos of positions) {
|
||
// 如果此图标已处理过,跳过
|
||
if (processedPos.has(pos)) {
|
||
continue;
|
||
}
|
||
|
||
let iconKey = this._posToIconKey.get(pos);
|
||
if (!iconKey) continue;
|
||
|
||
let iconNode = this._allIcons.get(iconKey);
|
||
if (!iconNode || !iconNode.isValid) continue;
|
||
|
||
let iconComponent = iconNode.getComponent(Icon);
|
||
if (!iconComponent) continue;
|
||
|
||
let startPos = iconComponent.startPos;
|
||
|
||
let height = iconComponent.lHeight || 1;
|
||
if (height > 1) {
|
||
for (let i = 0; i < height; i++) {
|
||
processedPos.add(startPos + i);
|
||
}
|
||
} else {
|
||
processedPos.add(startPos);
|
||
}
|
||
iconComponent.playWinAni(true);
|
||
iconComponent.playChangeAni(true);
|
||
}
|
||
}
|
||
|
||
chanegeIconAndFrameType(data: any[]) {
|
||
for (let i = 0; i < data.length; i++) {
|
||
let oldStartPos = data[i].oldStartPos;
|
||
let newIndex = data[i].newIndex;
|
||
let newFrameType = data[i].newFrameType;
|
||
let lheight = data[i].lheight || 1;
|
||
|
||
// 获取该位置对应的iconKey
|
||
const iconKey = this._posToIconKey.get(oldStartPos);
|
||
if (!iconKey) {
|
||
console.error('changeIconAndFrameType: iconKey is null for position', oldStartPos);
|
||
return;
|
||
}
|
||
|
||
// 获取对应的图标节点
|
||
const iconNode = this._allIcons.get(iconKey);
|
||
if (!iconNode || !iconNode.isValid) {
|
||
console.error('changeIconAndFrameType: iconNode is null or invalid for position', oldStartPos);
|
||
return;
|
||
}
|
||
|
||
// 获取Icon组件
|
||
const iconComponent = iconNode.getComponent(Icon);
|
||
if (!iconComponent) {
|
||
console.error('changeIconAndFrameType: iconComponent is null for position', oldStartPos);
|
||
return;
|
||
}
|
||
|
||
|
||
iconComponent.playChangeAni(false);
|
||
// 1. 从数据结构中删除旧图标
|
||
this._allIcons.delete(iconKey);
|
||
// 回收旧图标节点
|
||
this.iconFactory.recycleIcon(iconNode);
|
||
|
||
// 创建新图标
|
||
// 创建新的图标节点
|
||
const newIcon = this.iconFactory.icfactoryCreateIcon(newIndex);
|
||
// 初始化新的Icon组件
|
||
newIcon.getComponent(Icon).initIcon(newIndex, lheight, iconKey, newFrameType, this._rollerId);
|
||
// 设置新图标的位置
|
||
const newPos = this.getIconPosition(oldStartPos, lheight);
|
||
newIcon.setPosition(newPos);
|
||
// 添加到场景
|
||
this._content.addChild(newIcon);
|
||
|
||
// 3. 更新Roller的数据结构
|
||
// 将新图标添加到_allIcons中
|
||
this._allIcons.set(iconKey, newIcon);
|
||
|
||
}
|
||
|
||
}
|
||
|
||
|
||
/**
|
||
* 创建新icon
|
||
* @param createMsg 信息
|
||
*/
|
||
createNewIconTop(createDatas: number[][], CroSymbols: any) {
|
||
// 获取所有图标
|
||
let topY = this.getIconPosition(0, 1);
|
||
|
||
//总共掉落了多少格
|
||
let fallDownNum = 0
|
||
//已经计算的格数
|
||
let nowDownNum = 0
|
||
//692029aba23ce34d091e5c51
|
||
//[[12],[6,6,6],[12]]
|
||
//[[12],[6,6],[12]]
|
||
//[[12],[6,6,6,6]]
|
||
//[[12],[6,6],[8,8]]
|
||
for (let i = 0; i < createDatas.length; i++) {
|
||
fallDownNum += createDatas[i].length
|
||
}
|
||
|
||
|
||
for (let i = 0; i < createDatas.length; i++) {
|
||
let iconHeight = createDatas[i].length
|
||
let startPos = nowDownNum - fallDownNum;
|
||
let iconIndex = createDatas[i][0];
|
||
let iconKey = this.generateIconKey(startPos, iconHeight, startPos - iconHeight + 1);
|
||
let icon = this.iconFactory.icfactoryCreateIcon(iconIndex);
|
||
|
||
let iconFramType = 0
|
||
if (CroSymbols) {
|
||
for (let key in CroSymbols) {
|
||
if (CroSymbols[key].PosFirst / 5 == this._rollerId) {
|
||
if (CroSymbols[key].PosFirst % 5 == i) {
|
||
iconFramType = CroSymbols[key].Type
|
||
}
|
||
}
|
||
}
|
||
|
||
}
|
||
icon.getComponent(Icon).initIcon(iconIndex, iconHeight, iconKey, iconFramType, this._rollerId);
|
||
|
||
let y = topY.y + (((fallDownNum - nowDownNum) - 0.5 * (iconHeight - iconHeight > 1 ? 1 : 0)) * this.iconHeight);
|
||
icon.setPosition(0, y, 0); this._content.addChild(icon);
|
||
this._allIcons.set(iconKey, icon);
|
||
this._posToIconKey.set(startPos, iconKey);
|
||
nowDownNum += iconHeight
|
||
|
||
}
|
||
this.node.emit(ROLLER_EVENT.ICON_CREATE, this._rollerId);
|
||
}
|
||
|
||
|
||
/** icon进行掉落移动 */
|
||
iconFallDown(data: number[], CroSymbols: any) {
|
||
let updates = [];
|
||
let sortNewIconStartPos = this.getNewIconsStartPos(data, CroSymbols);
|
||
let sortIcons = this.getSortIcons();
|
||
for (let i = 0; i < sortNewIconStartPos.length; i++) {
|
||
let newStartPos = sortNewIconStartPos[i];
|
||
let oldIcon = sortIcons[i];
|
||
let oldIconStartPos = oldIcon.startPos;
|
||
|
||
|
||
if (oldIconStartPos === newStartPos) {
|
||
continue;
|
||
}
|
||
let oldIconNode = oldIcon.icon;
|
||
let oldIconComponent = oldIcon.component;
|
||
let oldIconkey = oldIconComponent.iconKey;
|
||
let lHeight = oldIconComponent.lHeight || 1;
|
||
let newIconkey = this.generateIconKey(newStartPos, 1, newStartPos);
|
||
let newY = this.getIconPosition(newStartPos, lHeight).y;
|
||
let oldY = oldIconNode.position.y;
|
||
|
||
updates.push({
|
||
node: oldIconNode,
|
||
component: oldIconComponent,
|
||
oldKey: oldIconkey,
|
||
oldStartPos: oldIconStartPos,
|
||
newStartPos: newStartPos,
|
||
newKey: newIconkey,
|
||
height: lHeight,
|
||
newY: newY,
|
||
})
|
||
|
||
|
||
}
|
||
|
||
let delayBetweenIcons = 0.05; // 每个图标之间的掉落延迟
|
||
// 添加依次掉落效果
|
||
for (let update of updates) {
|
||
|
||
let fallDelay = update.index * delayBetweenIcons; // 根据索引计算延迟
|
||
let fallTime = 0.3; // 掉落动画时间
|
||
|
||
// 清除可能存在的动画
|
||
tween(update.node).stop();
|
||
|
||
// 添加延迟和掉落动画
|
||
tween(update.node)
|
||
.delay(fallDelay) // 添加延迟
|
||
.to(fallTime, { position: v3(0, update.newY, 0) }, {
|
||
easing: 'quadIn' // 下落加速
|
||
})
|
||
|
||
.start();
|
||
}
|
||
|
||
for (let update of updates) {
|
||
this._allIcons.delete(update.oldKey);
|
||
for (let i = 0; i < update.height; i++) {
|
||
this._posToIconKey.delete(update.oldStartPos + i);
|
||
}
|
||
}
|
||
|
||
// 再添加所有新映射
|
||
for (let update of updates) {
|
||
// 添加新的映射
|
||
update.component.iconKey = update.newKey;
|
||
this._allIcons.set(update.newKey, update.node);
|
||
for (let i = 0; i < update.height; i++) {
|
||
this._posToIconKey.set(update.newStartPos + i, update.newKey);
|
||
}
|
||
}
|
||
|
||
// 根据最长的动画时间调整音效和事件触发时间
|
||
let totalFallTime = updates.length > 0 ?
|
||
(updates.length - 1) * delayBetweenIcons + 0.3 : 0.3;
|
||
|
||
|
||
|
||
// 在所有图标都完成掉落后触发事件
|
||
this.scheduleOnce(() => {
|
||
this.node.emit(ROLLER_EVENT.ICON_FALLEN, this._rollerId);
|
||
}, totalFallTime + 0.1); // 添加一点额外时间确保所有动画完成
|
||
}
|
||
|
||
getSortIcons() {
|
||
let iconInfos: { icon: Node, component: Icon, startPos: number }[] = [];
|
||
|
||
// 收集所有图标信息
|
||
for (let [iconKey, iconNode] of this._allIcons.entries()) {
|
||
if (!iconNode || !iconNode.isValid) continue;
|
||
|
||
let iconComponent = iconNode.getComponent(Icon);
|
||
if (!iconComponent) continue;
|
||
|
||
// 记录图标信息
|
||
iconInfos.push({
|
||
icon: iconNode,
|
||
component: iconComponent,
|
||
startPos: iconComponent.startPos
|
||
});
|
||
}
|
||
|
||
// 按startPos从小到大排序
|
||
iconInfos.sort((a, b) => a.startPos - b.startPos);
|
||
|
||
return iconInfos;
|
||
}
|
||
|
||
|
||
getNewIconsStartPos(data: number[], CroSymbols: any) {
|
||
// 存储所有图标的startPos
|
||
let startPositions: number[] = [];
|
||
|
||
// 记录已处理的位置
|
||
let processedPositions = new Set<number>();
|
||
// 记录已经处理的pos
|
||
let processedPos = new Set<number>();
|
||
// 首先处理不规则图标(n*1图标)
|
||
if (CroSymbols) {
|
||
for (let pos in CroSymbols) {
|
||
let id = CroSymbols[pos].id;
|
||
if (processedPos.has(id)) continue;
|
||
processedPos.add(id);
|
||
let iconSpecialMsg = CroSymbols[pos];
|
||
let startPos = iconSpecialMsg.startPos;
|
||
let endPos = iconSpecialMsg.endPos;
|
||
|
||
// 添加不规则图标的startPos
|
||
startPositions.push(startPos);
|
||
|
||
// 标记所有被占用的位置
|
||
for (let i = startPos; i <= endPos; i++) {
|
||
processedPositions.add(i);
|
||
}
|
||
}
|
||
}
|
||
|
||
// 然后处理常规图标
|
||
for (let i = 0; i < data.length; i++) {
|
||
// 如果该位置未被处理(不是不规则图标的一部分)
|
||
if (!processedPositions.has(i)) {
|
||
startPositions.push(i);
|
||
}
|
||
}
|
||
|
||
// 从小到大排序
|
||
startPositions.sort((a, b) => a - b);
|
||
return startPositions;
|
||
}
|
||
|
||
|
||
|
||
}
|