Skip to content

动画图片源(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);

属性 ​

属性类型说明
ScaleVector2缩放
Speedfloat全局速度乘数
FlipHorizontalbool水平翻转
FlipVerticalbool垂直翻转
IsAnimationFinishedbool动画是否结束(仅有限循环)
IsPausedbool是否暂停
CurrentAnimationNamestring?当前动画名
EffectiveSpeedfloat实际速度(全局速度 × 动画速度)
FallbackFrameStateFrameState?动画结束时的回退帧

查询 ​

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 ​

属性类型说明
NormalizedTimefloat归一化时间(0.0–1.0)
CurrentFrameIndexint当前帧索引
TotalFrameCountint总帧数
CurrentLoopint当前循环次数
TimeInLoopMillisecondsint循环内已播放毫秒
TotalDurationMillisecondsint总时长

LayerBounds ​

属性类型说明
X, Yfloat左上角
Width, Heightfloat尺寸
CenterX, CenterYfloat中心点

GameDataAnimatedImage ​

数据编辑器中定义的动画资源:

属性类型说明
ImageImage精灵表图片
CanvasSizeSize画布尺寸
FramesList<AnimatedImageFrame>帧定义
AnimationTagsList<AnimatedImageAnimationTag>动画标签(名称、帧范围、循环)
LayerDefinesList<AnimatedImageLayerDefine>图层定义
FramesPerRowint每行帧数
GlobalScaleVector2全局缩放
GlobalSpeedfloat全局速度
FallbackFrameIndexint?回退帧索引

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

相关文档 ​