# EVA.js学习笔记(四)实践fappy-bird游戏(上)
# EVA.js学习笔记(五)实践fappy-bird游戏(中)
# VA.js学习笔记(六)实践fappy-bird游戏(下)
目标:用eva.js在vue工程中实现fappy-bird游戏
完整的项目地址: demo
搭建vue框架,我使用的是vue cli3。先初始化好框架部分。删除不要的vue演示代码,定义自己的页面。
vue create evademo
然后安装eva.js
npm install @eva/eva.js
1、添加资源
首先将使用的资源图片统一放到一个js文件中,方便加载,新建一个resource.js文件,在里面定义资源对象。
一共使用了6个资源,其中2个静态图片(背景和地面),1个精灵图动画(小鸟扇翅膀),3个精灵图(开始界面菜单,结束界面菜单,上下管道)
资源图片放在了项目evademo/public/static 下面,(放在其他地方eva.js的resource读取不出来,有能力的小伙伴可以自己尝试来补充下)。
import { RESOURCE_TYPE } from "@eva/eva.js";
export default [
name: "bg",
type: RESOURCE_TYPE.IMAGE,
src: {
image: {
type: "png",
url: "./../../public/bg.png",
preload: true,
name: "ground",
type: RESOURCE_TYPE.IMAGE,
src: {
image: {
type: "png",
url: "@/../static/ground.png",
preload: true,
name: "ready",
type: RESOURCE_TYPE.SPRITE,
src: {
image: {
type: "png",
url: "@/../static/ready/ready.png",
json: {
type: "json",
url: "@/../static/ready/ready.json",
preload: true,
name: "bird",
type: RESOURCE_TYPE.SPRITE_ANIMATION,
src: {
image: {
type: "png",
url: "@/../static/bird/bird.png",
json: {
type: "json",
url: "@/../static/bird/bird.json",
preload: true,
name: 'over',
type: RESOURCE_TYPE.SPRITE,
src: {
image: {
type: 'png',
'@/../static/over/over.png',
json: {
type: 'json',
'@/../static/over/over.json',
preload: true,
name: 'bar',
type: RESOURCE_TYPE.SPRITE,
src: {
image: {
type: 'png',
'@/../static/bar/bar.png',
json: {
type: 'json',
'@/../static/bar/bar.json',
preload: true,
2、创建游戏对象
创建一个vue文件index.vue, 在其中创建canvas标签,导入相应的包,导入之前创建的资源js文件,添加资源,准备工作就完成了。
注意:在这里我们需要对之后会用到的一些组件安装、导入、并且在systems里设置new XXXX();如果不设置,在后面的addComponent中就不会有任何效果。如果发现添加了组件没有生效,记得检查是否在systems中配置了。
<template>
<canvas id="canvas"></canvas>
</div>
</template>
<script>
import { resource, Game, GameObject, RESOURCE_TYPE } from "@eva/eva.js";
import { Render, RenderSystem } from "@eva/plugin-renderer-render";
import { Event, EventSystem, HIT_AREA_TYPE } from "@eva/plugin-renderer-event";
import { Text, TextSystem } from "@eva/plugin-renderer-text";
import sources from "./birdResource";
import { Sprite, SpriteSystem } from "@eva/plugin-renderer-sprite";
import {
SpriteAnimation,
SpriteAnimationSystem,
} from "@eva/plugin-renderer-sprite-animation";
SpriteAnimation;
import { Transition, TransitionSystem } from "@eva/plugin-transition";
import {
TilingSprite,
TilingSpriteSystem,
} from "@eva/plugin-renderer-tiling-sprite";
import { PhysicsSystem, Physics, PhysicsType } from "@eva/plugin-matterjs";
import { Img, ImgSystem } from "@eva/plugin-renderer-img";
export default {
mounted() {
this.show();
methods: {
show() {
resource.addResource(sources);
this.game = new Game({
frameRate: 61, // 兼容Eva自身bug, 帧率必须大于60
autoStart: true, // 可选
systems: [
new RendererSystem({
canvas: document.querySelector("#canvas"),
width: this.sceneWidth,
height: this.sceneHeight,
resolution: window.devicePixelRatio / 2,
new TilingSpriteSystem(),
new TransitionSystem(),
new SpriteAnimationSystem(),
new RenderSystem(),
new EventSystem({
// moveWhenInside: true // 代表只有在元素内部才会执行move事件,默认为false
new ImgSystem(),
new TextSystem(),
new SpriteSystem(),
new PhysicsSystem({
resolution: window.devicePixelRatio / 2,
// isTest: true, // Whether to enable debugging mode
// element: document.querySelector(".debugger"),
world: {
gravity: {
y: 5, // gravity
</script>
<style>
#canvas {
width: 100%;
height: auto;
</style>
背景分为2个部分,一个部分背景,一部分是地面,用于判断小鸟落地。
1、添加背景
首先定义屏幕的宽度的长度
data() {
return {
sceneWidth: 750,
sceneHeight: (window.innerHeight / window.innerWidth) * 750,
game:''// 整个游戏的对象
创建的方法我们都放在creatBg()方法里
createBg() {
// 创建 game object
const bg = new GameObject("image", {
size: { width: this.sceneWidth, height: this.sceneHeight },
origin: { x: 0, y: 0 },
position: {
x: 0,
y: 0,
anchor: {
x: 0,
y: 0,
// 为 game object 添加 Image Component
bg.addComponent(
new Img({
resource: "bg",
this.game.scene.addChild(bg);
bg.addComponent(
new Render({
zIndex: 1, //将背景图放在底层
这样背景就会显示在我们的界面中了
2、添加地面
地面是一个png图片,如果直接添加的话对于之后的移动效果不太好实现,所以采用精灵平铺的方式,这样就可以做出一个无限长的地板图片,可以一直移动。通过地面的anchor、origin、scale属性将图置于背景图片的底部,这里的地板图片不是添加到game上,而是作为bg的addChild添加到了bg对象中
const ground = new GameObject("ground", {
size: { width: this.sceneWidth, height: this.sceneHeight / 4 },
position: { x: 0, y: 0 },
anchor: {
x: 0,
y: 1,//将地面至于背景底部
origin: {
x: 0.5,
y: 0.5,
scale: { x: 2, y: 2 }, // 缩放比例
const groundTilingSprite = new TilingSprite({
resource: "ground",
tileScale: { x: 1, y: 1 },
tilePosition: { x: 0, y: 0 },
ground.addComponent(groundTilingSprite);
//添加为地板添加物理属性,使小鸟可以落到地板上
ground.addComponent(
new Physics({
type: PhysicsType.RECTANGLE,
bodyOptions: {
isStatic: true,
bg.addChild(ground);//将地板添加在背景里
3、地面动起来
思路:通过改变Transform 属性,在每一帧调用一次position.x的位置改变
在Game对象中,有一个ticker.add方法可以执行每一帧调用一次的方法。
this.game.ticker.add(() => {
ground.tilePosition.x -= 1;
创建准备开始游戏界面
在我们的ready标题界面中,有2个元素(红色小鸟是后面添加,暂时当看不见),一个是绿色标题,一个是红色带点击部分的图片,2个图片放在了一个精灵图上
思路:创建一个总的readyBox的gameObject,然后分别添加2个元素进来
1、创建readyBox
const readyBox = new GameObject("readyBox", {
size: { width: 320, height: 80 },
position: {
x: 128,
y: this.sceneHeight / 4,
这里的x,y,宽度和高度是根据自己的需要计算出来的,主要是为了让第一个标题居中和定位他的离顶部的距离
2、添加标题和添加tap图片
添加标题的时候也是添加一个新的gameObject对象,在对象中添加Sprite组件,将我们的图片显示出来
const readyTitle = new GameObject("readyTitle", {
size: { width: 512, height: 144 },
position: {
x: 0,
y: 0,
readyTitle.addComponent(
new Sprite({
resource: "ready",
spriteName: "ready_title.png",
const readyTap = new GameObject("readyTitle", {
size: { width: 294, height: 273 },
position: {
x: 120,
y: 188,
readyTap.addComponent(
new Sprite({
resource: "ready",
spriteName: "ready_tap.png",
readyBox.addChild(readyTitle);
readyBox.addChild(readyTap);
this.game.scene.addChild(readyBox);
添加完成以后,还需要添加2个事件,就是隐藏和显示这个readyBox的事件。通过过渡动画来改变对象的透明度alpha来实现。
//如果需要在其他方法中调用,记得要把animation对象抛出来再调用
const animation = readyBox.addComponent(new Transition());
animation.group = {
hidden: [
name: "alpha",
component: render,
values: [
time: 0,
value: 1,
tween: "linear",
time: 20,
value: 0,
tween: "linear",
show: [
name: "alpha",
component: render,
values: [
time: 0,
value: 0,
tween: "linear",
time: 20,
value: 1,
tween: "linear",
通过调用创建的对象里的animation对象来触发显示隐藏
如果需要在其他方法中调用,记得要把animation对象抛出来再调用
animation.play("show", 1);//1代表执行1次
animation.play("hidden", 1);
3、添加点击开始事件
思路:通过交互事件实现,在我们需要点击的gameObject上添加Event对象,通过监听和触发emit来操作我们的raedyBox,由于我们只需要点击readyTap对象,所以事件只添加在这个对象上然后通过一个emit方法将操作抛出去,方便我们后续操作。之后在game对象的监听中调用。
const evt = readyTap.addComponent(
new Event({
type: HIT_AREA_TYPE.Rect,
evt.on("tap", () => {
//点击后调用
this.game.emit("on-game-ready");
//我们在之前的show()方法中监听抛出的来的方法
this.game.on("on-game-ready", (e) => {
//点击开始后你想做什么操作
EVA.js学习笔记(五)实践fappy-bird游戏(中)
EVA.js学习笔记(六)实践fappy-bird游戏(下