Input.warp_mouse
2026/4/14大约 7 分钟
最后同步日期:2026-04-15 | Godot 官方原文 — Input.warp_mouse
Input.warp_mouse
定义
想象你正在玩一个射击游戏,鼠标已经移到了屏幕最右边的边缘,但你的角色还需要继续往右转动视角。这时候怎么办?一种办法是把鼠标"瞬间传送"回屏幕中间,这样你又可以继续往右移了——这就是 warp_mouse 做的事情。
warp_mouse 可以把鼠标瞬间移动到屏幕上的指定位置,就像你用手直接把鼠标提起来放到另一个地方一样。关键的是,这个移动是"静悄悄"的——它不会触发鼠标移动事件(InputEventMouseMotion),所以你的游戏不会误以为玩家真的在移动鼠标。
一句话总结
warp_mouse = "把鼠标瞬间传送到指定位置"——不触发任何移动事件,适合重置鼠标位置。
和直接移动鼠标的区别
如果你在代码中直接修改鼠标的位置,可能会产生大量的鼠标移动事件,导致游戏误判。warp_mouse 专门解决了这个问题——它移动鼠标但不产生任何事件,是"隐身传送"。
函数签名
C#
public static void WarpMouse(Vector2 position)GDScript
Input.warp_mouse(position: Vector2) -> void参数说明
| 参数 | 类型 | 必需 | 说明 |
|---|---|---|---|
| position | Vector2 | 是 | 鼠标要传送到的目标位置,使用视口坐标(像素)。(0, 0) 是窗口左上角 |
返回值
此函数没有返回值(void)。
代码示例
基础用法
最简单的用法——把鼠标传送到屏幕中央:
C#
using Godot;
public partial class TestWarpMouse : Node
{
public override void _Ready()
{
// 获取视口大小(窗口尺寸)
Vector2 viewportSize = GetViewport().GetVisibleRect().Size;
// 计算屏幕中央的坐标
Vector2 center = viewportSize / 2f;
// 把鼠标瞬移到屏幕中央
Input.WarpMouse(center);
GD.Print("鼠标已传送到: " + center);
// 运行结果: 鼠标已传送到: (540, 360)
// (假设窗口大小为 1080x720)
}
}GDScript
extends Node
func _ready() -> void:
# 获取视口大小(窗口尺寸)
var viewport_size := get_viewport().get_visible_rect().size
# 计算屏幕中央的坐标
var center := viewport_size / 2.0
# 把鼠标瞬移到屏幕中央
Input.warp_mouse(center)
print("鼠标已传送到: " + str(center))
# 运行结果: 鼠标已传送到: (540.0, 360.0)
# (假设窗口大小为 1080x720)实际场景
在一个第一人称射击游戏中,当鼠标接近屏幕边缘时,把鼠标传送回屏幕中央,这样玩家就可以无限旋转视角——这是 3D 射击游戏中非常经典的"鼠标环绕"技术:
C#
using Godot;
public partial class FPSController : Node3D
{
[Export] public float ExMouseSensitivity = 0.003f;
[Export] public float ExWarpMargin = 50f;
private Node3D _cameraPivot;
private float _rotationX = 0f;
public override void _Ready()
{
_cameraPivot = GetNode<Node3D>("CameraPivot");
// 捕获鼠标,用于第一人称视角
Input.SetMouseMode(Input.MouseModeEnum.Captured);
}
public override void _UnhandledInput(InputEvent @event)
{
// 如果鼠标被捕获,用 Relative 数据控制视角(不需要 warp)
// 这里演示的是 Confined 模式下需要手动环绕的情况
if (@event is InputEventMouseMotion motion)
{
// 用鼠标的相对移动量来旋转视角
_rotationX -= motion.Relative.Y * ExMouseSensitivity;
_rotationX = Mathf.Clamp(_rotationX, Mathf.Pi / -3f, Mathf.Pi / 3f);
RotateY(-motion.Relative.X * ExMouseSensitivity);
_cameraPivot.RotationDegrees = new Vector3(
Mathf.RadiansToDegrees(_rotationX), 0, 0);
GD.Print("视角旋转: " + RotationDegrees);
// 运行结果: 视角旋转: (0, 15.3, 0)
}
}
// 如果需要在非 Captured 模式下实现无限旋转,可以在 _Process 中检查
public override void _Process(double delta)
{
Vector2 mousePos = Input.GetMousePosition();
Vector2 viewportSize = GetViewport().GetVisibleRect().Size;
// 当鼠标接近边缘时,传送回中央
if (mousePos.X < ExWarpMargin || mousePos.X > viewportSize.X - ExWarpMargin ||
mousePos.Y < ExWarpMargin || mousePos.Y > viewportSize.Y - ExWarpMargin)
{
Vector2 center = viewportSize / 2f;
Input.WarpMouse(center);
GD.Print("鼠标接近边缘,已重置到中央");
// 运行结果: 鼠标接近边缘,已重置到中央
}
}
}GDScript
extends Node3D
@export var ex_mouse_sensitivity: float = 0.003
@export var ex_warp_margin: float = 50.0
@onready var _camera_pivot: Node3D = $CameraPivot
var _rotation_x: float = 0.0
func _ready() -> void:
# 捕获鼠标,用于第一人称视角
Input.mouse_mode = Input.MOUSE_MODE_CAPTURED
func _unhandled_input(event: InputEvent) -> void:
# 如果鼠标被捕获,用 Relative 数据控制视角(不需要 warp)
# 这里演示的是 Confined 模式下需要手动环绕的情况
if event is InputEventMouseMotion:
var motion := event as InputEventMouseMotion
# 用鼠标的相对移动量来旋转视角
_rotation_x -= motion.relative.y * ex_mouse_sensitivity
_rotation_x = clampf(_rotation_x, -PI / 3.0, PI / 3.0)
rotate_y(-motion.relative.x * ex_mouse_sensitivity)
_camera_pivot.rotation_degrees = Vector3(
rad_to_deg(_rotation_x), 0, 0)
print("视角旋转: " + str(rotation_degrees))
# 运行结果: 视角旋转: (0, 15.3, 0)
func _process(delta: float) -> void:
var mouse_pos := Input.get_mouse_position()
var viewport_size := get_viewport().get_visible_rect().size
# 当鼠标接近边缘时,传送回中央
if mouse_pos.x < ex_warp_margin or mouse_pos.x > viewport_size.x - ex_warp_margin or \
mouse_pos.y < ex_warp_margin or mouse_pos.y > viewport_size.y - ex_warp_margin:
var center := viewport_size / 2.0
Input.warp_mouse(center)
print("鼠标接近边缘,已重置到中央")
# 运行结果: 鼠标接近边缘,已重置到中央进阶用法
实现一个弹弓发射器:玩家把鼠标往后拉(像拉弹弓一样),松开后发射物体。拉弹弓的过程中需要显示拉伸方向和力度,同时用 warp_mouse 在特定情况下重置鼠标位置:
C#
using Godot;
public partial class Slingshot : Node2D
{
[Export] public float ExMaxPullDistance = 150f;
[Export] public float ExLaunchForce = 5f;
private bool _isDragging = false;
private Vector2 _dragStart;
private Vector2 _slingOrigin;
private Line2D _pullLine;
private Label _forceLabel;
public override void _Ready()
{
_slingOrigin = GlobalPosition;
_pullLine = GetNode<Line2D>("PullLine");
_forceLabel = GetNode<Label>("ForceLabel");
}
public override void _UnhandledInput(InputEvent @event)
{
if (@event is InputEventMouseButton mouseEvent)
{
if (mouseEvent.ButtonIndex == MouseButton.Left)
{
if (mouseEvent.Pressed)
{
// 开始拖拽
_isDragging = true;
_dragStart = Input.GetMousePosition();
GD.Print("开始拉弹弓");
// 运行结果: 开始拉弹弓
}
else if (_isDragging)
{
// 松手:计算发射方向和力度
_isDragging = false;
Vector2 currentPos = Input.GetMousePosition();
Vector2 pullVector = _dragStart - currentPos;
float pullDistance = Mathf.Min(pullVector.Length(), ExMaxPullDistance);
if (pullDistance > 10f)
{
Vector2 launchDir = pullVector.Normalized();
float force = pullDistance * ExLaunchForce;
GD.Print($"发射!方向={launchDir}, 力度={force:F1}");
// 运行结果: 发射!方向=(0.707, -0.707), 力度=530.3
}
// 发射后,把鼠标传送回弹弓位置
Input.WarpMouse(_slingOrigin);
_pullLine.Visible = false;
_forceLabel.Text = "";
}
}
}
}
public override void _Process(double delta)
{
if (_isDragging)
{
Vector2 currentPos = Input.GetMousePosition();
Vector2 pullVector = _dragStart - currentPos;
float pullDistance = Mathf.Min(pullVector.Length(), ExMaxPullDistance);
// 绘制拉伸线
_pullLine.Visible = true;
_pullLine.ClearPoints();
_pullLine.AddPoint(_slingOrigin);
_pullLine.AddPoint(_slingOrigin + pullVector.Normalized() * pullDistance);
// 显示力度百分比
float percent = pullDistance / ExMaxPullDistance * 100f;
_forceLabel.Text = $"力度: {percent:F0}%";
_forceLabel.Position = _slingOrigin + new Vector2(20, -30);
// 如果拉得太远,重置鼠标到最大距离的位置
if (pullVector.Length() > ExMaxPullDistance)
{
Vector2 clampedPos = _dragStart - pullVector.Normalized() * ExMaxPullDistance;
Input.WarpMouse(clampedPos);
GD.Print("已达最大拉伸距离,鼠标位置已限制");
// 运行结果: 已达最大拉伸距离,鼠标位置已限制
}
}
}
}GDScript
extends Node2D
@export var ex_max_pull_distance: float = 150.0
@export var ex_launch_force: float = 5.0
var _is_dragging: bool = false
var _drag_start: Vector2
var _sling_origin: Vector2
@onready var _pull_line: Line2D = $PullLine
@onready var _force_label: Label = $ForceLabel
func _ready() -> void:
_sling_origin = global_position
func _unhandled_input(event: InputEvent) -> void:
if event is InputEventMouseButton:
var mouse_event := event as InputEventMouseButton
if mouse_event.button_index == MOUSE_BUTTON_LEFT:
if mouse_event.pressed:
# 开始拖拽
_is_dragging = true
_drag_start = Input.get_mouse_position()
print("开始拉弹弓")
# 运行结果: 开始拉弹弓
elif _is_dragging:
# 松手:计算发射方向和力度
_is_dragging = false
var current_pos := Input.get_mouse_position()
var pull_vector := _drag_start - current_pos
var pull_distance := minf(pull_vector.length(), ex_max_pull_distance)
if pull_distance > 10.0:
var launch_dir := pull_vector.normalized()
var force := pull_distance * ex_launch_force
print("发射!方向=%s, 力度=%.1f" % [launch_dir, force])
# 运行结果: 发射!方向=(0.707, -0.707), 力度=530.3
# 发射后,把鼠标传送回弹弓位置
Input.warp_mouse(_sling_origin)
_pull_line.visible = false
_force_label.text = ""
func _process(delta: float) -> void:
if _is_dragging:
var current_pos := Input.get_mouse_position()
var pull_vector := _drag_start - current_pos
var pull_distance := minf(pull_vector.length(), ex_max_pull_distance)
# 绘制拉伸线
_pull_line.visible = true
_pull_line.clear_points()
_pull_line.add_point(_sling_origin)
_pull_line.add_point(_sling_origin + pull_vector.normalized() * pull_distance)
# 显示力度百分比
var percent := pull_distance / ex_max_pull_distance * 100.0
_force_label.text = "力度: %.0f%%" % percent
_force_label.position = _sling_origin + Vector2(20, -30)
# 如果拉得太远,重置鼠标到最大距离的位置
if pull_vector.length() > ex_max_pull_distance:
var clamped_pos := _drag_start - pull_vector.normalized() * ex_max_pull_distance
Input.warp_mouse(clamped_pos)
print("已达最大拉伸距离,鼠标位置已限制")
# 运行结果: 已达最大拉伸距离,鼠标位置已限制注意事项
- 不触发鼠标移动事件:
warp_mouse最大的特点是它不会产生InputEventMouseMotion事件。这意味着你的游戏代码中监听鼠标移动的部分不会因为这次"传送"而被误触发。如果你的代码依赖鼠标事件来计算视角旋转,使用warp_mouse是安全的。 - 坐标是视口坐标:传入的
position参数是视口坐标,不是全局世界坐标。(0, 0)在窗口左上角,(viewportWidth, viewportHeight)在右下角。如果你有一个摄像机缩放了画面,需要注意坐标转换。 - 与 Captured 模式配合:在
MouseMode.Captured(鼠标捕获)模式下,warp_mouse通常不需要使用,因为捕获模式下鼠标的移动已经通过InputEventMouseMotion.Relative属性来追踪相对位移,不需要知道绝对位置。warp_mouse主要在 Confined 或 Visible 模式下用于重置鼠标位置。 - Web 平台限制:在 Web(HTML5)导出中,
warp_mouse的行为可能受到浏览器的安全限制。某些浏览器不允许网页随意移动鼠标光标,除非用户已经与页面进行了交互(比如点击过页面)。 - C# 差异:C# 中方法名用 PascalCase(
Input.WarpMouse),GDScript 中用 snake_case(Input.warp_mouse)。
