Gamua / Starling-Framework

The Cross Platform Game Engine
http://www.starling-framework.org
Other
2.85k stars 819 forks source link

Starling + Display List: Mouse Click Through / Hit Test Problems #861

Closed rewb0rn closed 5 years ago

rewb0rn commented 8 years ago

Mixing Starling and DisplayList content like popups introduces problems with the mouse handling. Because Starling always listens to the stage for Mouse Events, there is no easy way of preventing Starling to receive MouseEvents that were actually meant for a popup on top of the game.

An easy workaround is to call stopPropagation() on Mouse Events that end in a certain layer (for example the BasePopup), however this introduces new problems for components that rely on stage mouse events (for example a scroller that needs to know when the mouse button was released somewhere).

After looking into the Starling code it should be easy enough to extend Starling so that you could set a target DisplayObject that Starling uses for Mouse Events instead of the stage. That way, only mouse events that are received by this DisplayObject would be propagated into Starling, resulting in correct layer management for mouse events.

PrimaryFeather commented 8 years ago

Thanks for setting up the issue! I see your point, that should be made easier.

Anyone who's interested in this as well, please add your vote. I plan to add this in any case, but typically, the more pressure I feel, the faster it gets done. :wink:

rthery commented 6 years ago

We'd be very interested as well by this!

I can contribute on this although I'm not too familiar with that part of Starling yet. Although it seems we could achieve this by using our own TouchProcessor right ?

PrimaryFeather commented 6 years ago

Actually, it would be even better to add that to the Starling class itself, which is listening to touch events on the stage and forwards it to the TouchProcessor. Instead, we could make it listen to the events only on a specific (custom) display object.

Here's a quick experiment that you could test. Simply add this code anywhere to the core "Starling" class:

// custom touch source

private var _touchSource:IEventDispatcher;

public function get touchSource():IEventDispatcher
{
    return _touchSource || _nativeStage;
}

public function set touchSource(value:IEventDispatcher):void
{
    var prevSource:IEventDispatcher = this.touchSource;

    if (value != prevSource)
    {
        for each (var touchEventType:String in touchEventTypes)
        {
            prevSource.removeEventListener(touchEventType, onTouch, false);
            value.addEventListener(touchEventType, onTouch, false, 0, true);
        }

        _touchSource = value;
    }
}

With this in place, you could split up your nativeOverlay into two different parts: one that receives touch (mouse) input, and one that doesn't.

var overlay:Sprite = new Sprite(); // flash.display.Sprite
var layer1:Sprite = new Sprite();
var layer2:Sprite = new Sprite();
overlay.addChild(layer1);
overlay.addChild(layer2);

_starling.nativeOverlay = overlay;
_starling.touchSource = layer1;

Now, Starling would only recognize touch events from layer1, but will ignore everything on layer2.

I didn't test this code yet — maybe you could give it a try? Is that even what you were looking for?

Cheers!

rthery commented 6 years ago

Thanks, just tried it a bit and it's working well! We basically have our game world rendered by Starling and our UI by the classic DisplayList. Your suggestion correctly prevents interaction in the UI from interfering with the game world. Just noticed one minor issue so far. When Starling display objects has their buttonMode to true, moving from such display objects to one in classic display list (other than Starling touchSource) will not update Mouse.icon. Adding a MouseEvent.MOUSE_OUT listener on Starling touchSource to reset Mouse.icon fixes the issue.

I'll make sure to come back if I find other issues related to this! If we don't encounter other issues, will it be possible to have this change commited to Master ? I can also fork Starling and make a PR if you prefer :)

PrimaryFeather commented 6 years ago

Thanks for trying it out — I'll have a look at that remaining issue!

Non need for a pull request, I'll commit that code to master once we're both confident that it's fine. :smile:

PrimaryFeather commented 5 years ago

If anyone is still following this thread: I decided on a different implementation that's (IMHO) easier to use. There's a new property nativeOverlayBlocksTouches on Starling, which, if enabled, will make the native overlay block all touches. Basically, it will behave just as if it was a Starling display object with touchable enabled (which will prevent underlying objects from being touched).

I also made sure that the button mode issue, as @rthery mentioned, does not occur.

In my opinion, this is the most natural and intuitive way to handle the native overlay. I'm even considering defaulting this to true – but first, I want to check for feedback. :wink:

rthery commented 5 years ago

That sounds like an excellent solution! Defaulting it to true would make sense I guess, I'd imagine that you'd usually want to use Starling for the rendering of your game but keep the classic display list for your UI.

PrimaryFeather commented 5 years ago

Thanks a lot for the feedback, Romain! :smile: I'll change the default, then. You're right, that's probably the standard use-case for most situations. Cheers!