Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Topics - ChrisPruett

Pages: [1]
1
NGUI 3 Support / Fat finger multitouch problems
« on: March 02, 2014, 04:12:54 PM »
A year ago, when we shipped Rise of the Blobs, we got a large number of reports of problems involving the pause screen popping up in the middle of the game.  We tried forever to reproduce this without success.  We were able to do it once or twice (in ~2000 attempts) so we knew it was a legit bug, but we could not figure out the reproduction steps.

I finally had a flash of insight and realized that the bug was caused by a race condition. Two different mutually-exclusive things were happening the same frame ("start the game" and "back out of the current dialog") which was leaving the game in a bad state.  And the reason that this happened is that people were pressing two buttons at the same time.  Two buttons that, on devices with small screens, are in close physical proximity were being depressed and released simultaneously, causing two OnClick() messages to be sent on the same frame, causing the race condition.

Once we realized this was the problem we were able to break almost every dialog box in our game (of which there are a huge amount in that game) simply by pressing multiple buttons at once.

I think the OnClick() via SendMessage() is fundamentally problematic because it encourages writing small button scripts that know nothing about what else might be getting pressed in the system.  In fact, I think you almost never want to send more than one OnClick() for UI; it might be necessary for HUD buttons, but in the general case it seems more correct for the first button that responds to OnClick to eat the event.

Now, I know that I could just turn off UICamera.allowMultiTouch.  But that is a poor solution from a UX perspective, because now any stray finger on the screen will cause all of the buttons to stop working.  I'm not sure if you've ever seen somebody hold their phone such that their thumb slightly touches the side of the screen, but when this happens in apps that do not allow multitouch it causes the whole app to feel broken.

I can get Temple Run 2 menus to freak out a bit by double pressing buttons.  Subway Surfers has allowMultiTouch turned off.  I suspect this is a problem that affects a lot of NGUI-based games on mobile devices.

On Rise of the Blobs our fire-fighting fix was to visit every single OnClick() script in the game and add a check via a global singleton.  The singleton would allow one click per frame and then would return false for all subsequent requests.  This way, a stray finger that wasn't touching a collider would not prevent other buttons from working, but you were unable to simultaneously click more than one button at a time.  This works but it's not a very classy fix. 

Now, a year later, I'm about to ship our new game, Wind-up Knight 2, and lo and behold, despite being a lot more careful about potential vectors for race conditions we can still cause mega breakage by hitting buttons at the same time.

So, my question is: what's the best way to fix this in the short term?  I don't mind getting my hands dirty in NGUI code, but since the event is broadcast via SendMessage() I didn't have many good ideas for how to alter the existing code to stop sending OnClick() after the first one succeeds.  Secondly, what could we do to make this not possible in future versions of NGUI?

Thanks!

2
Sorry if this has been posed before--I searched the forums but didn't see it.

What is the correct way to change the mainTexture--but not the material--of a UIDrawTexture widget?

uiTexture.material.mainTexture = foo; isn't correct because UIWidget doesn't capture the change and does not mark the draw call for that material as dirty.  Changing the entire material works because it causes the entire widget to be removed and re-added to its parent panel, which causes the draw call to be recomputed.  There doesn't appear to be an accessor that updates mTex and marks the material as changed, which makes sense for other widgets (very rare to change the atlas texture, I guess) but not for UITexture.

Some detail for context: I have implemented an infinitely scrolling list of UITextures that represent contents loaded from the network.  They are all instantiated from a prefab but get a unique material per UITexture so that there's no sharing.  This material initially points at a "loading..." texture, which is then replaced by the bits that come over the wire sometime later.  If I swap the material when the new texture arrives, or if I force the material to be dirtied by disabling and then re-enabling the UITexture (to force a call to OnEnable()), my new texture data shows up.  If not, the texture in the material is changed but the draw call is not recreated and so the previous loading texture continues to show.

It seems like this is a bug, but it might be that I've just missed the correct way to talk to UITexture in this case.

Thanks!
Chris

Pages: [1]