需求 |
---|
1. 按住屏幕,棍子伸長,放開手指,棍子放下。 |
2. 具備計時功能。 |
3. 計時結束或者棍子沒有放到安全區域,則遊戲結束。 |
Scene
做爲背景,防止顯示 Scene
重疊時有遮擋// background.ts export default class extends Phaser.Scene { constructor() { super({ key: 'BackgroundScene', active: true }); } create(): void { const graphics = this.add.graphics(); graphics.fillGradientStyle(0x241a47, 0x241a47, 0x274aa0, 0x274aa0); graphics.fillRect(0, 0, window.game.width, window.game.height); this.scene.launch('FootScene'); this.scene.launch('StartScene'); } }
// foot.ts import pngCloud from '@images/cloud.png'; const config: FootConfig = { circleNum: 7, blueCircleFrame: 0, whileCircleFrame: 1, blueCloudY: 160, whileCloudY: 190, height: 90 }; export default class extends Phaser.Scene { constructor() { super({ key: 'FootScene' }); } preload(): void { this.load.spritesheet('ssCloud', pngCloud, { frameWidth: 256, frameHeight: 256}); } create(): void { const blueGroup = this.add.group([], { key: 'ssCloud', frame: [config.blueCircleFrame], frameQuantity: config.circleNum, setXY: { y: config.blueCloudY, stepX: 120, stepY: 0 } }); const whileGroup = this.add.group([], { key: 'ssCloud', frame: [config.whileCircleFrame], frameQuantity: config.circleNum, setXY: { y: config.whileCloudY, stepX: 120, stepY: 0 } }); this.resize(); window.addEventListener('resize', () => { this.resize(); }); } resize(): void { const viewHeight = document.documentElement.clientHeight / window.rem; const camerasY = (window.game.height - viewHeight) / 2 + viewHeight - config.height; this.cameras.main.setPosition(0, camerasY); } }
// foot.ts export default class extends Phaser.Scene { create(): void { this.add.tween({ targets: blueGroup.getChildren(), props: { y: (target) => { return target.y + Phaser.Math.Between(-5, 0); }, scale: (target) => { return target.scale + Phaser.Math.FloatBetween(-0.05, 0.05); } }, duration: 3000, yoyo: true, repeat: -1, ease: Phaser.Math.Easing.Sine.InOut }); this.add.tween({ targets: whileGroup.getChildren(), props: { y: (target) => { return target.y + Phaser.Math.Between(-5, 0); }, scale: (target) => { return target.scale + Phaser.Math.FloatBetween(0, 0.1); } }, duration: 3000, yoyo: true, repeat: -1, ease: Phaser.Math.Easing.Sine.InOut }); } }
// foot.ts export default class extends Phaser.Scene { create(): void { this.resize(); window.addEventListener('resize', () => { this.resize(); }); } resize(): void { const viewHeight = document.documentElement.clientHeight / window.rem; const camerasY = (window.game.height - viewHeight) / 2 + viewHeight - config.height; this.cameras.main.setPosition(0, camerasY); } }
// start.ts import pngBtnStart from '@images/btn_start.png'; import pngTitle from '@images/title.png'; export default class extends Phaser.Scene { constructor() { super({ key: 'StartScene' }); } preload(): void { this.load.image('imgBtnStart', pngBtnStart); this.load.image('imgTitle', pngTitle); } create(): void { this.add.rectangle(window.game.width / 2, window.game.height / 2, window.game.width, window.game.height, 0x000000, 0.5); this.add.image(window.game.width / 2, 240, 'imgTitle').setOrigin(0.5, 0); const btnStart = this.add.sprite(window.game.width / 2, window.game.height / 2, 'imgBtnStart').setInteractive(); btnStart.on('pointerdown', () => { this.scene.start('MainScene'); }); this.add.tween({ targets: btnStart, props: { y: (target) => { return target.y + 20; } }, yoyo: true, loop: -1, duration: 2000, ease: Phaser.Math.Easing.Sine.InOut }); } }
// main.ts import pngTips from '@images/tips.png'; import pngProcess from '@images/process_border.png'; import pngSprites from '@images/sprites.png'; import pngNinja from '@images/ninja.png'; let txtDistance: Phaser.GameObjects.Text; // 文本,顯示距離 let rect: Phaser.GameObjects.Rectangle; // 進度條 let stick: Phaser.GameObjects.Rectangle; // 棍子 let processTimerEvent: Phaser.Time.TimerEvent; // 計時事件 let overContainer: Phaser.GameObjects.Container; // 遊戲結束內容容器 let gameContainer: Phaser.GameObjects.Container; // 遊戲內容容器 let prePlatformDistance: number; // 到上一個站臺的距離 let nextPlatformDistance: number; // 到下一個站臺的距離 let curPlatformWidth: number; // 當前站臺寬度 let prePlatformWidth: number; // 上一個站臺寬度 let nextPlatformWidth: number; // 下一個站臺寬度 let curPlatformX: number; // 當前站臺位置 let nextPlatformX: number; // 下一個站臺位置 let ninja: Phaser.GameObjects.Sprite; // 不靠譜的忍者本者 let distance = 0; // 距離,站臺數 let isPlaying = false; // 是否處於處理流程中,區間在從按下到釋放後的動畫播放結束 let isStart = false; // 是否開始遊戲 const config: GameConfig = { processLen: 500, // 進度條長度 processHeight: 29, // 進度條高度 platformHeight: 600, // 站臺高度 stickWidth: 10, // 棍子寬度 stickHeight: -10 // 棍子長度,座標系緣由,取負值 }; // 變化的值單獨擰出來 let stickHeight = config.stickHeight; let processLen = config.processLen; export default class extends Phaser.Scene { constructor() { super({ key: 'MainScene' }); } preload(): void { this.load.image('imgTips', pngTips); this.load.image('imgProcess', pngProcess); this.load.spritesheet('ssSprites', pngSprites, { frameWidth: 150, frameHeight: 150}); this.load.spritesheet('ssNinja', pngNinja, { frameWidth: 462 / 6, frameHeight: 388 / 4}); } create(): void { // 提示信息 const tips = this.add.image(window.game.width / 2, 400, 'imgTips'); // 進度條 const process = this.add.image(0, 0, 'imgProcess'); txtDistance = this.add.text(260, -24, 'DISTANCE: 0', { fontFamily: 'Arial', fontSize: 40, color: '#ffffff' }).setOrigin(1); rect = this.add.rectangle(-250, 0, config.processLen, config.processHeight, 0xffffff).setOrigin(0, 0.5); // 進度條區域內容容器 const timerContainer = this.add.container(window.game.width / 2, 400, [process, txtDistance, rect]); timerContainer.setAlpha(0); // 遊戲結束內容 const btnPlay = this.add.sprite(-110, 0, 'ssSprites', 0).setInteractive(); const btnHome = this.add.sprite(110, 0, 'ssSprites', 1).setInteractive(); overContainer = this.add.container(window.game.width / 2, 1800, [btnPlay, btnHome]); overContainer.setAlpha(0.5); // 初始化忍者 ninja = this.add.sprite(90, -600, 'ssNinja', 0).setOrigin(1); this.anims.create({ key: 'stand', frames: this.anims.generateFrameNumbers('ssNinja', { start: 0, end: 11 }), frameRate: 12, repeat: -1, yoyo: true }); this.anims.create({ key: 'walk', frames: this.anims.generateFrameNumbers('ssNinja', { start: 12, end: 19 }), frameRate: 12, repeat: -1 }); ninja.play('stand'); // 初始化棍子 stick = this.add.rectangle(90, -config.platformHeight, config.stickWidth, config.stickHeight, 0x000000).setOrigin(1, 0); gameContainer = this.add.container(window.game.width / 2, window.game.height, [ninja, stick]); gameContainer.setSize(window.game.width, window.game.height); gameContainer.setPosition(0, window.game.height); // 初始化站臺 const firstPlatform = this.add.rectangle(0, 0, 100, config.platformHeight, 0x000000).setOrigin(0, 1); gameContainer.add(firstPlatform); gameContainer.bringToTop(ninja); curPlatformX = 0; curPlatformWidth = 100; prePlatformDistance = 0; } }
// main.ts export default class extends Phaser.Scene { create(): void { this.createPlatform(false); } createPlatform(playAnim: boolean): void { nextPlatformDistance = Phaser.Math.Between(150, 300); // 隨機下個站臺的距離 nextPlatformWidth = Phaser.Math.Between(80, 150); // 隨機下個站臺的寬度 nextPlatformX = curPlatformX + nextPlatformDistance + curPlatformWidth; // 計算下個站臺的位置 const rect = this.add.rectangle(nextPlatformX + 750, 0, nextPlatformWidth, config.platformHeight, 0x000000).setOrigin(0, 1); // 添加站臺 gameContainer.add(rect); gameContainer.bringToTop(ninja); // 忍者提到最上層,掉下去的時候不會被站臺遮擋 if (playAnim) { // 是否播放站臺出現時的動畫 this.add.tween({ // 遊戲容器移動 targets: gameContainer, props: { x: (target) => { return target.x - (prePlatformDistance + prePlatformWidth); } }, duration: 300, ease: Phaser.Math.Easing.Sine.In }); this.add.tween({ // 新增站臺移動 targets: rect, props: { x: nextPlatformX }, duration: 500, ease: Phaser.Math.Easing.Sine.In }); this.add.tween({ // 忍者回到指定位置 targets: ninja, props: { y: (target) => { return target.y + 10; }, x: curPlatformX + (curPlatformWidth - 20) }, duration: 300 }); this.add.tween({ // 棍子回到指定位置 targets: stick, props: { alpha: 0 }, duration: 300, onComplete: () => { stick.setSize(config.stickWidth, config.stickHeight); stick.setAngle(0); stick.setX(curPlatformX + (curPlatformWidth - 10)); stick.setAlpha(1); isPlaying = false; } }); } else { rect.setPosition(nextPlatformX, 0); } } }
// main.ts createWalkTimeline(): void { const walkTimeline = this.tweens.createTimeline(); // 棍子動畫 walkTimeline.add({ targets: stick, props: { angle: 90 }, duration: 500, ease: Phaser.Math.Easing.Bounce.Out, onComplete() { ninja.play('walk'); } }); // 走路動畫 walkTimeline.add({ targets: ninja, props: { x: (target) => { return target.x + Math.abs(stickHeight) + ninja.getBounds().width / 2; }, y: (target) => { return target.y - 10; } }, duration: 500, onComplete: () => { ninja.play('stand'); this.calcDistance(); walkTimeline.destroy(); } }); walkTimeline.play(); }
// main.ts calcDistance(): void { const near = Phaser.Math.Distance.Between(stick.x, 0, nextPlatformX, 0); // 下個站臺的近端 const far = Phaser.Math.Distance.Between(stick.x, 0, nextPlatformX + nextPlatformWidth, 0);// 下個站臺的遠端 // 當前值變化 curPlatformX = nextPlatformX; prePlatformWidth = curPlatformWidth; curPlatformWidth = nextPlatformWidth; prePlatformDistance = nextPlatformDistance; if (-stickHeight > near && -stickHeight < far) { // 安全區域 this.createPlatform(true); txtDistance.setText(`DISTANCE: ${++distance}`); // 距離 +1 } else { this.gameover(); // 遊戲結束 if (-stickHeight < near) { this.add.tween({ targets: stick, props: { angle: 180 }, duration: 800, ease: Phaser.Math.Easing.Bounce.Out }); } } // 重置棍子長度 stickHeight = config.stickHeight; }
// main.ts export default class extends Phaser.Scene { create(): void { let stickTimerEvent: Phaser.Time.TimerEvent; this.input.on('pointerdown', () => { // 點擊屏幕 if (!isPlaying) { tips.setAlpha(0); timerContainer.setAlpha(1); // 開始計時 if (!isStart) { isStart = true; processTimerEvent = this.time.addEvent({ callback: this.processTimer.bind(this), loop: true, delay: 1000 }); } // 棍子伸長事件 stickTimerEvent = this.time.addEvent({ callback: this.stickTimer, loop: true, delay: 10 }); } }); this.input.on('pointerup', () => { // 釋放 if (stickTimerEvent && !isPlaying) { isPlaying = true; stickTimerEvent.destroy(); this.createWalkTimeline(); } }); // 從新開始 btnPlay.on('pointerdown', (pointer, localX, localY, event) => { event.stopPropagation(); // 阻止冒泡 this.reset(); this.scene.restart(); }); // 回到首頁 btnHome.on('pointerdown', (pointer, localX, localY, event) => { event.stopPropagation(); this.reset(); this.scene.start('StartScene'); }); } stickTimer(): void { stickHeight -= 25; stick.setSize(config.stickWidth, stickHeight); } processTimer(): void { processLen -= 5; rect.setSize(processLen, config.processHeight); if (processLen === 0) { processTimerEvent.destroy(); this.gameover(); } } // 數據復位 reset(): void { processLen = config.processLen; isPlaying = false; isStart = false; distance = 0; } }
// main.ts gameover(): void { processTimerEvent.destroy(); // shake const vec2 = new Phaser.Math.Vector2(0.005, 0.01); this.add.tween({ targets: ninja, ease: Phaser.Math.Easing.Linear, duration: 600, props: { angle: 45, x: (target) => { return target.x + 50; }, y: 50 }, onComplete: () => { // 晃動效果 this.cameras.main.shake(200, vec2); this.scene.get('FootScene').cameras.main.shake(200, vec2); this.scene.get('BackgroundScene').cameras.main.shake(200, vec2); } }); this.add.tween({ targets: overContainer, props: { y: window.game.height / 2, alpha: 1 }, delay: 1200, duration: 800, ease: Phaser.Math.Easing.Back.InOut }); this.add.tween({ targets: gameContainer, props: { alpha: 0 }, delay: 1000, duration: 800, ease: Phaser.Math.Easing.Back.InOut }); }
import
以後再使用 phaser 的 loader
,不能一步到位。以上如有好的處理方法,還請各位不吝賜教。css
預覽:https://hewq.github.io/apps/a...代碼:https://github.com/hewq/Phase...html
參考:https://triqui.itch.io/irresp...webpack