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
相关推荐
应用市场3 天前
无人机姿态控制系统详解与实现
游戏引擎·cocos2d
陈言必行3 天前
Unity 性能优化 之 编辑器创建资源优化( 工作流 | 场景 | 预制体)
unity·编辑器·游戏引擎
1uther3 天前
Unity核心概念⑨:Screen
开发语言·游戏·unity·c#·游戏引擎
死也不注释3 天前
【Unity UGUI 交互组件——Slider(7)】
unity·游戏引擎·交互
相与还3 天前
godot+c#使用godot-sqlite连接数据库
数据库·c#·godot
挂科是不可能出现的3 天前
unity导入blender动画
unity·游戏引擎·blender
相与还3 天前
godot+c#操作sqlite并加解密
sqlite·c#·godot·sqlcipher
派葛穆4 天前
Unity-按钮实现场景跳转
java·unity·游戏引擎
绀目澄清4 天前
unity UGUI 鼠标画线
unity·计算机外设·游戏引擎
Magnum Lehar4 天前
3d wpf游戏引擎的导入文件功能c++的.h实现
3d·游戏引擎·wpf