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

投篮小游戏

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

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 同名)

SkiaSharp 之 WPF 自绘 投篮小游戏(案例版)的更多相关文章

  1. SkiaSharp 之 WPF 自绘 拖曳小球(案例版)

    感谢各位大佬和粉丝的厚爱和关心( 催更),我会再接再厉的,其实这也是督促自己的一种方式,非常感谢. 刚写了一篇万字长文,自己也休养生息(低调发育)了一段时间,接下来来几个小案例. 拖曳小球 WPF的拖 ...

  2. SkiaSharp 之 WPF 自绘 粒子花园(案例版)

    此案例包含了简单的碰撞检测,圆形碰撞检测方法,也可以说是五环弹球的升级版,具体可以根据例子参考. 粒子花园 这名字是案例的名字,效果更加具有科技感,很是不错,搞搞做成背景特效也是不错的选择. Wpf ...

  3. SkiaSharp 之 WPF 自绘 弹动小球(案例版)

    没想到粉丝对界面效果这么喜欢,接下来就尽量多来点特效,当然,特效也算是动画的一部分了.WPF里面已经包含了很多动画特效的功能支持了,但是,还是得自己实现,我这边就来个自绘实现的. 弹动小球 弹动小球是 ...

  4. SkiaSharp 之 WPF 自绘 五环弹动球(案例版)

    此案例基于拖曳和弹动球两个技术功能实现,如有不懂的可以参考之前的相关文章,属于递进式教程. 五环弹动球 好吧,名字是我起的,其实,你可以任意个球进行联动弹动,效果还是很不错的,有很多前端都是基于这个特 ...

  5. HTML5游戏开发,剪刀石头布小游戏案例

    剪刀石头布,非常可爱的小游戏,相信大家都非常的怀念这款小游戏,小时候也玩过很多次,陪伴着我的童年的成长,现在是不是还会玩一下,剪刀石头布游戏的规则我们都知道是:剪刀剪布,石头砸剪刀,布包石头.跟朋友. ...

  6. HTML5存储(带一个粗糙的打怪小游戏案例)

    本地存储localStorage设置存储内容setItem(key,value) localStorage.setItem('leo','23'); 更新存储内容对象[key]=value对象.key ...

  7. 练手WPF(四)——贪吃蛇小游戏的简易实现(下)

    八.生成新的单节蛇身我们这里先说说游戏小原理好了,游戏运行后,通过计时器事件不断生成新的单节蛇身类SnakeNode,添加到List中的0位置,原来的蛇头变成了第二节.该节新蛇头的坐标通过蛇头前进方向 ...

  8. 练手WPF(四)——贪吃蛇小游戏的简易实现(上)

    一. 游戏界面首先,按照惯例,编辑MainWindow.xaml,先将游戏界面制作好.非常简单:(1)主游戏区依然使用我们熟悉的Canvas控件,大小为640X480像素,设定每小格子为20px,所以 ...

  9. 用WPF做了几个小游戏

    最近看书看累了,参考别人的代码(其实差不多就是把代码重新打了一遍o(╯□╰)o),用wpf做了个<2048>小游戏,顺便在<Git教程>学习下git,也顺便把在<写让别人 ...

随机推荐

  1. Nvidia Triton使用教程:从青铜到王者

    1 相关预备知识 模型:包含了大量参数的一个网络(参数+结构),体积10MB-10GB不等 模型格式:相同的模型可以有不同的存储格式(可类比音视频文件),目前主流有torch.tf.onnx和trt, ...

  2. 一文带你搞懂 Kafka 的系统架构(深度好文,值得收藏)

    Kafka 简介 Kafka 是一种高吞吐.分布式.基于发布和订阅模型的消息系统,最初是由 LinkedIn 公司采用 Scala 和 java 开发的开源流处理软件平台,目前是 Apache 的开源 ...

  3. .NET 6 从0到1使用Docker部署至Linux环境

    前言 作为一名.Net菜鸟开发者,平时对Linux接触的并不多,项目部署这一块都是运维小哥顶着,但是作为混迹在云原生项目组的人咱也不能什么都不知道,该掌握的知识还是要懂的,所以借着这次机会,梳理一下项 ...

  4. webpack及其配置

    Webpack 1.webpack主要用来模块和打包 2.webpack和gulp对比: 什么时候用gulp a.如果你的工程模块依赖非常简单,甚至是没有用到模块化的概念 b.只需要进行简单的合并.压 ...

  5. 论文解读(gCooL)《Graph Communal Contrastive Learning》

    论文信息 论文标题:Graph Communal Contrastive Learning论文作者:Bolian Li, Baoyu Jing, Hanghang Tong论文来源:2022, WWW ...

  6. 基于原生JS实现的Bean容器和AOP编程

    Bean是什么 我们知道Bean是Spring最基础的核心构件,大多数逻辑代码都通过Bean进行管理.NestJS基于TypeScript和依赖注入也实现了类似于Spring Bean的机制:服务提供 ...

  7. Docker容器Nginx负载均衡配置、check及stub模块安装

    Nginx是一款高性能的HTTP和反向代理.负载均衡web服务器.本次在Docker容器中部署三个tomcat,Nginx代理三个tomcat服务(以下称节点)来模拟实现负载均衡效果,配置check模 ...

  8. Node.js精进(1)——模块化

    模块化是一种将软件功能抽离成独立.可交互的软件设计技术,能促进大型应用程序和系统的构建. Node.js内置了两种模块系统,分别是默认的CommonJS模块和浏览器所支持的ECMAScript模块. ...

  9. Python:socket编程教程

    ocket是基于C/S架构的,也就是说进行socket网络编程,通常需要编写两个py文件,一个服务端,一个客户端. 首先,导入Python中的socket模块: import socket Pytho ...

  10. 使用Kind快速构建k8s

    什么是 KindKind(Kubernetes in Docker) 是一个 Kubernetes 孵化项目,Kind 是一套开箱即用的 Kubernetes 环境搭建方案.顾名思义,就是将 Kube ...