import { _decorator, Node, tween, Vec3, Sprite, Asset, ImageAsset, SpriteFrame, Texture2D, Label, UIOpacity, Animation, math, VideoPlayer, UITransform, VideoClip, Color, Button } from 'cc'; import BaseView from '../../../../../../extensions/app/assets/base/BaseView'; import { PageTips } from '../../tips/native/PageTips'; import { app } from 'db://assets/app/app'; import { Tools } from 'db://assets/res-native/tools/Tools'; import { PageRewardhistory } from '../../rewardhistory/native/PageRewardhistory'; import { USERDATA } from 'db://assets/res-native/data/UserData'; import { LANG_ENUM, LANG_SET, LangType } from 'db://assets/res-native/setting/LangSet'; const { ccclass, property } = _decorator; /** 移动方向 */ const enum M_DIR { CLOCKWISE = 0, /** 顺时针 */ COUNTER_CLOCKWISE = 1, /** 逆时针 */ } const AUTO_TIMES : any[] = [ "∞", "500", "200", "10", ] @ccclass('PageMain') export class PageMain extends BaseView { /** 声音按钮 */ @property(Node) btn_music: Node = null!; /** 当前余额 */ @property(Label) lab_gold: Label = null!; /** 姓名 */ @property(Label) lab_name: Label = null!; /** 中奖提示 */ @property(Label) lab_earn: Label = null!; /** 倍率显示 */ @property(Label) lab_ante: Label = null!; /** 方向选择 */ @property(Node) n_dir: Node[] = []; /** 标签 */ @property(Node) sign: Node = null!; /** 方块 */ @property(Node) blocks: Node[] = []; /** 骰子 */ @property(Node) dices: Node[] = []; /** 中奖 */ @property(Node) rw: Node = null!; /** 未中奖 */ @property(Node) norw: Node = null!; /** 开始游戏按钮 */ @property(Node) btn_play: Node = null!; @property(Node) btn_auto: Node = null!; /** 胜利数字 */ @property(Node) lab_add: Node = null!; /** 失败数字 */ @property(Node) lab_sub: Node = null!; /** 分数标签 */ @property(Node) lab_point: Node = null!; /** 金币雨 */ @property(Node) gold_rain: Node = null!; /** 背景视频 */ @property(VideoPlayer) video_bg: VideoPlayer = null!; /** 视频背景图片 */ @property(Sprite) video_sprite: Sprite = null!; /** 骰子动画 */ @property(Node) ani_dice: Node = null!; /** 骰子总点数 */ @property(Node) dice_all: Node = null!; /** 倍数减按钮 */ @property(Node) btn_ante_sub: Node = null!; @property(Node) btn_ante_add: Node = null!; /** 失败视频资源 */ @property(VideoClip) video_win: VideoClip = null!; @property(VideoClip) video_lose: VideoClip = null!; @property(VideoClip) video_normal: VideoClip = null!; @property(Node) item_times: Node = null!; @property(Node) list_times: Node = null!; @property(Node) pnl_times: Node = null!; /** 方块移动方向 */ private _move_dir: M_DIR = M_DIR.CLOCKWISE; /** 是否正在游戏 */ private _is_playing = false; /** 骰子数据 */ private _game_data: any = null; /** 是否正在连续 */ private _is_continuous : boolean = false /** 棋盘数据 */ private _board_data: any[] = [] /** 消耗倍数索引 */ private _ante_index : number = 0 /** 倍数数据 */ private _ante_data : any[] = null /** 剩余次数 */ private _now_rest_times : number = 0 /** 标签位置 */ private _sign_position: Vec3[] =[ new Vec3(11, 491.7, 0), new Vec3(178.2, 407, 0), new Vec3(323.4, 310.2, 0), new Vec3(456.5, 227.7, 0), new Vec3(341, 143, 0), new Vec3(233.2, 63.8, 0), new Vec3(121, 0, 0), new Vec3(231, -77, 0), new Vec3(341, -143, 0), new Vec3(467.5, -214.5, 0), new Vec3(341, -321.2, 0), new Vec3(231, -401.5, 0), new Vec3(116.6, -473, 0), new Vec3(0, -550, 0), new Vec3(-117.7, -484, 0), new Vec3(-234.3, -407, 0), new Vec3(-344.3, -344.3, 0), new Vec3(-465.3, -242, 0), new Vec3(-336.6, -159.5, 0), new Vec3(-226.6, -92.4, 0), new Vec3(-115.5, -18.7, 0), new Vec3(-227.7, 48.4, 0), new Vec3(-341, 121, 0), new Vec3(-451, 201.3, 0), new Vec3(-301.4, 298.1, 0), new Vec3(-159.5, 402.6, 0), ] private _video:HTMLVideoElement = null private _canvas:HTMLCanvasElement = null private _ctx:CanvasRenderingContext2D = null // 在类中新增变量用于缓存旧资源 private _currentSpriteFrame: SpriteFrame | null = null; private _isUpdating = false; // 初始化的相关逻辑写在这 onLoad() {} // 界面打开时的相关逻辑写在这(onShow可被多次调用-它与onHide不成对) onShow(params: any) { this.initUserData() this.initDir() this.onClickTips() this.bindVideoEvents() this.getConfig() this.initAutoTimes() this._canvas = document.createElement('canvas'); this._canvas.width = this.video_sprite.getComponent(UITransform).width; this._canvas.height = this.video_sprite.getComponent(UITransform).height; this._canvas.style.display = 'none'; this._ctx = this._canvas.getContext('2d'); tween(this.ani_dice) .repeatForever( tween() // 0.8秒内:放大到1.2倍 + 顺时针旋转180度 .to(0.8, { scale: new Vec3(1.2, 1.2, 1.2), angle: 180 // 如果是2D节点用angle,3D节点建议用 eulerAngles }, { easing: 'sineOut' }) // 0.8秒内:缩小回1倍 + 再旋转180度(总共360度) .to(0.8, { scale: new Vec3(1, 1, 1), angle: 360 }, { easing: 'sineIn' }) // 完成一圈后,手动重置 angle 为 0,防止数值无限叠加导致精度问题 .call(() => { this.ani_dice.angle = 0; }) ) .start() } // 界面关闭时的相关逻辑写在这(已经关闭的界面不会触发onHide) onHide(result: undefined) { // app.manager.ui.show({name: 'PageMain', onHide:(result) => { 接收到return的数据,并且有类型提示 }}) return result; } initUserData(){ this.lab_gold.string = USERDATA.coin.toString() this.lab_name.string = USERDATA.name } bindVideoEvents() { if (this.video_bg) { this.video_bg.node.on('ready-to-play', () => { this._video = this.video_bg.nativeVideo }, this); } } /** 初始化方向选择 */ initDir(){ for (let i = 0; i < 2; i++) { const element = this.n_dir[i]; Tools.ActChild(element, "light", i == this._move_dir) Tools.ActChild(element, "dark", i != this._move_dir) } } getConfig(){ Tools.httpReq("game/anteConfig", {}, (res:any) => { this._ante_data = res // 默认倍数 for (let index = 0; index < res.length; index++) { const element = res[index]; if (element.is_default == 1){ this._ante_index = index break } } this.initAnteSet() Tools.httpReq("game/lotteryPool", {}, (res:any) => { this._board_data = res this.initBoradData() }) }) } initAutoTimes(){ for (let i = 0; i < AUTO_TIMES.length; i++) { const str = AUTO_TIMES[i] let item = Tools.AddChild(this.list_times, this.item_times) item.active = true Tools.SetChildText(item, "lab", str) Tools.SetTouchEndEvt(item, () => { if (str == "∞"){ this._now_rest_times = -1 } else{ this._now_rest_times = parseInt(str) } this._is_continuous = true this.hideSelectTimes() Tools.ActChild(this.btn_auto, "lab", false) this.btnGameStart() }) } } hideSelectTimes(){ this.pnl_times.active = false } switchAuto(){ if (this._is_continuous){ this._is_continuous = false this._now_rest_times = 0 Tools.ActChild(this.btn_auto, "lab", true) Tools.SetChildText(this.btn_auto, "times", "") }else { if (this._is_playing) return this.pnl_times.active = true } } initAnteSet(){ let d = this._ante_data[this._ante_index] this.lab_ante.string = d.name this.btn_ante_sub.getComponent(Sprite).grayscale = this._ante_index == 0 this.btn_ante_add.getComponent(Sprite).grayscale = this._ante_index == this._ante_data.length - 1 } btnAnteAdd(){ if (this._is_playing) return if (this._ante_index < this._ante_data.length - 1) { this._ante_index++ this.initAnteSet() this.initBoradData() } } btnAnteSub(){ if (this._is_playing) return if (this._ante_index > 0) { this._ante_index-- this.initAnteSet() this.initBoradData() } } initBoradData(){ for (let index = 0; index < this._board_data.length; index++) { const d = this._board_data[index]; let num = parseFloat(d.ui_text) ? parseFloat(d.ui_text) * this._ante_data[this._ante_index].mult : d.ui_text Tools.SetChildText(this.blocks[index], "num", num) Tools.SetChildText(this.blocks[index], "light/num", num) Tools.SetChildText(this.blocks[index], "point", d.grid_number) Tools.SetChildText(this.blocks[index], "light/point", d.grid_number) } } chooseDir(d:any, dir: number){ this._move_dir = dir this.initDir() } /** 点击提示 */ onClickTips(){ app.manager.ui.show({name: 'PageTips'}) } /** 点击音乐 */ oncClickMusic(){ let music = app.manager.sound.isMusicPlaying music ? app.manager.sound.stopMusic() : app.manager.sound.playDefaultMusic() this.loadRes(music ? "main_btn_sounds_off" : "main_btn_sounds_on", Asset, (res: ImageAsset)=>{ let sp = new SpriteFrame() let tex = new Texture2D(); tex.image = res; sp.texture = tex this.btn_music.getComponent(Sprite).spriteFrame = sp }); } /** 点击历史 */ onClickRwHis(){ app.manager.ui.show({name: 'PageRewardhistory'}) } /** 开始游戏 */ btnGameStart(){ if (this._is_playing) return this._is_playing = true this.lab_earn.string = "" this.btn_ante_sub.getComponent(Sprite).grayscale = true this.btn_ante_add.getComponent(Sprite).grayscale = true this.btn_play.getComponent(Sprite).grayscale = true if (this._is_continuous){ this._now_rest_times = this._now_rest_times == -1 ? -1 : this._now_rest_times - 1 Tools.SetChildText(this.btn_auto, "times", this._now_rest_times == -1 ? "∞" : this._now_rest_times.toString()) } Tools.httpReq("game/playStart", { "direction" : this._move_dir, "ante" : this._ante_data[this._ante_index].mult }, (res:any) => { this._game_data = res this._move_dir = res.direction USERDATA.coin -= res.use_coin this.initUserData() let steps = 0 for (let i = 0; i < res.roll_array.length; i++) { const element = res.roll_array[i]; steps += element } let index:any = this._board_data.findIndex(v => v.grid_number == steps.toString()) let b = this.blocks[index] // 计算移动路径 let path = this.calculatePath(index, steps); // 播放骰子动画 this.playDiceAni(res.roll_array) // 判断是否是豹子 let baozi = this._game_data.is_win == 1 Tools.ActChild(this.rw, "bg/bg_title_super", baozi) Tools.ActChild(this.rw, "bg/bg_title", !baozi) this.gold_rain.active = baozi // 如果是豹子,展示豹子特效后停止继续 if (baozi){ this.scheduleOnce(() => { this.resetStopGame() app.manager.sound.playEffect({name : "effect/eff_unsheathe"}) for (let i = 0; i < this.dices.length; i++) { let eff = this.dices[i].getChildByPath("dice/eff") eff.active = true eff.setPosition(new Vec3(-25, -40, 0)) tween(eff) .to(0.5, { position: new Vec3(25, 45, 0) }) .call(() => { this.scheduleOnce(() => { eff.active = false }, 0.2) }) .start() } this.scheduleOnce(() => { Tools.ActChild(this.rw, "bg/win_lab/en", LANG_SET.langIndex == LangType.EN) Tools.ActChild(this.rw, "bg/win_lab/zh", LANG_SET.langIndex == LangType.ZH) Tools.SetChildText(this.rw, "bg/win_lab/" + LANG_ENUM[LANG_SET.langIndex] + "/num", this._game_data.win_coin) this.rw.active = true app.manager.sound.playEffect({name : "effect/eff_gold_rain"}) app.manager.sound.playEffect({name : "effect/eff_bigwin"}) USERDATA.coin = this._game_data.coin USERDATA.total_ticket_count = this._game_data.total_ticket_count this._is_playing = false this.initUserData() }, 1) }, 1.2) }else { // 骰子总数展示 this.scheduleOnce(() => { this.dice_all.active = true Tools.SetChildText(this.dice_all, "dice/lab", steps.toString()) }, 1.2) this.scheduleOnce(() => { this.dice_all.active = false }, 2.4) //亮灯 this.scheduleOnce(() => { Tools.ActChild(b, "light", true) // 设置初始位置 this.sign.setPosition(this._sign_position[index]); this.sign.active = true; }, 2.4); let lastIndex = index if (this._move_dir == M_DIR.CLOCKWISE) { lastIndex = (lastIndex + steps) % this._sign_position.length; } else { lastIndex = (lastIndex - steps + this._sign_position.length) % this._sign_position.length; } // 移动标签 this.scheduleOnce(() => { // 执行动画 this.animateSign(path, steps * 0.12, lastIndex); Tools.ActChild(b, "light", false) }, 3); } },()=>{ this._is_playing = false this.resetStopGame() }) } /** * 计算移动路径 * @param index 当前位置索引 * @param steps 移动步数 */ private calculatePath(index:number, steps: number): Vec3[] { let path: Vec3[] = []; let currentIndex = index; for (let i = 0; i < steps; i++) { if (this._move_dir == M_DIR.CLOCKWISE) { currentIndex = (currentIndex + 1) % this._sign_position.length; } else { currentIndex = (currentIndex - 1 + this._sign_position.length) % this._sign_position.length; } path.push(this._sign_position[currentIndex]); } return path; } /** * 执行动画 * @param path 移动路径 * @param duration 总时间(秒) * @param lastIndex 最后一个位置索引 */ private animateSign(path: Vec3[], duration: number, lastIndex: number = 0) { let totalTime = duration; let segmentTime = totalTime / path.length; let moveNext = (index: number) => { let targetPos = path[index]; app.manager.sound.playEffect({name : "effect/eff_jump", volume: 0.5}) /** 跟随闪烁 */ this.scheduleOnce(() => { let n = this._sign_position.findIndex(v => v == targetPos) let light = this.blocks[n].getChildByName("light") light.active = true let opc = light.getComponent(UIOpacity) opc.opacity = 0 tween(opc) .to(0.12, { opacity: 255 }) // 渐隐 .to(0.12 / 2, { opacity: 0 }) // 渐显 tween(opc) .call(() => { light.active = false opc.opacity = 255 }) .start(); }, 0.06 / 2); tween(this.sign) .to(segmentTime, { position: targetPos }, { easing: "linear" }) .call(() => { if (index + 1 >= path.length) { this.scheduleOnce(() => { this.sign.active = false; let b = this.blocks[lastIndex] if (this._game_data.win_coin >= 0){ app.manager.sound.playEffect({name : "effect/eff_start"}) }else{ app.manager.sound.playEffect({name : "effect/eff_finish", volume: 0.5}) } this.blinkLightWithTween(this.blocks[lastIndex].getChildByName("light"), 1, 2) }, 0.5); return; } this.scheduleOnce(() => { moveNext(index + 1); }, 0.06); }) .start(); }; moveNext(0); } /** 闪烁动画 * @param node 节点 * @param duration 持续时间(秒) * @param times 闪烁次数 */ private blinkLightWithTween(node: Node, duration: number, times: number) { let opc = node.getComponent(UIOpacity); if (!opc) return; node.active = true /** 胜利失败动画 */ this.video_bg.clip = this._game_data.win_coin >= 0 ? this.video_win : this.video_lose app.manager.sound.playEffect({name : this._game_data.win_coin >= 0 ? "effect/eff_happy" : "effect/eff_thunder", volume: 0.5}) this.scheduleOnce(() => { this.video_bg.clip = this.video_normal }, 3.2); let earn_str = this._game_data.win_coin > 0 ? "+" + this._game_data.win_coin : this._game_data.win_coin this.lab_earn.string = earn_str this.lab_earn.color = this._game_data.win_coin > 0 ? new Color(255, 91, 69) : new Color(96, 255, 38) let blinkCount = 0; const blink = () => { tween(opc) .to(duration / 2, { opacity: 0 }) // 渐隐 .to(duration / 2, { opacity: 255 }) // 渐显 .call(() => { blinkCount++; if (blinkCount < times) { blink(); // 重复执行 // 游戏结束 }else{ opc.opacity = 255 node.active = false this._is_playing = false if (this._is_continuous){ let lab = Tools.AddChild(this.lab_point, this._game_data.win_coin > 0 ? this.lab_add : this.lab_sub) let cb = () => { lab.destroy() USERDATA.coin = this._game_data.coin this.initUserData() if (this._now_rest_times > 0 || this._now_rest_times == -1){ this.btnGameStart() }else{ this.resetStopGame() } } // T5是再来一次,不需要飘字 if (this._game_data.tier != "T5"){ let pos = new Vec3(node.parent.position.x, node.parent.position.y + 80, 0 ) lab.setPosition(pos) lab.active = true let earn_str = this._game_data.win_coin > 0 ? "+" + this._game_data.win_coin : this._game_data.win_coin Tools.SetText(lab, earn_str) app.manager.sound.playEffect({name : this._game_data.win_coin > 0 ? "effect/eff_win" : "effect/eff_lose"}) tween(lab) .by(1, { position: new Vec3(0, 200, 0) }) .call(()=>{ cb() }) .start() }else { cb() } }else { if (this._game_data.tier != "T5"){ if (this._game_data.win_coin > 0){ Tools.ActChild(this.rw, "bg/win_lab/en", LANG_SET.langIndex == LangType.EN) Tools.ActChild(this.rw, "bg/win_lab/zh", LANG_SET.langIndex == LangType.ZH) Tools.SetChildText(this.rw, "bg/win_lab/" + LANG_ENUM[LANG_SET.langIndex] + "/num", this._game_data.win_coin) app.manager.sound.playEffect({name : this._game_data.win_coin > 0 ? "effect/eff_win" : "effect/eff_lose"}) this.rw.active = true }else{ this.norw.active = true } } USERDATA.coin = this._game_data.coin this.resetStopGame() this.initUserData() } } }) .start(); }; blink(); } resetStopGame(){ this._is_continuous = false Tools.ActChild(this.btn_auto, "lab", true) Tools.SetChildText(this.btn_auto, "times", "") this.btn_play.getComponent(Sprite).grayscale = false this.initAnteSet() } /** 播放骰子动画 */ playDiceAni(array:any[]){ app.manager.sound.playEffect({name : "effect/shaizi"}) for (let i = 0; i < this.dices.length; i++) { let dice = this.dices[i] let animation = dice.getComponent(Animation); if (animation) { this.scheduleOnce(() => { animation.play(); }, math.random() * 0.1); animation.once(Animation.EventType.FINISHED, () => { this.loadSprite("dice_" + array[i], dice.getChildByName("dice")) }); } } } loadSprite(pic: string, node: Node){ this.loadRes(pic, Asset, (res: ImageAsset)=>{ let sp = new SpriteFrame() let tex = new Texture2D(); tex.image = res; sp.texture = tex node.getComponent(Sprite).spriteFrame = sp }); } closeRw(){ this.rw.active = false this.norw.active = false } update(){ if (this._video) { this.updateTexture(); } } private async updateTexture() { if (this._isUpdating) return; this._isUpdating = true; try { this._ctx.save(); // 保存当前状态 this._ctx.clearRect(0, 0, this._canvas.width, this._canvas.height); // 绘制视频帧 this._ctx.drawImage(this._video, 0, 0, this._canvas.width, this._canvas.height); this._ctx.restore(); // 恢复原始状态 const canvasData = this._canvas; // 销毁旧纹理 if (this._currentSpriteFrame) { const oldTexture = this._currentSpriteFrame.texture; oldTexture?.destroy(); this._currentSpriteFrame.destroy(); } const texture = new Texture2D(); texture.image = new ImageAsset(canvasData); const spriteFrame = new SpriteFrame(); spriteFrame.texture = texture; this.video_sprite.spriteFrame = spriteFrame; this._currentSpriteFrame = spriteFrame; } finally { this._isUpdating = false; } } }