主题
动画图片源(AnimatedImageSource)
基于精灵表(sprite sheet)的帧动画系统,用于在 Canvas 上绘制 2D 动画。
如果动画本质上是一个普通 UI 控件,例如加载光效、按钮高亮、头像框或面板装饰,优先使用 GameUI.Control.Primitive.Sprites。AnimatedImageSource 更适合 2D 角色、敌人、飞行物、命名动画、图层、命中框和需要跟随游戏循环绘制的对象。完整选型见 GameUI 序列帧选型指南。
定义位置:GameCore.Animation.AnimatedImageSource
创建
csharp
var anim = new AnimatedImageSource(animatedImageLink);参数为 IGameLink<GameDataAnimatedImage>,指向数据编辑器中定义的动画资源。
播放控制
csharp
// 播放动画(按动画名,animationName 对应 GameDataAnimatedImage 中的 AnimationTags)
anim.PlayAnimation("idle", canvasElapsedMs);
anim.PlayAnimation("walk", canvasElapsedMs, startTimeInMilliseconds: 0,
startTimeSetType: AnimationStartTimeSetType.FromCurrentTime);
// 按时长播放(自动计算速度)
anim.PlayAnimationWithDuration("attack", canvasElapsedMs, targetDurationMilliseconds: 500);
// 暂停/恢复
anim.Pause(canvasElapsedMs);
anim.Resume(canvasElapsedMs);
anim.TogglePause(canvasElapsedMs);AnimationStartTimeSetType
| 值 | 说明 |
|---|---|
FromBeginning | 从动画开头播放 |
FromCurrentTime | 从当前 canvas 时间计算 |
绘制
在 Canvas / CanvasAnimated 的渲染事件中调用。动态游戏通常使用 CanvasAnimated.OnAnimatedRender,因为事件参数包含帧间隔时间和总时间:
csharp
// 方式 1:使用 AnimatedImageSource.Draw
anim.Draw(canvasImage, canvasElapsedMs, x, y);
// 方式 2:使用 Canvas 扩展方法
canvas.DrawAnimatedImage(anim, x, y);
// 绘制指定帧
anim.DrawFrame(canvasImage, frameIndex: 0, x, y);属性
| 属性 | 类型 | 说明 |
|---|---|---|
Scale | Vector2 | 缩放 |
Speed | float | 全局速度乘数 |
FlipHorizontal | bool | 水平翻转 |
FlipVertical | bool | 垂直翻转 |
IsAnimationFinished | bool | 动画是否结束(仅有限循环) |
IsPaused | bool | 是否暂停 |
CurrentAnimationName | string? | 当前动画名 |
EffectiveSpeed | float | 实际速度(全局速度 × 动画速度) |
FallbackFrameState | FrameState? | 动画结束时的回退帧 |
查询
csharp
// 获取当前帧状态
var frame = anim.GetExpectedFrameState(canvasElapsedMs);
// 获取播放进度
AnimationProgress? progress = anim.GetProgress(canvasElapsedMs);
if (progress.HasValue)
{
float normalized = progress.Value.NormalizedTime; // 0.0–1.0
int currentFrame = progress.Value.CurrentFrameIndex;
int totalFrames = progress.Value.TotalFrameCount;
int currentLoop = progress.Value.CurrentLoop;
}
// 获取图层边界(用于碰撞检测等)
LayerBounds? bounds = anim.GetLayerBounds("hitbox", canvasElapsedMs);
LayerBounds? worldBounds = anim.GetLayerWorldBounds("hitbox", canvasElapsedMs, worldX, worldY);AnimationProgress
| 属性 | 类型 | 说明 |
|---|---|---|
NormalizedTime | float | 归一化时间(0.0–1.0) |
CurrentFrameIndex | int | 当前帧索引 |
TotalFrameCount | int | 总帧数 |
CurrentLoop | int | 当前循环次数 |
TimeInLoopMilliseconds | int | 循环内已播放毫秒 |
TotalDurationMilliseconds | int | 总时长 |
LayerBounds
| 属性 | 类型 | 说明 |
|---|---|---|
X, Y | float | 左上角 |
Width, Height | float | 尺寸 |
CenterX, CenterY | float | 中心点 |
GameDataAnimatedImage
数据编辑器中定义的动画资源:
| 属性 | 类型 | 说明 |
|---|---|---|
Image | Image | 精灵表图片 |
CanvasSize | Size | 画布尺寸 |
Frames | List<AnimatedImageFrame> | 帧定义 |
AnimationTags | List<AnimatedImageAnimationTag> | 动画标签(名称、帧范围、循环) |
LayerDefines | List<AnimatedImageLayerDefine> | 图层定义 |
FramesPerRow | int | 每行帧数 |
GlobalScale | Vector2 | 全局缩放 |
GlobalSpeed | float | 全局速度 |
FallbackFrameIndex | int? | 回退帧索引 |
Image 字段使用引擎 UI 图片路径:图片文件放在项目 ui/ 下,代码或数据中的路径从 ui/ 下一级开始写。例如 ui/image/player/run.png 对应 Image = "image/player/run.png"。不要把精灵表图片放到 user_files。完整规则见 项目资源路径指南。
循环方向(AnimatedImageLoopDirection)
Forward, Backward, PingPong, PingPongFromEnd
结束行为(AnimationEndBehavior)
StopAtLastFrame, Hide, FallbackToDefault
完整示例
csharp
#if CLIENT
using GameCore.Animation;
using GameUI.Control.Primitive;
using GameUI.Control.Primitive.Struct;
public class SpriteRenderer : IGameClass
{
private static AnimatedImageSource? characterAnim;
private static CanvasAnimated? canvas;
public static void OnRegisterGameClass()
{
Game.OnGameTriggerInitialization += OnGameTriggerInitialization;
}
private static void OnGameTriggerInitialization()
{
if (Game.GameModeLink != ScopeData.GameDataGameMode.MapGameMode) return;
// animatedImageLink 是项目中定义的 GameLink<GameDataAnimatedImage, GameDataAnimatedImage>
characterAnim = new AnimatedImageSource(animatedImageLink);
canvas = new CanvasAnimated();
canvas.FullScreen();
canvas.StartTiming();
canvas.OnAnimatedRender += OnRender;
canvas.AddToVisualTree();
}
private static void OnRender(object? sender, CanvasAnimatedEventArgs e)
{
if (canvas == null || characterAnim == null) return;
int elapsed = e.TotalElapsedTimeInMilliseconds;
if (characterAnim.CurrentAnimationName == null)
{
characterAnim.PlayAnimation("idle", elapsed);
}
canvas.DrawAnimatedImage(characterAnim, 100f, 100f);
}
}
#endif相关文档
- GameUI 序列帧选型指南 —
SpritesvsAnimatedImageSource+CanvasAnimated - Canvas 绘制系统 — Canvas 绘制 API
- GameData 系统 — IGameLink 数据引用