Godot插值、贝塞尔曲线和Astar寻路

一、插值

线性插值是采用一次多项式上进行的插值计算,任意给定两个值A和B,那么在A和B之间的任意值可以定义为:P(t) = A * (1 - t) + B * t,0 <= t <= 1。

数学中用于线性拟合,游戏应用可以做出跟随效果(宠物跟随、npc跟随)

java 复制代码
const FOLLOW_SPEED = 4.0

func _physics_process(delta):
	var mouse_pos = get_local_mouse_position()

	$Sprite2D.position = $Sprite2D.position.lerp(mouse_pos, delta * FOLLOW_SPEED)

二、贝塞尔

贝塞尔是插值的应用之一。贝塞尔曲线是为工业设计,是图形软件行业中的流行工具。不过在游戏中会出现曲线扭曲的情况,应用并不好。

1.二次贝塞尔:

我们首先使用 0 到 1 之间的值,在两个线段的每个顶点上逐步插值。当我们把 t 值从 0 变成 1 时,就得到了两个沿着线段移动的点。然后,我们插值 q0 和 q1,以获得沿着曲线移动的单点 r。

java 复制代码
func _quadratic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, t: float):
	var q0 = p0.lerp(p1, t)
	var q1 = p1.lerp(p2, t)
    var r = q0.lerp(q1, t)
    return r

2.三次贝塞尔

同理三个线段的时候称为三次贝塞尔曲线。

java 复制代码
func _cubic_bezier(p0: Vector2, p1: Vector2, p2: Vector2, p3: Vector2, t: float):
	var q0 = p0.lerp(p1, t)
	var q1 = p1.lerp(p2, t)
	var q2 = p2.lerp(p3, t)
	var r0 = q0.lerp(q1, t)
	var r1 = q1.lerp(q2, t)
	var s = r0.lerp(r1, t)
	return s

三、Astar寻路

Astar算法是最广泛应用的寻路算法,它的特点是基于网格,而且可以快速的求解某个点到另一个点的最短有效路径。Godot种提供了算法的封装类可以直接使用。2D版本是AStar2D、AStarGrid2D。

1.思路

添加可以到达的位置

将可以行走的点两两连接,形成路径

通过其方法直接求取某个位置到目标位置的最短路径

让玩家或其他角色按照路径上点的顺序依次前进,直到到达目标位置

java 复制代码
extends Node2D

var astar = AStar2D.new() # 实例化

func _ready():
	# 添加可以到达的位置
	astar.add_point(0,Vector2(0,0))
	astar.add_point(1, Vector2(0, 0))
	astar.add_point(2, Vector2(0, 1), 1) # 默认权重为 1
	astar.add_point(3, Vector2(1, 1))
	astar.add_point(4, Vector2(2, 0))
	# 在点之间创建连接,形成路径
	astar.connect_points(1, 2, false)
	astar.connect_points(2, 3, false)
	astar.connect_points(4, 3, false)
	astar.connect_points(1, 4, false)
	# 查询某两个位置之间的路径
	var res = astar.get_id_path(1, 4) # [1,4]

add_point()的时候传入了一个ID,可以将其想象为是一个唯一索引值,对点的标记。
get_id_path()方法获取的是两个对应ID的点之间的最短路径,返回的是包含路径经过的所有点的ID所组成的数组。

你也可以用get_point_path()方法直接获取两个点之间的最短路径,返回的额是包含所有经过的点数组。

2.应用

在Tilemap挂载方法(版本4.2.1):

java 复制代码
extends TileMap

@onready var astar_node: AStar2D = AStar2D.new()

var map_size: Vector2i = get_used_rect().size

func _ready():
	# 遍历所有层,将可以寻路的tile加入Astar图的节点
	for layer in range(get_layers_count()):
		var cells = get_used_cells(layer)
		for cell in cells:
			var tileData = get_cell_tile_data(layer, cell)
			var nav = tileData.get_navigation_polygon(0)
			if nav == null:
				continue
			var point_index = calculate_point_index(cell)
			astar_node.add_point(point_index, Vector2(cell.x, cell.y))
			
	# 移除有碰撞体的点
	for layer in range(get_layers_count()):
		var cells = get_used_cells(layer)
		for cell in cells:
			var tileData = get_cell_tile_data(layer, cell)
			var collision = tileData.get_collision_polygons_count(0)
			if collision <= 0:
				continue
			var point_index = calculate_point_index(cell)
			if astar_node.has_point(point_index):
				astar_node.remove_point(point_index)
				
	# 连接图的节点
	for id in astar_node.get_point_ids():
		var cellPosition = Vector2i(astar_node.get_point_position(id))
		var relativeCells: Array[Vector2i] = [
			cellPosition + Vector2i.RIGHT,
			cellPosition + Vector2i.LEFT,
			cellPosition + Vector2i.DOWN,
			cellPosition + Vector2i.UP,			
		]
		for relativeCell in relativeCells:
			var relativeCellIndex = calculate_point_index(relativeCell)
			if is_outside_map_bounds(relativeCell):
				continue
			if astar_node.has_point(relativeCellIndex):
				astar_node.connect_points(id, relativeCellIndex, false)
				
func calculate_point_index(point: Vector2i) -> int:
	return point.x + point.y * map_size.x

func is_outside_map_bounds(point: Vector2i) -> bool:
	return point.x <0 || point.y < 0 || point.x >= map_size.x || point.y >= map_size.y
	
func get_nav_path(startCellPosition: Vector2i, endCellPosition: Vector2i) -> PackedVector2Array:
	var navCellPath = astar_node.get_point_path(calculate_point_index(startCellPosition), calculate_point_index(endCellPosition))
	var localPath = PackedVector2Array()
	for cellPosition in navCellPath:
		localPath.push_back(map_to_local(Vector2i(cellPosition)))
	return localPath
相关推荐
郝学胜-神的一滴1 小时前
[简化版 GAMES 101] 计算机图形学 08:三角形光栅化上
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal
nnsix2 小时前
Unity ILRuntime 笔记
unity·游戏引擎
nnsix4 小时前
Unity API 兼容的 .NET Standard 2.1 和 .NET Framework 区别
unity·游戏引擎·.net
mxwin4 小时前
Unity Shader 制作半透明物体 使用多Pass提前写入深度的方式 避免穿模
unity·游戏引擎
nnsix6 小时前
Unity HybridCLR 笔记
笔记·unity·游戏引擎
nnsix7 小时前
Unity Addressables 笔记
unity·游戏引擎
RReality7 小时前
【Unity Shader URP】视差贴图 实战教程
ui·平面·unity·游戏引擎·图形渲染·贴图
mxwin1 天前
Unity SetPassCall和DrawCall的区别是什么
unity·游戏引擎·shader
电子云与长程纠缠1 天前
UE5 GameFeature创建与使用
开发语言·学习·ue5·游戏引擎
moonsims1 天前
AiBrainLink:无人化系统异构连接架构-多执行体、多链路(5G+自组网)、多业务流(控制、遥测、视频、文件)透明传输、多对多控制
unity·游戏引擎