<pc-sprite>
<pc-sprite> 在实体处绘制角色、金币、地面或景物等二维图像。simple 显示资源单帧,animated 播放 <pc-sprite-clip> 子片段。
- 必须直接位于
<pc-entity>、<pc-model>或<pc-node>下。 - 可以包含多个命名
<pc-sprite-clip>,仅 animated 播放。
精灵资源
通过两类 <pc-asset> 构成,先声明图集:
- textureatlas:包含全部帧的图片,以及 data JSON 的 frames 对象。rect 为 [x, y, width, height] 像素坐标,从左下角测量。pivot 为放到实体位置的点,左下 [0, 0]、右上 [1, 1],[0.5, 0] 可使角色站稳。用于九宫格的帧还需 border,按左、下、右、上像素指定。
- 从图集取出的一个或多个 sprite。atlas 指图集,frame-keys 按序列出帧,pixels-per-unit 决定尺寸,例如高 24 像素、每单位 16 像素,对应 1.5 世界单位。render-mode 决定如何填充指定尺寸,详见平铺和切片。
<pc-app>
<!-- The atlas: one image, and where each frame lies in it -->
<pc-asset id="atlas" type="textureatlas" src="sprites.png"
srgb mipmaps="false" min-filter="nearest" mag-filter="nearest"
data='{ "frames": {
"coin-0": { "rect": [0, 0, 12, 12], "pivot": [0.5, 0.5] },
"coin-1": { "rect": [12, 0, 12, 12], "pivot": [0.5, 0.5] },
"coin-2": { "rect": [24, 0, 12, 12], "pivot": [0.5, 0.5] },
"platform": { "rect": [201, 46, 28, 16], "pivot": [0.5, 1], "border": [6, 0, 6, 0] }
} }'></pc-asset>
<!-- Sprites cut from it: a run of frames, at a scale -->
<pc-asset id="coin" type="sprite" atlas="atlas" frame-keys="coin-0 coin-1 coin-2" pixels-per-unit="16"></pc-asset>
<pc-asset id="platform" type="sprite" atlas="atlas" frame-keys="platform" pixels-per-unit="16" render-mode="tiled"></pc-asset>
<pc-scene>
<pc-entity name="coin" position="0 1 0">
<pc-sprite asset="coin"></pc-sprite>
</pc-entity>
</pc-scene>
</pc-app>
simple 通过 frame 选择资源的一帧,因此可用同一资源提供变体。精灵示例通过六帧 decor 绘制全部花、蘑菇和岩石;动画片段则顺序播放。
像素画
像素画需要上例图集的四个选项:
- min-filter 和 mag-filter 设 nearest,将像素采成硬边方块。默认线性会混合邻近像素,放大后模糊。
- mipmaps=false 关闭预模糊缩小副本,避免缩小时变软,以及相邻图集帧颜色渗入边缘。
- srgb 表明图片保存伽马编码颜色,与图像编辑器绘制的 PNG 一致,否则颜色偏淡。
每个图像像素覆盖整数个屏幕像素时才保持方正。正交摄像机应像示例脚本一样,在窗口变化时按画布高度计算 ortho-height。
属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
asset | 资源 ID | - | sprite 资源,仅 simple 使用;animated 从片段取帧,留空不显示 |
clip | String(字符串) | - | animated 活动片段名称,修改切换,重复设置可重播已结束动作,详见播放片段 |
color | Color(颜色) | "1 1 1 1" | 与纹理相乘的染色,Alpha 忽略,透明用 opacity |
enabled | Boolean(布尔) | "true" | 组件启用状态 |
flip-x | Boolean(布尔) | "false" | 水平镜像,例如改变角色朝向 |
flip-y | Boolean(布尔) | "false" | 垂直镜像 |
frame | Number(数字) | "0" | 当前资源的帧索引。动画中设置会跳到该帧继续,但片段开始总从首帧,因此标记中的 frame 无效 |
height | Number(数字) | "1" | 世界单位高度,仅 sliced 或 tiled 使用 |
opacity | Number(数字) | "1" | 不透明度,0 到 1 |
speed | Number(数字) | "1" | 全部片段 fps 的倍率,0 冻结 |
type | Enum(枚举) | "simple" | simple 显示资源单帧,animated 播放子片段,由 clip 选择 |
width | Number(数字) | "1" | 世界单位宽度,仅 sliced 或 tiled 使用 |
精灵不受光照、环境色、雾和色调映射影响,显示纹理乘 color 的颜色。作为 World 图层透明几何体,由远到近排序,不写深度。重叠精灵应有不同深度,例如示例沿 Z 从天空 -10 到史莱姆 1 分层。
播放片段
animated 改为播放命名子片段,每个 <pc-sprite-clip> 指定名称、资源、帧率和循环,由 clip 选择:
<pc-sprite id="slime" type="animated" clip="idle">
<pc-sprite-clip name="idle" asset="slime-idle" fps="8" loop></pc-sprite-clip>
<pc-sprite-clip name="hop" asset="slime-hop" fps="12" loop></pc-sprite-clip>
<pc-sprite-clip name="land" asset="slime-land" fps="12"></pc-sprite-clip>
</pc-sprite>
clip 实时生效,可从标记、开发者工具或 JavaScript 修改。
- 改变名称立即切换,从首帧开始,不支持交叉淡变。
- 重复设置同名会重启已结束片段,适合落地等单次动作;仍在播放则不受影响。
- 重新启用精灵从首帧播放当前片段。禁用组件或实体会停止,重新启用前可先设置 clip。
- 名称无匹配时不操作且不警告,之后添加同名子片段会立即播放。空 clip 保留当前播放。
非循环片段停在末帧,并触发底层 end,可据此切换下一动作:
import { whenReady } from '@playcanvas/web-components';
const slime = await whenReady('#slime');
slime.clip = 'land'; // the property and the attribute are interchangeable
slime.component.on('end', (clip) => {
if (clip.name === 'land') {
slime.clip = 'idle';
}
});
平铺与九宫格精灵
资源默认 simple 按帧尺寸绘制,width 和 height 无效。tiled 或 sliced 则按组件指定的世界单位宽高绘制,角落保留,边缘和中部拉伸或重复。
常见用途有两个:
- 重复地砖。 没有 border 时整帧属于中间区,tiled 可将一块 16 像素草地重复为任意长度。
- 任意尺寸面板或平台。 border 保持两端,例如示例平台左右各 6 像素,无论宽度如何,圆角不变,仅重复中间。
<!-- One tile of grass, repeated along 96 units of ground -->
<pc-sprite asset="grass" width="96" height="1"></pc-sprite>
<!-- The same platform sprite at two widths, its ends unchanged -->
<pc-sprite asset="platform" width="2.75"></pc-sprite>
<pc-sprite asset="platform" width="4.75"></pc-sprite>
宽高默认均为 1,除非该方向原本就是一个单位,应显式设置。例如每单位 16 像素、平台高 16 像素时只需设置宽。
示例
平铺草地、九宫格平台、同一三帧资源的三朵花,以及动画史莱姆。按钮切换 clip 和 flip-x,可改平台宽、史莱姆颜色或 speed=0.5:
<pc-app>
<!-- One atlas image holding every frame, read pixel for pixel -->
<pc-asset id="atlas" type="textureatlas" src="https://developer.playcanvas.com/assets/sprites.png" srgb mipmaps="false" min-filter="nearest" mag-filter="nearest" data='{ "frames": {
"slime-idle-0": { "rect": [150, 164, 24, 24], "pivot": [0.5, 0] },
"slime-idle-1": { "rect": [175, 164, 24, 24], "pivot": [0.5, 0] },
"slime-idle-2": { "rect": [200, 164, 24, 24], "pivot": [0.5, 0] },
"slime-idle-3": { "rect": [225, 164, 24, 24], "pivot": [0.5, 0] },
"slime-idle-4": { "rect": [150, 139, 24, 24], "pivot": [0.5, 0] },
"slime-idle-5": { "rect": [175, 139, 24, 24], "pivot": [0.5, 0] },
"slime-idle-6": { "rect": [200, 139, 24, 24], "pivot": [0.5, 0] },
"slime-idle-7": { "rect": [225, 139, 24, 24], "pivot": [0.5, 0] },
"slime-hop-0": { "rect": [150, 114, 24, 24], "pivot": [0.5, 0] },
"slime-hop-1": { "rect": [175, 114, 24, 24], "pivot": [0.5, 0] },
"slime-hop-2": { "rect": [200, 114, 24, 24], "pivot": [0.5, 0] },
"slime-hop-3": { "rect": [225, 114, 24, 24], "pivot": [0.5, 0] },
"slime-hop-4": { "rect": [150, 89, 24, 24], "pivot": [0.5, 0] },
"slime-hop-5": { "rect": [175, 89, 24, 24], "pivot": [0.5, 0] },
"grass": { "rect": [232, 46, 16, 16], "pivot": [0.5, 1] },
"platform": { "rect": [201, 46, 28, 16], "pivot": [0.5, 1], "border": [6, 0, 6, 0] },
"flower-red": { "rect": [245, 8, 7, 10], "pivot": [0.5, 0] },
"flower-yellow": { "rect": [13, 1, 7, 10], "pivot": [0.5, 0] },
"flower-blue": { "rect": [21, 1, 7, 10], "pivot": [0.5, 0] }
} }'></pc-asset>
<!-- Sprites cut from the atlas, at 16 pixels per world unit -->
<pc-asset id="slime-idle" type="sprite" atlas="atlas" frame-keys="slime-idle-0 slime-idle-1 slime-idle-2 slime-idle-3 slime-idle-4 slime-idle-5 slime-idle-6 slime-idle-7" pixels-per-unit="16"></pc-asset>
<pc-asset id="slime-hop" type="sprite" atlas="atlas" frame-keys="slime-hop-0 slime-hop-1 slime-hop-2 slime-hop-3 slime-hop-4 slime-hop-5" pixels-per-unit="16"></pc-asset>
<pc-asset id="grass" type="sprite" atlas="atlas" frame-keys="grass" pixels-per-unit="16" render-mode="tiled"></pc-asset>
<pc-asset id="platform" type="sprite" atlas="atlas" frame-keys="platform" pixels-per-unit="16" render-mode="tiled"></pc-asset>
<pc-asset id="flowers" type="sprite" atlas="atlas" frame-keys="flower-red flower-yellow flower-blue" pixels-per-unit="16"></pc-asset>
<pc-scene>
<pc-entity name="camera" position="0 1.5 10">
<pc-camera projection="orthographic" ortho-height="2.5" clear-color="#161a3c"></pc-camera>
</pc-entity>
<!-- Tiled: one tile repeated across the view -->
<pc-entity name="grass">
<pc-sprite asset="grass" width="12"></pc-sprite>
</pc-entity>
<!-- Tiled with borders: the ends keep their size, the middle repeats -->
<pc-entity name="platform" position="2.25 2.25 0">
<pc-sprite asset="platform" width="3.5"></pc-sprite>
</pc-entity>
<!-- Simple: one frame each of the same sprite -->
<pc-entity name="flower" position="-3.25 0 0.5">
<pc-sprite asset="flowers" frame="0"></pc-sprite>
</pc-entity>
<pc-entity name="flower" position="0.25 0 0.5">
<pc-sprite asset="flowers" frame="1"></pc-sprite>
</pc-entity>
<pc-entity name="flower" position="3 2.25 0.5">
<pc-sprite asset="flowers" frame="2"></pc-sprite>
</pc-entity>
<!-- Animated: plays the clip that clip names -->
<pc-entity name="slime" position="-1.5 0 1">
<pc-sprite id="slime" type="animated" clip="idle">
<pc-sprite-clip name="idle" asset="slime-idle" fps="8" loop></pc-sprite-clip>
<pc-sprite-clip name="hop" asset="slime-hop" fps="12" loop></pc-sprite-clip>
</pc-sprite>
</pc-entity>
</pc-scene>
</pc-app>
<div class="controls">
<button id="idle">Idle</button>
<button id="hop">Hop</button>
<button id="flip">Flip</button>
</div>
<style>
.controls {
position: absolute;
top: 12px;
left: 12px;
display: flex;
gap: 8px;
}
</style>
<script type="module">
const slime = document.getElementById('slime');
document.getElementById('idle').onclick = () => slime.setAttribute('clip', 'idle');
document.getElementById('hop').onclick = () => slime.setAttribute('clip', 'hop');
document.getElementById('flip').onclick = () => slime.toggleAttribute('flip-x');
</script>
完整精灵示例据此构建横向关卡,包含视差景物、可收集金币及跳跃史莱姆。
JavaScript 接口
可以通过 SpriteComponentElement API创建和操作元素。
asset、clip、color、flipX、flipY、frame、height、opacity、speed、type 和 width 均有对应 property。
component 是底层 SpriteComponent,就绪前为 null,支持 pause、resume、stop、clip(name)、layers、drawOrder 和 batchGroupId。切换优先使用元素 clip,而非底层 play(name),因为重新启用时按 clip 播放,play 不更新它。
组件在各播放阶段触发事件,参数为对应 SpriteAnimationClip:
| 组件事件 | 说明 |
|---|---|
play | 开始播放 |
pause | 暂停 |
resume | 恢复暂停 |
stop | 禁用或 stop() 导致停止,切换片段不触发 |
end | 非循环片段到达末帧 |
loop | 循环片段到末帧后重启 |
另请参阅
<pc-sprite-clip>:声明动画片段。<pc-asset>:图集和精灵资源。<pc-camera>:二维侧视正交摄像机。<pc-element>:在界面中绘制精灵。
示例:精灵。