一、插值
线性插值是采用一次多项式上进行的插值计算,任意给定两个值A和B,那么在A和B之间的任意值可以定义为:P(t) = A * (1 - t) + B * t,0 <= t <= 1。
数学中用于线性拟合,游戏应用可以做出跟随效果(宠物跟随、npc跟随)
const FOLLOW_SPEED = 4.0func _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。
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.三次贝塞尔
同理三个线段的时候称为三次贝塞尔曲线。
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.思路
添加可以到达的位置
将可以行走的点两两连接,形成路径
通过其方法直接求取某个位置到目标位置的最短路径
让玩家或其他角色按照路径上点的顺序依次前进,直到到达目标位置
extends Node2Dvar 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) # 默认权重为 1astar.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):
extends TileMap@onready var astar_node: AStar2D = AStar2D.new()var map_size: Vector2i = get_used_rect().sizefunc _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:continuevar 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:continuevar 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):continueif 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.xfunc is_outside_map_bounds(point: Vector2i) -> bool:return point.x <0 || point.y < 0 || point.x >= map_size.x || point.y >= map_size.yfunc 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