ToB企服应用市场:ToB评测及商务社交产业平台

标题: SkiaSharp 之 WPF 自绘 粒子花园(案例版) [打印本页]

作者: 吴旭华    时间: 2022-9-16 17:14
标题: SkiaSharp 之 WPF 自绘 粒子花园(案例版)
此案例包含了简单的碰撞检测,圆形碰撞检测方法,也可以说是五环弹球的升级版,具体可以根据例子参考。
粒子花园

这名字是案例的名字,效果更加具有科技感,很是不错,搞搞做成背景特效也是不错的选择。
Wpf 和 SkiaSharp

新建一个 WPF 项目,然后,Nuget 包即可
要添加 Nuget 包
  1. Install-Package SkiaSharp.Views.WPF -Version 2.88.0
复制代码
其中核心逻辑是这部分,会以我设置的 60FPS 来刷新当前的画板。
  1. skContainer.PaintSurface += SkContainer_PaintSurface;
  2. _ = Task.Run(() =>
  3. {
  4.     while (true)
  5.     {
  6.         try
  7.         {
  8.             Dispatcher.Invoke(() =>
  9.             {
  10.                 skContainer.InvalidateVisual();
  11.             });
  12.             _ = SpinWait.SpinUntil(() => false, 1000 / 60);//每秒60帧
  13.         }
  14.         catch
  15.         {
  16.             break;
  17.         }
  18.     }
  19. });
复制代码
弹球实体代码 (Ball.cs)
  1. public class Ball
  2. {
  3.     public double X { get; set; }
  4.     public double Y { get; set; }
  5.     public double VX { get; set; }
  6.     public double VY { get; set; }
  7.     public int Radius { get; set; }
  8.     public int Move { get; set; }
  9.     public SKColor sKColor { get; set; } = SKColors.Blue;
  10.     /// <summary>
  11.     /// 检查球的碰撞
  12.     /// </summary>
  13.     public static void CheckBallHit(Ball b1, Ball b2)
  14.     {
  15.         var dx = b2.X - b1.X;
  16.         var dy = b2.Y - b1.Y;
  17.         var dist = Math.Sqrt(Math.Pow(dx,2) + Math.Pow(dy, 2));
  18.         if (dist < b1.Radius + b2.Radius)
  19.         {
  20.             var angle = Math.Atan2(dy, dx);
  21.             var sin = Math.Sin(angle);
  22.             var cos = Math.Cos(angle);
  23.             // 以b1为参照物,设定b1的中心点为旋转基点
  24.             double x1 = 0;
  25.             double y1 = 0;
  26.             var x2 = dx * cos + dy * sin;
  27.             var y2 = dy * cos - dx * sin;
  28.             // 旋转b1和b2的速度
  29.             var vx1 = b1.VX * cos + b1.VY * sin;
  30.             var vy1 = b1.VY * cos - b1.VX * sin;
  31.             var vx2 = b2.VX * cos + b2.VY * sin;
  32.             var vy2 = b2.VY * cos - b2.VX * sin;
  33.             // 求出b1和b2碰撞之后的速度
  34.             var vx1Final = ((b1.Move - b2.Move) * vx1 + 2 * b2.Move * vx2) / (b1.Move + b2.Move);
  35.             var vx2Final = ((b2.Move - b1.Move) * vx2 + 2 * b1.Move * vx1) / (b1.Move + b2.Move);
  36.             // 处理两个小球碰撞之后,将它们进行归位
  37.             var lep = (b1.Radius + b2.Radius) - Math.Abs(x2 - x1);
  38.             x1 = x1 + (vx1Final < 0 ? -lep / 2 : lep / 2);
  39.             x2 = x2 + (vx2Final < 0 ? -lep / 2 : lep / 2);
  40.             b2.X = b1.X + (x2 * cos - y2 * sin);
  41.             b2.Y = b1.Y + (y2 * cos + x2 * sin);
  42.             b1.X = b1.X + (x1 * cos - y1 * sin);
  43.             b1.Y = b1.Y + (y1 * cos + x1 * sin);
  44.             b1.VX = vx1Final * cos - vy1 * sin;
  45.             b1.VY = vy1 * cos + vx1Final * sin;
  46.             b2.VX = vx2Final * cos - vy2 * sin;
  47.             b2.VY = vy2 * cos + vx2Final * sin;
  48.         }
  49.     }
  50. }
复制代码
粒子花园核心类 (ParticleGarden.cs)
  1. /// <summary>
  2. /// 粒子花园
  3. /// </summary>
  4. public class ParticleGarden
  5. {
  6.     public SKPoint centerPoint;
  7.     public double Spring = 0.0001;
  8.     public int ParticelCount = 100;
  9.     public List<Ball> Particles = new List<Ball>();
  10.     public SKCanvas canvas;
  11.     /// <summary>
  12.     /// 渲染
  13.     /// </summary>
  14.     public void Render(SKCanvas canvas, SKTypeface Font, int Width, int Height)
  15.     {
  16.         this.canvas = canvas;
  17.         canvas.Clear(SKColors.Black);
  18.         centerPoint = new SKPoint(Width / 2, Height / 2);
  19.         if (Particles.Any() == false)
  20.         {
  21.             for (int i = 0; i < ParticelCount; i++)
  22.             {
  23.                 Random random = new Random((int)DateTime.Now.Ticks);
  24.                 var Length = random.Next(3, 10);
  25.                 Particles.Add(new Ball()
  26.                 {
  27.                     X = random.Next(0, Width),
  28.                     Y = random.Next(0, Height),
  29.                     sKColor = SKColors.White,
  30.                     VX = random.NextInt64(-2, 2),
  31.                     VY = random.NextInt64(-2, 2),
  32.                     Radius = Length,
  33.                     Move = Length
  34.                 });
  35.             }
  36.         }
  37.         //画线
  38.         for (int i = 0; i < Particles.Count; i++)
  39.         {
  40.             Move(Particles[i], i, Width, Height);
  41.         }
  42.         //画球
  43.         foreach (var item in Particles)
  44.         {
  45.             DrawCircle(canvas, item);
  46.         }
  47.         using var paint = new SKPaint
  48.         {
  49.             Color = SKColors.Blue,
  50.             IsAntialias = true,
  51.             Typeface = Font,
  52.             TextSize = 24
  53.         };
  54.         string by = $"by 蓝创精英团队";
  55.         canvas.DrawText(by, 600, 400, paint);
  56.     }
  57.     public void Move(Ball p, int i, int width, int height)
  58.     {
  59.         p.X += p.VX;
  60.         p.Y += p.VY;
  61.         for (var j = i + 1; j < Particles.Count; j++)
  62.         {
  63.             var target = Particles[j];
  64.             CheckSpring(p, target, width, height);
  65.             Ball.CheckBallHit(p, target);
  66.         }
  67.         if (p.X - p.Radius > width)
  68.         {
  69.             p.X = -p.Radius;
  70.         }
  71.         else if (p.X + p.Radius < 0)
  72.         {
  73.             p.X = width + p.Radius;
  74.         }
  75.         if (p.Y - p.Radius > height)
  76.         {
  77.             p.Y = -p.Radius;
  78.         }
  79.         else if (p.Y + p.Radius < 0)
  80.         {
  81.             p.Y = height + p.Radius;
  82.         }
  83.     }
  84.     public void CheckSpring(Ball current, Ball target, int width, int height)
  85.     {
  86.         var dx = target.X - current.X;
  87.         var dy = target.Y - current.Y;
  88.         var dist = Math.Sqrt(Math.Pow(dx, 2) + Math.Pow(dy, 2));
  89.         var minDist = width > height ? width / 10 : height / 5;
  90.         if (dist < minDist)
  91.         {
  92.             DrawLine(current, target, dist, minDist);
  93.             var ax = dx * Spring;
  94.             var ay = dy * Spring;
  95.             current.VX += ax / current.Move;
  96.             current.VY += ay / current.Move;
  97.             target.VX -= ax / target.Move;
  98.             target.VY -= ay / target.Move;
  99.         }
  100.     }
  101.     public void DrawLine(Ball current, Ball target, double dist, int minDist)
  102.     {
  103.         var StrokeWidth = (float)(2 * Math.Max(0, (1 - dist / minDist)));
  104.         var Alpha = Math.Max(0, (1 - dist / minDist)) * byte.MaxValue;
  105.         var Color = current.sKColor.WithAlpha((byte)Alpha);
  106.         //划线
  107.         using var LinePaint = new SKPaint
  108.         {
  109.             Color = Color,
  110.             Style = SKPaintStyle.Fill,
  111.             StrokeWidth = StrokeWidth,
  112.             IsStroke = true,
  113.             StrokeCap = SKStrokeCap.Round,
  114.             IsAntialias = true
  115.         };
  116.         var path = new SKPath();
  117.         path.MoveTo((float)current.X, (float)current.Y);
  118.         path.LineTo((float)target.X, (float)target.Y);
  119.         path.Close();
  120.         canvas.DrawPath(path, LinePaint);
  121.     }
  122.     /// <summary>
  123.     /// 画一个圆
  124.     /// </summary>
  125.     public void DrawCircle(SKCanvas canvas, Ball ball)
  126.     {
  127.         using var paint = new SKPaint
  128.         {
  129.             Color = ball.sKColor,
  130.             Style = SKPaintStyle.Fill,
  131.             IsAntialias = true,
  132.             StrokeWidth = 2
  133.         };
  134.         canvas.DrawCircle((float)ball.X, (float)ball.Y, ball.Radius, paint);
  135.     }
  136. }
复制代码
效果如下:


效果放大看,还是很心旷神怡的。
总结

这个特效的案例重点是碰撞检测,同时又产生了奇妙的特效效果,很是不错。
代码地址

https://github.com/kesshei/WPFSkiaParticleGardenDemo.git
https://gitee.com/kesshei/WPFSkiaParticleGardenDemo.git


一键三连呦!,感谢大佬的支持,您的支持就是我的动力!
版权

蓝创精英团队(公众号同名,CSDN 同名,CNBlogs 同名)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4