From d583192bed3589774ddd69fb29a787d58e6281e9 Mon Sep 17 00:00:00 2001 From: Golen Date: Fri, 17 Nov 2023 21:09:40 +0100 Subject: [PATCH] Add basic Turtle class --- src/assets/assets.ts | 4 + src/components/Button.ts | 163 +++++++++++++++++++++++++++++++ src/components/Player.ts | 191 ------------------------------------- src/components/Turtle.ts | 108 +++++++++++++++++++++ src/components/UI.ts | 4 +- src/scenes/GameScene.ts | 57 ++++------- src/scenes/PreloadScene.ts | 2 +- 7 files changed, 294 insertions(+), 235 deletions(-) create mode 100644 src/components/Button.ts delete mode 100644 src/components/Player.ts create mode 100644 src/components/Turtle.ts diff --git a/src/assets/assets.ts b/src/assets/assets.ts index 91d02c1..56d7d28 100644 --- a/src/assets/assets.ts +++ b/src/assets/assets.ts @@ -5,9 +5,13 @@ import { image, sound, music, loadFont, spritesheet } from './util'; const images: Image[] = [ // Backgrounds image('backgrounds/background', 'background'), + image('backgrounds/trampoline', 'trampoline'), // Characters image('characters/player', 'player'), + image('characters/turtle_jumping', 'turtle_jumping'), + image('characters/turtle_stuck', 'turtle_stuck'), + image('characters/turtle_waiting', 'turtle_waiting'), // Items image('items/coin', 'coin'), diff --git a/src/components/Button.ts b/src/components/Button.ts new file mode 100644 index 0000000..da6e76c --- /dev/null +++ b/src/components/Button.ts @@ -0,0 +1,163 @@ +import { BaseScene } from "./../scenes/BaseScene"; + +export class Button extends Phaser.GameObjects.Container { + public scene: BaseScene; + private _hold: boolean; + private _hover: boolean; + protected blocked: boolean; + public liftSmooth: number; + public hoverSmooth: number; + public holdSmooth: number; + public category: number; + public aliveValue: number; + private hoverTween: Phaser.Tweens.Tween; + private holdTween: Phaser.Tweens.Tween; + public enabled: boolean; + + constructor(scene: BaseScene, x: number, y: number) { + super(scene, x, y); + this.scene = scene; + scene.add.existing(this); + + this._hover = false; + this._hold = false; + this.blocked = false; + this.enabled = true; + + this.liftSmooth = 0; + this.hoverSmooth = 0; + this.holdSmooth = 0; + this.aliveValue = 0; + } + + bindInteractive( + gameObject: Phaser.GameObjects.GameObject, + draggable = false + ) { + gameObject.removeInteractive(); + gameObject + .setInteractive({ useHandCursor: true, draggable: draggable }) + .on("pointerout", this.onOut, this) + .on("pointerover", this.onOver, this) + .on("pointerdown", this.onDown, this) + .on("pointerup", this.onUp, this) + .on("dragstart", this.onDragStart, this) + .on("drag", this.onDrag, this) + .on("dragend", this.onDragEnd, this); + return gameObject; + } + + get hover(): boolean { + return this._hover; + } + + set hover(value: boolean) { + if (value != this._hover) { + if (this.hoverTween) { + this.hoverTween.stop(); + } + if (value) { + this.hoverTween = this.scene.tweens.add({ + targets: this, + hoverSmooth: { from: 0.0, to: 1.0 }, + ease: "Cubic.Out", + duration: 100, + }); + } else { + this.hoverTween = this.scene.tweens.add({ + targets: this, + hoverSmooth: { from: 1.0, to: 0.0 }, + ease: (v: number) => { + return Phaser.Math.Easing.Elastic.Out(v, 1.5, 0.5); + }, + duration: 500, + }); + } + } + + this._hover = value; + } + + get hold(): boolean { + return this._hold; + } + + set hold(value: boolean) { + if (value != this._hold) { + if (this.holdTween) { + this.holdTween.stop(); + } + if (value) { + this.holdTween = this.scene.tweens.add({ + targets: this, + holdSmooth: { from: 0.0, to: 1.0 }, + ease: "Cubic.Out", + duration: 100, + }); + } else { + this.holdTween = this.scene.tweens.add({ + targets: this, + holdSmooth: { from: 1.0, to: 0.0 }, + ease: (v: number) => { + return Phaser.Math.Easing.Elastic.Out(v, 1.5, 0.5); + }, + duration: 500, + }); + } + } + + this._hold = value; + } + + onOut(pointer: Phaser.Input.Pointer, event: Phaser.Types.Input.EventData) { + this.hover = false; + this.hold = false; + } + + onOver( + pointer: Phaser.Input.Pointer, + localX: number, + localY: number, + event: Phaser.Types.Input.EventData + ) { + this.hover = true; + } + + onDown( + pointer: Phaser.Input.Pointer, + localX: number, + localY: number, + event: Phaser.Types.Input.EventData + ) { + this.hold = true; + this.blocked = false; + this.emit("down"); + } + + onUp( + pointer: Phaser.Input.Pointer, + localX: number, + localY: number, + event: Phaser.Types.Input.EventData + ) { + if (this.hold && !this.blocked) { + this.hold = false; + this.emit("click"); + } + } + + onDragStart(pointer: Phaser.Input.Pointer, dragX: number, dragY: number) {} + + onDrag(pointer: Phaser.Input.Pointer, dragX: number, dragY: number) {} + + onDragEnd( + pointer: Phaser.Input.Pointer, + dragX: number, + dragY: number, + dropped: boolean + ) {} + + block() { + this.blocked = true; + } +} diff --git a/src/components/Player.ts b/src/components/Player.ts deleted file mode 100644 index 462721e..0000000 --- a/src/components/Player.ts +++ /dev/null @@ -1,191 +0,0 @@ -import { GameScene } from "@/scenes/GameScene"; - -const ACCELERATION = 150; -const MAX_SPEED = 400; -const FRICTION = 0.7; -const TAPPING_TIMER = 200; // ms -console.assert( - ACCELERATION / (1 - FRICTION) >= MAX_SPEED, - "Max speed unreachable" -); - -export class Player extends Phaser.GameObjects.Container { - public scene: GameScene; - - // Sprites - private spriteSize: number; - private sprite: Phaser.GameObjects.Sprite; - private tween: Phaser.Tweens.Tween; - - // Controls - private keyboard: any; - public isTouched: boolean; - public isTapped: boolean; - private tappedTimer: number; - private inputVec: Phaser.Math.Vector2; // Just used for keyboard -> vector - private touchPos: Phaser.Math.Vector2; - public velocity: Phaser.Math.Vector2; - private border: { [key: string]: number }; - - constructor(scene: GameScene, x: number, y: number) { - super(scene, x, y); - scene.add.existing(this); - this.scene = scene; - - /* Sprite */ - this.spriteSize = 200; - this.sprite = this.scene.add.sprite(0, 0, "player"); - this.sprite.setOrigin(0.5, 1.0); - this.sprite.y += this.spriteSize / 2; - this.sprite.setScale(this.spriteSize / this.sprite.width); - this.add(this.sprite); - - /* Controls */ - if (this.scene.input.keyboard) { - this.keyboard = this.scene.input.keyboard.addKeys({ - up1: "W", - down1: "S", - left1: "A", - right1: "D", - up2: "Up", - down2: "Down", - left2: "Left", - right2: "Right", - }); - this.scene.input.keyboard - .addKey(Phaser.Input.Keyboard.KeyCodes.SPACE) - .on("down", this.doABarrelRoll, this); - } - this.isTouched = false; - this.isTapped = false; - this.tappedTimer = 0; - this.inputVec = new Phaser.Math.Vector2(0, 0); - this.touchPos = new Phaser.Math.Vector2(0, 0); - this.velocity = new Phaser.Math.Vector2(0, 0); - this.border = { - left: 100, - right: scene.W - 100, - top: 100, - bottom: scene.H - 100, - }; - } - - update(time: number, delta: number) { - // Movement - this.handleInput(); - - this.inputVec.limit(1); - // this.inputVec.normalize(); - this.inputVec.scale(ACCELERATION); - - if (this.isTapped) { - this.tappedTimer -= delta; - if (this.tappedTimer <= 0) { - this.isTapped = false; - } - } else { - this.velocity.scale(FRICTION); - this.velocity.add(this.inputVec); - this.velocity.limit(MAX_SPEED); - } - - this.x += (this.velocity.x * delta) / 1000; - this.y += (this.velocity.y * delta) / 1000; - - // Border collision - if (this.x < this.border.left) { - this.x = this.border.left; - } - if (this.x > this.border.right) { - this.x = this.border.right; - } - if (this.y < this.border.top) { - this.y = this.border.top; - } - if (this.y > this.border.bottom) { - this.y = this.border.bottom; - } - - // Animation (Change to this.sprite.setScale if needed) - const squish = 1.0 + 0.02 * Math.sin((6 * time) / 1000); - this.setScale(1.0, squish); - } - - handleInput() { - this.inputVec.reset(); - - // Keyboard input to vector - if (!this.isTouched) { - if (this.keyboard) { - this.inputVec.x = - (this.keyboard.left1.isDown || this.keyboard.left2.isDown ? -1 : 0) + - (this.keyboard.right1.isDown || this.keyboard.right2.isDown ? 1 : 0); - this.inputVec.y = - (this.keyboard.up1.isDown || this.keyboard.up2.isDown ? -1 : 0) + - (this.keyboard.down1.isDown || this.keyboard.down2.isDown ? 1 : 0); - } - } - // Touch to input vector - else { - this.inputVec.copy(this.touchPos); - this.inputVec.x -= this.x; - this.inputVec.y -= this.y; // If needed, add offset so finger doesn't block, see TW. - // if (this.inputVec.length() < 8) { - // this.inputVec.reset(); - // } - this.inputVec.scale(1 / 50); - } - } - - touchStart(x: number, y: number) { - this.isTouched = true; - this.isTapped = false; - this.touchPos.x = x; - this.touchPos.y = y; - - if (this.touchInsideBody(x, y)) { - this.isTapped = true; - this.tappedTimer = TAPPING_TIMER; - } - } - - touchDrag(x: number, y: number) { - this.touchPos.x = x; - this.touchPos.y = y; - - if (this.isTapped && !this.touchInsideBody(x, y)) { - this.isTapped = false; - } - } - - touchEnd(x: number, y: number) { - if (this.isTapped && this.tappedTimer > 0) { - this.emit("action"); - } - - this.isTouched = false; - this.isTapped = false; - } - - touchInsideBody(x: number, y: number) { - return ( - Phaser.Math.Distance.Between(this.x, this.y, x, y) < - this.spriteSize - ); - } - - doABarrelRoll() { - if (!this.tween || !this.tween.isActive()) { - this.tween = this.scene.tweens.add({ - targets: this.sprite, - scaleX: { - from: this.sprite.scaleX, - to: -this.sprite.scaleX, - ease: "Cubic.InOut", - }, - duration: 300, - yoyo: true, - }); - } - } -} diff --git a/src/components/Turtle.ts b/src/components/Turtle.ts new file mode 100644 index 0000000..9e47d16 --- /dev/null +++ b/src/components/Turtle.ts @@ -0,0 +1,108 @@ +import { GameScene } from "@/scenes/GameScene"; +import { Button } from "./Button"; + +const ACCELERATION = 150; +const MAX_SPEED = 400; +const FRICTION = 0.7; +const TAPPING_TIMER = 200; // ms +console.assert( + ACCELERATION / (1 - FRICTION) >= MAX_SPEED, + "Max speed unreachable" +); + +export class Turtle extends Button { + public scene: GameScene; + + // Sprites + private spriteSize: number; + private sprite: Phaser.GameObjects.Sprite; + private tween: Phaser.Tweens.Tween; + + // Controls + public velocity: Phaser.Math.Vector2; + private border: { [key: string]: number }; + private dragOffset: Phaser.Math.Vector2; + + constructor(scene: GameScene, x: number, y: number) { + super(scene, x, y); + scene.add.existing(this); + this.scene = scene; + + /* Sprite */ + this.spriteSize = 200; + this.sprite = this.scene.add.sprite(0, 0, "turtle_waiting"); + this.sprite.setOrigin(0.5, 1.0); + this.sprite.y += this.spriteSize / 2; + this.sprite.setScale(this.spriteSize / this.sprite.width); + this.add(this.sprite); + + /* Controls */ + this.velocity = new Phaser.Math.Vector2(0, 0); + this.border = { + left: 100, + right: scene.W - 100, + top: 100, + bottom: scene.H - 100, + }; + + /* Input */ + this.dragOffset = new Phaser.Math.Vector2(); + this.bindInteractive(this.sprite, true); + } + + update(time: number, delta: number) { + // this.x += (this.velocity.x * delta) / 1000; + // this.y += (this.velocity.y * delta) / 1000; + + // Border collision + // if (this.x < this.border.left) { + // this.x = this.border.left; + // } + // if (this.x > this.border.right) { + // this.x = this.border.right; + // } + // if (this.y < this.border.top) { + // this.y = this.border.top; + // } + // if (this.y > this.border.bottom) { + // this.y = this.border.bottom; + // } + + // Animation (Change to this.sprite.setScale if needed) + const squish = 0.02 * Math.sin((6 * time) / 1000); + this.setScale(1.0 + squish, 1.0 - squish); + } + + onDragStart(pointer: Phaser.Input.Pointer, dragX: number, dragY: number) { + this.dragOffset.set(dragX, dragY); + } + + onDrag(pointer: Phaser.Input.Pointer, dragX: number, dragY: number) { + this.setPosition( + dragX + this.dragOffset.x, + dragY + this.dragOffset.y, + ); + } + + onDragEnd( + pointer: Phaser.Input.Pointer, + dragX: number, + dragY: number, + dropped: boolean + ) {} + + doABarrelRoll() { + if (!this.tween || !this.tween.isActive()) { + this.tween = this.scene.tweens.add({ + targets: this.sprite, + scaleX: { + from: this.sprite.scaleX, + to: -this.sprite.scaleX, + ease: "Cubic.InOut", + }, + duration: 300, + yoyo: true, + }); + } + } +} diff --git a/src/components/UI.ts b/src/components/UI.ts index 045bf41..e9fbf38 100644 --- a/src/components/UI.ts +++ b/src/components/UI.ts @@ -12,7 +12,7 @@ export class UI extends Phaser.GameObjects.Container { scene.add.existing(this); this.scene = scene; - const panelHeight = 200; + const panelHeight = 150; this.panel = this.scene.add.container(0, 0); this.add(this.panel); @@ -34,7 +34,7 @@ export class UI extends Phaser.GameObjects.Container { this.panel.setPosition( this.scene.W - this.background.displayWidth / 2 - 30, - this.scene.H - this.background.displayHeight / 2 - 30 + this.background.displayHeight / 2 + 30 ); } diff --git a/src/scenes/GameScene.ts b/src/scenes/GameScene.ts index 2371460..d74eb01 100644 --- a/src/scenes/GameScene.ts +++ b/src/scenes/GameScene.ts @@ -1,10 +1,10 @@ import { BaseScene } from "@/scenes/BaseScene"; -import { Player } from "@/components/Player"; +import { Turtle } from "@/components/Turtle"; import { UI } from "@/components/UI"; export class GameScene extends BaseScene { private background: Phaser.GameObjects.Image; - private player: Player; + private turtles: Turtle[]; private ui: UI; constructor() { @@ -18,52 +18,27 @@ export class GameScene extends BaseScene { this.background.setOrigin(0); this.fitToScreen(this.background); - this.player = new Player(this, this.CX, this.CY); - this.player.on("action", () => { - this.player.doABarrelRoll(); - }); + this.turtles = []; + for (let i = 0; i < 5; i++) { + this.addTurtle(); + } this.ui = new UI(this); - - this.initTouchControls(); } update(time: number, delta: number) { - this.player.update(time, delta); - } - - - initTouchControls() { - this.input.addPointer(2); - - // let touchArea = this.add.rectangle(0, 0, this.W, this.H, 0xFFFFFF).setOrigin(0).setAlpha(0.001); - // touchArea.setInteractive({ useHandCursor: true, draggable: true }); - - let touchId: number = -1; - let touchButton: number = -1; - - this.input.on('pointerdown', (pointer: Phaser.Input.Pointer) => { - if (!this.player.isTouched) { - this.player.touchStart(pointer.x, pointer.y); - touchId = pointer.id; - touchButton = pointer.button; - } - else if (this.player.isTouched && !this.player.isTapped) { // Use second touch point as a trigger - this.player.doABarrelRoll(); - } - }); - - this.input.on('pointermove', (pointer: Phaser.Input.Pointer) => { - if (touchId == pointer.id) { - this.player.touchDrag(pointer.x, pointer.y); - } + this.turtles.forEach((turtle) => { + turtle.update(time, delta); }); + } - this.input.on('pointerup', (pointer: Phaser.Input.Pointer) => { - if (touchId == pointer.id && touchButton == pointer.button) { - // this.ui.debug.setText(`${new Date().getTime()} - id:${pointer.id} button:${pointer.button}`); - this.player.touchEnd(pointer.x, pointer.y); - } + addTurtle() { + let x = this.W * (0.25 + 0.5 * Math.random()); + let y = this.H * (0.5 + 0.25 * Math.random()); + let turtle = new Turtle(this, x, y); + turtle.on("action", () => { + turtle.doABarrelRoll(); }); + this.turtles.push(turtle); } } diff --git a/src/scenes/PreloadScene.ts b/src/scenes/PreloadScene.ts index 63a454c..41312b1 100644 --- a/src/scenes/PreloadScene.ts +++ b/src/scenes/PreloadScene.ts @@ -76,7 +76,7 @@ export class PreloadScene extends BaseScene { create() { this.fade(true, 100, 0x000000); this.addEvent(100, () => { - this.scene.start("TitleScene"); + this.scene.start("GameScene"); }); } }