import { _decorator, Component, Node, Prefab, Vec2, Vec3, randomRangeInt, CCInteger, AudioClip } from 'cc'; import ObjectPool from '../Pool/ObjectPool'; import { Ball } from '../GamePlay/Ball'; import Utilities from '../Utilities'; import GameState from '../Enum/GameState'; import { EventManger } from './EventManger'; import GameEvent from '../Events/GameEvent'; import ScoreType from '../Enum/ScoreType'; import { FloatingText } from '../Environments/FloatingText'; import { SoundManager } from './SoundManager'; import TimeConfig from '../Enum/TimeConfig'; const { ccclass, property } = _decorator; @ccclass('GameManager') export class GameManager extends Component { //singleton private static _instance: GameManager = null; public static get instance(): GameManager { return GameManager._instance; } @property({ type: Prefab, visible: true }) private _ballPrefab: Prefab; @property({ type: Prefab, visible: true }) private _floatingScoreText: Prefab; @property({ type: Node, visible: true }) private _floatingTextContainer: Node; @property({ visible: true }) private _ballSpawnPosition: Vec3; @property({ type: CCInteger, visible: true }) private readonly _timePlay = 3; @property({ type: AudioClip, visible: true }) private _startSound: AudioClip; @property({ type: AudioClip, visible: true }) private _backgroundMusic: AudioClip; private _ballPool: ObjectPool; private _FloatingScorePool: ObjectPool; private _gameState: GameState; private _timer: number; private _score = 0; private _isMultiBall = false; private _currentBallInGame = 0; public get score() { return this._score; } protected onLoad(): void { GameManager._instance = this; this._ballPool = new ObjectPool(this._ballPrefab, 10, true, Ball); this._FloatingScorePool = new ObjectPool(this._floatingScoreText, 10, true); } protected start(): void { this.changeGameState(GameState.Init); } protected update(dt: number): void { if (this._gameState != GameState.Playing) return; this._timer -= dt; if (this._timer <= 0) { this._timer = 0; this.gameOver(); } EventManger.instance.emit(GameEvent.TimeUpdate, this._timer); } private changeGameState(state: GameState) { this._gameState = state; EventManger.instance.emit(GameEvent.GameStateChange, this._gameState); } private addScore(score: number, type: ScoreType, position: Vec3) { this._score += score; const floatingScore = this._FloatingScorePool.get(this._floatingTextContainer, FloatingText); floatingScore.show(`+${score}`, position, score >= 100 ? 1.5 : 1, score >= 100 ? 1 : 0.7); EventManger.instance.emit(GameEvent.Score, [this._score, type]); } private setCurrentBallInGame(value: number) { this._currentBallInGame += value; if (this._currentBallInGame >= 2) { if (!this._isMultiBall) { this._isMultiBall = true; EventManger.instance.emit(GameEvent.MultiBall, true); } } if (this._currentBallInGame <= 0) { if (this._isMultiBall) { this._isMultiBall = false; EventManger.instance.emit(GameEvent.MultiBall, false); } } } public spawnBall(throwBall: boolean): Ball { if (this._gameState != GameState.Playing) return; SoundManager.instance.playSfx(this._startSound); this.setCurrentBallInGame(1); const ball = this._ballPool.get(this.node, Ball); ball.node.setPosition(this._ballSpawnPosition); if (!throwBall) return ball; let dir = randomRangeInt(-1, 2); while (dir == 0) { dir = randomRangeInt(-1, 2); } const force = new Vec2(dir, 1); ball.throwBall(force.multiply2f(1, 40)); return ball; } public async ballOut() { this.setCurrentBallInGame(-1); if (this._currentBallInGame <= 0) { EventManger.instance.emit(GameEvent.BallOut, null); await Utilities.delay(TimeConfig.DelayPLay); this.spawnBall(true); } } public async goal(bonusScore: number, position: Vec3) { this.addScore(this._isMultiBall ? bonusScore * 2 : bonusScore, ScoreType.Goal, position); this.setCurrentBallInGame(-1); if (this._currentBallInGame <= 0) { await Utilities.delay(TimeConfig.DelayGoal); this.spawnBall(true); } } public async destroyEnvironmentObject(bonusScore: number, position: Vec3, bonusTime?: number) { if (bonusScore) { this.addScore(bonusScore, ScoreType.DestroyObject, position); await Utilities.delay(0.3); } if (bonusTime) { this.addTime(bonusTime); const floatingScore = this._FloatingScorePool.get(this._floatingTextContainer, FloatingText); floatingScore.show(`+${bonusTime}⏰`, position, 0.7); } } public addTime(time: number) { this._timer += time; } private gameOver() { this._ballPool.releaseAll(); this.changeGameState(GameState.GameOver); } public async play() { this._timer = this._timePlay + TimeConfig.DelayPLay; this._score = 0; this._currentBallInGame = 0; this._isMultiBall = false; SoundManager.instance.playBGM(this._backgroundMusic, 0.5); this.changeGameState(GameState.Playing); await Utilities.delay(TimeConfig.DelayPLay); this.spawnBall(true); } public onRevive() { throw new Error('Method not implemented.'); } }