此案例主要是針對光線投影法碰撞檢測功能的示例,順便做成了一個小游戲,很簡單,但是,效果卻很不錯。
投籃小游戲
規則,點擊投籃目標點,就會有一個球沿著相關拋物線,然后,判斷是否進入籃子里,其實就是一個矩形,直接是按照碰撞檢測來的,碰到就算進去了,對其增加了一個分數統計等功能。
Wpf 和 SkiaSharp
新建一個 WPF 項目,然后,Nuget 包即可 要添加 Nuget 包
Install-Package?SkiaSharp.Views.WPF?-Version?2.88.0
其中核心邏輯是這部分,會以我設置的 60FPS 來刷新當前的畫板。
skContainer.PaintSurface?+=?SkContainer_PaintSurface;
_?=?Task.Run(()?=>
{while?(true){try{Dispatcher.Invoke(()?=>{skContainer.InvalidateVisual();});_?=?SpinWait.SpinUntil(()?=>?false,?1000?/?60);//每秒60幀}catch{break;}}
});
彈球實體代碼 (Ball.cs)
public?class?Ball
{public?double?X?{?get;?set;?}public?double?Y?{?get;?set;?}public?double?VX?{?get;?set;?}public?double?VY?{?get;?set;?}public?int?Radius?{?get;?set;?}
}
##粒子花園核心類 (ParticleGarden.cs)
///?<summary>
///?光線投影法碰撞檢測
///?投籃小游戲
///?</summary>
public?class?RayProjection
{public?SKPoint?centerPoint;public?double?G?=?0.3;public?double?F?=?0.98;public?double?Easing?=?0.03;public?bool?IsMoving?=?false;public?SKPoint?CurrentMousePoint?=?SKPoint.Empty;public?SKPoint?lastPoint?=?SKPoint.Empty;public?Rect?Box;public?Ball?Ball;public?SKCanvas?canvas;public?int?ALLCount?=?10;public?List<bool>?bools?=?new?List<bool>();public?bool?IsOver?=?false;///?<summary>///?渲染///?</summary>public?void?Render(SKCanvas?canvas,?SKTypeface?Font,?int?Width,?int?Height){canvas.Clear(SKColors.White);this.canvas?=?canvas;centerPoint?=?new?SKPoint(Width?/?2,?Height?/?2);//球if?(Ball?==?null){Ball?=?new?Ball(){X?=?50,Y?=?Height?-?50,Radius?=?30};}//箱子var?boxX?=?Width?-?170;var?boxY?=?Height?-?80;if?(Box.X?==?0){Box?=?new?Rect(boxX,?boxY,?120,?70);}else{if?(Box.X?!=?boxX?&&?Box.Y?!=?boxY){Box.X?=?boxX;Box.Y?=?boxY;}}if?(bools.Count?>=?ALLCount){IsOver?=?true;}if?(!IsOver){if?(IsMoving){BallMove(Width,?Height);}else{DrawLine();}//彈球DrawCircle(canvas,?Ball);//矩形DrawRect(canvas,?Box);//計分using?var?paint1?=?new?SKPaint{Color?=?SKColors.Blue,IsAntialias?=?true,Typeface?=?Font,TextSize?=?24};string?count?=?$"總次數:{ALLCount}?剩余次數:{ALLCount?-?bools.Count}?投中次數:{bools.Count(t?=>?t)}";canvas.DrawText(count,?100,?20,?paint1);}else{SKColor?sKColor?=?SKColors.Blue;//計分var?SuccessCount?=?bools.Count(t?=>?t);string?count?=?"";switch?(SuccessCount){case?0:{count?=?$"太糗了吧,一個都沒投中!";sKColor?=?SKColors.Black;}break;case?1:case?2:case?3:case?4:case?5:{count?=?$"你才投中:{SuccessCount}次,繼續努力!";sKColor?=?SKColors.Blue;}break;case?6:case?7:case?8:case?9:{count?=?$"恭喜?投中:{SuccessCount}次!!!";sKColor?=?SKColors.YellowGreen;}break;case?10:?{?count?=?$"全部投中,你太厲害了!";sKColor?=?SKColors.Red;}?break;}using?var?paint1?=?new?SKPaint{Color?=?sKColor,IsAntialias?=?true,Typeface?=?Font,TextSize?=?48};var?fontCenter?=?paint1.MeasureText(count);canvas.DrawText(count,?centerPoint.X?-?fontCenter?/?2,?centerPoint.Y,?paint1);}using?var?paint?=?new?SKPaint{Color?=?SKColors.Blue,IsAntialias?=?true,Typeface?=?Font,TextSize?=?24};string?by?=?$"by?藍創精英團隊";canvas.DrawText(by,?600,?20,?paint);}///?<summary>///?畫一個圓///?</summary>public?void?DrawCircle(SKCanvas?canvas,?Ball?ball){using?var?paint?=?new?SKPaint{Color?=?SKColors.Blue,Style?=?SKPaintStyle.Fill,IsAntialias?=?true,StrokeWidth?=?2};canvas.DrawCircle((float)ball.X,?(float)ball.Y,?ball.Radius,?paint);}///?<summary>///?畫一個矩形///?</summary>public?void?DrawRect(SKCanvas?canvas,?Rect?box){using?var?paint?=?new?SKPaint{Color?=?SKColors.Green,Style?=?SKPaintStyle.Fill,IsAntialias?=?true,StrokeWidth?=?2};canvas.DrawRect((float)box.X,?(float)box.Y,?(float)box.Width,?(float)box.Height,?paint);}///?<summary>///?劃線///?</summary>public?void?DrawLine(){//劃線using?var?LinePaint?=?new?SKPaint{Color?=?SKColors.Red,Style?=?SKPaintStyle.Fill,StrokeWidth?=?2,IsStroke?=?true,StrokeCap?=?SKStrokeCap.Round,IsAntialias?=?true};var?path?=?new?SKPath();path.MoveTo((float)CurrentMousePoint.X,?(float)CurrentMousePoint.Y);path.LineTo((float)Ball.X,?(float)Ball.Y);path.Close();canvas.DrawPath(path,?LinePaint);}public?void?BallMove(int?Width,?int?Height){Ball.VX?*=?F;Ball.VY?*=?F;Ball.VY?+=?G;Ball.X?+=?Ball.VX;Ball.Y?+=?Ball.VY;var?hit?=?CheckHit();//?邊界處理和碰撞檢測if?(hit?||?Ball.X?-?Ball.Radius?>?Width?||?Ball.X?+?Ball.Radius?<?0?||?Ball.Y?-?Ball.Radius?>?Height?||?Ball.Y?+?Ball.Radius?<?0){bools.Add(hit);IsMoving?=?false;Ball.X?=?50;Ball.Y?=?Height?-?50;}lastPoint.X?=?(float)Ball.X;lastPoint.Y?=?(float)Ball.Y;}public?bool?CheckHit(){var?k1?=?(Ball.Y?-?lastPoint.Y)?/?(Ball.X?-?lastPoint.X);var?b1?=?lastPoint.Y?-?k1?*?lastPoint.X;var?k2?=?0;var?b2?=?Ball.Y;var?cx?=?(b2?-?b1)?/?(k1?-?k2);var?cy?=?k1?*?cx?+?b1;if?(cx?-?Ball.Radius?/?2?>?Box.X?&&?cx?+?Ball.Radius?/?2?<?Box.X?+?Box.Width?&&?Ball.Y?-?Ball.Radius?>?Box.Y){return?true;}return?false;}public?void?MouseMove(SKPoint?sKPoint){CurrentMousePoint?=?sKPoint;}public?void?MouseDown(SKPoint?sKPoint){CurrentMousePoint?=?sKPoint;}public?void?MouseUp(SKPoint?sKPoint){if?(bools.Count?<?ALLCount){IsMoving?=?true;Ball.VX?=?(sKPoint.X?-?Ball.X)?*?Easing;Ball.VY?=?(sKPoint.Y?-?Ball.Y)?*?Easing;lastPoint.X?=?(float)Ball.X;lastPoint.Y?=?(float)Ball.Y;}}
}
效果如下:

還不錯,得了7分,當然,我也可以得10分的,不過,還好了。
總結
這個特效的案例重點是光線投影法碰撞檢測,同時又對其增加了游戲的屬性,雖然東西都很簡單,但是作為一個雛形來講也是不錯的。
SkiaSharp 基礎系列算是告一段落了,基礎知識相關暫時都已經有了一個深度的了解,對于它的基礎應用已經有一個不錯的認識了,那么,基于它的應用應該也會多起來,我這邊主要參考Avalonia的內部SkiaSharp使用原理,當然,用法肯定不局限的。
代碼地址
https://github.com/kesshei/WPFSkiaRayProjectionDemo.git
https://gitee.com/kesshei/WPFSkiaRayProjectionDemo.git
閱
一鍵三連呦!,感謝大佬的支持,您的支持就是我的動力!
版權
藍創精英團隊(公眾號同名,CSDN 同名,CNBlogs 同名)