Phaser 3补间动画无法启动求助:确认后轮盘不转动
Phaser 3 模态框确认后轮盘补间动画不启动问题排查
问题描述
我有一个Phaser 3项目,游戏场景内的按钮会打开一个HTML实现的确认模态框。当用户点击确认按钮时,应调用游戏场景中的spinWheel()函数使轮盘转动,但该函数虽已执行,补间动画却未启动。
相关代码
模态框代码
<div class="modal fade" id="Modal" onclick="runGameScene()" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true"> <div class="modal-dialog"> <div class="modal-content"> <div class="modal-body"> <div style="text-align:center;"> Do you want to spin the wheel? </div> <div class="modal-footer"> <div style="margin:auto"> <button type="button" class="btn btn-primary blue-btn" onclick="startWheelSpin()">yes</button> <button type="button" class="btn btn-secondary red-btn" onclick="runGameScene()" data-dismiss="modal"> no </button> </div> </div> </div> </div> </div> </div>
game.js代码
var game; var gameOptions = { slices: 6, sliceColors: [0x16A085, 0x2989B9, 0x8E44AD, 0xF39C12, 0xD35400, 0xC0392B], slicePrizes: ["RED", "GREEN", "BLUE", "WHITE", "CYAN", "YELLOW", "GREY", "PURPLE"], rotationTime: 3000, wheelRadius: 100, spinCounter: 5 } const ratio = Math.max(window.innerWidth / window.innerHeight, window.innerHeight / window.innerWidth) const DEFAULT_HEIGHT = 2400 // any height you want const DEFAULT_WIDTH = ratio * DEFAULT_HEIGHT var gameConfig = { type: Phaser.AUTO, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT, scale: { mode: Phaser.Scale.FIT, autoCenter: Phaser.Scale.CENTER_BOTH, width: DEFAULT_WIDTH, height: DEFAULT_HEIGHT }, backgroundColor: 0xd7eef7, scene: [playGame] }; window.onload = function() { // game constructor game = new Phaser.Game(gameConfig); }
游戏场景代码
class playGame extends Phaser.Scene { constructor(){ super("PlayGame"); } create(){ var sliceDegrees = 360 / gameOptions.slices; // making a graphic object without adding it to the game var graphics = this.make.graphics({x: 0,y: 0,add: false}); for(var i = 0; i < gameOptions.slices; i++){ graphics.fillStyle(gameOptions.sliceColors[i], 1); graphics.slice(gameOptions.wheelRadius, gameOptions.wheelRadius, gameOptions.wheelRadius, Phaser.Math.DegToRad(270 + i * sliceDegrees), Phaser.Math.DegToRad(270 + (i + 1) * sliceDegrees), false); graphics.fillPath(); } graphics.generateTexture("wheel", gameOptions.wheelRadius * 2, gameOptions.wheelRadius * 2); // creating a sprite with wheel image as if it was a preloaded image this.wheel = this.add.sprite(game.config.width / 2, game.config.height / 2, "wheel"); // spin circle this.spinBtn = new RoundedButton(this, game.config.width / 2, game.config.height / 2, 100, "spin", () => {this.confirmSpin()}); // a custom button with confirmSpin() as callback this.add.existing(this.spinBtn); // the game has just started = we can spin the wheel this.canSpin = true; } // function to spin the wheel spinWheel() { if(this.canSpin){ var rounds = Phaser.Math.Between(2, 4); var degrees = Phaser.Math.Between(0, 360); var prize = gameOptions.slices - 1 - Math.floor(degrees / (360 / gameOptions.slices)); // now the wheel cannot spin because it's already spinning this.canSpin = false; this.tweens.add({ targets: [this.wheel], angle: 360 * rounds + degrees, duration: gameOptions.rotationTime, ease: "Cubic.easeOut", callbackScope: this, onStart: function() { console.log("tween starts."); }, onComplete: function(tween){ console.log("prize:", prize); // player can spin again this.canSpin = true; } }); } } confirmSpin() { this.scene.pause('PlayGame'); $('#spentModal').modal('show'); // open confirm spin modal }
调用spinWheel的代码
runGameScene = function() { game.scene.run('PlayGame'); } startWheelSpin = function(){ $('#Modal').modal('hide'); runGameScene(); const scene = game.scene.keys.PlayGame; scene.spinWheel(); }
问题根源
- 场景暂停导致tween系统休眠:调用
this.scene.pause('PlayGame')暂停场景后,Phaser的补间(tween)系统会随场景一起暂停,无法处理新的补间请求。 - 场景恢复的异步性:
game.scene.run()恢复暂停场景时,场景的更新循环和tween系统需要一帧时间重新激活,直接在其后调用spinWheel()会导致补间被添加到未完全恢复的系统中,无法执行。 - 模态框误触发场景恢复:模态框根元素绑定的
onclick="runGameScene()"会导致点击模态框内任意区域都触发场景恢复,造成状态混乱。 - 模态框ID不匹配:
confirmSpin中打开的是#spentModal,但实际模态框ID是Modal,可能导致模态框显示异常。
修复方案
1. 移除模态框根元素的冗余点击事件
删掉模态框最外层的onclick="runGameScene()",避免误触发:
<div class="modal fade" id="Modal" tabindex="-1" aria-labelledby="ModalLabel" aria-hidden="true"> <!-- 内部内容保持不变 --> </div>
2. 确保场景完全恢复后执行补间
修改startWheelSpin函数,利用Phaser的延迟调用确保tween系统恢复后再启动动画:
startWheelSpin = function(){ $('#Modal').modal('hide'); const scene = game.scene.keys.PlayGame; if (scene.scene.isPaused()) { scene.scene.resume(); // 延迟到下一帧执行,确保场景tween系统已激活 scene.time.delayedCall(0, () => { scene.spinWheel(); }, [], scene); } else { scene.spinWheel(); } }
3. 修正场景恢复函数的逻辑
将runGameScene改为针对暂停场景的恢复操作,避免重复启动:
runGameScene = function() { const scene = game.scene.keys.PlayGame; if (scene.scene.isPaused()) { scene.scene.resume(); } }
4. 统一模态框ID
修正confirmSpin中的模态框ID,确保正确显示:
confirmSpin() { this.scene.pause('PlayGame'); $('#Modal').modal('show'); // 匹配实际模态框ID }
原理说明
Phaser场景暂停时,所有的tween、定时器和更新循环都会停止。恢复场景后,这些系统需要一帧时间重新初始化。使用time.delayedCall(0)可以让代码在下一帧执行,此时tween系统已完全激活,补间能被正常加入到更新队列中运行。
内容的提问来源于stack exchange,提问作者Faezeh Keshmiri
相关产品推荐
相关产品推荐

