EVA.js学习笔记(四)实践fappy-bird游戏(上)

535 阅读5分钟

# 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',
        url:
          '@/../static/over/over.png',
      },
      json: {
        type: 'json',
        url:
          '@/../static/over/over.json',
      },
    },
    preload: true,
  },
  {
    name: 'bar',
    type: RESOURCE_TYPE.SPRITE,
    src: {
      image: {
        type: 'png',
        url:
          '@/../static/bar/bar.png',
      },
      json: {
        type: 'json',
        url:
          '@/../static/bar/bar.json',
      },
    },
    preload: true,
  },
];

2、创建游戏对象

创建一个vue文件index.vue, 在其中创建canvas标签,导入相应的包,导入之前创建的资源js文件,添加资源,准备工作就完成了。

注意:在这里我们需要对之后会用到的一些组件安装、导入、并且在systems里设置new XXXX();如果不设置,在后面的addComponent中就不会有任何效果。如果发现添加了组件没有生效,记得检查是否在systems中配置了。

<template>
  <div>
    <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;
});

底板动起来.gif

创建准备开始游戏界面

在我们的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游戏(下