
LibGDX 默认使用 OpenGL 坐标系(Y 向上),而屏幕坐标系是 Y 向下,导致触摸位置与游戏世界坐标不匹配;需通过 camera.unproject() 将屏幕坐标转换为世界坐标,而非手动计算。
libgdx 默认使用 opengl 坐标系(y 向上),而屏幕坐标系是 y 向下,导致触摸位置与游戏世界坐标不匹配;需通过 `camera.unproject()` 将屏幕坐标转换为世界坐标,而非手动计算。
在 LibGDX 中,触摸输入(Gdx.input.getX() / Gdx.input.getY())返回的是窗口像素坐标:原点 (0, 0) 位于屏幕左下角(OpenGL 约定),但实际 Android/iOS/桌面窗口的默认显示原点是左上角——这正是 Y 轴“翻转”的根本原因。你观察到的 y 值异常(如本应出现在 y=72 却报告 173–312)、按钮尺寸误判(64px 变成 139px),本质是未将屏幕坐标正确映射到你的逻辑视口(Viewport)所定义的世界坐标系中。
手动缩放或翻转(如 touchPosition.y = (Gdx.graphics.getHeight() - y) * 64 / Gdx.graphics.getHeight())不仅易错、耦合屏幕分辨率,更会破坏 Viewport 的抽象意义——它本应屏蔽底层设备差异。正确做法是:始终使用相机的 unproject() 方法进行坐标转换。
以下是你代码中关键修复段的规范写法(已适配 StretchViewport 和 OrthographicCamera):
if (Gdx.input.isTouched()) {
// 获取原始触摸点(像素坐标,原点在左上角)
int screenX = Gdx.input.getX();
int screenY = Gdx.input.getY();
// 构造 Vector3 并执行 unproject —— 自动处理 Y 翻转 + 视口缩放 + 相机偏移
Vector3 worldPos = new Vector3(screenX, screenY, 0);
gamecam.unproject(worldPos); // ✅ 核心:转换为游戏世界坐标(单位:逻辑像素)
// 此时 worldPos.x / worldPos.y 已与你在 batch.draw() 中使用的坐标系完全一致
float buttonX = LearningLetters.V_WIDTH / 2f + 64;
float buttonY = LearningLetters.V_HEIGHT / 2f - 32; // playButton.getHeight()/2 = 32
float buttonWidth = 64;
float buttonHeight = 64;
if (worldPos.x >= buttonX &&
worldPos.x = buttonY &&
worldPos.y <p>⚠️ 注意事项:</p>
-
必须对同一相机调用
unproject():你绘制按钮时使用gamecam(batch.setProjectionMatrix(gamecam.combined)),那么触摸检测也必须用gamecam.unproject(),不可混用waterCam或其他相机。 -
unproject()是唯一可靠方式:它内部自动完成三步:① 将屏幕 Y 翻转(y → height - y);② 按视口比例缩放至逻辑尺寸;③ 应用相机位置/缩放/旋转。手动计算极易出错(如你原代码中混合了Gdx.graphics.getHeight()和V_HEIGHT导致比例失真)。 -
确保
Viewport.update()已在resize()中正确调用:你已在resize()中更新了gamePort和waterPort,这是正确的;但注意gamecam本身需由Viewport管理(推荐改用gamePort.getCamera()避免引用不一致)。 -
避免在
render()中频繁创建对象:Vector3可声明为成员变量并复用(如private final Vector3 tmpVec3 = new Vector3();),提升性能。
总结:LibGDX 的坐标一致性依赖于“统一坐标流”——所有渲染和交互都基于同一套世界坐标(由 Viewport + Camera 定义)。放弃手动像素换算,拥抱 unproject(),是解决 Y 轴翻转、点击错位、尺寸失真的根本之道。










