Quick Links: Download Gideros Studio | Gideros Documentation | Gideros community chat | DONATE
Changed behavior of Sprite:hitTestPoint() without a fourth argument — Gideros Forum

Changed behavior of Sprite:hitTestPoint() without a fourth argument

Regarding the newish (in 2023.2) fourth argument to Sprite:hitTestPoint(): If I understand correctly, including no fourth argument should produce the same behavior as in previous releases, which would be the same as including “stage” for any sprite whose parent or parent of a parent, etc., is the stage. I’m seeing something different.

I have a button within a sprite within a sprite, etc., one of which is a scroll view that can move the button vertically. The great great grandparent of the button is the stage. The button sometimes reacts to mouse events that are far off the position of the button at the time of the event, even when the button has been scrolled up beyond the top of the screen.

What I’ve found is that the button reacts to mouse events as though a fourth argument was included specifying either its grandparent or great grandparent. If I include “stage” as the fourth argument, or button:getParent():getParent():getParent():getParent(), it works as it should.

If it’s accurate to say that sprite:hitTestPoint(x, y, clipping) with no fourth argument should behave the same as sprite:hitTestPoint(x, y, clipping, stage) when sprite is a descendent of the stage, then the code below demonstrates a bug. The code moves the grandparent of a sprite up the screen while checking for hits on the sprite on a constant screen location. For every line of output saying a hit was detected using “stage” as the fourth argument, there should also be a line of output saying a hit was detected using no fourth argument. That’s what happens in 2022.12, but not on 2023.2 and later builds.
local x = 100
local y = 600
rt = RenderTarget.new(1, 1)
b = Bitmap.new(rt)
b:setScale(100, 100)
b:setPosition(x, y)
parent = Sprite.new()
grandparent = Sprite.new()
		y -= 10
		grandparent:setPosition(0, y)
		if b:hitTestPoint(150, 200, true) then
			local bx, by = b:localToGlobal(0, 0)
			print("Hit without fourth argument. y:", y, bx, by)
		if b:hitTestPoint(150, 200, true, stage) then
			local bx, by = b:localToGlobal(0, 0)
			print("Hit with stage as fourth argument. y:", y, bx, by)
It seems the workaround is trivial: Just include "stage" as a fourth argument to get the original behavior. Still, I thought this was worth reporting. I found this during prerelease testing of a game update, and in the context of that game it only happens in certain conditions so I could easily have missed it. Where it showed up it was causing a button intended to delete user created content to react when it wasn't pressed. There's also a confirmation screen for that action or this would have deleted some local content of my own.


Likes: MoKaLux, pie

+1 -1 (+2 / -0 )Share on Facebook


  • hgy29hgy29 Maintainer
    Accepted Answer
    Thanks for the sample code, I’ll debug that asap.

    Likes: MoKaLux, PaulH

    +1 -1 (+2 / -0 )Share on Facebook
  • MoKaLuxMoKaLux Member
    edited August 2023
    I have a question regarding hitTestPoint please.

    (bool) = Sprite:hitTestPoint(x,y,visible,ref)

    Right now we have for visible:
    - FALSE (by default) -> hit test hidden sprites
    - TRUE -> don't hit test hidden sprites

    Shouldn't the visible behavior be:
    - FALSE (by default) -> don't hit test hidden sprites
    - TRUE -> hit test hidden sprites

    Imho that would make more sense? Thank you ;)

    EDIT: I am not sure how the new fix behaves :s
    EDIT2: as per the comment below, fair enough, it is only a matter of adding true as the third parameter. I will update the wikiDONE, thank you hgy29 ;)
    my growING GIDEROS github repositories: https://github.com/mokalux?tab=repositories
  • hgy29hgy29 Maintainer
    @MoKaLux, agreed about default behavior, but it was done that way to keep compatibility with existing lua code when it was implemented.

    hitTestPoint code has become very complex over time, at the beginning, it used to be just a matter of computing the sprite bounds, its world transform matrix, then converting global point to local point and checking if it lays inside the sprite bounds.

    With 'visible' flag, we also need to check if every parent is visible and apply clipping on every Sprite in the hierarchy (parents of the tested Sprite but also its children. This added a lot of work and made hitTestPoint slow, so I recently added a bounds cache to avoid recomputing everything on each touch or mouse move. Changing anything in the Sprite that would cause its bounds to change invalidates the cache, the latest patch fix a case were the cache wasn't invalidated/revalidated as needed.

    Likes: MoKaLux

    +1 -1 (+1 / -0 )Share on Facebook
Sign In or Register to comment.