一起用代码吸猫!本文正在参与【喵星人征文活动】。
演示地址
先上地址: 跳跳猫
玩法说明
长按蓄力,松开起跳。
技术使用
使用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文件夹,分别对应代表组件,控制,工具,完成后目录基本如下:
第二步,创建场景
先引入pixijis
import * as PIXI from 'pixi.js';
然后初始化舞台,并添加到body下
const app = new PIXI.Application({width: 640, height: 360});
document.body.appendChild(app.view);
这时候运行项目就会出现一个黑色的背景
然后这时候舞台下没有任何东西,我们就需要将图片添加进去,这里我直接使用的是pixi的Loader.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); // 添加都舞台
运行效果:
第三步:生成悬浮地面
方法基本同上,只是需要生成多个,并且随机增加x坐标值,随机width值,效果如下:
第四步:生成一只猫
首先:我们得有一只猫:
然后网上找猫猫模板,PS中大概照着她的样式做个横版猫猫,效果如下:
然后猫猫图片资源导入,用上面得同样得方法将其加入到舞台上,设定好坐标,效果如下:
最后一步:实现游戏逻辑
呃..呃..呃..
代码写得烂,实在是难以描述,已经开源了,大家想看的去看源码吧😣😣😣。
部分代码:
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)
}
});
})
}
结语
加油!