# Eva.js (互动游戏引擎)




中文 | [English](./README.md)
## 介绍
Eva.js 是一个专注于开发互动游戏项目的前端游戏引擎。
**易用**:Eva.js 提供开箱即用的游戏组件供开发人员立即使用。是的,它简单而优雅!
**高性能**:Eva.js 由高效的运行时和渲染管道 ([Pixi.JS](http://pixijs.io/)) 提供支持,这使得释放设备的全部潜力成为可能。
**可扩展**:得益于 ECS(实体-组件-系统)架构,你可以通过高度可定制的 API 扩展您的需求。唯一的限制是你的想象力!
## 文档
你可以在官方网站 [eva.js.org](https://eva-engine.gitee.io/#/) 找到 Eva.js 的文档, 在 [文档仓库](https://github.com/eva-engine/eva-engine.github.io) 提交 PR 为 Eva.js 文档进行查漏补缺。
- [在线案例](https://eva.js.org/playground)
- [快速开始](https://eva.js.org/#/tutorials/quickstart)
- [基础脚手架](https://github.com/eva-engine/start-demo)
- [Awesome](https://github.com/eva-engine/awesome)
## 包列表
| 包名 | 描述 |
|------|------|
| [`@eva/eva.js`](#核心---evaevejs) | 核心引擎:Game、GameObject、Component、System、Resource |
| [`@eva/plugin-renderer`](#渲染系统---evaplugin-renderer) | 核心渲染器 (PixiJS) |
| [`@eva/plugin-renderer-img`](#图片---evaplugin-renderer-img) | 图片渲染 |
| [`@eva/plugin-renderer-text`](#文本--html文本--位图文本---evaplugin-renderer-text) | 文本渲染 (Text, HTMLText, BitmapText) |
| [`@eva/plugin-renderer-sprite`](#精灵---evaplugin-renderer-sprite) | 精灵图渲染 |
| [`@eva/plugin-renderer-sprite-animation`](#精灵动画---evaplugin-renderer-sprite-animation) | 帧动画 |
| [`@eva/plugin-renderer-spine`](#spine---evaplugin-renderer-spine) | Spine 骨骼动画 |
| [`@eva/plugin-renderer-dragonbone`](#dragonbone---evaplugin-renderer-dragonbone) | DragonBones 骨骼动画 |
| [`@eva/plugin-renderer-lottie`](#lottie---evaplugin-renderer-lottie) | Lottie 动画 |
| [`@eva/plugin-renderer-graphics`](#图形---evaplugin-renderer-graphics) | 矢量图形绘制 |
| [`@eva/plugin-renderer-nine-patch`](#九宫格---evaplugin-renderer-nine-patch) | 九宫格缩放 |
| [`@eva/plugin-renderer-tiling-sprite`](#平铺精灵---evaplugin-renderer-tiling-sprite) | 平铺精灵 |
| [`@eva/plugin-renderer-mask`](#遮罩---evaplugin-renderer-mask) | 遮罩/裁剪 |
| [`@eva/plugin-renderer-mesh`](#透视网格---evaplugin-renderer-mesh) | 透视网格变形 |
| [`@eva/plugin-renderer-render`](#渲染属性---evaplugin-renderer-render) | 渲染属性 (alpha, zIndex, visible) |
| [`@eva/plugin-renderer-event`](#事件---evaplugin-renderer-event) | 触摸/指针事件 |
| [`@eva/plugin-sound`](#音频---evaplugin-sound) | 音频播放 |
| [`@eva/plugin-transition`](#过渡动画---evaplugin-transition) | 补间动画 |
| [`@eva/plugin-a11y`](#无障碍---evaplugin-a11y) | 无障碍支持 |
| [`@eva/plugin-evax`](#evax---evaplugin-evax) | 全局状态管理 |
| [`@eva/plugin-matterjs`](#物理引擎---evaplugin-matterjs) | 物理引擎 (Matter.js) |
| [`@eva/plugin-layout`](#布局---evaplugin-layout) | Flexbox 布局 |
| [`@eva/plugin-stats`](#性能监控---evaplugin-stats) | 性能监控 |
## 使用
### 安装
```bash
npm i @eva/eva.js @eva/plugin-renderer @eva/plugin-renderer-img --save
```
### 快速开始
```html
```
```javascript
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
游戏引擎入口,管理系统、场景和游戏循环。
```javascript
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 架构中的实体,持有组件并支持父子层级关系。
```javascript
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
所有组件的基类。
```javascript
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
所有系统的基类,每帧处理组件。
```javascript
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
全局资源管理器单例。
```javascript
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 的核心渲染系统,所有渲染插件均需依赖此系统。
```javascript
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`
渲染单张图片。
```javascript
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`
从精灵图集中渲染子图片。
```javascript
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`
从精灵图集播放逐帧动画。
```javascript
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`
三种渲染模式的文本内容渲染。
```javascript
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 富文本(支持 , , ,
标签)
go.addComponent(new HTMLText({
text: '粗体 和 斜体',
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 绘制矢量图形。
```javascript
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`
九宫格缩放,角落保持固定,边缘和中心拉伸。
```javascript
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`
在区域内重复平铺纹理。
```javascript
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 的显示区域。
```javascript
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`
通过调整四个角点实现透视网格变形。
```javascript
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`
控制渲染属性:可见性、透明度、层级排序。
```javascript
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 添加触摸/指针交互。
```javascript
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 格式。
```javascript
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) 动画。
```javascript
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 的音频播放。
```javascript
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`
带关键帧和缓动的补间动画。
```javascript
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 层。
```javascript
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 物理引擎。
```javascript
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 的布局系统。
```javascript
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 等指标的性能监控面板。
```javascript
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](https://gitter.im/eva-engine/Eva.js) 或者 [微信](https://weixin.qq.com/) 扫码 [二维码](https://gw.alicdn.com/imgextra/i4/O1CN015W09ux1QD1RAxBkYN_!!6000000001941-2-tps-610-1279.png) 进行交流.
## Issues
按照 [Issue Reporting Checklist](.github/ISSUE_TEMPLATE.md) 提交 issue. 不符合准则的问题可能会立即关闭。
## Changelog
[版本记录](https://github.com/eva-engine/eva.js/releases).
## Contribute
[如何贡献](.github/HOW_TO_CONTRIBUTE.md)
## License
MIT, See [LICENSE file](./LICENSE).