123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- import * as PIXI from "pixi.js";
- import "pixi-plugin-bump";
- const { Application, Sprite, Container } = PIXI;
- const { TextureCache } = PIXI.utils;
- import { loaderRes } from "./util";
- import createGroundData from "./groudData";
- const GAME_RESULT_STATE = {
- ON: "游戏开始",
- OVER: "游戏失败",
- SUCC: "通关完成"
- };
- const ROLESTATE = {
- READY: "READY",
- RUN: "RUN",
- JUMP: "JUMP",
- DOWN: "DOWN"
- };
- class Game {
- constructor(wrapper) {
- this.wrapper = wrapper; //外层element
- this.stageWidth = 750;
- this.stageHeight = 1334;
- this.app = null;
- this.bump = null; //碰撞检测实例
- this.gameState = GAME_RESULT_STATE.ON; //游戏状态
- this._roleState = ROLESTATE.READY; //人物状态
- this.roleReadyStep = 0; //人物准备状态的当前帧数
- this.roleReadyTimer = null; //人物准备动画的计时器
- this.roleRunStep = 0; //人物跑动状态的当前帧数
- this.roleRunTimer = null; // 人物跑动动画的计时器
- this.fallingDown = false; //下坠状态
- //地面
- this.groundData = null; //地面构造数据
- this.groundArr = []; //地面元素数组
- this.totalWidth = 0; //总宽度,主要用于布局
- //倒计时
- this.uiCountdownTimes = 3; //倒计时次数 3 2 1 开始
- this.uiCountdownTimer = null; //倒计时计时器
- //ui
- this.uiRole = null; //人物sprite
- this.uiuiGroundContainer = null; //地面总容器
- this.uiGameTopTip = null; //游戏顶部提示
- this.uiCountdown = null; //游戏开始倒计时
- this.uiFireworksContanier = null; //烟花容器
- this.fireworkOpts = {
- green: {
- delay: 0,
- pos: {
- x: 588,
- y: 230
- }
- },
- orange: {
- delay: 300,
- pos: {
- x: 290,
- y: 375
- }
- },
- purple: {
- delay: 500,
- pos: {
- x: 400,
- y: 200
- }
- },
- red: {
- delay: 600,
- pos: {
- x: 450,
- y: 370
- }
- }
- };
- this.decibelArr = []; //声贝队列
- this.decibel = 0; //声贝峰值
- this.init();
- this.initProxy();
- }
- //底部小游戏
- init() {
- this.app = new Application({
- width: this.stageWidth,
- height: this.stageHeight,
- transparent: true
- });
- this.wrapper.appendChild(this.app.view);
- this.bump = new PIXI.extras.Bump();
- loaderRes(["/scene.json"], this.loadedSource, this);
- }
- //roleState代理
- initProxy() {
- Object.defineProperty(this, "roleState", {
- configurable: true,
- enumerable: true,
- get: () => {
- return this._roleState;
- },
- set: value => {
- if (this._roleState === value) return;
- this._roleState = value;
- this.clearRoleStateEffect();
- switch (value) {
- case ROLESTATE.READY:
- this.roleReadyStep = 0;
- this.roleReadyLoop();
- break;
- case ROLESTATE.RUN:
- this.roleRunStep = 0;
- this.roleRunLoop();
- break;
- case ROLESTATE.JUMP:
- this.uiRole.texture = TextureCache[`p_jump_0.png`];
- break;
- case ROLESTATE.DOWN:
- this.uiRole.texture = TextureCache[`p_jump_1.png`];
- break;
- }
- }
- });
- }
- clearRoleStateEffect() {
- clearTimeout(this.roleReadyTimer);
- clearTimeout(this.roleRunTimer);
- }
- roleReadyLoop() {
- this.roleReadyTimer = setTimeout(() => {
- this.uiRole.texture =
- TextureCache[`p_stand_${this.roleReadyStep % 2}.png`];
- this.roleReadyStep++;
- this.roleReadyLoop();
- }, 500);
- }
- roleRunLoop() {
- this.roleRunTimer = setTimeout(() => {
- this.uiRole.texture =
- TextureCache[`p_run_${this.roleRunStep % 2}.png`];
- this.roleRunStep++;
- this.roleRunLoop();
- }, 100);
- }
- loadedSource() {
- this.initGameReady();
- this.initGround();
- this.initRole();
- this.initFireworks();
- this.readyStart();
- }
- //开始倒计时
- initGameReady() {
- this.uiGameTopTip = Sprite.from("word_tips.png");
- this.uiGameTopTip.anchor.x = 0.5;
- this.uiGameTopTip.x = 375;
- this.uiGameTopTip.y = 20;
- this.app.stage.addChild(this.uiGameTopTip);
- this.uiCountdown = Sprite.from("word_3.png");
- this.uiCountdown.anchor.x = 0.5;
- this.uiCountdown.anchor.y = 0.5;
- this.uiCountdown.x = 375;
- this.uiCountdown.y = 400;
- this.app.stage.addChild(this.uiCountdown);
- }
- // 创建地面容器
- initGround() {
- this.groundData = createGroundData();
- this.groundArr = [];
- this.totalWidth = 0;
- this.uiGroundContainer = new Container();
- this.uiGroundContainer._vx = 2.5;
- this.app.stage.addChild(this.uiGroundContainer);
- this.groundData.forEach(this.createGround.bind(this));
- this.totalWidth = this.uiGroundContainer.width;
- }
- //生成地面迭代器函数
- createGround(groundData, index, arr) {
- let { height, blockNum, gap } = groundData;
- this.totalWidth += gap;
- const groundUnitContainer = new Container();
- groundData.x = groundUnitContainer.x = this.totalWidth;
- groundData.y = groundUnitContainer.y = this.stageHeight - height + 24; //草地高度除外
- groundData.width = blockNum * 100;
- groundUnitContainer.cacheAsBitmap = true;
- let totalX = 0;
- for (let i = 0; i < blockNum; i++) {
- const ground = Sprite.fromFrame(
- `bg_${i === 0 ? "l" : i === blockNum - 1 ? "r" : "m"}.png`
- );
- ground.x = totalX;
- ground.y = -24; //负值,刚好贴着地面。方便碰撞检测
- groundUnitContainer.addChild(ground);
- totalX += ground.width;
- }
- this.totalWidth += groundData.width;
- this.groundArr.push(groundUnitContainer);
- this.uiGroundContainer.addChild(groundUnitContainer);
- //最后一个,需要添加通关门UI
- if (index === arr.length - 1) {
- let uiCastle = Sprite.from("castle.png");
- uiCastle.anchor.x = 0.7;
- uiCastle.anchor.y = 1;
- uiCastle.x = totalX;
- groundUnitContainer.addChild(uiCastle);
- }
- }
- //创建角色
- initRole() {
- this.uiRole = Sprite.fromFrame("people.png");
- this.uiRole.x = this.stageWidth / 2;
- this.uiRole.y = this.stageHeight - this.groundData[0].height + 24; //初始化位置
- this.uiRole._vy = 400 / 60;
- this.uiRole.anchor.x = 0.5;
- this.uiRole.anchor.y = 1;
- this.app.stage.addChild(this.uiRole);
- this.roleState = ROLESTATE.READY;
- }
- //生成烟花
- initFireworks() {
- this.uiFireworksContanier = new Container();
- Object.keys(this.fireworkOpts).forEach(key => {
- let firework = this.fireworkOpts[key];
- firework.frameFirework = 0; //下标
- firework.timer = null; //计时器
- let uiFirework = Sprite.from(
- `fireworks_${key}_${firework.frameFirework}.png`
- ); //ui
- uiFirework.anchor.x = 0.5;
- uiFirework.anchor.y = 0.5;
- uiFirework.x = firework.pos.x;
- uiFirework.y = firework.pos.y;
- firework.uiFirework = uiFirework;
- this.uiFireworksContanier.addChild(uiFirework);
- });
- this.uiFireworksContanier.visible = false;
- this.app.stage.addChild(this.uiFireworksContanier);
- }
- //开始倒计时
- readyStart() {
- this.uiCountdownTimes = 3;
- this.uiCountdown.texture = TextureCache[`word_3.png`];
- this.uiCountdown.visible = true;
- this.countDownLoop();
- }
- countDownLoop() {
- this.uiCountdownTimer = setTimeout(() => {
- this.uiCountdownTimes--;
- if (this.uiCountdownTimes === 0) {
- this.uiCountdown.texture = TextureCache[`word_start.png`];
- this.gameStart();
- } else if (this.uiCountdownTimes < 0) {
- clearTimeout(this.uiCountdownTimer);
- this.uiCountdown.visible = false;
- } else {
- this.uiCountdown.texture =
- TextureCache[`word_${this.uiCountdownTimes}.png`];
- }
- this.countDownLoop();
- }, 1000);
- }
- gameStart() {
- this.roleState = ROLESTATE.RUN;
- this.groundWalk();
- }
- restartGame() {
- console.log("restartGame");
- this.gameState = GAME_RESULT_STATE.ON;
- this.uiGroundContainer.x = 0;
- this.uiRole.x = 375;
- this.uiRole.y = this.groundData[0].y;
- this.gameResultContainer.visible = false;
- this.decibel = 0;
- this.roleState = ROLESTATE.RUN;
- this.app.ticker.start();
- }
- //每帧多要去判断人物状态
- walkLoop() {
- this.uiGroundContainer.x -= this.uiGroundContainer._vx;
- let rolePosX = Math.abs(this.uiGroundContainer.x - 375); //人物所在位置
- let rolePosY = this.uiRole.y;
- if (rolePosX >= this.totalWidth - 375) {
- console.log("完成游戏");
- this.gameSucc();
- return;
- } else if (rolePosY >= this.stageHeight) {
- console.log("掉坑里挂了");
- this.gameOver();
- return;
- }
- //寻找人物是否在地面上方。如果不在,就寻找下一个可能会碰撞的地面
- let index = this.groundData.findIndex(
- ground =>
- rolePosX >= ground.x && rolePosX <= ground.x + ground.width
- );
- if (index === -1) {
- /**
- * 掉坑里了
- * 如果不是跳起状态,默认下坠
- * */
- console.log("在坑里的范围内");
- if ((this.roleState !== ROLESTATE.JUMP && this.roleState !== ROLESTATE.DOWN) && !this.fallingDown) {
- this.fallDown();
- }
- //寻找下一个地面作为碰撞检测
- index = this.groundData.findIndex(ground => rolePosX <= ground.x);
- }
- if (index >= 0) {
- /**
- * 人物下方的方块下标
- * 判断是否和方块有碰撞
- */
- let ground = this.groundArr[index];
- if (ground.y <= rolePosY) {
- let collision = this.bump.hit(
- this.uiRole,
- ground,
- true,
- true,
- true
- );
- console.log("collision", collision);
- if (collision) {
- switch (collision) {
- case "bottom":
- console.log("站上了");
- this.standGround();
- break;
- default:
- console.log("撞上了");
- this.gameOver();
- break;
- }
- }
- }
- }
- }
- //站在地面上
- standGround() {
- this.decibel = 0;
- this.decibelArr = [];
- this.roleState = ROLESTATE.RUN;
- this.fallingDown = false;
- this.app.ticker.remove(this.downLoop, this);
- }
- //最后即将通关时候,人物开始走动,而不是背景移动。
- roleWalk() {
- this.app.ticker.add(this.roleWalkLoop, this);
- }
- //人物停止走动
- roleStop() {
- this.app.ticker.remove(this.roleWalkLoop, this);
- }
- //人物走动帧函数
- roleWalkLoop() {
- this.uiRole.x += 2.5;
- if (this.uiRole.x >= 750 + this.uiRole.width / 2) {
- console.log("人物通关");
- this.roleStop();
- this.startFireworks();
- }
- }
- //背景移动,人物不动。
- groundWalk() {
- this.app.ticker.add(this.walkLoop, this);
- }
- //背景停止移动
- groundStop() {
- this.app.ticker.remove(this.walkLoop, this);
- }
- /**
- *
- * @param {number} decibel 声贝差值
- * 取声贝峰值算法
- * 1.如果声贝大于设置值,一旦呈下降趋势,则去当前峰值。
- * 2.如果声贝一直增长,取第30帧的值。
- */
- jump(decibel) {
- if (this.gameState === GAME_RESULT_STATE.OVER) return;
- if (
- this.roleState === ROLESTATE.JUMP ||
- this.roleState === ROLESTATE.DOWN ||
- this.roleState === ROLESTATE.READY
- ) {
- return;
- }
- console.log(this.decibelArr);
- if (this.decibelArr.length >= 1) {
- if (this.decibelArr.length >= 30) {
- this.decibel = decibel * 10;
- this.startJump();
- } else {
- let isAscend = this.decibelArr.every(item => decibel >= item);
- if (isAscend) {
- this.decibelArr.push(decibel);
- } else {
- this.decibel = this.decibelArr.pop() * 10;
- this.startJump();
- }
- }
- } else {
- this.decibelArr.push(decibel);
- }
- }
- startJump() {
- console.log("startJump");
- this.app.ticker.add(this.jumpLoop, this);
- this.fallingDown = false;
- this.app.ticker.remove(this.fallDownLoop, this);
- this.app.ticker.remove(this.downLoop, this);
- }
- jumpLoop() {
- if (this.decibel > 0) {
- let distance = Math.min(this.uiRole._vy, this.decibel);
- this.uiRole.y -= distance;
- this.decibel -= distance;
- console.log("distance,decibel", distance, this.decibel);
- this.roleState = ROLESTATE.JUMP;
- if (this.decibel === 0) {
- this.app.ticker.remove(this.jumpLoop, this);
- this.app.ticker.add(this.downLoop, this);
- }
- }
- }
- downLoop() {
- this.roleState = ROLESTATE.DOWN;
- this.uiRole.y += this.uiRole._vy;
- }
- fallDownLoop() {
- this.uiRole.y += this.uiRole._vy;
- }
- fallDown() {
- this.fallingDown = true;
- this.roleState = ROLESTATE.DOWN;
- this.app.ticker.add(this.fallDownLoop, this);
- }
- startFireworks() {
- this.uiFireworksContanier.visible = true;
- Object.keys(this.fireworkOpts).forEach(key => {
- let firework = this.fireworkOpts[key];
- firework.frameFirework = 0;
- clearInterval(firework.timer);
- setTimeout(() => {
- firework.timer = setInterval(() => {
- firework.uiFirework.visible = firework.frameFirework <= 6;
- if (firework.frameFirework <= 6) {
- firework.uiFirework.texture =
- TextureCache[
- `fireworks_${key}_${firework.frameFirework}.png`
- ];
- }
- firework.frameFirework = (firework.frameFirework + 1) % 12;
- }, 100);
- }, firework.delay);
- });
- }
- gameOver() {
- this.roleState = ROLESTATE.READY;
- this.fallingDown = false;
- this.app.ticker.remove(this.fallDownLoop, this);
- this.gameState = GAME_RESULT_STATE.OVER;
- this.app.ticker.stop();
- }
- gameSucc() {
- this.groundStop();
- this.roleWalk();
- }
- }
- export default Game;
|