WPF+SkiaSharp實(shí)現(xiàn)自繪投籃小游戲
投籃小游戲
規(guī)則,點(diǎn)擊投籃目標(biāo)點(diǎn),就會(huì)有一個(gè)球沿著相關(guān)拋物線,然后,判斷是否進(jìn)入籃子里,其實(shí)就是一個(gè)矩形,直接是按照碰撞檢測(cè)來(lái)的,碰到就算進(jìn)去了,對(duì)其增加了一個(gè)分?jǐn)?shù)統(tǒng)計(jì)等功能。
Wpf 和 SkiaSharp
新建一個(gè) WPF 項(xiàng)目,然后,Nuget 包即可
要添加 Nuget 包
Install-Package SkiaSharp.Views.WPF -Version 2.88.0
其中核心邏輯是這部分,會(huì)以我設(shè)置的 60FPS 來(lái)刷新當(dāng)前的畫(huà)板。
skContainer.PaintSurface += SkContainer_PaintSurface; _ = Task.Run(() => { while (true) { try { Dispatcher.Invoke(() => { skContainer.InvalidateVisual(); }); _ = SpinWait.SpinUntil(() => false, 1000 / 60);//每秒60幀 } catch { break; } } });
彈球?qū)嶓w代碼 (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> /// 光線投影法碰撞檢測(cè) /// 投籃小游戲 /// </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); //計(jì)分 using var paint1 = new SKPaint { Color = SKColors.Blue, IsAntialias = true, Typeface = Font, TextSize = 24 }; string count = $"總次數(shù):{ALLCount} 剩余次數(shù):{ALLCount - bools.Count} 投中次數(shù):{bools.Count(t => t)}"; canvas.DrawText(count, 100, 20, paint1); } else { SKColor sKColor = SKColors.Blue; //計(jì)分 var SuccessCount = bools.Count(t => t); string count = ""; switch (SuccessCount) { case 0: { count = $"太糗了吧,一個(gè)都沒(méi)投中!"; sKColor = SKColors.Black; } break; case 1: case 2: case 3: case 4: case 5: { count = $"你才投中:{SuccessCount}次,繼續(xù)努力!"; 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 藍(lán)創(chuàng)精英團(tuán)隊(duì)"; canvas.DrawText(by, 600, 20, paint); } /// <summary> /// 畫(huà)一個(gè)圓 /// </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> /// 畫(huà)一個(gè)矩形 /// </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(); // 邊界處理和碰撞檢測(cè) 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; } } }
效果如下
還不錯(cuò),得了7分,當(dāng)然,我也可以得10分的,不過(guò),還好了。
總結(jié)
這個(gè)特效的案例重點(diǎn)是光線投影法碰撞檢測(cè),同時(shí)又對(duì)其增加了游戲的屬性,雖然東西都很簡(jiǎn)單,但是作為一個(gè)雛形來(lái)講也是不錯(cuò)的。
SkiaSharp 基礎(chǔ)系列算是告一段落了,基礎(chǔ)知識(shí)相關(guān)暫時(shí)都已經(jīng)有了一個(gè)深度的了解,對(duì)于它的基礎(chǔ)應(yīng)用已經(jīng)有一個(gè)不錯(cuò)的認(rèn)識(shí)了,那么,基于它的應(yīng)用應(yīng)該也會(huì)多起來(lái),我這邊主要參考Avalonia的內(nèi)部SkiaSharp使用原理,當(dāng)然,用法肯定不局限的。
代碼地址
https://github.com/kesshei/WPFSkiaRayProjectionDemo.git
https://gitee.com/kesshei/WPFSkiaRayProjectionDemo.git
到此這篇關(guān)于WPF+SkiaSharp實(shí)現(xiàn)自繪投籃小游戲的文章就介紹到這了,更多相關(guān)WPF SkiaSharp投籃游戲內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
C#開(kāi)發(fā)Windows UWP系列之布局面板RelativePanel
這篇文章介紹了C#開(kāi)發(fā)Windows UWP系列之布局面板RelativePanel,對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2022-06-06C#聯(lián)合VisionPro實(shí)現(xiàn)TCP/IP通信詳解
TCP/IP(傳輸控制協(xié)議/互聯(lián)網(wǎng)協(xié)議)是一組用于在網(wǎng)絡(luò)上進(jìn)行通信的通信協(xié)議,本文主要為大家詳細(xì)介紹了C#如何聯(lián)合VisionPro實(shí)現(xiàn)TCP/IP通信,希望對(duì)大家有所幫助2024-02-02C# 使用PictureBox實(shí)現(xiàn)圖片按鈕控件的示例步驟
這篇文章主要介紹了C# 使用PictureBox實(shí)現(xiàn)圖片按鈕控件的示例步驟,幫助大家更好的理解和使用c#,感興趣的朋友可以了解下2021-02-02使用快捷鍵在Unity中快速鎖定和解鎖Inspector右上角的鎖功能
這篇文章主要為大家介紹了使用快捷鍵在Unity中快速鎖定和解鎖Inspector右上角的鎖功能詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-08-08利用C#實(shí)現(xiàn)AOP常見(jiàn)的幾種方法詳解
AOP面向切面編程(Aspect Oriented Programming),是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)程序功能的統(tǒng)一維護(hù)的一種技術(shù)。下面這篇文章主要給大家介紹了關(guān)于利用C#實(shí)現(xiàn)AOP常見(jiàn)的幾種方法,需要的朋友可以參考借鑒,下面來(lái)一起看看吧。2017-09-09