当前位置: 首页 > news >正文

【Godot4.3】简单物理模拟之圆粒子碰撞检测

概述

最近开始研究游戏物理的内容,研究运动、速度、加速度之类的内容。也开始模仿一些简单的粒子模拟。这些都是一些基础、简单且古老的算法,但是对于理解游戏内的物理模拟很有帮助。甚至你可以在js、Python或者其他程序语言中实现它们。

图形的碰撞检测是第一个我想要实践的内容,而在碰撞检测中最简单的应该就是圆的碰撞检测了,本篇就简单实践一下圆的碰撞检测。

从圆开始

为什么,因为圆的圆心到其表面的距离是一样的。那么判断两个圆之间的重叠就非常简单,只要判断两个圆圆心之间的距离是否小于两者半径之和就可以了。如果是大小相同的粒子系统,每个圆的半径一样。

在这里插入图片描述

  • 重叠距离:等于两者半径之和-两圆心之间的距离。
  • 检测到重叠之后,将它们沿着圆形连线所在直线,沿着相反方向各自推动1/2的重叠距离,就可以将它们互相推开。
  • 当然这里没有考虑粒子各自的质量,默认将它们都是相同的质量,否则各自推开的距离应该是不一样的,比如小圆质量小应该推开更大的距离

测试

实现两个圆形粒子间的重叠检测和推开

extends Node2Dvar particles:PackedVector2Array = Points2D.Vec2Arr("300,420 320,450")
var radius = 50.0
var color = Color.AQUAfunc _draw() -> void:for i in range(particles.size()):draw_circle(particles[i],radius,color,false,1)# 鼠标中键改变第二个圆的位置
func _input(event: InputEvent) -> void:if event is InputEventMouseButton:if event.button_index == MOUSE_BUTTON_RIGHT:particles[1] = get_global_mouse_position()queue_redraw()# 点击按钮执行重叠检测
func _on_button_pressed() -> void:test_overlap()# 检测圆之间是否重叠
func test_overlap():var p1 = particles[0]  # 圆1var p2 = particles[1]  # 圆2var dis = p1.distance_to(p2)  # 距离var dir = p1.direction_to(p2) # 方向if dis < radius * 2:var over_dis = radius * 2 - disparticles[0] = particles[0] - dir  * over_dis/2.0 # 推开重叠距离的1/2particles[1] = particles[1] + dir  * over_dis/2.0 # 推开重叠距离的1/2queue_redraw()

运行效果:

点击“执行检测”按钮,如果两个圆之间的距离小于它们半径之和,表示有重叠,就会沿两者圆心连线方向将彼此推开重叠距离的1/2。推开后的效果:

这样,基于两个圆之间的重叠检测和推开功能已经实现。

动态检测

通过用鼠标动态控制两圆中的一个,并且将碰撞检测放到_process()中,就可以实现动态的碰撞检测。

extends Node2Dvar particles:PackedVector2Array = Points2D.Vec2Arr("300,420 320,450")
var radius = 50.0
var color = Color.AQUAfunc _process(delta: float) -> void:particles[1] = get_global_mouse_position()queue_redraw()test_overlap()func _draw() -> void:for i in range(particles.size()):draw_circle(particles[i],radius,color,false,1)# 检测圆之间是否重叠
func test_overlap():var p1 = particles[0]  # 圆1var p2 = particles[1]  # 圆2var dis = p1.distance_to(p2)  # 距离var dir = p1.direction_to(p2) # 方向if dis < radius * 2:var over_dis = radius * 2 - disparticles[0] = particles[0] - dir  * over_dis/2.0 # 推开重叠距离的1/2particles[1] = particles[1] + dir  * over_dis/2.0 # 推开重叠距离的1/2queue_redraw()

运行效果:

可以看到有一种用一个圆推动另一个圆的效果。

多粒子测试

在两个粒子时用PackedVector2Array是没有问题的,但是当粒子比较多时,最好还是使用一个简单的自定义类型来表示粒子。

这里我申明一个Particle类,表示简单的圆形粒子:

# 粒子类型
class Particle:var position:Vector2  # 位置var radius:float      # 半径func _init(position:Vector2,radius:float) -> void:self.position = positionself.radius = radius

则测试代码可以改成:

extends Node2Dvar particles:Array[Particle]
var radius = 50.0
var color = Color.AQUA
var max_count = 100   # 粒子数目func _ready() -> void:set_process(false) # 禁用processvar rect = get_viewport_rect()# 创建粒子for i in range(max_count):var pos = Vector2(randf_range(0,rect.size.x),randf_range(0,rect.size.y))var p = Particle.new(pos,radius)particles.append(p)func _process(delta: float) -> void:for i in range(particles.size()):test_overlap_all(i)func _draw() -> void:for i in range(particles.size()):draw_circle(particles[i].position,radius,color,false,1)# 遍历检测所有的粒子
func test_overlap_all(idx:int):var p1 = particles[idx]for i in range(particles.size()):if i != idx:var p2 = particles[i]test_overlap(p1,p2)# 检测圆之间是否重叠
func test_overlap(p1:Particle,p2:Particle):var dis = p1.position.distance_to(p2.position)  # 距离var dir = p1.position.direction_to(p2.position) # 方向var min_dis = p1.radius + p2.radiusif dis < min_dis:var over_dis = min_dis - disp1.position = p1.position - dir  * over_dis/2.0 # 推开重叠距离的1/2p2.position = p2.position + dir  * over_dis/2.0 # 推开重叠距离的1/2queue_redraw()# 启用重叠检测
func _on_button_pressed() -> void:set_process(true)

这里:

  • 在运行时创建随机位置的100个半径为50像素的圆粒子,初始禁用_process和重叠检测
  • 点击“开启检测”按钮时,启用_process和重叠检测
  • 通过不断的遍历所有圆粒子和其他粒子,并进行重叠检测和推开操作,可以让所有粒子都不重叠

通过鼠标操纵其中的一个圆的位置,便可以实现动态的碰撞检测效果。

func _process(delta: float) -> void:particles[1].position = get_global_mouse_position()queue_redraw()for i in range(particles.size()):test_overlap_all(i)

运行效果:

总结

  • 本篇简述在Godot用CanvasItem绘图函数实现简单的圆粒子和元粒子的重叠检测与推开操作
  • 这是图形碰撞检测算法的第一篇,后续文章将讨论矩形包围盒的算法
  • 这个算法在粒子数目较多时有明显的延迟,后文将讨论优化算法

相关文章:

  • 【Java】虚拟机(JVM)内存模型全解析
  • RM服务器研究(一)
  • SpringBoot3.X配置OAuth
  • vLLM (6) - Scheduler BlockSpaceManager
  • 数据结构:栈 及其应用
  • 多元函数微分学基础题
  • 【开源免费】基于SpringBoot+Vue.JS服装销售平台(JAVA毕业设计)
  • 【C++】二义性
  • ffmpeg拉取rtsp网络视频流报错解析
  • 学校周赛(2)
  • 如何在银河麒麟操作系统中查看内存页大小
  • 大数据Hologres(一):Hologres 简单介绍
  • 【数据结构初阶】排序算法(中)快速排序专题
  • 人工智能实战用折线图解读产业GDP发展态势
  • 【自用】jlu 数据库 第一章 Introduction
  • “寒冬”下的金三银四跳槽季来了,帮你客观分析一下局面
  • 【399天】跃迁之路——程序员高效学习方法论探索系列(实验阶段156-2018.03.11)...
  • 【面试系列】之二:关于js原型
  • Android开发 - 掌握ConstraintLayout(四)创建基本约束
  • canvas绘制圆角头像
  • Map集合、散列表、红黑树介绍
  • React-Native - 收藏集 - 掘金
  • Redis的resp协议
  • Redux系列x:源码分析
  • Spring框架之我见(三)——IOC、AOP
  • Webpack4 学习笔记 - 01:webpack的安装和简单配置
  • 后端_MYSQL
  • 技术:超级实用的电脑小技巧
  • 如何用vue打造一个移动端音乐播放器
  • 什么软件可以剪辑音乐?
  • 微信如何实现自动跳转到用其他浏览器打开指定页面下载APP
  • 小程序button引导用户授权
  • 小试R空间处理新库sf
  • 中文输入法与React文本输入框的问题与解决方案
  • “十年磨一剑”--有赞的HBase平台实践和应用之路 ...
  • Play Store发现SimBad恶意软件,1.5亿Android用户成受害者 ...
  • 专访Pony.ai 楼天城:自动驾驶已经走过了“从0到1”,“规模”是行业的分水岭| 自动驾驶这十年 ...
  • 昨天1024程序员节,我故意写了个死循环~
  • # SpringBoot 如何让指定的Bean先加载
  • #mysql 8.0 踩坑日记
  • #NOIP 2014#day.2 T1 无限网络发射器选址
  • #传输# #传输数据判断#
  • $emit传递多个参数_PPC和MIPS指令集下二进制代码中函数参数个数的识别方法
  • (1)STL算法之遍历容器
  • (4)事件处理——(2)在页面加载的时候执行任务(Performing tasks on page load)...
  • (42)STM32——LCD显示屏实验笔记
  • (BFS)hdoj2377-Bus Pass
  • (done) NLP “bag-of-words“ 方法 (带有二元分类和多元分类两个例子)词袋模型、BoW
  • (poj1.3.2)1791(构造法模拟)
  • (第9篇)大数据的的超级应用——数据挖掘-推荐系统
  • (二十九)STL map容器(映射)与STL pair容器(值对)
  • (分类)KNN算法- 参数调优
  • (含react-draggable库以及相关BUG如何解决)固定在左上方某盒子内(如按钮)添加可拖动功能,使用react hook语法实现
  • (论文阅读40-45)图像描述1
  • (三十)Flask之wtforms库【剖析源码上篇】