跳转到内容

自定义绘制

大多数视觉是组件——精灵、瓦片地图、粒子。当它们 表达不了你要的东西——调试覆盖层、程序化形状、自定义几何——引擎把绘制层直接暴露 给你。

API 模型 适用场景
Draw 即时模式,全局——命令自动合批、每帧清空。 调试覆盖层、gizmo、任何每帧从头重画的东西。
Graphics 保留式路径记录器——构建一次,flush() 时经 Draw 回放。 带路径/贝塞尔/圆弧的矢量形状,不想每帧重新细分。
ShapeRenderer / Mesh2D 组件——带排序层、材质、编辑器 gizmo 的实体。 属于场景的形状/网格(见精灵)。

经验法则:视觉属于场景(参与排序、保存、可检视)就做成组件;属于某一帧 (调试、类 HUD 覆盖、程序化效果)就在回调里画。

注册一个回调,它每帧在渲染通道内执行,相机的 view-projection 已经就位——无需任何 设置,直接画:

import { registerDrawCallback, Draw } from 'esengine';
registerDrawCallback('debug-overlay', (elapsed) => {
Draw.line({ x: 0, y: 0 }, { x: 100, y: 50 }, { r: 1, g: 0, b: 0, a: 1 }, 2);
Draw.circleOutline({ x: 0, y: 0 }, aggroRadius, { r: 1, g: 1, b: 0, a: 0.5 });
});

回调画在场景之上;registerPreSceneDrawCallback 画在场景之下(其回调收到 { width, height, elapsed })。用 unregisterDrawCallback(id) 移除。坐标都是 世界空间。

Draw 是一个即时模式单例——命令自动合批、每帧清空,所以想让形状可见就每帧画:

方法 画什么
line(from, to, color, thickness?) 一条线段(粗细单位 px,默认 1)。
rect(pos, size, color, filled?) / rectOutline(pos, size, color, thickness?) 一个矩形——pos中心
circle(center, radius, color, filled?, segments?) / circleOutline(…) 一个圆(默认 32 段)。
texture(pos, size, textureHandle, tint?) 一个贴图四边形。
textureRotated(pos, size, rotation, textureHandle, tint?) 同上,带旋转(弧度)。
drawMesh(geometry, shader, transform) / drawMeshWithMaterial(geometry, material, transform?) 按句柄绘制自定义网格(见下面的 Geometry API)。
setLayer(layer) / setDepth(depth) 后续命令的排序。
setBlendMode(mode) / setDepthTest(on) 后续命令的混合 / 深度状态。
getDrawCallCount() / getPrimitiveCount() 本帧统计。

由路径构成的形状——折线、贝塞尔、圆弧、圆角矩形——用 Graphics 记录路径, flush() 时经 Draw 回放:

import { Graphics, registerDrawCallback } from 'esengine';
const g = new Graphics();
g.lineStyle(2, { r: 1, g: 1, b: 1, a: 1 });
g.beginFill({ r: 0, g: 0.5, b: 1, a: 1 });
g.drawRoundRect(-50, -25, 100, 50, 8);
g.endFill();
g.moveTo(0, 0);
g.curveTo(50, 80, 100, 0); // 二次贝塞尔
registerDrawCallback('hud-shape', () => g.flush());

路径方法:moveTo / lineTocurveTo(二次)/ cubicCurveToarcdrawRect / drawRoundRect / drawCircle / drawEllipseclear()lineStyle(thickness, color) 设描边;beginFill(color) / endFill() 的填充 支持矩形和圆(路径轮廓保持描边)。路径是保留的——构建一次,每帧 flush()

Mesh2D 组件渲染任意的索引三角形列表——程序化地形切片、径向血条弧、水面。 在组件上编写几何(positions 是 x,y 对;逐顶点 UV 与颜色可选):

cmds.spawn()
.insert(Transform, { position: { x: 0, y: 0, z: 0 } })
.insert(Mesh2D, {
geometry: {
positions: [0, 0, 100, 0, 50, 80],
colors: [1,0,0,1, 0,1,0,1, 0,0,1,1],
indices: [0, 1, 2],
},
});

网格共享标准渲染面:texturecolorlayerlitparallaxmaterial。场景里(或生成时)设置的几何自动上传;要在运行时改几何,走 Meshes2D 资源——setGeometry(entity, geometry) 重新上传(索引在引擎侧校验), 另有 getGeometry / clearGeometry。只改 geometry 字段本身不会被变更检测。

Mesh2D组件路径,Geometry 则是即时路径:它上传一个索引顶点缓冲,交回 一个句柄由你自己绘制——配上材质Draw.drawMeshWithMaterial(geometry, material, transform?)(或配原始着色器用 Draw.drawMesh(geometry, shader, transform))。

import { Geometry, Draw, Material, registerDrawCallback } from 'esengine';
const quad = Geometry.createQuad(120, 120); // 1 单位 ≙ 1 设计像素
const mat = Material.create({ shader }); // 一个 compileShader 材质
registerDrawCallback('custom-mesh', () => {
Draw.drawMeshWithMaterial(quad, mat); // 单位矩阵变换
});
字段 默认 说明
vertices 交错的顶点数据,一个 Float32Array(每次上传上限 64K 个 float)。
layout VertexAttributeDescriptor[]——按缓冲顺序、每个属性一个 { name, type }
indices Uint16ArrayUint32Array 三角形列表(每次上传上限 16K 个索引)。
dynamic false updateVertices 的频繁更新分配。

DataType 命名每个属性的形状:FloatFloat2Float3Float4IntInt2Int3Int4

方法 作用
create(options) 上传网格;返回 GeometryHandle(失败或数据超限时抛错)。
updateVertices(handle, vertices, offset?) 重写 dynamic 几何的顶点数据(offset 单位 float)。
release(handle) / isValid(handle) 释放 / 探测句柄。
createQuad(width?, height?) 居中四边形(默认 1×1),布局为 a_position + a_texCoord
createCircle(radius?, segments?) 三角扇圆形(默认 1、32),同一布局。
createPolygon(points) {x, y} 点扇形三角化的多边形,UV 取自包围盒。

辅助网格都用双属性 Float2 位置 + Float2 UV 布局,可直接配只写片元的 .esshader 材质。

小地图、头像、镜面——以及位图缓存的底层—— RenderTexture 分配一个离屏渲染目标。把绘制命令包在 begin/end 之间即渲染 进去;得到的 texture 是普通纹理句柄,哪里都能用(Sprite.textureDraw.texture、材质纹理参数):

import { RenderTexture, Draw } from 'esengine';
const rt = RenderTexture.create({ width: 256, height: 256, filter: 'linear' });
RenderTexture.begin(rt, viewProjection); // 你提供的正交 view-projection
Draw.circle({ x: 0, y: 0 }, 80, { r: 1, g: 0.5, b: 0, a: 1 });
RenderTexture.end();
sprite.texture = rt.texture;
字段 默认 说明
width / height 目标像素尺寸(必填)。
depth true 附加深度缓冲——纯 2D 合成可关掉省内存。
filter 'nearest' 结果被绘制时的采样过滤:'nearest''linear'
方法 作用
create(options) 分配目标;返回带 texture(组件用的资源表句柄)、textureId(底层设备 id)、widthheight 的句柄。
begin(rt, viewProjection) 把后续绘制重定向进目标(矩阵由你提供)。
end() 结束离屏渲染,回到屏幕。
resize(rt, width, height) 释放并重建——返回句柄(texture 句柄会变)。
getDepthTexture(rt) 以可采样纹理句柄取深度附件。
release(rt) 释放目标及其纹理。

这是低层接口:它不会替你渲染场景——只是把你在 begin/end 之间画的东西, 在你构建的 view-projection 矩阵下重定向到目标。