Game Development Community

Sprites & Input Events

by Chris Haigler · in Torque 2D Professional · 12/05/2015 (6:54 pm) · 3 replies

Need some feedback on this one:

Currently T2D determines which sprites are involved in an input event by calling into Box2D to check the mouse position against the bounding box (and then) collision hulls of all sprites in the scene (this is done in SceneWindow::sendObjectInputEvent()).

The issue with this is any sprites with a bounding box larger than their "real" image size trigger input events too early. An example of this is a circular button sprite:

i.imgur.com/xsKG3Vs.jpg
In the image above, the sprite is a 66px circle contained within a 128x128 texture. Typically you would want input events to trigger only when the mouse is over the circle, but T2D will trigger them if the mouse comes anywhere within the bounding box (128x128 in this case).

Anyone have any thoughts on getting around this?

#1
12/05/2015 (7:04 pm)
One idea that was suggested was to wrap the script-side input callbacks (onTouchDown, onTouchEnter, etc.) with another function that does an additional test to see if the mouse position is within some user-defined area (maybe a radius in the case of the circle button above).

It'd look something like:
function CircleButton::onTouchEnter(%this, %touchID, %worldPosition)
{
    if(%this.isTouchInside(%worldPosition))
    {
        %this.onEnter();
    }
}

The problem with this is the engine will call the input callbacks as soon as the mouse enters the sprite's bounding box so the %worldPosition of the mouse will be too far away from the circle sprite when isTouchInside() tests its position.
#2
12/05/2015 (8:45 pm)
Perhaps try using an additional collision shape for the mouse event? Maybe a sensor, then use hits on that shape to trigger the click function. It's been a long time since I looked at that but I'm pretty sure there's a way around this....
#3
12/06/2015 (5:49 am)
That brings up another interesting point, Richard. If you trace SceneWindow::sendObjectInputEvent() you'll see it calling WorldQuery::anyQueryPoint():

U32 WorldQuery::anyQueryPoint( const Vector2& point )
{
    // Debug Profiling.
    PROFILE_SCOPE(WorldQuery_AnyQueryPoint);

    // Query.
    oobbQueryPoint( point );
    mMasterQueryKey--;
    collisionQueryPoint( point );

    // Inject always-in-scope.
    injectAlwaysInScope();

    return getQueryResultsCount();
}

anyQueryPoint first tests the mouse position against all box2d OOBBs. Then it tests the mouse position against collision polys. It doesn't make much sense to test against both the OOBB and collision shapes, because the engine will only fire input events once per "occurrence" - so whichever volume (OOBB or collision) the mouse hits against first is the one that gets the input event.

If I comment out the oobbQueryPoint() and mMasterQueryKey lines, then I can create objects with collision shapes that are better matches for their actual shape and everything works fine.