SkiaSharp 之 WPF 自绘 投篮小游戏(案例版)

此案例主要是针对光线投影法碰撞检测功能的示例,顺便做成了一个小游戏,很简单,但是,效果却很不错。

投篮小游戏

规则,点击投篮目标点,就会有一个球沿着相关抛物线,然后,判断是否进入篮子里,其实就是一个矩形,直接是按照碰撞检测来的,碰到就算进去了,对其增加了一个分数统计等功能。

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;}}
}

效果如下:

8b901f1784ad842705fca38a5796be1d.gif

还不错,得了7分,当然,我也可以得10分的,不过,还好了。

总结

这个特效的案例重点是光线投影法碰撞检测,同时又对其增加了游戏的属性,虽然东西都很简单,但是作为一个雏形来讲也是不错的。

SkiaSharp 基础系列算是告一段落了,基础知识相关暂时都已经有了一个深度的了解,对于它的基础应用已经有一个不错的认识了,那么,基于它的应用应该也会多起来,我这边主要参考Avalonia的内部SkiaSharp使用原理,当然,用法肯定不局限的。

代码地址

https://github.com/kesshei/WPFSkiaRayProjectionDemo.git

https://gitee.com/kesshei/WPFSkiaRayProjectionDemo.git

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

版权

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/283986.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

zuul集成ribbon完成服务通信和负载均衡

目录 Zuul2服务通信 超时相关 默认超时配置 自定义超时配置 负载均衡 Zuul2服务通信 描述&#xff1a;zuul2通过Ribbon完成客户端负载均衡以及与服务器群集进行通信。 zuul2的通信是集成Ribbon实现的&#xff0c;在Origin中集成Ribbon基本配置&#xff08;例如IClientCo…

时任上海来伊份互联网事业群总裁王戈钧 :传统企业(线上+线下)移动互联网改造...

2017年12月22日-23日&#xff0c;第13届信息化领袖峰会暨2017中国数字化贡献人物颁奖盛典在上海盛大开幕。本次峰会由上海市经济和信息化委员会指导&#xff0c;上海市国有资产信息中心、上海市计算机用户协会、上海市信息服务业行业协会、上海大数据联盟、上海市高等教育学会支…

【.NET6+Modbus】Modbus TCP协议解析、仿真环境以及基于.NET实现基础通信

接下来的内容&#xff0c;我会以从头开发一个简单的基于modbus tcp通信的案例&#xff0c;来实现一个基础的通信功能。有关环境&#xff1a;开发环境&#xff1a;VS 2022企业版运行环境&#xff1a;Win 10 专业版.NET 环境版本&#xff1a;.NET 6【备注】 源码在文末 1、新建一…

源码深度剖析Eureka与Ribbon服务发现原理

本文基于 spring cloud dalston&#xff0c;同时文章较长&#xff0c;请选择舒服姿势进行阅读。 Eureka 与 Ribbon 是什么&#xff1f;和服务发现什么关系&#xff1f; Eureka 与 Ribbon 都是 Netflix 提供的微服务组件&#xff0c;分别用于服务注册与发现、负载均衡。同时&a…

std的find和reverse_iterator联合使用

上代码&#xff1a; // test2013.cpp : 定义控制台应用程序的入口点。 //#include "stdafx.h" #include <stdlib.h> #include <stdio.h> #include<iostream> #include<vector> #include<map> #include<string> using namespace …

论如何提升学习的能力

为啥要学习如果有一件事情是能改变你自己的&#xff0c;我想这件事情必然就是学习&#xff0c;我的人生重要的转折点也是从学习这件事情始发的&#xff0c;那么&#xff0c;我们就从这里开始。学习不仅仅是为了找到答案&#xff0c;而是为了找到方法&#xff0c;找到一个可以找…

CSS布局解决方案(终结版)

前端布局非常重要的一环就是页面框架的搭建&#xff0c;也是最基础的一环。在页面框架的搭建之中&#xff0c;又有居中布局、多列布局以及全局布局&#xff0c;今天我们就来总结总结前端干货中的CSS布局。 居中布局 水平居中 1&#xff09;使用inline-blocktext-align&#xff…

基于ABP和Magicodes实现Excel导出操作

前端使用的vue-element-admin框架&#xff0c;后端使用ABP框架&#xff0c;Excel导出使用的Magicodes.IE.Excel.Abp库。Excel导入和导出操作几乎一样&#xff0c;不再介绍。文本主要介绍Excel导出操作和过程中遇到的坑&#xff0c;主要是Excel文件导出后无法打开的问题。一.Mag…

消息模式在实际开发应用中的优势

曾经.NET面试过程中经常问的一个问题是&#xff0c;如果程序集A&#xff0c;引用B &#xff0c;B 引用C&#xff0c;那么C怎么去访问A中的方法呢。 这个问题初学.net可能一时想不出该咋处理&#xff0c;这涉及到循环引用问题。但有点经验的可能就简单了&#xff0c;通过委托的方…

微服务:注册中心ZooKeeper、Eureka、Consul 、Nacos对比

前言 服务注册中心本质上是为了解耦服务提供者和服务消费者。对于任何一个微服务&#xff0c;原则上都应存在或者支持多个提供者&#xff0c;这是由微服务的分布式属性决定的。更进一步&#xff0c;为了支持弹性扩缩容特性&#xff0c;一个微服务的提供者的数量和分布往往是动…

为了高性能、超大规模的模型训练,这个组合“出道”了

点击上方蓝字关注我们&#xff08;本文阅读时间&#xff1a;3分钟)近年来&#xff0c;在大量数据上训练的基于 transformer 的大规模深度学习模型在多项认知任务中取得了很好的成果&#xff0c;并且被使用到一些新产品和功能背后&#xff0c;进一步增强了人类的能力。在过去五年…

SpringCloud必会知识点大全

为什么要学习Spring Cloud 在项目开发中随着业务越来越多&#xff0c;导致功能之间耦合性高、开发效率低、系统运行缓慢难以维护、不稳定。微服务 架构可以解决这些问题&#xff0c;而Spring Cloud是微服务架构最流行的实现. 1.微服务 微服务架构是使用一套小服务来开发单个应用…

30分钟掌握 C#7

1. out 变量&#xff08;out variables&#xff09; 以前我们使用out变量必须在使用前进行声明&#xff0c;C# 7.0 给我们提供了一种更简洁的语法 “使用时进行内联声明” 。如下所示&#xff1a; 1 var input ReadLine(); 2 if (int.TryParse(input, out var result)) 3 …

在 C# 中如何检查参数是否为 null

前言前不久&#xff0c;微软宣布从 C# 11 中移除参数空值检查功能&#xff0c;该功能允许在方法开始执行之前&#xff0c;在参数名称的末尾提供参数空值检查&#xff08;!!操作符&#xff09;。那么&#xff0c;在 C# 中如何检查参数是否为 null 呢&#xff1f;1. null这个可能…

带你剖析WebGis的世界奥秘----Geojson数据加载(高级)

前言&#xff1a;前两周我带你们分析了WebGis中关键步骤瓦片加载点击事件&#xff08;具体的看前两篇文章&#xff09;&#xff0c;下面呢&#xff0c;我带大家来看看Geojson的加载及其点击事件 Geojson数据解析 GeoJSON是一种对各种地理数据结构进行编码的格式。GeoJSON对象可…

「Docker入门指北」容器很难理解?带你从头到尾捋一遍

文章目录 1. 初始虚拟化 &#x1f351; 虚拟化概念&#x1f351; 硬件虚拟化2. Docker容器 &#x1f351; Docker技术的诞生&#x1f351; 容器与虚拟化&#x1f351; 性能差别&#x1f351; Docker优势 编排有序高效易迁移快速部署3. 容器生态系统 &#x1f351; 核心技术 容器…

微服务:事务管理

几乎所有的信息管理系统都会涉及到事务&#xff0c;事务的目的是为了保证数据的一致性&#xff0c;这里说的一致性是数据库状态的一致性。说到数据库状态的一致性&#xff0c;相信大家都会想到 ACID &#xff1a;原子性&#xff08;Atomic&#xff09;&#xff1a;在一个事件的…

Redis的那些事:一文入门Redis的基础操作

Redis是什么Redis&#xff0c;全称是Remote Dictionary Service,翻译过来就是&#xff0c;远程字典服务。redis属于nosql非关系型数据库。Nosql常见的数据关系&#xff0c;基本上是以key-value键值对形式存在的。Key-value: 就像翻阅中文字典或者单词字典&#xff0c;通过指定的…

10种提问型爆文标题句式 直接套用

如果你用1天的时间来写篇好文章&#xff0c;那你花掉半天时间想一个好标题都不过分&#xff01; 你是不是觉得我有点言过其实了&#xff1f;没关系&#xff0c;先来问你2个问题&#xff1a; 1、花了很长时间&#xff0c;写了一篇很牛的卖货推文&#xff0c;定稿后&#xff0c…

2016 China Joy抢先看,文末有彩蛋!

这里只有你想不到的&#xff0c;没有你看不到的。 2016 China Joy开幕在即&#xff0c;天气成了最折磨各种媒体、展商和观众的小妖精&#xff0c;一会艳阳天&#xff0c;一会大暴雨&#xff0c;轩轩现在是这样的&#xff01; 七月底的魔都&#xff0c;热的那叫一个销魂&#x…