From 44e9ca6bc32684afc334c9a87aa01e49e9338fc9 Mon Sep 17 00:00:00 2001 From: j-tap Date: Fri, 17 Jun 2022 11:27:07 +0300 Subject: [PATCH] refactoring, adaptive, fx bomb --- src/configs/phaser.js | 43 +++++--- src/index.js | 15 ++- src/{objects => models}/Bonus.js | 6 +- src/objects/AppGame.js | 72 +++++++++++++ src/objects/SceneLevel.js | 92 +++++++++++------ src/plugins/gridTiles/GridTilesGameObject.js | 100 ++++++++++++------- src/plugins/scoreBar/ScoreBarGameObject.js | 29 +++--- src/plugins/tileItem/TileItemGameObject.js | 33 ++++-- src/plugins/topBar/TopBarGameObject.js | 10 +- src/scenes/ScenePreload.js | 5 +- src/services/BonusesService.js | 2 +- 11 files changed, 291 insertions(+), 116 deletions(-) rename src/{objects => models}/Bonus.js (82%) create mode 100644 src/objects/AppGame.js diff --git a/src/configs/phaser.js b/src/configs/phaser.js index 3e56e90..0269311 100644 --- a/src/configs/phaser.js +++ b/src/configs/phaser.js @@ -1,7 +1,32 @@ +const DEFAULT_WIDTH = 800 +const DEFAULT_HEIGHT= 800 +const MAX_WIDTH = 1920 +const MAX_HEIGHT = 1920 +const SCALE_MODE = 'FIT' /* (FIT, SMOOTH) */ + export default { - parent: 'game', - transparent: true, + /* custom properties */ + DEFAULT_WIDTH, + DEFAULT_HEIGHT, + MAX_WIDTH, + MAX_HEIGHT, + SCALE_MODE, + + orientation: 'landscape', + styles: { + colorText: 0x222222, + fontSize: 18, + fontSizeH3: 24 + }, + + /* phaser properties */ + disableContextMenu: true, + autoFocus: true, + pixelArt: false, antialias: true, + transparent: false, + parent: 'game', + physics: { default: 'arcade', arcade: { @@ -10,16 +35,8 @@ export default { }, }, scale: { - parent: 'game', - mode: Phaser.Scale.FIT, - autoCenter: Phaser.Scale.CENTER_HORIZONTALLY, - min: { - width: 320, - height: 320, - }, - max: { - width: 1980, - height: 1980, - }, + mode: 0, /* 0 - NONE */ + width: DEFAULT_WIDTH, + height: DEFAULT_HEIGHT, }, } diff --git a/src/index.js b/src/index.js index bd7e5b8..3a27172 100644 --- a/src/index.js +++ b/src/index.js @@ -1,4 +1,4 @@ -import { Game } from 'phaser' +import AppGame from '@/objects/AppGame' import configPhaser from '@/configs/phaser' @@ -22,6 +22,7 @@ require('@/assets/styles/index.styl') const config = { ...configPhaser, + plugins: { global: [ { key: 'BtnPlugin', plugin: BtnPlugin, start: true }, @@ -44,4 +45,14 @@ const config = { ], } -new Game(config) +window.addEventListener('load', () => +{ + const game = new AppGame(config) + + window.addEventListener('resize', () => + { + game.resize() + }) + + game.resize() +}) diff --git a/src/objects/Bonus.js b/src/models/Bonus.js similarity index 82% rename from src/objects/Bonus.js rename to src/models/Bonus.js index 4296b23..8eba0a0 100644 --- a/src/objects/Bonus.js +++ b/src/models/Bonus.js @@ -1,13 +1,9 @@ -import bonusModel from '@/models/BonusModel' - export default class Bonus { #params - constructor ({ name, title, amount, params }) + constructor ({ name = null, title = null, amount = 0, params = {} }) { - Object.assign(this, bonusModel) - this.model = bonusModel this.name = name this.title = title this.amount = amount diff --git a/src/objects/AppGame.js b/src/objects/AppGame.js new file mode 100644 index 0000000..a552f50 --- /dev/null +++ b/src/objects/AppGame.js @@ -0,0 +1,72 @@ +import { Game } from 'phaser' + +export default class AppGame extends Game +{ + constructor(config) + { + super(config) + + this.#initCustomParameters(config) + } + + #initCustomParameters (config) + { + Object.keys(config).forEach(key => + { + if (this.config[key] === undefined) + { + this.config[key] = config[key] + } + }) + } + + resize () + { + const w = window.innerWidth + const h = window.innerHeight + + const width = this.config.DEFAULT_WIDTH + const height = this.config.DEFAULT_HEIGHT + const maxWidth = this.config.MAX_WIDTH + const maxHeight = this.config.MAX_HEIGHT + const scaleMode = this.config.SCALE_MODE + + const scale = Math.min(w / width, h / height) + const newWidth = Math.min(w / scale, maxWidth) + const newHeight = Math.min(h / scale, maxHeight) + + const defaultRatio = width / height + const maxRatioWidth = maxWidth / height + const maxRatioHeight = width / maxHeight + + /* smooth scaling */ + let smooth = 1 + if (scaleMode === 'SMOOTH') + { + const maxSmoothScale = 1.15 + const getValuePercentFromRange = (value, min, max) => + { + return (value - min) / (max - min) + } + if (width / height < w / h) + { + smooth = + -getValuePercentFromRange(newWidth / newHeight, defaultRatio, maxRatioWidth) / (1 / (maxSmoothScale - 1)) + maxSmoothScale + } else { + smooth = + -getValuePercentFromRange(newWidth / newHeight, defaultRatio, maxRatioHeight) / (1 / (maxSmoothScale - 1)) + maxSmoothScale + } + } + + /* resize the game */ + this.scale.resize(newWidth * smooth, newHeight * smooth) + + /* scale the width and height of the css */ + this.canvas.style.width = newWidth * scale + 'px' + this.canvas.style.height = newHeight * scale + 'px' + + /* center the game with css margin */ + this.canvas.style.marginTop = `${(h - newHeight * scale) / 2}px` + this.canvas.style.marginLeft = `${(w - newWidth * scale) / 2}px` + } +} diff --git a/src/objects/SceneLevel.js b/src/objects/SceneLevel.js index 4194de9..2af582e 100644 --- a/src/objects/SceneLevel.js +++ b/src/objects/SceneLevel.js @@ -31,6 +31,9 @@ export default class SceneLevel extends SceneGame { super.create() + this.padding = 20 + this.bonusesBlocks = {} + this.scoresService.init({ scoresTarget: this.targetScoresCount, movesLimit: this.maxMovesCount, @@ -53,7 +56,7 @@ export default class SceneLevel extends SceneGame this.bonusesBlocks[name].update(bonus) }) - this.grid.gridUpdate() + this.grid.update() } audioManage () @@ -66,11 +69,58 @@ export default class SceneLevel extends SceneGame draw () { - this.bonusesBlocks = [] - const { fontFamily, colorTextBar } = this.configGame - const { width } = this.cameras.main + const { width } = this.game.scale + + this.containerRight = this.add.container(width / 2 + 150, 0) + + this.#drawTopBar() + this.#drawGrid() + this.#drawScoreBar() + this.#drawBonuses() + } + + #drawTopBar () + { + const { width } = this.game.scale + this.topBar = this.add.topBar(width / 2, 0) + + this.containerRight.setY(this.topBar.displayHeight + this.padding) + } + + #drawGrid () + { + const { width } = this.game.scale + const x = 0 + const y = this.topBar.displayHeight + this.padding + + this.grid = this.add.gridTiles(x, y, this.gridTilesParams) + .on('clickOnTile', (...args) => this.clickOnTile(...args)) + + this.grid.setX(width / 2 - (this.grid.displayWidth - 6)) + } + + #drawScoreBar () + { + const x = 0 + const y = 50 + + this.scoreBar = this.add.scoreBar(x, y) + this.containerRight.add(this.scoreBar) + .setSize(this.scoreBar.displayWidth, this.scoreBar.displayHeight * 2) + } + + #drawBonuses () + { + const title = 'Bonuses:' const bonuses = this.bonusesService.getBonusesList() - const padding = 20 + + if (!bonuses.length) return + + const { fontFamily, colorTextBar } = this.configGame + const yTitle = this.scoreBar.displayHeight + this.padding + 40 + const width = this.containerRight.displayWidth + console.log(width); + const styleText = { fontFamily, fontSize: 24, @@ -78,35 +128,19 @@ export default class SceneLevel extends SceneGame color: colorTextBar, } - this.topBar = this.add.topBar() + this.textTitle = this.add.text(width / 2, yTitle, title, styleText) + .setOrigin(.5, 0) - this.grid = this.add.gridTiles(padding, 120, this.gridTilesParams) - .on('clickOnTile', (tile) => this.clickOnTile(tile)) - - this.scoreBar = this.add.scoreBar(0, 160) - this.scoreBar.setX(width - this.scoreBar.displayWidth - padding) - - if (bonuses.length) - { - this.add.text( - width - this.scoreBar.displayWidth / 2 - padding, - this.scoreBar.y + this.scoreBar.displayHeight + padding, - 'Bonuses:', - styleText, - ) - .setOrigin(.5, 0) - } + const yBonus = yTitle + this.textTitle.displayHeight + this.padding bonuses.forEach((bonus, i) => { - const { displayWidth, displayHeight, y } = this.scoreBar - const bonusY = y + displayHeight + 60 - - this.bonusesBlocks[bonus.name] = this.add.bonusBlock(0, bonusY, bonus) - - const bonusX = width - displayWidth - 70 + this.bonusesBlocks[bonus.name].displayWidth * i - this.bonusesBlocks[bonus.name].setX(bonusX) + this.bonusesBlocks[bonus.name] = this.add.bonusBlock(0, yBonus, bonus) + const x = this.bonusesBlocks[bonus.name].displayWidth * i + this.bonusesBlocks[bonus.name].setX(x) }) + + this.containerRight.add([this.textTitle, ...Object.values(this.bonusesBlocks)]) } clickOnTile ({ isCondition, tilesTarget }) diff --git a/src/plugins/gridTiles/GridTilesGameObject.js b/src/plugins/gridTiles/GridTilesGameObject.js index 11bf72d..467d9d6 100644 --- a/src/plugins/gridTiles/GridTilesGameObject.js +++ b/src/plugins/gridTiles/GridTilesGameObject.js @@ -8,10 +8,12 @@ export default class GridTilesGameObject extends GameObjects.Container { super(scene, x, y) - this.imageBg = 'grid-bg' + this.imageBgName = 'grid-bg' this.grid = grid this.tilesFrames = tiles this.minTilesTarget = minTilesTarget + this.padding = { x: 16, y: 13 } + this.prevPosition = { x: 0, y: 0 } this.scene.gridService = new GridService({ frames: this.tilesFrames, @@ -23,42 +25,51 @@ export default class GridTilesGameObject extends GameObjects.Container #draw () { - const padding = { x: 16, y: 13 } - const gridImage = this.scene.add.image(0, 0, this.imageBg) + const imageBg = this.scene.add.image(0, 0, this.imageBgName) .setOrigin(0) .setScale(.32) - this.containerTiles = this.scene.add.container(this.x + padding.x, this.y + padding.y) - .setSize(gridImage.displayWidth, gridImage.displayHeight) + this.containerTiles = this.scene.add.container(this.padding.x, this.padding.y) + .setSize(imageBg.displayWidth, imageBg.displayHeight) .setDepth(1) - this.add(gridImage, this.containerTiles, tilesMask) - - const tilesMask = this.scene.make.graphics() - .fillRect( - this.containerTiles.x, - this.containerTiles.y, - this.containerTiles.displayWidth - padding.x * 2, - this.containerTiles.displayHeight - padding.y * 2 - ) - .createGeometryMask() - - this.containerTiles.setMask(tilesMask) + this.add([imageBg, this.containerTiles]) this.scene.time.delayedCall(200, () => { this.containerTiles.setData({ allowFall: true }) }) + + this.setSize(imageBg.displayWidth, imageBg.displayHeight) + + this.#drawMask() + } + + #drawMask () + { + this.containerTiles.clearMask() + + const tilesMaskGraphic = this.scene.make.graphics() + .fillRect( + this.x + this.padding.x, + this.y + this.padding.y, + this.containerTiles.displayWidth - this.padding.x * 2, + this.containerTiles.displayHeight - this.padding.y * 2 + ) + + this.containerTiles.setMask(tilesMaskGraphic.createGeometryMask()) } #tileClickHandler ({ tile }) { - const tilesTarget = this.getSelectedTiles(tile) + const { tilesTarget, bonus } = this.getSelectedTiles(tile) const isCondition = tilesTarget.length >= this.minTilesTarget if (isCondition) { this.scene.gridService.removeTiles(tilesTarget) + this.containerTiles.getByName(tile.name) + .setData('bonus', bonus) } else { const tileObject = this.containerTiles.getByName(tile.name) @@ -76,23 +87,7 @@ export default class GridTilesGameObject extends GameObjects.Container this.containerTiles.add(tileObject) } - getSelectedTiles (tile) - { - const bonusName = this.scene.bonusesService.getActive() - - if (bonusName) - { - const bonus = this.scene.bonusesService.getBonus(bonusName) - if (bonus.name === 'bomb') - { - const { params } = bonus.accept() - return this.scene.gridService.getNearestTilesRadius(tile, params.range) - } - } - return this.scene.gridService.getNearestTilesByType(tile) - } - - gridUpdate () + #updateTiles () { const tilesObjects = this.containerTiles.getAll() @@ -124,4 +119,39 @@ export default class GridTilesGameObject extends GameObjects.Container } }) } + + #updatePositionGrid () + { + if (this.prevPosition.x !== this.x) + { + this.#drawMask() + } + + this.prevPosition.x = this.x + this.prevPosition.y = this.y + } + + getSelectedTiles (tile) + { + const bonusName = this.scene.bonusesService.getActive() + + if (bonusName) + { + const bonus = this.scene.bonusesService.getBonus(bonusName) + if (bonus.name === 'bomb') + { + const { params } = bonus.accept() + const tilesTarget = this.scene.gridService.getNearestTilesRadius(tile, params.range) + return { tilesTarget, bonus } + } + } + const tilesTarget = this.scene.gridService.getNearestTilesByType(tile) + return { tilesTarget } + } + + update () + { + this.#updatePositionGrid() + this.#updateTiles() + } } diff --git a/src/plugins/scoreBar/ScoreBarGameObject.js b/src/plugins/scoreBar/ScoreBarGameObject.js index 61a9e04..1f1c68f 100644 --- a/src/plugins/scoreBar/ScoreBarGameObject.js +++ b/src/plugins/scoreBar/ScoreBarGameObject.js @@ -7,7 +7,7 @@ export default class ScoreBarGameObject extends GameObjects.Container super(scene, x, y) this.options = options - this.imageBg = 'score-bg' + this.imageBgName = 'score-bg' this.#draw() } @@ -17,12 +17,12 @@ export default class ScoreBarGameObject extends GameObjects.Container const { fontFamily, colorTextBar } = this.scene.configGame const color = colorTextBar - const scoreImage = this.scene.add.image(0, 0, this.imageBg) + this.imageBg = this.scene.add.image(0, 0, this.imageBgName) .setOrigin(0, 0) .setScale(.32) - const movesText = this.scene.make.text({ - x: scoreImage.displayWidth / 2, + this.textMove = this.scene.make.text({ + x: this.imageBg.displayWidth / 2, y: 50, style: { fontSize: 80, @@ -30,11 +30,11 @@ export default class ScoreBarGameObject extends GameObjects.Container color, }, }) - .setName('movesText') + .setName('textMove') .setOrigin(.5, 0) - const scoreTitleText = this.scene.make.text({ - x: scoreImage.displayWidth / 2, + this.textTitle = this.scene.make.text({ + x: this.imageBg.displayWidth / 2, y: 215, text: 'Score:', style: { @@ -45,8 +45,8 @@ export default class ScoreBarGameObject extends GameObjects.Container }) .setOrigin(.5, 0) - const scoreNumText = this.scene.make.text({ - x: scoreImage.displayWidth / 2, + this.textScoreNum = this.scene.make.text({ + x: this.imageBg.displayWidth / 2, y: 245, text: '150', style: { @@ -55,22 +55,19 @@ export default class ScoreBarGameObject extends GameObjects.Container color, }, }) - .setName('scoresNumText') .setOrigin(.5, 0) - this.add([scoreImage, movesText, scoreTitleText, scoreNumText]) - this.setSize(scoreImage.displayWidth, scoreImage.displayHeight) + this.add([this.imageBg, this.textMove, this.textTitle, this.textScoreNum]) + this.setSize(this.imageBg.displayWidth, this.imageBg.displayHeight) } updateMove (value) { - this.getByName('movesText') - .setText(`${value}`) + this.textMove.setText(`${value}`) } updateScore (value) { - this.getByName('scoresNumText') - .setText(`${value}`) + this.textScoreNum.setText(`${value}`) } } diff --git a/src/plugins/tileItem/TileItemGameObject.js b/src/plugins/tileItem/TileItemGameObject.js index 3f015e2..0f93d3a 100644 --- a/src/plugins/tileItem/TileItemGameObject.js +++ b/src/plugins/tileItem/TileItemGameObject.js @@ -5,7 +5,15 @@ export default class TileItemGameObject extends GameObjects.Image constructor(scene, { tile }) { const key = 'tiles-spr' - const { frame, type, color, colorName, posOnGrid, name, empty } = tile + const { + frame, + type, + color, + colorName, + name, + empty, + posOnGrid = { x: 0, y: 0 }, + } = tile super(scene, 0, 0, key, frame) @@ -14,7 +22,7 @@ export default class TileItemGameObject extends GameObjects.Image this.color = color this.posOnGrid = { x: posOnGrid.x, - y:posOnGrid.y, + y: posOnGrid.y, } this.name = name this.empty = empty @@ -79,20 +87,28 @@ export default class TileItemGameObject extends GameObjects.Image const emitter = this.scene.add.particles(particlesSprite) .setDepth(2) - this.particles.destroyEmitter = emitter.createEmitter({ + const particlesConfig = { x: this.x + this.displayWidth / 2, y: this.y + this.displayHeigth / 2, on: false, blendMode: 'ADD', - lifespan: 2000, + lifespan: 600, alpha: { 'start': 1, 'end': 0 }, speed: { 'min': 0, 'max': 200 }, scale: { 'start': .4, 'end': 0 }, - gravityY: 600, + gravityY: 300, bounce: 1, - maxParticles: 15, + maxParticles: 5, tint: [this.color], frame: ['particle-1'], + } + + this.particles.destroyEmitter = emitter.createEmitter(particlesConfig) + this.particles.bombEmitter = emitter.createEmitter({ + ...particlesConfig, + gravityY: 0, + maxParticles: particlesConfig * 10, + speed: 600, }) } @@ -162,8 +178,11 @@ export default class TileItemGameObject extends GameObjects.Image if (!this.tweens.destroyTween.isPlaying()) { const { tx, ty } = this.getWorldTransformMatrix() + const bonus = this.getData('bonus') + const name = bonus ? bonus.name : 'destroy' - this.particles.destroyEmitter.explode(100, tx, ty) + this.particles[`${name}Emitter`] + .explode(100, tx, ty) this.tweens.destroyTween .on('complete', () => diff --git a/src/plugins/topBar/TopBarGameObject.js b/src/plugins/topBar/TopBarGameObject.js index 953a679..6ec40ff 100644 --- a/src/plugins/topBar/TopBarGameObject.js +++ b/src/plugins/topBar/TopBarGameObject.js @@ -15,16 +15,13 @@ export default class TopBarGameObject extends GameObjects.Container #draw () { - const { centerX } = this.scene.cameras.main const { fontFamily, colorTextBar } = this.scene.configGame - this.setX(centerX) - - const topBarImage = this.scene.add.image(0, 0, this.imageBg) + const imagebg = this.scene.add.image(0, 0, this.imageBg) .setOrigin(.5, 0) .setScale(.32) - const topBarProgressText = this.scene.make.text({ + const textTitle = this.scene.make.text({ x: 0, y: 6, text: 'Progress', @@ -40,7 +37,8 @@ export default class TopBarGameObject extends GameObjects.Container width: this.widthProgress, }) - this.add([topBarImage, topBarProgressText, this.progress]) + this.add([imagebg, textTitle, this.progress]) + this.setSize(imagebg.displayWidth, imagebg.displayHeight) } updateProgress (value) diff --git a/src/scenes/ScenePreload.js b/src/scenes/ScenePreload.js index c30e6f2..bb219b2 100644 --- a/src/scenes/ScenePreload.js +++ b/src/scenes/ScenePreload.js @@ -37,12 +37,13 @@ export default class ScenePreload extends SceneGame this.load.image('red-btn', redBtnImg) this.load.image('score-bg', scoreBgImg) this.load.image('top-bar-bg', topBarBgImg) + this.load.atlas('tiles-spr', tilesSprImg, tilesJson) + this.load.atlas('particles-spr', particlesSpr, particlesJson) + this.load.audio('tile-click', [tileClickSound]) this.load.audio('main-music', [mainMusicSound]) - this.load.audio('defeat-sound', [defeatSound]) - this.load.atlas('particles-spr', particlesSpr, particlesJson) this.load.audio('win-sound', [winSound]) } diff --git a/src/services/BonusesService.js b/src/services/BonusesService.js index 5fcfe7d..0943df1 100644 --- a/src/services/BonusesService.js +++ b/src/services/BonusesService.js @@ -1,4 +1,4 @@ -import Bonus from '@/objects/Bonus' +import Bonus from '@/models/Bonus' export default class BonusesService {