【Godot4.3】匀速和匀变速直线运动粒子

概述

本篇论述,如何用加速度在Godot中控制粒子运动。

匀速和匀变速直线运动的统一

以下是匀变速运动的速度和位移公式:

v t = v 0 + a t x t = v 0 t + 1 2 a t 2 v_t=v_0 + at \\ x_t=v_0t + \frac{1}{2}at^2 vt=v0+atxt=v0t+21at2

当a = 0 时:

v t = v 0 x t = v 0 t v_t=v_0 \\ x_t=v_0t vt=v0xt=v0t

所以匀速直线运动可以看成是a = 0 的特殊匀变速直线运动,两者可以共用一套公式。

Godot中的匀变速直线运动实现

另外,我们在Godot的_process()或者_physics_process()中得到的delta其实就是 Δ t \Delta t Δt,而不是一个连续累计的时间 t t t。

我们需要计算的当前帧基于前一帧的速度和位移,也就是:

v f r a m e = v f r a m e − 1 + a Δ t x f r a m e = v f r a m e − 1 Δ t + 1 2 a Δ t 2 v_{frame} = v_{frame-1} + a \Delta t \\ x_{frame} = v_{frame-1}\Delta t + \frac{1}{2} a {\Delta t}^2 vframe=vframe−1+aΔtxframe=vframe−1Δt+21aΔt2

其中:

  • v f r a m e v_{frame} vframe表示当前帧的速度, v f r a m e − 1 v_{frame-1} vframe−1表示上一帧的速度
  • x f r a m e x_{frame} xframe表示当前帧的位置, x f r a m e − 1 x_{frame-1} xframe−1表示上一帧的位置

其实也就是:

Δ v = v f r a m e − v f r a m e − 1 = a Δ t Δ x = x f r a m e − x f r a m e − 1 = v f r a m e − 1 Δ t + 1 2 a Δ t 2 \Delta v = v_{frame} - v_{frame-1} = a \Delta t \\ \Delta x = x_{frame} - x_{frame-1} = v_{frame-1}\Delta t + \frac{1}{2} a {\Delta t}^2 Δv=vframe−vframe−1=aΔtΔx=xframe−xframe−1=vframe−1Δt+21aΔt2

所以当前帧:

v f r a m e = v f r a m e − 1 + Δ v x f r a m e = x f r a m e − 1 + Δ x v_{frame} = v_{frame-1} + \Delta v \\ x_{frame} = x_{frame-1} + \Delta x vframe=vframe−1+Δvxframe=xframe−1+Δx

速度和位移都变成了基于前一帧的累计值,而与初始的速度 v 0 v_0 v0无关,同样加速度a = 0时, Δ v \Delta v Δv=0,当前帧速度保持不变, Δ x = v f r a m e − 1 Δ t \Delta x = v_{frame-1}\Delta t Δx=vframe−1Δt,当前帧的位置 = 上一帧位置 + Δ x \Delta x Δx。

实现粒子类

基于上面的认识,我们可以编写一个粒子类。它的代码如下,其中update()用于粒子基于_process()或者_physics_process()中得到的delta更新粒子速度和位置,是完全按照上面的思路实现的。

swift 复制代码
# 粒子
class Particle:
	var position:Vector2
	var velocity:Vector2
	var acceleration:Vector2

	func _init(position:Vector2,velocity:Vector2,acceleration:Vector2) -> void:
		self.position = position
		self.velocity = velocity
		self.acceleration = acceleration
	
	# 更新速度和位置
	func update(d_t: float)-> void:
		var d_v = acceleration * d_t
		velocity += d_v
		position += velocity * d_t + (d_v * d_t)/2
	
	# 绘制粒子
	func draw_particle(
		canvas_item:CanvasItem,
		color:=Color.AQUAMARINE,
		r:=3.0,
		fill:=true,
		border_width:=1
	):
		canvas_item.draw_circle(position,r,color,fill,border_width)
	
	# 绘制粒子的速度矢量
	func draw_velocity(
		canvas_item:CanvasItem,
		color:=Color.GREEN_YELLOW,
		border_width:=1
	):
		canvas_item.draw_line(position,position+velocity,color,border_width)
	
	# 绘制粒子的加速度矢量
	func draw_acceleration(
		canvas_item:CanvasItem,
		color:=Color.ORANGE_RED,
		border_width:=1
	):
		canvas_item.draw_line(position,position+acceleration,color,border_width)

测试代码

swift 复制代码
extends Node2D

var pos:Vector2 = Vector2(100,100) # 位置
var v := Vector2()                 # 速度
var a := Vector2.RIGHT * 20        # 加速度


# 创建粒子实例
var p = Particle.new(pos,v,a)

func _process(delta: float) -> void:
	p.update(delta) # 更新粒子的速度和位置
	queue_redraw()  # 请求重绘

# 绘制
func _draw() -> void:
	p.draw_particle(self)     # 绘制粒子
	p.draw_velocity(self)     # 绘制速度向量
	p.draw_acceleration(self) # 绘制加速度向量

可以看到:

  • 我们在创建粒子实例时,只需要设定起始位置、初始速度以及加速度就可以了。
  • 程序便会自动随时间更新粒子的速度和位置,并且绘制出粒子、粒子当前的速度以及加速度

通过设定不同的起始位置、初始速度以及加速度,我们就可以模拟出匀速直线运动、匀加速直线运动和匀减速直线运动。

swift 复制代码
# 匀减速直线运动
var pos:Vector2 = Vector2(100,100) # 起始位置
var v := Vector2.RIGHT * 100       # 初始速度
var a := Vector2.LEFT * 20         # 加速度
swift 复制代码
# 初速度为0的匀加速直线运动
var pos:Vector2 = Vector2(100,100) # 起始位置
var v := Vector2()                 # 初始速度
var a := Vector2.RIGHT * 20        # 加速度
swift 复制代码
# 初速度不为0的匀速直线运动
var pos:Vector2 = Vector2(100,100) # 起始位置
var v := Vector2.RIGHT * 100       # 初始速度
var a := Vector2()                 # 加速度

用曲线控制速度和加速度变化

swift 复制代码
extends Node2D

# 匀减速直线运动
var pos:Vector2 = Vector2(300,300) # 起始位置
var v := Vector2.RIGHT * 100       # 初始速度
var a := Vector2.LEFT * 0         # 加速度

@export var velocity_curve:Curve

# 创建粒子实例
var p = Particle.new(pos,v,a)

var offset:= 0.0
var step:=0.005


func _process(delta: float) -> void:
	p.velocity = v * velocity_curve.sample(offset)
	offset += step
	if not(offset <=1.0 and offset >= 0.0):
		step *= -1
	
	p.update(delta) # 更新粒子的速度和位置
	queue_redraw()

# 绘制
func _draw() -> void:
	p.draw_particle(self,Color.AQUAMARINE,20.0)     # 绘制粒子
	p.draw_velocity(self)     # 绘制速度向量
	p.draw_acceleration(self) # 绘制加速度向量

效果:

相关推荐
代码盗圣6 小时前
GODOT 4 不用scons编译cpp扩展的方法
游戏引擎·godot
知兀16 天前
单例模式(自动加载)
笔记·游戏引擎·godot
知兀19 天前
Godot中的信号
笔记·游戏引擎·godot
知兀20 天前
【godot游戏引擎学习笔记】初识界面
笔记·游戏引擎·godot
知兀20 天前
Godot中类和静态类型
笔记·游戏引擎·godot
知兀21 天前
编辑器、节点树、基础设置
笔记·游戏引擎·godot
网络研究院25 天前
数据中心物理安全的历史和演变
安全·数据中心·历史·观点·物理·演变
bluebean1 个月前
转动惯量矩阵推导
物理·刚体动力学·旋转动力学
LIURUOYU4213081 个月前
基于三体强相互作用力的材料——一种理论探讨
物理