Skip to content

fix: hit area detection when wandering Doro faces right (flip_h=true)#9

Open
last-Medjay wants to merge 1 commit into
MelanTech:masterfrom
last-Medjay:fix/hit-area-flip-h-right-facing
Open

fix: hit area detection when wandering Doro faces right (flip_h=true)#9
last-Medjay wants to merge 1 commit into
MelanTech:masterfrom
last-Medjay:fix/hit-area-flip-h-right-facing

Conversation

@last-Medjay

Copy link
Copy Markdown

💬 Bilingual / 双语 — 中文在前,English follows below.

摘要

修复了闲逛的 Doro 转向朝右(flip_h = true)后无法触发身体部位点击交互的 bug。模型仍然有动画,但 SmileEyeClosedSullen 等表情从朝右姿态下永远不会被触发。

根本原因

model.gd 通过将 Node2D 的 scale 设置为 (-0.3, 0.3) 来实现 flip_h。这个负的 x scale 会自动流经 Godot 自身的坐标变换,所以当 hit_area_handler.gd 中的 recalc_mouse_position 运行时:

  1. model.to_local(event.position) 已经将 x 镜像了一次(除以负的 scale)。
  2. position * model.get_scale() 又乘了一次(把 x 镜像回 viewport 坐标)。

接着函数在第 28-29 行又做了第三次翻转——在 viewport 空间,在居中和 canvas-scale 转换之前;第 36-37 行做了第四次正确翻转,在 canvas 空间。第三次翻转就是 bug 所在。

数值验证:在朝右的模型上点击 viewport (320, 320) 得到的是 canvas (1024, 0) 而不是正确的 (0, 0)——偏移了整整一个 canvas 宽度。对朝右 Doro 的每次点击都落在 canvas 之外。

修复方法

删除冗余的 viewport 空间翻转(第一个 if model.flip_h 块)。保留函数末尾的 canvas 空间翻转——那才是正确的一次翻转,它围绕 canvas 原点做镜像,使得 hit-test 目标落在 hit-area 系统所期望的未翻转坐标系中。

 func recalc_mouse_position(position):
 	position = position * model.get_scale()
-	if model.flip_h:
-		position.x = -position.x
-	
+
 	if canvas_info.is_empty() != true:
 		var vct_viewport_size = Vector2(root.get_viewport_rect().size)
 		var scale: float = vct_viewport_size.y / max(canvas_info.size_in_pixels.x, canvas_info.size_in_pixels.y)
 		position -= vct_viewport_size / 2.0
 		position /= Vector2(scale, scale)
 		if model.flip_h:
 			position.x = -position.x
-		
+
 	return position

数学验证

将新旧两条代码路径在若干点击位置下逐一追踪:

输入 旧版(有 bug) 新版(已修复)
flip_h=false, click=(320,320) (0, 0) (0, 0)
flip_h=false, click=(500,100) (288, -352) (288, -352)
flip_h=true, click=(320,320) (1024, 0) (0, 0)
flip_h=true, click=(500,100) (1312, -352) (-288, -352)

flip_h=false 路径在新旧代码下逐字节相同——默认的朝左状态没有 regression 风险。

flip_h=true 路径现在产生的 canvas 坐标正确地镜像了未翻转布局:朝右 Doro 视觉右侧的点击会映射到与朝左 Doro 视觉左侧点击相同的 canvas 位置,这正是 hit-area 检测所需要的。

测试范围

请在合并前于 Windows 上做一次 regression 检查。

  • Bug 分析与平台无关——只依赖 Godot 的 Node2D 变换语义以及 model.gdflip_h 设置负 x-scale 这两点。在 Windows 上和在 Linux 上一样适用。
  • 在下游 RHEL 9.7 / X11 构建(WSL 2 + WSLg)上做了非正式的手动点击测试。让闲逛的 Doro 走到屏幕右边缘后,确认 SmileEyeClosed 表情从朝左和朝右两个朝向都能可靠触发。
  • 未在上游 Windows 构建上重新测试。 希望维护者在合并前在 Windows 上做一次 regression 检查,特别是 flip_h=false 路径,以确认朝左 pet 的用户不受影响。

建议的手动测试步骤

  1. 在 Windows 上运行项目。
  2. 打开设置 → 交互 → 启用「闲逛」。
  3. 等待 Doro 走到任一屏幕边缘(或中键点击 → 召回,然后再让她闲逛)。
  4. 当她朝右时:右键点击她的脸——确认 SmileEyeClosed 表情触发。也试一下后腿。
  5. 当她朝左(默认)时:重复同样的点击——确认没有 regression。

相关说明

这个 fix 是在准备一个独立的下游 RHEL 9 / X11 移植时发现的。如果你想看看那项工作 —— 7 个 C# autoload 重写为跨平台、Linux 构建链、gd_cubism 在 Linux 上的编译等等 —— 可以看这里:

https://github.com/last-Medjay/Dororo-RHEL9-X11

不期望合并到上游(改动面太大,且涉及 Live2D SDK 在 Linux 上的构建,绑定了一些不可重新分发的二进制),但希望让 Linux 用户能找到它。如果 Linux 构建对上游社区有意义,欢迎在 follow-up issue 中讨论;此 PR 中不主动提议。


Summary (English)

Fixes a bug where the wandering Doro (闲逛的 Doro) stops responding to clicks on her body parts once she turns to face right (flip_h = true). The model still animates, but no hit-area expression — SmileEyeClosed, Sullen, etc. — ever triggers from a right-facing pose.

Root cause

model.gd implements flip_h by setting the Node2D's scale to (-0.3, 0.3). That negative-x scale silently flows through Godot's own coordinate transforms, so by the time recalc_mouse_position runs in hit_area_handler.gd:

  1. model.to_local(event.position) has already mirrored x (divided by the negative scale).
  2. position * model.get_scale() mirrors x again (back to viewport coords).

The function then performed a third flip on lines 28-29 — in viewport space, before centering and canvas-scale conversion — and a fourth correct flip on lines 36-37 in canvas space. The third flip was the bug.

Numerically: a click at viewport (320, 320) on a flipped model came out as canvas (1024, 0) instead of (0, 0) — one full canvas-width off. Every click on a flipped Doro fell outside the canvas.

Fix

Remove the redundant in-viewport-space flip (the first if model.flip_h block). Keep the canvas-space flip at the end of the function — that's the correct one, mirroring around the canvas origin so the hit-test target lands in the unflipped coordinate system the hit-area system expects.

(See diff above.)

Math verification

Input Old (broken) New (fixed)
flip_h=false, click=(320,320) (0, 0) (0, 0)
flip_h=false, click=(500,100) (288, -352) (288, -352)
flip_h=true, click=(320,320) (1024, 0) (0, 0)
flip_h=true, click=(500,100) (1312, -352) (-288, -352)

The flip_h=false path is byte-identical between old and new code — no regression risk for the default left-facing state.

The flip_h=true path now produces canvas coords that correctly mirror the unflipped layout: a click on the visual-right of a flipped Doro maps to the same canvas position as a click on the visual-left of an unflipped one, which is what hit-area detection needs.

Testing scope

Please regression-check on Windows before merging.

  • Bug analysis is platform-independent — depends only on Godot's Node2D transform semantics and on model.gd setting a negative x-scale for flip_h. Both apply on Windows the same way they apply on Linux.
  • Verified informally via manual click-testing on a downstream RHEL 9.7 / X11 build (WSL 2 + WSLg). Walked the wandering Doro to the right edge of the screen and confirmed the SmileEyeClosed expression triggers on the face hit area from both facing directions.
  • Not re-tested on the upstream Windows build. A maintainer regression check there before merging would be appreciated, particularly on the flip_h=false path to confirm nothing changes for users whose pets face left.

Suggested manual test plan

  1. Run the project on Windows.
  2. Open settings → Interact → enable 闲逛 (stroll).
  3. Wait for Doro to walk to either screen edge (or middle-click → recall, then send her wandering again).
  4. When she's facing right: right-click on her face — confirm the smile/closed-eyes expression triggers. Try the back leg too.
  5. When she's facing left (default): repeat the same clicks — confirm no regression.

Related

The fix was discovered while preparing a separate downstream RHEL 9 / X11 port of this project. If you'd like to see that work — 7 C# autoloads rewritten as dual-platform, Linux build chain, gd_cubism compiled on Linux, etc. — it lives here:

https://github.com/last-Medjay/Dororo-RHEL9-X11

Not proposing to merge it (too much surface area, and the build relies on Live2D-derived binaries that can't be redistributed), but wanted you to know it exists so Linux users can find it. Happy to open a follow-up issue if a Linux build is of interest to the upstream community; not proposing it here.

@last-Medjay last-Medjay force-pushed the fix/hit-area-flip-h-right-facing branch from 3988c10 to 3bab298 Compare May 23, 2026 01:11
When the wandering Doro turned to face right, clicks on body parts
(face, legs, etc.) would not trigger interactions. The model still
animated, but `recalc_mouse_position` returned coordinates far outside
the canvas so no hit-area test ever matched.

Root cause: in `model.gd`, `flip_h = true` sets the Node2D scale to
`(-0.3, 0.3)`. That negative-x scale flows through Godot's coordinate
transforms automatically:

  1. `model.to_local(event.position)` already divides by the negative
     scale, mirroring x once.
  2. `position * model.get_scale()` then multiplies by the same
     negative scale, mirroring x back to viewport space.

So at line 27 the position is *already* in unflipped viewport coords.
The extra `if model.flip_h: position.x = -position.x` on lines 28-29
was a spurious third flip, performed in viewport coords *before* the
center offset and canvas-scale division. The later flip on lines 36-37
is the correct one: it mirrors around the canvas origin (after the
viewport-center subtraction), which is what hit-area detection
expects when the model is rendered mirrored.

With both flips active, a click at viewport (320, 320) on a flipped
model came out as (1024, 0) in canvas coords instead of the correct
(0, 0) — one full canvas-width off, so no hit area ever triggered.

Fix: drop the redundant first flip; keep the canvas-space flip at the
end of the function. The unflipped (flip_h=false) code path is
unchanged — same diff outputs for every input I traced through.

Testing scope (please regression-check before merging):

  - Bug analysis is platform-independent — depends only on Godot's
    Node2D transform semantics and on `model.gd` setting a negative
    x-scale for flip_h. Both apply on Windows the same way they
    apply on Linux.
  - Verified informally by manual click-testing on a downstream RHEL
    9.7 / X11 build (via WSL 2 + WSLg). Walked the wandering Doro
    right-ward and confirmed the SmileEyeClosed expression triggers
    on the face hit area from both facing directions.
  - NOT re-tested on the upstream Windows build. A regression check
    there before merging would be appreciated, particularly on the
    flip_h=false path to confirm nothing changes for Windows users
    whose pets face left.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@last-Medjay last-Medjay force-pushed the fix/hit-area-flip-h-right-facing branch from 3bab298 to f90b580 Compare May 23, 2026 01:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant