Skip to content

Latest commit

 

History

History
911 lines (690 loc) · 22.6 KB

File metadata and controls

911 lines (690 loc) · 22.6 KB

Eva.js (互动游戏引擎)

Eva.js logo

npm-version npm-size npm-download

中文 | English

介绍

Eva.js 是一个专注于开发互动游戏项目的前端游戏引擎。

易用:Eva.js 提供开箱即用的游戏组件供开发人员立即使用。是的,它简单而优雅!

高性能:Eva.js 由高效的运行时和渲染管道 (Pixi.JS) 提供支持,这使得释放设备的全部潜力成为可能。

可扩展:得益于 ECS(实体-组件-系统)架构,你可以通过高度可定制的 API 扩展您的需求。唯一的限制是你的想象力!

文档

你可以在官方网站 eva.js.org 找到 Eva.js 的文档, 在 文档仓库 提交 PR 为 Eva.js 文档进行查漏补缺。

包列表

包名 描述
@eva/eva.js 核心引擎:Game、GameObject、Component、System、Resource
@eva/plugin-renderer 核心渲染器 (PixiJS)
@eva/plugin-renderer-img 图片渲染
@eva/plugin-renderer-text 文本渲染 (Text, HTMLText, BitmapText)
@eva/plugin-renderer-sprite 精灵图渲染
@eva/plugin-renderer-sprite-animation 帧动画
@eva/plugin-renderer-spine Spine 骨骼动画
@eva/plugin-renderer-dragonbone DragonBones 骨骼动画
@eva/plugin-renderer-lottie Lottie 动画
@eva/plugin-renderer-graphics 矢量图形绘制
@eva/plugin-renderer-nine-patch 九宫格缩放
@eva/plugin-renderer-tiling-sprite 平铺精灵
@eva/plugin-renderer-mask 遮罩/裁剪
@eva/plugin-renderer-mesh 透视网格变形
@eva/plugin-renderer-render 渲染属性 (alpha, zIndex, visible)
@eva/plugin-renderer-event 触摸/指针事件
@eva/plugin-sound 音频播放
@eva/plugin-transition 补间动画
@eva/plugin-a11y 无障碍支持
@eva/plugin-evax 全局状态管理
@eva/plugin-matterjs 物理引擎 (Matter.js)
@eva/plugin-layout Flexbox 布局
@eva/plugin-stats 性能监控

使用

安装

npm i @eva/eva.js @eva/plugin-renderer @eva/plugin-renderer-img --save

快速开始

<canvas id="canvas"></canvas>
import { Game, GameObject, resource, RESOURCE_TYPE } from '@eva/eva.js';
import { RendererSystem } from '@eva/plugin-renderer';
import { Img, ImgSystem } from '@eva/plugin-renderer-img';

resource.addResource([
  {
    name: 'imageName',
    type: RESOURCE_TYPE.IMAGE,
    src: {
      image: {
        type: 'png',
        url: 'https://gw.alicdn.com/tfs/TB1DNzoOvb2gK0jSZK9XXaEgFXa-658-1152.webp',
      },
    },
    preload: true,
  },
]);

const game = new Game();
await game.init({
  systems: [
    new RendererSystem({
      canvas: document.querySelector('#canvas'),
      width: 750,
      height: 1000,
    }),
    new ImgSystem(),
  ],
});

const image = new GameObject('image', {
  size: { width: 750, height: 1319 },
  origin: { x: 0, y: 0 },
  position: { x: 0, y: -319 },
  anchor: { x: 0, y: 0 },
});

image.addComponent(
  new Img({
    resource: 'imageName',
  }),
);

game.scene.addChild(image);

API 参考

核心 - @eva/eva.js

Game

游戏引擎入口,管理系统、场景和游戏循环。

import { Game } from '@eva/eva.js';

const game = new Game();
await game.init({
  autoStart: true,       // 自动启动游戏循环(默认: true)
  frameRate: 60,         // 目标帧率(默认: 60)
  systems: [],           // 注册的系统
  needScene: true,       // 自动创建默认场景(默认: true)
});
方法 描述
addSystem(system) 注册系统
removeSystem(system) 移除系统
getSystem(SystemClass) 获取已注册的系统实例
start() 启动游戏循环
pause() 暂停游戏循环
resume() 恢复游戏循环
destroy() 销毁游戏
loadScene({ scene, mode?, params? }) 加载场景
findByName(name) 按名称查找第一个 GameObject
findAllByName(name) 按名称查找所有 GameObject
属性 描述
scene 当前主场景
playing 游戏是否运行中
ticker Ticker 实例
systems 已注册系统数组

GameObject

ECS 架构中的实体,持有组件并支持父子层级关系。

import { GameObject } from '@eva/eva.js';

const go = new GameObject('name', {
  position: { x: 0, y: 0 },
  size: { width: 100, height: 100 },
  origin: { x: 0, y: 0 },     // 变换原点
  anchor: { x: 0.5, y: 0.5 }, // 锚点
  scale: { x: 1, y: 1 },
  rotation: 0,                  // 弧度
  skew: { x: 0, y: 0 },
});
方法 描述
addComponent(component) 添加组件实例
addComponent(ComponentClass, params) 通过类+参数添加组件
removeComponent(component) 移除组件
getComponent(ComponentClass) 通过类获取组件
addChild(gameObject) 添加子 GameObject
removeChild(gameObject) 移除子 GameObject
remove() 从父级移除自身
destroy() 销毁自身及所有子对象
属性 描述
transform Transform 组件
parent 父 GameObject
children 子 GameObject 列表
scene 所属场景

Component

所有组件的基类。

import { Component } from '@eva/eva.js';

class MyComponent extends Component {
  static componentName = 'MyComponent';

  init(params) {}        // 添加到 GameObject 时调用
  awake() {}             // init 之后调用
  start() {}             // 第一次 update 之前调用
  update({ deltaTime, time, fps }) {}
  lateUpdate({ deltaTime }) {}
  onPause() {}
  onResume() {}
  onDestroy() {}
}

System

所有系统的基类,每帧处理组件。

import { System } from '@eva/eva.js';

class MySystem extends System {
  static systemName = 'MySystem';

  init(params) {}
  awake() {}
  start() {}
  update({ deltaTime, time, fps }) {}
  lateUpdate({ deltaTime }) {}
  onPause() {}
  onResume() {}
  onDestroy() {}
}

resource

全局资源管理器单例。

import { resource, RESOURCE_TYPE, LOAD_EVENT } from '@eva/eva.js';

// 添加资源
resource.addResource([
  {
    name: 'img',
    type: RESOURCE_TYPE.IMAGE,
    src: { image: { type: 'png', url: 'path/to/image.png' } },
    preload: true,
  },
]);

// 预加载所有 preload:true 的资源
resource.preload();

// 监听加载进度
resource.on(LOAD_EVENT.PROGRESS, (progress) => {});  // 0-1
resource.on(LOAD_EVENT.COMPLETE, () => {});
resource.on(LOAD_EVENT.ERROR, (err) => {});

// 获取资源(异步)
const res = await resource.getResource('img');

// 销毁资源
resource.destroy('img');

RESOURCE_TYPE: IMAGE, SPRITE, SPRITE_ANIMATION, AUDIO, VIDEO, FONT


渲染系统 - @eva/plugin-renderer

基于 PixiJS 的核心渲染系统,所有渲染插件均需依赖此系统。

import { RendererSystem } from '@eva/plugin-renderer';

new RendererSystem({
  canvas: document.querySelector('#canvas'),
  width: 750,
  height: 1000,
  preference: 'webgl',   // 'webgl' | 'webgpu' | 'canvas'
  backgroundAlpha: 1,    // 0=完全透明, 1=不透明
  antialias: false,
  resolution: window.devicePixelRatio,
  backgroundColor: 0x000000,
  enableScroll: false,
  debugMode: false,
});
方法 描述
resize(width, height) 调整画布大小

图片 - @eva/plugin-renderer-img

渲染单张图片。

import { Img, ImgSystem } from '@eva/plugin-renderer-img';

// 注册系统
game.addSystem(new ImgSystem());

// 添加组件
go.addComponent(new Img({ resource: 'imageName' }));
参数 类型 描述
resource string 资源名称 (IMAGE 类型)

精灵 - @eva/plugin-renderer-sprite

从精灵图集中渲染子图片。

import { Sprite, SpriteSystem } from '@eva/plugin-renderer-sprite';

game.addSystem(new SpriteSystem());

go.addComponent(new Sprite({
  resource: 'spriteName',
  spriteName: 'frame01.png',
}));
参数 类型 描述
resource string 资源名称 (SPRITE 类型)
spriteName string 精灵图集中的子图片名称

精灵动画 - @eva/plugin-renderer-sprite-animation

从精灵图集播放逐帧动画。

import { SpriteAnimation, SpriteAnimationSystem } from '@eva/plugin-renderer-sprite-animation';

game.addSystem(new SpriteAnimationSystem());

const anim = go.addComponent(new SpriteAnimation({
  resource: 'animResource',
  autoPlay: true,
  speed: 100,       // 每帧毫秒数
  forwards: false,  // 完成后停留在最后一帧
}));

anim.play(3);                // 播放 3 次
anim.gotoAndPlay(5);         // 跳到第 5 帧并播放
anim.gotoAndStop(0);         // 跳到第 0 帧并停止
anim.stop();
参数 类型 默认值 描述
resource string 资源名称 (SPRITE_ANIMATION 类型)
autoPlay boolean true 加载后自动播放
speed number 100 每帧毫秒数
forwards boolean false 完成后停留在最后一帧
属性 描述
currentFrame 当前帧号
totalFrames 总帧数
事件 描述
complete 所有播放次数完成
loop 每次循环
frameChange 帧变化

文本 / HTML文本 / 位图文本 - @eva/plugin-renderer-text

三种渲染模式的文本内容渲染。

import { Text, HTMLText, BitmapText, TextSystem } from '@eva/plugin-renderer-text';

game.addSystem(new TextSystem());

// Canvas 文本
go.addComponent(new Text({
  text: 'Hello World',
  style: {
    fontFamily: 'Arial',
    fontSize: 36,
    fill: 0xff1010,
    stroke: { color: 0xffffff, width: 5 },
    fontWeight: 'bold',
    wordWrap: true,
    wordWrapWidth: 200,
    align: 'center',
    dropShadow: {
      alpha: 1, angle: Math.PI / 6,
      blur: 5, color: 0x000000, distance: 5,
    },
  },
}));

// HTML 富文本(支持 <b>, <i>, <span>, <br> 标签)
go.addComponent(new HTMLText({
  text: '<b>粗体</b> 和 <i>斜体</i>',
  style: {
    fontFamily: 'Arial',
    fontSize: 24,
    fill: 0x000000,
    wordWrap: true,
    wordWrapWidth: 300,
  },
}));

// 位图文本(使用位图字体资源)
go.addComponent(new BitmapText({
  text: 'Score: 100',
  style: {
    fontFamily: 'myBitmapFont',
    fontSize: 32,
  },
}));

图形 - @eva/plugin-renderer-graphics

使用 PixiJS Graphics API 绘制矢量图形。

import { Graphics, GraphicsSystem } from '@eva/plugin-renderer-graphics';

game.addSystem(new GraphicsSystem());

const comp = go.addComponent(new Graphics());
// 直接使用 PixiJS Graphics API
comp.graphics.rect(0, 0, 100, 100);
comp.graphics.fill(0xff0000);
comp.graphics.circle(50, 50, 30);
comp.graphics.fill(0x00ff00);

九宫格 - @eva/plugin-renderer-nine-patch

九宫格缩放,角落保持固定,边缘和中心拉伸。

import { NinePatch, NinePatchSystem } from '@eva/plugin-renderer-nine-patch';

game.addSystem(new NinePatchSystem());

go.addComponent(new NinePatch({
  resource: 'panelImg',
  leftWidth: 20,
  topHeight: 20,
  rightWidth: 20,
  bottomHeight: 20,
}));
参数 类型 描述
resource string 图片或精灵资源名称
spriteName string 子图片名称(使用 SPRITE 资源时)
leftWidth number 左侧不拉伸宽度
topHeight number 顶部不拉伸高度
rightWidth number 右侧不拉伸宽度
bottomHeight number 底部不拉伸高度

平铺精灵 - @eva/plugin-renderer-tiling-sprite

在区域内重复平铺纹理。

import { TilingSprite, TilingSpriteSystem } from '@eva/plugin-renderer-tiling-sprite';

game.addSystem(new TilingSpriteSystem());

go.addComponent(new TilingSprite({
  resource: 'bgTexture',
  tileScale: { x: 1, y: 1 },
  tilePosition: { x: 0, y: 0 },
}));
参数 类型 默认值 描述
resource string 图片资源名称
tileScale {x, y} {x:1, y:1} 平铺缩放
tilePosition {x, y} {x:0, y:0} 平铺偏移

遮罩 - @eva/plugin-renderer-mask

裁剪 GameObject 的显示区域。

import { Mask, MaskSystem, MASK_TYPE } from '@eva/plugin-renderer-mask';

game.addSystem(new MaskSystem());

// 圆形遮罩
go.addComponent(new Mask({
  type: MASK_TYPE.Circle,
  style: { x: 50, y: 50, radius: 50 },
}));

// 矩形遮罩
go.addComponent(new Mask({
  type: MASK_TYPE.Rect,
  style: { x: 0, y: 0, width: 200, height: 100 },
}));

// 图片遮罩(基于透明度)
go.addComponent(new Mask({
  type: MASK_TYPE.Img,
  resource: 'maskImg',
  style: { x: 0, y: 0, width: 200, height: 200 },
}));

MASK_TYPE: Circle, Ellipse, Rect, RoundedRect, Polygon, Img, Sprite


透视网格 - @eva/plugin-renderer-mesh

通过调整四个角点实现透视网格变形。

import { PerspectiveMesh, MeshSystem } from '@eva/plugin-renderer-mesh';

game.addSystem(new MeshSystem());

const mesh = go.addComponent(new PerspectiveMesh({
  resource: 'cardImg',
  verticesX: 10,    // 水平顶点数
  verticesY: 10,    // 垂直顶点数
}));

// 设置四个角点 (x0,y0 x1,y1 x2,y2 x3,y3)
// 左上, 右上, 右下, 左下
mesh.setCorners(0, 0, 200, 20, 180, 300, 20, 280);

渲染属性 - @eva/plugin-renderer-render

控制渲染属性:可见性、透明度、层级排序。

import { Render, RenderSystem } from '@eva/plugin-renderer-render';

game.addSystem(new RenderSystem());

go.addComponent(new Render({
  alpha: 1,               // 透明度 0-1
  visible: true,
  zIndex: 0,
  sortableChildren: false,
  resolution: 1,
}));

事件 - @eva/plugin-renderer-event

为 GameObject 添加触摸/指针交互。

import { Event, EventSystem, HIT_AREA_TYPE } from '@eva/plugin-renderer-event';

game.addSystem(new EventSystem());

const evt = go.addComponent(new Event({
  hitArea: {
    type: HIT_AREA_TYPE.Rect,
    style: { x: 0, y: 0, width: 100, height: 100 },
  },
}));

evt.on('tap', (e) => {
  console.log('点击了!', e.data.position);
});

evt.on('touchstart', (e) => {
  e.stopPropagation(); // 阻止冒泡
});

事件类型: tap, touchstart, touchmove, touchend, touchendoutside, touchcancel

事件数据:

  • data.position - 全局坐标 {x, y}
  • data.localPosition - 本地坐标 {x, y}
  • data.pointerId - 指针 ID
  • gameObject - 目标 GameObject
  • stopPropagation() - 阻止事件冒泡

HIT_AREA_TYPE: Circle, Ellipse, Polygon, Rect, RoundedRect


Spine - @eva/plugin-renderer-spine

播放 Spine 骨骼动画。使用 @eva/plugin-renderer-spine36 支持 Spine 3.6 格式。

import { Spine, SpineSystem } from '@eva/plugin-renderer-spine';

game.addSystem(new SpineSystem());

const spine = go.addComponent(new Spine({
  resource: 'spineRes',
  animationName: 'idle',
  autoPlay: true,
  scale: 1,
}));

spine.play('walk', true);               // 循环播放
spine.stop();
spine.addAnimation('attack', 0, false); // 队列动画
spine.setMix('idle', 'walk', 0.2);      // 过渡混合
spine.setAttachment('weapon', 'sword'); // 插槽附件(换装)
spine.getBone('head');                   // 获取骨骼

// 将 GameObject 挂载到 spine 插槽
spine.addSlotObject('hand', weaponGameObject);
spine.removeSlotObject(weaponGameObject);
事件 描述
complete 动画完成
start 动画开始
end 动画结束
event Spine 事件触发
interrupt 动画被打断

Lottie - @eva/plugin-renderer-lottie

播放 Lottie (After Effects) 动画。

import { Lottie, LottieSystem } from '@eva/plugin-renderer-lottie';

game.addSystem(new LottieSystem());

const lottie = go.addComponent(new Lottie({
  resource: 'lottieRes',
  autoStart: false,
}));

// 播放帧范围 [0, 60],无限循环
lottie.play([0, 60], { repeats: -1 });

// 带插槽替换的播放
lottie.play([0, 100], {
  slot: [
    { type: 'IMAGE', name: 'layerName', url: 'newImg.png' },
    { type: 'TEXT', name: 'textLayer', value: '新文本', style: { fontSize: 24 } },
  ],
});

// 命名图层的点击交互
lottie.onTap('buttonLayer', () => console.log('点击了'));
事件 描述
complete 播放完成
loopComplete 循环迭代
enterFrame 每帧

音频 - @eva/plugin-sound

基于 Web Audio API 的音频播放。

import { Sound, SoundSystem } from '@eva/plugin-sound';

game.addSystem(new SoundSystem({
  autoPauseAndStart: true,  // 与游戏暂停/恢复同步
}));

const sound = go.addComponent(new Sound({
  resource: 'bgm',
  autoplay: false,
  loop: true,
  volume: 0.8,
  speed: 1,
  muted: false,
  onEnd: () => console.log('播放结束'),
}));

sound.play();
sound.pause();
sound.resume();
sound.stop();
sound.volume = 0.5;
sound.muted = true;
属性 描述
playing 是否正在播放
volume 音量 (0-1)
muted 静音状态
state 'unloaded' / 'loading' / 'loaded'

过渡动画 - @eva/plugin-transition

带关键帧和缓动的补间动画。

import { Transition, TransitionSystem } from '@eva/plugin-transition';

game.addSystem(new TransitionSystem());

const render = go.addComponent(new Render({ alpha: 1 }));

const transition = go.addComponent(new Transition({
  group: {
    fadeIn: [
      {
        name: 'alpha',
        component: render,
        values: [
          { time: 0, value: 0, tween: 'ease-in' },
          { time: 1000, value: 1 },
        ],
      },
    ],
    moveRight: [
      {
        name: 'position.x',
        component: go.transform,
        values: [
          { time: 0, value: 0, tween: 'ease-out' },
          { time: 500, value: 300 },
        ],
      },
    ],
  },
}));

transition.play('fadeIn');
transition.play('moveRight', Infinity);  // 无限循环
transition.stop('fadeIn');

transition.on('finish', (name) => console.log(`${name} 完成`));

缓动函数: linear, ease-in, ease-out, ease-in-out, bounce-in, bounce-out, bounce-in-out


无障碍 - @eva/plugin-a11y

无障碍支持。在画布上方创建带 ARIA 属性的透明 DOM 层。

import { A11y, A11ySystem, A11yActivate } from '@eva/plugin-a11y';

game.addSystem(new A11ySystem({
  debug: false,
  activate: A11yActivate.CHECK,  // CHECK | ENABLE | DISABLE
  delay: 100,
}));

go.addComponent(new A11y({
  hint: '播放按钮',
  role: 'button',
  'aria-label': '开始游戏',
}));

物理引擎 - @eva/plugin-matterjs

基于 Matter.js 的 2D 物理引擎。

import { Physics, PhysicsSystem, PhysicsType } from '@eva/plugin-matterjs';

game.addSystem(new PhysicsSystem({
  resolution: 1,
  isTest: false,           // 调试渲染
  world: {
    gravity: { x: 0, y: 1 },
  },
}));

// 矩形刚体
go.addComponent(new Physics({
  type: PhysicsType.RECTANGLE,
  bodyOptions: {
    isStatic: false,
    restitution: 0.8,
    density: 0.01,
  },
}));

// 圆形刚体
go.addComponent(new Physics({
  type: PhysicsType.CIRCLE,
  radius: 25,
  bodyOptions: { restitution: 1 },
}));

// 多边形刚体
go.addComponent(new Physics({
  type: PhysicsType.POLYGON,
  sides: 6,
  radius: 30,
}));

PhysicsType: RECTANGLE, CIRCLE, POLYGON


布局 - @eva/plugin-layout

类 Flexbox 的布局系统。

import { Layout, LayoutChild, LayoutSystem } from '@eva/plugin-layout';

game.addSystem(new LayoutSystem());

// 容器
container.addComponent(new Layout({
  direction: 'row',           // 'row' | 'column'
  justifyContent: 'center',  // 'start' | 'center' | 'end' | 'space-between' | 'space-around'
  alignItems: 'center',      // 'start' | 'center' | 'end' | 'stretch'
  gap: 10,
  padding: [10, 20],         // number | [v,h] | [top,right,bottom,left]
  autoSize: true,             // 自动适应容器大小
}));

// 子项
child.addComponent(new LayoutChild({
  flexGrow: 1,
  flexShrink: 0,
  alignSelf: 'center',
  margin: 5,
  fixedSize: { width: 100 },
}));

性能监控 - @eva/plugin-stats

显示 FPS 等指标的性能监控面板。

import { Stats, StatsSystem } from '@eva/plugin-stats';

game.addSystem(new StatsSystem({
  show: true,
  style: { x: 0, y: 0, width: 200, height: 100 },
}));

go.addComponent(new Stats());

问题交流

可以在 Gitter 或者 微信 扫码 二维码 进行交流.

Issues

按照 Issue Reporting Checklist 提交 issue. 不符合准则的问题可能会立即关闭。

Changelog

版本记录.

Contribute

如何贡献

License

MIT, See LICENSE file.