- Home /
Strange touch behavior?
Strange touch behavior?
I have some code below and I cannot seem to work out where I am going wrong. Have spent the good part of two weeks testing many different combinations and I am starting to pull out when little hair I have left. Any help much appreciated.
I am trying to reliably spawn a prefab and get it to track under a finger position and when finger removed the prefab is destroyed. To do this I setup a prefab with a script that has a finger ID set when Instantiated. This ID is used to track the specific finger. Its also used to destroy the prefab if the same ID is matched with the fingerIDActive array.
Main Game Script
#pragma strict
var fingerPointPrefab : GameObject;
static var maxTouches : int = 11;
static var touchPos : Vector2[];
static var fingerIDActive : int[];
private var touchPhase : TouchPhase[];
function Start()
{
touchPos = new Vector2[ maxTouches ];
touchPhase = new TouchPhase[ maxTouches ];
fingerIDActive = new int[ maxTouches ];
}
function Update () {
var count = Input.touchCount;
for ( var i : int = 0;i < count; i++) {
var touch : Touch = Input.GetTouch( i );
var figID = touch.fingerId;
// Cache touch data.
touchPos[ figID ] = touch.position;
touchPhase[ figID ] = touch.phase;
// End finger marker
if (touchPhase[ figID ] == TouchPhase.Ended){
fingerIDActive[ figID ] = 0;
}
// Spawn finger marker
if ((touchPhase[ figID ] == TouchPhase.Began) && (fingerIDActive[ figID ] == 0)){
Instantiate(fingerPointPrefab, touch.position, Quaternion.identity);
fingerPointPrefab.GetComponent(JSFingerTracker).finderNumber = figID;
var PFname: String = ("FingerID" + figID);
fingerPointPrefab.name = PFname;
fingerIDActive[ figID ] = 1;
}
}
}
Prefab Script
#pragma strict
var finderNumber : int = 0;
private var fingerPos: Vector3;
private var worldPos: Vector3;
function Update () {
fingerPos = JSGameControler.touchPos[ finderNumber ];
fingerPos.z = 10;
worldPos = Camera.main.ScreenToWorldPoint(fingerPos);
transform.position = worldPos;
if (JSGameControler.fingerIDActive[finderNumber] == 0) {
Destroy(gameObject);
}
}
Expected behavior: Should create a prefab under finger, track finger when moved and if finger removed the prefab should be destroyed.
Observed behavior: (When compiled on iPad1) Prefabs are not spawned or destroyed reliably. Many times when an additional finger is added, the previous finger placed gets its prefab spawned correctly under it. And this is the strangest behavior and does not seem to happen 100% but it seems the more fingers you place down the more you get random jittering. This seems to get worse when fingers are placed in a line horizontally on the iPad when held in landscape mode. This gets so bad that they pop all over the screen appearing and disappearing randomly. Have even seen it spawning phantom finger points. Maybe my iPad is possessed!! lol
Note, I am fairly new to JavaScript and still have much to learn.
in short, don't instantiate, use a simple array (or maybe a pool)...
http://answers.unity3d.com/questions/321762/how-to-assign-variable-to-a-prefabs-child.html
regarding accurately dealing with glass V. real world measurements
http://answers.unity3d.com/questions/292333/how-to-calculate-swipe-speed-on-ios.html
Regarding the actual process of handling many fingers and putting a red cube under each finger, explained in long answer below.
Answer by Fattie · Oct 02, 2012 at 03:23 PM
This may help .. drop this on something and run.
Play with three fingers on the screen .. it might help demonstrate what the hell is going on!
function Update()
{
if ( Input.touches.Length == 1 )
Debug.Log(" " +Input.touches[0].fingerId);
if ( Input.touches.Length == 2 )
Debug.Log(" " +Input.touches[0].fingerId
+" " +Input.touches[1].fingerId
);
if ( Input.touches.Length == 3 )
Debug.Log(" " +Input.touches[0].fingerId
+" " +Input.touches[1].fingerId
+" " +Input.touches[2].fingerId
);
}
Next step!
Next step you have to process each touch based on it's Unity touchId:
function Update()
{
for ( var t:int=0; t<Input.touches.Length; ++t )
processATouchPerFingerCodeNumber(
Input.touches[t], Input.touches[t].fingerId );
}
So in the next function you have a touch to deal with, and it's Unity code number. Try this code, and extensively play with your iPad. You will really see how it works, it's great.
function processATouchPerFingerCodeNumber( t:Touch, n:int )
{
if ( t.phase == TouchPhase.Began )
{
Debug.Log("A finger has ARRIVED. it's arbitrary +"
"code number is: " + n);
return;
}
if ( t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled )
{
Debug.Log("Well that's it. A finger went away .. being "+
"arbitrary code number: " + n);
Debug.Log("(Don't get confused .. the code numbers "+
will be resued, perhaps immediately.)");
return;
}
// you can also process .Moved and/or .Stationary here
}
when you have that down, add this clause to the processing function
if ( t.phase == TouchPhase.Moved )
{
Debug.Log("You moved this finger: " + n);
return;
}
Now it, carefully moving one finger at a time.
OK so you've got that. Now here's an important point. On many systems those id numbers are just arbitrary, like 343243. (Or you don't even get an id number, it's just a pointer.)
So let's say it was 343243. Now, you have an array of say ten of the image of the red spot - you want to put a red spot under each finger.
Again - the next id number happens to be 343243, and you ave the array of ten.
So what you have to do is, take the "first available" red spot image in the array of red spot images. Let's say it's number 2 currently. So, you have to associate "343243" with YOUR array item 2. (Using a dictionary, lookup, whatever.)
Next time when processATouchPerFingerCodeNumber is called, and the number is "343243" THEN, IN FACT, you have to "look up" what your number it is. (In fact 2 in the example.)
So that's what you normally have to do ...
BUT ......
very conveniently, Unity ensures thas the id code numbers, will run from 0-10. (I"m not sure what the max is on different platforms, but it doesn't matter. YOU should impose a maximum of six, ten or however many fingers you wish to be the maximum.)
Again NORMALLY you MUST do a mapping between the CODE NUMBER (say 424234) and your OWN array (which runs from 0 to 9.)
BUT VERY HAPPILY in the specific Unity case, you don't need to do that. Because Unity guarantees to recycle them, and they always stay below 10, you can and should ...... SIMPLY USE THAT ID NUMBER, as your own array number.
So in short, your code for processATouchPerFingerCodeNumber will look precisely like this.
function processATouchPerFingerCodeNumber( t:Touch, n:int )
{
if ( t.phase == TouchPhase.Began )
{
// a new finger has arrived! fortunately WE KNOW it's
// perfectly OK here in Unity to simply use that same number,
// "our" array. in most systems, at this stage you would
// "find an empty slot" and make a note of the mapping from
// 424341 (or whatever) to our slot. again Unity GUARANTEES
// to recycle them and always stays low so it is absolutely
// OK to use it. but it's extremely important to realize
// you can't normally do this - it's just Unity!!
if ( t >= 7 ) return;
// we enforce a maximum of seven (say) images, if someone
// puts more than that many fingers on the screen, they can
// go to hell
myImages[ t ].showTheImageNow();
myImages[ t ].positionImageAt( t.position );
// it's that easy
return;
}
if ( t.phase == TouchPhase.Ended || t.phase == TouchPhase.Canceled )
{
myImages[ t ].hideTheImageNow();
// it's that easy
return;
}
if ( t.phase == TouchPhase.Moved )
{
myImages[ t ].positionImageAt( t.position );
// it's that easy
// nb as with any touch code, it's always tricky to know
// how much to move it, you may have to convert from glass
// to real world, scale, do a raycast, whatever.
return;
}
// you can also process .Stationary here if relevant,
// perhaps spin it or whatever is relevant to you
}
Hopefully this helps you or someone else reading. Cheers.
from memory, any more than 5 Input.touches nullifies all those inputs and all the previous touches are assigned TouchPhase.Cancelled
Upvoted for another awesome informative answer.
@alucardj Hmmm, not sure you're right there. I've done stuff with 6 different touches on the iPad without any problems.
Wow, thanks. $$anonymous$$y information was for phone, but a quick search reveals you are both correct for iPad (I never doubted it really!) :
Thank you so much for your swift and informative answer. I feel I need to go back to the drawing board. There is so much more code I cobbled together that needs to be thrown away. Wish I had asked the question a bit earlier, its all good learning though :) Thank you
right -- I know just what you mean.
it's actually very hard to do this ...
myImages[ t ].showTheImageNow();
don't do it ! Heh !!!!!!!! If you like start a NEW question asking just that and you'll get vast answers.
please just do this:
function showOneImageNow( whichImageNumber:int );
or you may need something like
function showImageAtPosition( whichImageNumber:int, p:Vector3 );
"where best to put code" .. make many small functions, all in one big script. it's that simple.
you mention the st*tic keyword. Never - ever - EVER use the static keyword. If you are using it now, delete it. so forget that was ever mentioned!
now, your one big script may get a little too big....
if so you can make two (or more) different scripts
to get from one to the other is incredibly simple
GetComponent( otherScriptName )
so to call a function in another script
GetComponent( OtherScriptName ).showFireworksNow()
to get to a script ON A DIFFERNT OBJECT is very simple
GameObject.Find("potato)").GetComponent(SomeScriptName);
really the doco is very good on this. go to unity, doco, scriptin, basics.
cheers!
Answer by mr-eaves · Jan 28, 2013 at 04:20 PM
The bit about Unity clamping to 0 - 9, it doesn't at all.
Do a simple Android app just to print out the ID's, press however many fingers on the screen, say 5 so you get ID's 0 - 4. Hit the home button, then go back into the app again, you'll see the ID's are now 5 - 9, keep doing this with 5 fingers, and it will keep increasing.
So it is a bit of a pain really.
Well, that's a very unusual case to suspend the app while you hold fingers down. If the app is completely restarted it's cleared again.
Of course when the Touch ended events occure while the app is suspended, Unity has no chance of getting notified about that.
Btw, if you stack the ids up to ~20 it seems that GUI buttons don't work anymore ;) It's actually a nasty bug, but again a very rare case.
You don't need to keep your fingers held down. Just touch the screen with 5 fingers to generate 5 touches. Remove your fingers, wait a few seconds if you like. Hit the home button to come back to the OS, the resume the app, you'll find that the touch ids will now have moved on by 5.
Any more information on this? it is annoying that the FingerID's get messed up when the app is paused and resumed.
Just wanted to verify that we're seeing the same behaviour reported by mr-eaves. Just tap with two fingers (and release), then return to the home screen, then back to your unity app. All the fingerIds will be 2 & 3; do the same and you start getting 4&5, and so on (past 10; I got up to 13). This is using 4.0.1f2 (on a Samsung Galaxy SII running Android 4.0.3)
Your answer
Follow this Question
Related Questions
unity 5.2 Input.GetTouch(0).position incorrect? 3 Answers
Input.touches vs Input.GetTouch 1 Answer
Input.GetTouch(0) doesn't work 2 Answers
Count a touch in half of screen 2 Answers
2D Raycast touch 1 Answer