以自家猫猫为原型,做一款跳跳猫小游戏

1,585 阅读3分钟

一起用代码吸猫!本文正在参与【喵星人征文活动】

演示地址

先上地址: 跳跳猫

玩法说明

长按蓄力,松开起跳。

技术使用

使用vite打包,typescript语法,pixijs渲染引擎。

第一步:创建一个项目

方法1:使用vite创建项目,然后删除掉其中vue相关的所有东西,只保留ts,vite,然后执行npm install pixijs

方法2:npm init 然后使用npm安装vite,typescript,pixijs,并配置一下vite,tsconfig

我懒,所以就选择了第一种

完成后,删除src下的除了main.ts所有文件及文件夹,然后重新创建components,control,utils文件夹,分别对应代表组件,控制,工具,完成后目录基本如下:

image.png

第二步,创建场景

先引入pixijis

import * as PIXI from 'pixi.js';

然后初始化舞台,并添加到body下

const app = new PIXI.Application({width: 640, height: 360});
document.body.appendChild(app.view);

这时候运行项目就会出现一个黑色的背景

image.png

然后这时候舞台下没有任何东西,我们就需要将图片添加进去,这里我直接使用的是pixiLoader.shared,分别加载了背景,悬浮地面,猫等资源,如下:

PIXI.Loader.shared.add(['./image/bg.png', './image/ground.png', './image/replay.png', './image/jump_cat.json']).load(() => setup(app));

这样图片资源就载入了,但是还是没有使用,因此要使用资源,这里传入了一个setup函数,在该函数内,即可通过PIXI.Loader.shared.resources[你的资源路径(如上图的./image/bg.png)].texture得到该图片纹理,将其传给new PIXI.Sprite(图片纹理);,即可得到该Sprite实例。

创建函数:

/**
   * 创建精灵
   * @returns 
   */
  private createSprite(): PIXI.Sprite{
    let Sprite = PIXI.Sprite;
    let loader = PIXI.Loader.shared;
    return new Sprite(loader.resources[this.spritePath].texture);
  }

创建了sprite怎么将它放入舞台呢?这时候就要用到一个函数:

this.app.stage.addChild(this.sprite);

具体如下:

    this.sprite = new Sprite(texture);
    this.sprite.width = 640; //设置宽度
    this.sprite.height = 480; // 设置高度
    this.sprite.position.set(0, 0); // 设置坐标
    this.app.stage.addChild(this.catSprite); // 添加都舞台

运行效果:

image.png

第三步:生成悬浮地面

方法基本同上,只是需要生成多个,并且随机增加x坐标值,随机width值,效果如下:

image.png

第四步:生成一只猫

首先:我们得有一只猫:

db2387e25043afb326a22d69ff01de7.jpg

然后网上找猫猫模板,PS中大概照着她的样式做个横版猫猫,效果如下:

jump_cat.png

然后猫猫图片资源导入,用上面得同样得方法将其加入到舞台上,设定好坐标,效果如下:

image.png

最后一步:实现游戏逻辑

呃..呃..呃..

代码写得烂,实在是难以描述,已经开源了,大家想看的去看源码吧😣😣😣。

部分代码:

import * as PIXI from 'pixi.js';
import SceneSprite from '../components/scene';
import RoleSprite from '../components/role';
import { formatPower } from '../utils/common';
import { hitTestRectangle } from '../utils/testHit';
import Gameover from '../components/gameover';

export function setup (app:PIXI.Application) {
  
  let clickStatus = 0;
  let pointerTime = 0;
  // 构建场景
  const scene = new SceneSprite(app);
  const role = new RoleSprite(app);
  

  function reset(){
    scene.reset();
    role.reset();
    app.stage.interactive = true;
    app.stage.buttonMode = true;
    clickStatus = 0;
  }
  const over = new Gameover(app, reset);
  

  app.stage.interactive = true;
  app.stage.buttonMode = true;
  app.stage.on('pointerdown', () => {
    pointerTime = Date.now();
  })
  app.stage.on('pointerup', () => {
    if (clickStatus !== 0) {
      return;
    }
    clickStatus = 1;
    const power = formatPower(Date.now() - pointerTime);
    role.jump(power, (offsetX: number) => {
      const { children } = scene.getSprite().children[1] as PIXI.Container;
      let isHit = false;
      const {width,height,x,y} = role.getSprite();
      const newSize = {
        width: width - 50,
        height,
        x: x + 20,
        y
      }
      for (let i = 0; i < children.length; i++) {
        const groundSprite = children[i] as PIXI.Sprite;
        const ct = { ...groundSprite.getGlobalPosition(), width: groundSprite.width, height: groundSprite.height }
        isHit = isHit || hitTestRectangle(newSize, ct);
      }
      // 如果有碰撞,就继续
      if (isHit) {
        let counter = 0;
        const speed = 5;
        const executeAnimation = () => {
          counter+=speed;
          scene.move(speed);
          role.move(speed);
          if (counter >= offsetX) {
            clickStatus = 0;
            app.ticker.remove(executeAnimation);
          }
        }
        app.ticker.add(executeAnimation);
      } else { // 没有任何碰撞,就掉下去,游戏结束
        let counter = 0;
        const executeDown = () => {
          counter+=10;
          role.down(10);
          if (counter >= 160) {
            app.ticker.remove(executeDown);
            app.stage.interactive = false;
            app.stage.buttonMode = false;
            over.show(role.getScore());
          }
        }
        app.ticker.add(executeDown)
      }
      
    });
  })
}

结语

加油!