fljot / Gestouch

Gestouch: multitouch gesture recognition library for Flash (ActionScript) development.
MIT License
356 stars 84 forks source link

detect a touch of an object beneath a gesture layer? #35

Closed CoIin closed 11 years ago

CoIin commented 11 years ago

Please let me know if there is a better place to ask GesTouch questions that are not related to Starling. Meanwhile:

I'm using the freeTransform to manipulate objects. I don't want the user to have to actually touch the object, which may be scaled quite small at the time. This is easily achieved by having a transparent movieclip that sits on top of the objects layer, and that's the movieclip passed in when making the freeTransform.

I do want the user to be able to select a different object, which may or may not be beneath the currently selected object, but no matter what, it will be beneath the transparent gesture layer movieclip.

What would be a way to detect a single finger touch on the lower level object without triggering the freeTransform Change event in the gesture overlay movieclip?

fljot commented 11 years ago

Yes, you can ask questions here. I can mark them with "question" tag so it becomes sort of question-answer database.

Can you explain use-case in more details? Do you want to select another object while some TransformGesture is in progress (more than 2 touches on the screen)? Or just bring another object in front via touch or tap?

CoIin commented 11 years ago

I worked around my issue by using the 'began' event, and to do a hittestpoint on the objects that are underneath the gesture layer. That's a good enough work around for now.

To explain more though, if you imagine a scene where there are a number of props, and you want to select a prop and use gestures to scale, rotate, and position the prop, the way to do that is to have an object that sits over the whole scene, that the gesture listener is attached to. That way the user doesn't have to try to actually touch the currently selected prop, which can be very difficult if the prop is scaled to a small size.

So, the problem then becomes how do you select a different prop? Ideally I would want a brief touch to do that for you. That brief touch wouldn't register as a gesture. Unfortunately, the gesture listener on the overlay object prevents mouse events from getting through to the props.

Does that make it clearer? Is there a better way to have a single finger touch select the underlying object?

fljot commented 11 years ago

Well let's start with the root of the problem: you want to manipulate objects that are too small to manipulate normally. Do you really need it or it's just a not so well designed thing? If you say you actually need it — I see three ways:

  1. TransformGesture for each of your objects + custom Gestouch hit-tester (@see Gestouch#addTouchHitTester) to choose appropriate touch target + transparent sprites for your objects — this way you can literally choose whether touch goes through your one's object transparent sprite in case there's another object right under your touch. A bit tricky, but imho elegant solution since you attenuate the process at the very very beginning (detecting Touch#target and therefore affected set of gestures). Will work perfectly for multi-touch and multi-user scenarios.
  2. TransformGesture for each of your objects + transparent sprites for your objects — that's probably what you wrote above. Instead of transforming the gesture#target that comes from event, choose the appropriate target when gesture starts. Or even extend the gesture class and add some event/callback to know when first touch comes (will happen before BEGAN). Looks a bit dirty, but should work. NB! Not perfect for multi-user scenario.
  3. Single TransformGesture (on your objects container) — similar to 2nd option. But you totally can't manipulate more than one object at the time.
CoIin commented 11 years ago

When I make progress I'll post something for you to see why it's going to be common to have objects that are too small to touch.

  1. I've read about addTouchHitTester, mostly in Starling examples. I think my current approach of doing a hittest is achieving roughly the same results, and it's working ok.
  2. I think I would have to have the listener on all my objects, and on the background too (in case the user touched in between all of them). That's a possibility, but if I can solve the issue with less listeners I will.
  3. For my application I only need to move one object at a time, but I do want to tell the difference between a single touch on an object, and a two finger touch anywhere. I read this thread: https://github.com/fljot/Gestouch/issues/19 and that gave me an idea. I'm now using this at the start of my began listener:

private function onFreeTransformBegan(event:GestureEvent):void { if(Gestouch.touchesManager.activeTouchesCount>1) return; //the following lines use hittest to figure out which object you're intending to select...

That then stops it from accidentally selecting an underneath object if the users is currently scaling or rotating another already selected object.

With it how I have it now it's almost perfect. But, the user does have to move their one finger in order to select the underneath object, it would be nice if a brief touch would do that too.

fljot commented 11 years ago

I'd say go with the first option. Because it's the right way to solve the problem, it's the solution to the root of the problem — it's about choosing the proper target (object) for your touch. And anyway you're doing some hittesting, just move it to the right place in your code. This solution is conceptually the right one.

As to "brief touch", there's no way at the moment to know when gesture receives first touch. Before I had IDLE state, then POSSIBLE, so when state changes you can understand that it receives touch. But that was a bit messy since states are recognition states, and that extra state was not so much about recognition itself... But I also see myself that it's necessary to know when gesture receives touch (first or any). Any ideas how to implement it (API-wise)? Dispatching some GestureEvent.TOUCHES_COUNT_CHANGED (once touch added/removed from the gesture)? Or some GestureEvent.INTERACTION_BEGAN? Anyhow, for now you can extend TransformGesture class and do the following:

override protected function onTouchBegin(touch:Touch):void
{
    super.onTouchBegin(touch);

    if (isTrackingTouch(touch.id) && touchesCount == 1)
    {
        // Gesture has just started tracking the first touch,
        // dispatch your event / signal / callback.
    }
}

And I didn't get all this one-two fingers idea... But that Gestouch.touchesManager.activeTouchesCount is totally dirty, I wouldn't do it. You see, you're trying to deal with the consequences, but you don't really solve the problem (I mean technically). If you don't want to react on gesture — disable it (enabled property) or at least don't let it be recognized in the first place (using delegate and gestureShouldBegin() method). This way there won't be any useless processing happening behind the curtains. So tell me again please, what's the special case we're talking about regarding multi-touchness here? So scenario: 1st finger is down on the object — you select it or bring it in front, whatever you need (using the custom gesture technique I wrote above). a) you move it, so gesture is recognized b) you don't move it, so gesture is still in POSSIBLE state Then 2nd finger comes: a) on the same object — so you're about to transform it b) on another object that is really really close to your one — ??? c) on another object that is away from your one — ??? d) on empty space between objects — ???

CoIin commented 11 years ago

Yes, most of your a-d at the end may apply. Things are good enough for now, and when I have something to show you'll start to see why there is a lot of overlapping of objects.

CoIin commented 11 years ago

Something that will amuse you, hopefully, in trying to reproduce another issue I started tracking all the events for gesture. The 'possible' event is perfect for solving my original problem here. By listening for 'possible', and checking if it's just one touch, I can do the instant selecting of objects, without having to wait for the user to move the object.

fljot commented 11 years ago

You must be using some older version. From now on (in 'develop' branch atm) there's no IDLE state that I had for a while. So you won't receive any special event once first touch comes. So my code above might be still useful for you.

CoIin commented 11 years ago

The files are from September 5th. I'll look for a more recent version, and see if that helps my other issue too.

fljot commented 11 years ago

Closing it for now.