我正在尝试使用Xamarin SkiaSharp实现流畅的动画。核心问题是调用canvasView.InvalidateSurface()之间的时间;并点击方法OnCanvasViewPaintSurface进行重绘可能会在3到30毫秒之间变化,当您在屏幕上移动对象时,外观会有些抖动。我试图通过在绘图代码中添加死循环来缓解这种情况,这虽然有些帮助,但不是一个很好的解决方案。我不明白为什么时间变化如此之大,我也看不出有什么办法。您无法在绘图代码中入睡。游戏如何实现流畅的动画?我的代码如下
async Task DoAnimationLoop()
{
while (DoAnimation)
{
AccumulatedTime = StopWatch1.ElapsedMilliseconds;
await Task.Delay(TimeSpan.FromMilliseconds(5));
if (AccumulatedTime > 50)
{
StopWatch1.Restart();
MoveItems();
SKCanvasView canvasView = Content as SKCanvasView;
TotalBounds = new Size(canvasView.Width,
canvasView.Height);
canvasView.InvalidateSurface();
}
}
}
private void OnCanvasViewPaintSurface(object sender,
SKPaintSurfaceEventArgs e)
{
AccumulatedTime = StopWatch1.ElapsedMilliseconds;
while (AccumulatedTime < 30)
{
AccumulatedTime = StopWatch1.ElapsedMilliseconds;
}
e.Surface.Canvas.Clear();
e.Surface.Canvas.DrawBitmap(Background, 0, 0);
foreach(Item item in AllItems)
{
e.Surface.Canvas.DrawBitmap(item.CurrentBitmap,
item.CurrentPositionX, item.CurrentPositionY);
}
}
答案 0 :(得分:0)
对于未来的读者:
根据经验,我通过创建具有 Bindable 属性的 SkiaSharp SKCanvasViews 来使用 SkiaSharp 获得最流畅的动画,这些属性可以使用 Xamarin.Forms.Animate 递增。 Animate 会根据您对其进行配置的变量来处理所有计时和休眠代码。如果需要循环,可以将 repeat
委托设置为在调用 Animate.Commit( ... repeat: () => true ...)
这是一个方法示例,它通过增加 ProgressBar
的 PercentageFilled
属性将进度条“填充”到 100%(不循环)。请注意您可以配置的计时设置:Refresh rate = 11ms
(相当于“90 fps”:1000ms/90 = 11.11),timeToAnimate
是以毫秒为单位完成动画所需的时间长度,并且您可以从多个缓动函数中进行选择。
private void AnimateProgressBar()
{
double startPercentage = 0; //start at 0 percent
double endPercentage = 1; //fill to 100 percent (Forms.Animate will evenly increment
//between 0 and 1 , and in this case the ProgressBar's OnPaintSurface method knows how to draw based
//on the given decimal i.e. if PercentageFilled is .5 it will draw the bar to
//50 percent of its possible max length)
uint timeToAnimate = 1000;
Xamarin.Forms.Animation animation = new Animation(v => _ProgressBar.PercentageFilled = (float)v, startPercentage, endPercentage, easing: Easing.CubicOut);
animation.Commit(_ProgressBar, "FillPercentage", length: timeToAnimate, finished: (l, c) => animation = null, rate: 11);
}
当 PercentageFilled
属性更改时,它会通过在 InvalidateSurface
方法中调用 InvalidateSurface()
来触发 OnPropertyChanged
。要在您的 OnPropertyChanged
派生类中像这样覆盖 SKCanvasView
:
class ProgressBar: SKCanvasView
{
//...
public BindableProperty PercentageFilledProperty =
BindableProperty.Create(nameof(PercentageFilled), typeof(float), typeof(ProgressBar), 0f);
public float PercentageFilled
{
get { return (float)GetValue(PercentageFilledProperty ); }
set { SetValue(PercentageFilledProperty , value); }
}
protected override void OnPropertyChanged(string propertyName = null)
{
base.OnPropertyChanged(propertyName);
InvalidateSurface();
}
protected override void OnPaintSurface(SKPaintSurfaceEventArgs args)
{
//Draws progress bar based on the PercentageFilled filled property
}
//....
}
在问题的代码中,似乎有很多项目正在按顺序移动(MoveItems();
),这种逐个移动绘制项目的技术可能是抖动的原因?但 MoveItems 似乎是您可能想要使用 Forms.Animate 的地方,即您可以使用 Forms.Animation
作为 MoveItems()
创建一个 Callback
。查看“Custom Animations in Xamarin.Forms”的微软文档,了解有关如何使用回调制作动画的更多信息。
另请查看 Konrad Müller 在 Medium 上撰写的“The basics to create custom Xamarin.Forms controls using SkiaSharp”,其中包含此有用的段落,如果您正在制作游戏,请考虑:
<块引用>基本的 SkiaSharp.Views.Forms 提供了两个视图,您可以将其用作 控件的基础:SKCanvasView 和 SKGLView。 CanvasView 使用 CPU 加速后端,而 GLView 使用 OpenGL,因此 GPU。您可能直觉地认为使用 GPU 来处理图形 操作总是更好的选择,但事实上,OpenGL 有很高的 创建 GL 上下文时的开销。 CanvasView 简单地分配 它需要的内存,只要有足够的 CPU 能力,它就会 可以毫无问题地渲染。理论上,CanvasView 应该是 更适合要求不高的渲染,而 GlView 更适合 复杂的渲染,但现代智能手机的强大功能使这些 差异大多不明显。我建议简单地坚持 如果渲染到了 CanvasView 并切换到 GlView 复杂,您会注意到性能问题。