中文 | 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);游戏引擎入口,管理系统、场景和游戏循环。
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 |
已注册系统数组 |
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 |
所属场景 |
所有组件的基类。
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() {}
}所有系统的基类,每帧处理组件。
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() {}
}全局资源管理器单例。
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
基于 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) |
调整画布大小 |
渲染单张图片。
import { Img, ImgSystem } from '@eva/plugin-renderer-img';
// 注册系统
game.addSystem(new ImgSystem());
// 添加组件
go.addComponent(new Img({ resource: 'imageName' }));| 参数 | 类型 | 描述 |
|---|---|---|
resource |
string |
资源名称 (IMAGE 类型) |
从精灵图集中渲染子图片。
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 |
精灵图集中的子图片名称 |
从精灵图集播放逐帧动画。
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 |
帧变化 |
三种渲染模式的文本内容渲染。
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,
},
}));使用 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);九宫格缩放,角落保持固定,边缘和中心拉伸。
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 |
底部不拉伸高度 |
在区域内重复平铺纹理。
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} |
平铺偏移 |
裁剪 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
通过调整四个角点实现透视网格变形。
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);控制渲染属性:可见性、透明度、层级排序。
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,
}));为 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- 指针 IDgameObject- 目标 GameObjectstopPropagation()- 阻止事件冒泡
HIT_AREA_TYPE: Circle, Ellipse, Polygon, Rect, RoundedRect
播放 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 (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 |
每帧 |
基于 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' |
带关键帧和缓动的补间动画。
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
无障碍支持。在画布上方创建带 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': '开始游戏',
}));基于 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
类 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 },
}));显示 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());按照 Issue Reporting Checklist 提交 issue. 不符合准则的问题可能会立即关闭。
版本记录.
MIT, See LICENSE file.
