【Godot4.3】斜抛运动轨迹曲线点求取函数
概述
原文写于去年9月。一篇测试性的文章。
基于初始位置和初始速度的抛物线
# 抛物运动轨迹曲线 - 基于初始位置和初始速度计算
func projectile_motion_curve(
start_pos:Vector2, # 物体的起始位置
velocity:Vector2, # 初始速度
nums:int, # 取点的个数
delta:=1.0, # 时间间隔,越小曲线越平滑
g = 9.8 # 重力加速度
) -> PackedVector2Array:
var points:PackedVector2Array
var vx0 = Vector2.RIGHT * velocity.x # x轴方向的初始速度
var vy0 = Vector2.DOWN * velocity.y # y轴方向的初始速度
var pos = start_pos
var v = velocity
# 计算轨迹点
for t in range(nums):
var v1 = vx0 * t * delta
var v2 = vy0 * t * delta + Vector2.DOWN * (0.5 * g * pow(t * delta,2))
v = v1 + v2
pos = start_pos + v
points.append(pos)
return points
测试
# 抛物运动曲线测试
extends Node2D
## 曲线和点颜色
@export var color:=Color.AQUA
var force = 50.0 # 力的大小
var start_pos := Vector2(100,100) # 物体起始位置
var velocity = Vector2() # 初始的速度
var nums:int = 20 # 取点的个数
var delta:=1.0 # 时间间隔,越小曲线越平滑
var g = 9.8 # 重力加速度
func _process(delta: float) -> void:
velocity = start_pos.direction_to(get_global_mouse_position()) * force
queue_redraw()
func _draw() -> void:
var curve = projectile_motion_curve(start_pos,velocity,nums,delta,g)
draw_polyline(curve,color,1)
for p in curve:
draw_circle(p,3,color)
pass
- 曲线点的个数由
nums
参数控制 - 单位时间间隔由
delta
参数控制,越小,点之间的距离越小,绘制曲线后越平滑
基于指定两点的抛物线
也就是知道抛出点和目标点的抛物线。
# 抛物线轨迹 - 基于初始位置和目标位置
func projectile_motion_curve2(
start_pos:Vector2, # 物体的起始位置
target_pos:Vector2, # 目标位置
delta:=1.0, # 时间间隔,越小曲线越平滑
g = 9.8 # 重力加速度
) -> PackedVector2Array:
var points:PackedVector2Array
var d = target_pos - start_pos # 位移向量
var t2:float = sqrt((2 * d.y) / g)
#print(t)
var velocity = Vector2.UP * 0 + Vector2.RIGHT * (d.x / t2)
var vx0 = Vector2.RIGHT * velocity.x # x轴方向的初始速度
var vy0 = Vector2.DOWN * velocity.y # y轴方向的初始速度
var pos = start_pos
var v = velocity
# 计算轨迹点
for t in range(t2/delta):
var v1 = vx0 * t * delta
var v2 = vy0 * t * delta + Vector2.DOWN * (0.5 * g * pow(t * delta,2))
v = v2 + v1
pos = start_pos + v
points.append(pos)
if points.size() >0:
if target_pos.distance_to(points[points.size()-1]) > 0.0:
points.append(target_pos)
return points
测试
# 抛物运动曲线测试
extends Node2D
## 曲线和点颜色
@export var color:=Color.AQUA
var force = 50.0 # 力的大小
var start_pos := Vector2(100,100) # 物体起始位置
var target_pos:Vector2 # 目标位置
var delta:=1.0 # 时间间隔,越小曲线越平滑
var g = 9.8 # 重力加速度
func _process(delta: float) -> void:
target_pos = get_global_mouse_position()
queue_redraw()
func _draw() -> void:
var curve = projectile_motion_curve2(start_pos,target_pos,delta,g)
draw_polyline(curve,color,1)
for p in curve:
draw_circle(p,3,color)
pass
改进,加入一定的抬高
其中: t = 2 ( 2 k + d y ) g t=\sqrt{\frac{2(2k+dy)}{g}} t=g2(2k+dy)那么最高点的速度: ∣ v k ∣ = d x t |v_k|= \frac{d_{x}}{t} ∣vk∣=tdx
又因为: t k = 2 k g t_k=\sqrt{\frac{2k}{g}} tk=g2k所以 ∣ v k y ∣ = g ⋅ t k |v_{ky}|=g\cdot t_k ∣vky∣=g⋅tk
所以初始速度: v 0 = v k y + v k v_{0}=v_{ky} + v_{k} v0=vky+vk
当目标点的纵坐标在起始点的上方时:
其中: t = 2 ( 2 k + d y ) g t=\sqrt{\frac{2(2k+dy)}{g}} t=g2(2k+dy)最高点的速度 ∣ v k ∣ = d x t |v_k|= \frac{d_{x}}{t} ∣vk∣=tdx还是保持不变。
但是: ∣ v k y ∣ = g ⋅ t ( k + d y ) |v_{ky}|=g \cdot t_{(k+dy)} ∣vky∣=g⋅t(k+dy)而 t ( k + d y ) = 2 ( k + d y ) g t_{(k+dy)}=\sqrt{\frac{2(k+dy)}{g}} t(k+dy)=g2(k+dy)
# 抛物线轨迹 - 基于初始位置和目标位置 加入一定的抬高距离k
func projectile_motion_curve3(
start_pos:Vector2, # 物体的起始位置
target_pos:Vector2, # 目标位置
k:=50.0, # 抬高距离
delta:=1.0, # 时间间隔,越小曲线越平滑
g = 9.8 # 重力加速度
) -> PackedVector2Array:
var points:PackedVector2Array
var d = target_pos - start_pos # 位移向量
# 到目标位置的总时长
var t2:float
# 走距离k的时间
var t1:float
# 走距离k的时间
var t3:float
var vx0 # x轴方向的初始速度
var vy0 # y轴方向的初始速度
if target_pos.y < start_pos.y:
t2 = sqrt(2.0 * (2.0 * k + abs(d.y)) / float(g))
t3 = sqrt(2.0 * (k + abs(d.y)) / float(g))
vy0 = Vector2.UP * (g * t3)
else:
t2 = sqrt(2.0 * (2.0 * k + d.y) / float(g))
t1 = sqrt((2.0 * k)/ float(g))
vy0 = Vector2.UP * (g * t1)
vx0 = Vector2.RIGHT * (d.x/float(t2))
# 初速度
var velocity = vx0 + vy0
var pos = start_pos
var v = velocity
# 计算轨迹点
for t in range(t2/delta):
var v1 = vx0 * t * delta
var v2 = vy0 * t * delta + Vector2.DOWN * (0.5 * g * pow(t * delta,2))
v = v2 + v1
pos = start_pos + v
points.append(pos)
if points.size() >0:
if target_pos.distance_to(points[points.size()-1]) > 0.0:
points.append(target_pos)
return points
测试:
可以看到,鼠标位置在起始点上方时,也可以显示曲线,但是不是太精确。