这张是在网络上下载的一张《杀戮尖塔》的游戏截图,里面卡牌施法的箭头通用于各类卡牌游戏的开发,但本质上是一个三阶贝塞尔曲线。
贝塞尔曲线是一种平滑曲线,由数学家 Pierre Bézier 在 20 世纪 60 年代提出。它通过控制点的位置来定义曲线的形状。贝塞尔曲线常用于计算机图形学、几何建模和动画等领域。
贝塞尔曲线的特点是它的起点和终点由控制点确定,而曲线在起点和终点之间的形状则由额外的控制点来调整。这些额外的控制点称为控制点或锚点。通过调整控制点的位置和个数,可以创造出各种不同形状的曲线,包括直线、曲线、圆弧等。
贝塞尔曲线的度数决定了控制点的数量。一阶贝塞尔曲线(线性贝塞尔曲线)只有两个控制点,即起点和终点。二阶贝塞尔曲线(二次贝塞尔曲线)有三个控制点,其中两个控制点位于曲线的两侧,起到调整曲线形状的作用。三阶贝塞尔曲线(三次贝塞尔曲线)有四个控制点,可以更加灵活地调整曲线的形状。
贝塞尔曲线的计算公式使用了多项式插值的方法,通过参数化的方式来生成曲线上的点。通过调整参数的取值范围,可以获得曲线上的各个点,从而绘制出整条曲线。
贝塞尔曲线在计算机图形学中广泛应用,例如绘制平滑的曲线、形状变换、曲面细分等。其优点是简单易懂、计算效率高,并且能够通过调整控制点来实现精确的形状设计。
在这里我们要实现的利用贝塞尔曲线的原理实现一条曲线,先在Godot里面新建一个Control类,并且添加.gd代码, 然后编辑如下代码。
extends Control
class_name bezier
#贝塞尔曲线,使用方法:
#作为组件添加调用 reset(起点,终点),箭头和圆点样式可以自己改
var arrow_num:int =10
var list=[]
@export var t_arrow_head:Texture2D
@export var t_arrow_body:Texture2D
func _ready() -> void:
#创建界面箭头图形
for i in range(arrow_num-1):#把身体加进去
var sprite = Sprite2D.new()
add_child(sprite)
list.append(sprite)
sprite.texture = t_arrow_body
sprite.scale = Vector2(1,1)*(i/arrow_num*1+0.2)
sprite.offset = Vector2(-50,0)
var sprite = Sprite2D.new()#把箭头加进去
add_child(sprite)
list.append(sprite)
sprite.texture = t_arrow_head
sprite.offset = Vector2(-50,0)
func reset(start_Pos,end_Pos):
self.show()
var ctrl_a_pos = Vector2()
var ctrl_b_pos = Vector2()
ctrl_a_pos.x = start_Pos.x + (start_Pos.x - end_Pos.x) * 0.1
ctrl_a_pos.y = end_Pos.y - (end_Pos.y - start_Pos.y) * 0.2
ctrl_b_pos.x = start_Pos.x - (start_Pos.x - end_Pos.x) * 0.3
ctrl_b_pos.y = end_Pos.y + (end_Pos.y - start_Pos.y) * 0.3
for i in range(arrow_num):
var t = float(i)/(arrow_num - 1)
var pos = (start_Pos * ((1-t)*(1-t)*(1-t)))+(3*ctrl_a_pos*t*(1-t)*(1-t))+(3*ctrl_b_pos*t*t*(1-t))+(end_Pos*t*t*t)
#list[i].position = pos
var sprite = list[i]
sprite.global_position = pos
sprite.scale = Vector2(1, 1) * (t + 0.2)
sprite.offset = Vector2(-50, 0)
update_angle()
func update_angle():
for i in range(arrow_num):
if i == 0:
list[i].rotation_degrees = 270
else:
var current = list[i]
var last = list[i-1]
var lenvec = current.global_position - last.global_position
var a = lenvec.angle()
a = rad_to_deg(a)#弧度制转角度制
current.rotation_degrees = a
这个代码也并不是我原创,更详细的讲解可以参考:
1.在reset
方法中,使用了贝塞尔曲线的计算公式来确定曲线上每个点的位置:
for i in range(arrow_num):
var t = float(i)/(arrow_num - 1)
var pos = (start_Pos * ((1-t)*(1-t)*(1-t))) + (3*ctrl_a_pos*t*(1-t)*(1-t)) + (3*ctrl_b_pos*t*t*(1-t)) + (end_Pos*t*t*t)
在这段代码中,根据贝塞尔曲线的公式,通过控制点 ctrl_a_pos
、ctrl_b_pos
和起点终点 start_Pos
、end_Pos
来计算出曲线上各个点的位置。
2.在 update_angle
方法中,根据曲线上相邻两个点的位置计算角度,并将箭头图形旋转至该角度:
for i in range(arrow_num):
if i == 0:
list[i].rotation_degrees = 270
else:
var current = list[i]
var last = list[i-1]
var lenvec = current.global_position - last.global_position
var a = lenvec.angle()
a = rad_to_deg(a) # 弧度制转角度制
current.rotation_degrees = a
在这段代码中,通过计算相邻两点的向量,并根据向量的角度来设置箭头的旋转角度,从而使箭头沿着贝塞尔曲线的方向进行旋转。reset
和 update_angle
方法体现了贝塞尔曲线的核心算法公式,其中reset是对外提供的接口,只需要传入起点坐标和终点坐标,基本是vector2类型,就能对路径进行均匀的曲线补间。
然后在@export暴露出的t_arrow_head和t_arrow_body接口添加合适的图片,就能实现正确的效果。
在我使用Godot开发的游戏《威小鼠和猪大常》 (Mighty Mouse and Great Piggy)里面三阶贝塞尔曲线施法箭头就得到了应用。