作为一名C#开发者,你是否还在为WinForms的绘制性能和过时的API而烦恼?当你尝试使用最新版SkiaSharp时,是否遇到了DrawText方法过期的警告?别担心,今天我将手把手教你如何用现代化的SkiaSharp API构建一个完整的2D游戏精灵引擎,不仅解决API过期问题,还能完美支持中文显示!
这不仅仅是一次API升级,更是一次性能革命。我们将从零开始构建一个包含碰撞检测、动画系统和精灵管理的完整游戏引擎,让你的WinForms应用焕发新生。
在使用SkiaSharp进行WinForms开发时,开发者经常遇到这些问题:
SKCanvas.DrawText(string, float, float, SKPaint)方法被标记为过期最大的痛点在于:新版SkiaSharp要求使用SKFont对象,而不是直接在SKPaint中设置字体属性。

c#// ❌ 过期写法
canvas.DrawText("Hello World", 10, 30, paint);
// ✅ 现代写法
var font = new SKFont(typeface, 16);
canvas.DrawText("Hello World", 10, 30, SKTextAlign.Left, font, paint);
过期的方法不少。。。
c#private void InitializeFontsAndPaints()
{
// 创建支持中文的字体 - 多层备用方案
var typeface = SKTypeface.FromFamilyName("Microsoft YaHei",
SKFontStyleWeight.Normal, SKFontStyleWidth.Normal, SKFontStyleSlant.Upright)
?? SKTypeface.FromFamilyName("SimHei")
?? SKTypeface.FromFamilyName("Arial Unicode MS")
?? SKTypeface.Default;
infoFont = new SKFont(typeface, 16);
infoPaint = new SKPaint
{
Color = SKColors.White,
IsAntialias = true,
FilterQuality = SKFilterQuality.High // 高质量渲染
};
}
💎 最佳实践要点:
c#public partial class FrmMain : Form
{
private List<Sprite> sprites;
private Timer gameTimer;
private SKFont infoFont;
private SKPaint infoPaint;
private float currentFps;
// 资源缓存 - 避免重复创建
private SKBitmap backgroundBitmap;
}
c#private void DrawInfo(SKCanvas canvas)
{
if (infoFont == null || infoPaint == null) return;
// 绘制半透明背景
var backgroundPaint = new SKPaint
{
Color = SKColors.Black.WithAlpha(120),
Style = SKPaintStyle.Fill
};
canvas.DrawRect(5, 5, 200, 120, backgroundPaint);
backgroundPaint.Dispose();
canvas.DrawText($"精灵数量: {sprites.Count}", 10, 30, SKTextAlign.Left, infoFont, infoPaint);
canvas.DrawText($"游戏状态: {(isPlaying ? "运行中" : "暂停")}", 10, 55, SKTextAlign.Left, infoFont, infoPaint);
canvas.DrawText($"FPS: {CalculateFPS():F1}", 10, 80, SKTextAlign.Left, infoFont, infoPaint);
canvas.DrawText($"碰撞检测: 开启", 10, 105, SKTextAlign.Left, infoFont, infoPaint);
var canvasHeight = canvas.LocalClipBounds.Height;
canvas.DrawText("操作提示:", 10, canvasHeight - 65, SKTextAlign.Left, infoFont, infoPaint);
canvas.DrawText("• 左键点击: 添加白色精灵", 10, canvasHeight - 45, SKTextAlign.Left, infoFont, infoPaint);
canvas.DrawText("• 右键点击: 删除最近精灵", 10, canvasHeight - 25, SKTextAlign.Left, infoFont, infoPaint);
}
c#private void UpdateSprites()
{
foreach (var sprite in sprites.ToList())
{
sprite.Update();
// 边界检查 - 让精灵在边界反弹而不是消失
if (sprite.X <= 0 || sprite.X >= skiaCanvas.Width)
{
sprite.VelocityX = -sprite.VelocityX;
sprite.X = Math.Max(0, Math.Min(skiaCanvas.Width, sprite.X));
}
if (sprite.Y <= 0 || sprite.Y >= skiaCanvas.Height)
{
sprite.VelocityY = -sprite.VelocityY;
sprite.Y = Math.Max(0, Math.Min(skiaCanvas.Height, sprite.Y));
}
}
// 碰撞检测
CheckCollisions();
}
private void CheckCollisions()
{
for (int i = 0; i < sprites.Count; i++)
{
for (int j = i + 1; j < sprites.Count; j++)
{
if (sprites[i].CollidesWith(sprites[j]))
{
sprites[i].OnCollision(sprites[j]);
sprites[j].OnCollision(sprites[i]);
}
}
}
}
c#protected override void OnClosed(EventArgs e)
{
gameTimer?.Stop();
gameTimer?.Dispose();
backgroundBitmap?.Dispose();
infoFont?.Dispose();
infoPaint?.Dispose();
base.OnClosed(e);
}
c#using SkiaSharp;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace AppSpriteGameEngine
{
public class CircleSprite : Sprite
{
public float Radius { get; private set; }
public CircleSprite(float x, float y, float velocityX, float velocityY, SKColor color, float radius)
: base(x, y, velocityX, velocityY, color)
{
Radius = radius;
Width = Height = radius * 2;
}
public override void Draw(SKCanvas canvas)
{
var paint = new SKPaint
{
Color = Color,
IsAntialias = true,
Style = SKPaintStyle.Fill
};
// 绘制主体
canvas.DrawCircle(X, Y, Radius, paint);
// 绘制边框
paint.Style = SKPaintStyle.Stroke;
paint.Color = SKColors.White;
paint.StrokeWidth = 2;
canvas.DrawCircle(X, Y, Radius, paint);
// 绘制光效
paint.Style = SKPaintStyle.Fill;
paint.Color = Color.WithAlpha(100);
canvas.DrawCircle(X - Radius / 3, Y - Radius / 3, Radius / 3, paint);
}
}
}

c#private void UpdateFPS()
{
frameCount++;
var now = DateTime.Now;
var elapsed = (now - lastFpsUpdate).TotalSeconds;
if (elapsed >= 1.0)
{
currentFps = frameCount / (float)elapsed;
frameCount = 0;
lastFpsUpdate = now;
}
}
通过这次实战,我们成功解决了SkiaSharp API过期和中文显示问题,构建了一个高性能的2D游戏引擎。
🔑 三个核心要点:
💡 金句总结:
🎮 收藏级代码模板已在文中提供,直接复制即可在项目中使用!
你在使用SkiaSharp时还遇到过哪些坑?或者你有更好的性能优化方案?欢迎在评论区分享你的实战经验!
觉得这篇文章对你有帮助,请转发给更多正在做WinForms开发的同行,让大家一起告别过时API,拥抱现代化开发!
本文作者:技术老小子
本文链接:
版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!