angle_difference
2026/4/15大约 4 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — angle_difference
angle_difference
定义
angle_difference() 用于计算两个角度之间的最短差值。它会考虑角度的环绕特性(也就是转满一圈会回到起点),始终返回从 from 到 to 之间最近的路径,结果范围在 -PI 到 PI 之间(弧度制)。
打一个生活化的比方:想象你站在钟表的中心,现在想从 1 点转到 11 点。你可以选择顺时针走 10 个小时,也可以逆时针走 2 个小时。angle_difference 就是那个帮你选更短路的函数——它永远帮你挑最省力的走法。
这个函数在制作角色朝向旋转、炮塔追踪、敌人视线方向等场景中非常好用,因为它能避免角色出现"绕远路转一大圈"的奇怪行为。
函数签名
C#
// Mathf.AngleDifference — 返回从 from 到 to 的最短角度差(弧度)
public static float AngleDifference(float from, float to)GDScript
# angle_difference — 返回从 from 到 to 的最短角度差(弧度)
func angle_difference(from: float, to: float) -> float参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
from | float | 是 | 起始角度,单位为弧度(不是度数)。例如 PI/2 代表 90 度 |
to | float | 是 | 目标角度,单位为弧度。这是你想要到达的角度 |
弧度小贴士:如果你习惯了"度数"(比如 90 度、180 度),可以用 deg_to_rad() 把度数转成弧度。例如 deg_to_rad(90) 就等于 PI/2。
返回值
返回一个 float 类型的值,表示从 from 走到 to 的最短角度差(单位为弧度)。
- 返回值为正数:表示从
from到to的最短路径是逆时针方向 - 返回值为负数:表示从
from到to的最短路径是顺时针方向 - 返回值的范围始终在 -PI 到 PI 之间
代码示例
基本用法:计算两个角度的最短差
C#
using Godot;
public partial class AngleExample : Node
{
public override void _Ready()
{
// 从 0 弧度(正右方)到 PI 弧度(正左方)
float diff1 = Mathf.AngleDifference(0f, Mathf.Pi);
GD.Print($"0 -> PI 的最短差: {diff1}"); // 输出约 3.14159(逆时针半圈)
// 从 PI 弧度到 0 弧度
float diff2 = Mathf.AngleDifference(Mathf.Pi, 0f);
GD.Print($"PI -> 0 的最短差: {diff2}"); // 输出约 -3.14159(顺时针半圈)
// 超过一圈的情况:从 0 到 3*PI(相当于 0 到 PI,多转了一圈)
float diff3 = Mathf.AngleDifference(0f, 3f * Mathf.Pi);
GD.Print($"0 -> 3PI 的最短差: {diff3}"); // 输出约 3.14159(自动忽略整圈)
}
}GDScript
extends Node
func _ready():
# 从 0 弧度(正右方)到 PI 弧度(正左方)
var diff1 = angle_difference(0.0, PI)
print("0 -> PI 的最短差: ", diff1) # 输出约 3.14159(逆时针半圈)
# 从 PI 弧度到 0 弧度
var diff2 = angle_difference(PI, 0.0)
print("PI -> 0 的最短差: ", diff2) # 输出约 -3.14159(顺时针半圈)
# 超过一圈的情况:从 0 到 3*PI(相当于 0 到 PI,多转了一圈)
var diff3 = angle_difference(0.0, 3.0 * PI)
print("0 -> 3PI 的最短差: ", diff3) # 输出约 3.14159(自动忽略整圈)实战场景:让角色平滑转向目标
这是 angle_difference 最常见的用途——让角色或炮塔以平滑的方式转向目标方向,而不是瞬间"跳转"。
C#
using Godot;
public partial class Turret : CharacterBody2D
{
// 导出属性:转向速度(弧度/秒)
[Export] public float ExRotateSpeed = 3.0f;
// 内部变量
private float _currentAngle = 0f;
public override void _Process(double delta)
{
// 获取目标方向:从炮塔位置指向鼠标位置
Vector2 mousePos = GetGlobalMousePosition();
float targetAngle = (mousePos - GlobalPosition).Angle();
// 计算当前角度到目标角度的最短差
float diff = Mathf.AngleDifference(_currentAngle, targetAngle);
// 每帧只转动一定角度,实现"平滑追踪"效果
float maxStep = ExRotateSpeed * (float)delta;
if (Mathf.Abs(diff) < maxStep)
{
_currentAngle = targetAngle; // 差值很小了,直接到位
}
else
{
_currentAngle += Mathf.Sign(diff) * maxStep; // 朝最短方向转一步
}
// 应用旋转
Rotation = _currentAngle;
}
}GDScript
extends CharacterBody2D
# 导出属性:转向速度(弧度/秒)
@export var ex_rotate_speed: float = 3.0
# 内部变量
var _current_angle: float = 0.0
func _process(delta):
# 获取目标方向:从炮塔位置指向鼠标位置
var mouse_pos = get_global_mouse_position()
var target_angle = (mouse_pos - global_position).angle()
# 计算当前角度到目标角度的最短差
var diff = angle_difference(_current_angle, target_angle)
# 每帧只转动一定角度,实现"平滑追踪"效果
var max_step = ex_rotate_speed * delta
if abs(diff) < max_step:
_current_angle = target_angle # 差值很小了,直接到位
else:
_current_angle += sign(diff) * max_step # 朝最短方向转一步
# 应用旋转
rotation = _current_angle注意事项
- 输入是弧度,不是度数:
angle_difference接收的参数和返回的结果都是弧度。如果你习惯用度数,记得先用deg_to_rad()转换,拿到结果后再用rad_to_deg()转回度数。 - 结果在 -PI 到 PI 之间:不管你传入多大的角度值(比如 100*PI),返回的结果永远不会超过半个圆的范围。这保证了你拿到的永远是"最短路"。
- 常与
lerp_angle()搭配使用:在角色平滑转向的场景中,angle_difference负责计算差值,lerp_angle()负责按比例插值,两者是好搭档。 - 正负方向约定:返回正值表示逆时针方向更近,返回负值表示顺时针方向更近。这个约定在 2D 和 3D 中都适用,但要注意 Godot 的坐标系(2D 中 Y 轴朝下,3D 中 Y 轴朝上)可能会影响你对"顺时针/逆时针"的直觉判断。
