Problems/Suggestions : Unity Forum, Discord, or mail to 5argon@exceed7.com

How To Use

First of all Native Touch comes with Assembly Definition File .asmdef because it is the new best practice of Unity. If your game is using one already you can refer to with E7.NativeTouch. If not yet, you can delete the file to make the plugin code spill all over the general catch-all dll. Also it comes with packages.json for Unity Package Manager support in the near future.

1. Decide if you want to receive callbacks, or do the ring buffer iteration

I assume you had read the implementation page and know what that means. If you want to receive callbacks, follow these steps. If not, you can skip to the next section.

1.1 Declare the touch-receiving static callback methods

You have 2 choices of static method signatures that you can use. Only one type is usable at once but you can have many of one type.

Minimal Mode

public static void MyStaticMethod(NativeTouchData ntd)

Only essential values of the touch in its original form from the OS. For those concerning with an overhead of getting too much unnecessary values this could be the best. What values are important to most users is by my subjective judgement which you can see in the reference page.

Of course with the source code from your purchase you could modify what's coming with minimal mode if you like a bit more.

Full Mode

public static void MyStaticMethod(NativeTouchDataFull ntd)

You get almost all possible data available from iOS and Android that can be interop to C#. Many values are exclusive to each platform and is not available in others, the code will not compile and warn you thanks to the use of preprocessor directive that makes the property disappear when you are on a wrong platform. This mode is EXPERIMENTAL, I lacked proper tools that report all possible data (like iPad Pro, iPhone X's touch weight, or Apple Pencil 2's tilt) to test on. As long as I could not confirm this will remains in EXPERIMENTAL.

1.2. Register the static methods

Import the namespace using E7.Native; then use NativeTouch.RegisterCallback(callback) like in this example. You can register multiple callbacks.

using E7.Native;
public static void OpenPauseMenu(NativeTouchData ntd)
{
    //top left corner
    if(ntd.X < 50 && ntd.Y < 50 && ntd.Phase == TouchPhase.Began) 
        Debug.Log("Pause");
    }
}

//The somewhere else...
NativeTouch.RegisterCallback(OpenPauseMenu);

If you don't like the namespace coding practice you can edit the source code and remove them. There are just a few files.

Advanced use! : There is a RegisterCallback with 4 arguments. 4 static methods will map to each touch phase from the native side. (The one argument version is equivalent to use the same static method for all 4 phases.) It will match the main touch's action with each type, but give you all related touches. For example touching down one finger while other 2 are moving would invoke only the BEGAN/DOWN callback with 3 touches : BEGAN / MOVED / MOVED all inside your BEGAN callback, and not invoking the MOVED callback at all. You might be assuming BEGAN event will exclusively be in BEGAN callback and MOVED event is exclusively go to MOVED callback. (it is not the case) This might looks weird to you but this reflects precisely how it works at the native side. And it is actually a blessing that you could differentiate between MOVED that comes together with other main event, and MOVED that is really pure MOVED.

2. Start Native Touch

Call NativeTouch.Start(startOption). In the startOption you can specify whether you want the "full mode" or not. Moreover you could completely disable Unity's input with the option. And then finally with noCallback you could exclusively do the ring buffer iteration on platform with slow callbacks, like Android IL2CPP. (I can also add other features here in the future)

If you registered only minimal mode callbacks and you start full mode, you will get no callback. And vice-versa. With noCallback option it won't invoke any callbacks even if you had registered them.

As the same with other native plugins, of course it won't work in the editor. You can use #if UNITY_EDITOR or Application.isEditor to take the code out.

3. Use the touches

If by callbacks, you just wait for your registered delegates to be called with NativeTouchData. It would be called instantly, might be on the other thread, might crash Unity if you do something strange that is not cross-thread compatible, etc. And even could be slower if the callback is slow. Keep in mind everything I said in the implementation page especially on Android where there is a thread difference.

If by ring buffer iteration, on your Update() you can access NativeTouch.touches. This is the basic way of using touches in this ring buffer :

using E7.Native;
public void Update()
{
    //Consume touches on the ring buffer until the cursor catch up with the latest touch.
    while(NativeTouch.touches.TryGetAndMoveNext(out NativeTouchData ntd))
    {
        if(ntd.X < 50 && ntd.Y < 50 && ntd.Phase == TouchPhase.Began) 
            Debug.Log("Pause");
        }
    }
}

One big point to keep in mind is Native Touch is to be as native as possible. Since performance is the most concerning, I will try not to add any calulations to touch data from the native side if possible. Knowing that you are adding something on top of a native touch yourself is important. For example you even have to flip the Y axis manually.

On top of that some properties with the same name has different meaning on each platform. Some properties is not even available in other platform. I will make no effort to make the data consistent. (Except the TouchPhase, explained in the Callback Details page.)

The upside is that you can fully consult Google's MotionEvent docs or Apple's UITouch docs and that's exactly what each property is about. It is just pascal-cased in C# compared to its Objective-C/Java counterpart.

The properties are available or not by the use of #if UNITY_IOS, #if UNITY_ANDROID in Native Touch source code, so it "fails loudly" when you slip an impossible data query. You should use #if UNITY_IOS, #if UNITY_ANDROID as well to make your code compiles and when that is achieved, you can be sure that the data exists. (Previous version has common fields with the value inside as -1 to signify unavailability, that was really error prone)

Each properties has been decorated heavily with a lot of documentations which will show up in your Intellisense and also available in reference page. Do not assume what you knew from Unity will be the same here. Unity processed the data a lot to achieve "unity" of all platforms but our point here is to be raw, fast, and hard core.

For example :

So please read the reference and also the Callback Details page carefully. Especially on Android, you will suffer through mysterious hard crashes if you don't read it.

For the timestamps that came with the touches, use NativeTouch.GetNativeTouchTime() anytime to get a new separated reference time to compare with them.

4. Stop and clear callbacks

When you want to stop using, call NativeTouch.Stop(). Use NativeTouch.ClearCallbacks() to empty the registered callback. The clear is important since static methods persists indefinitely in your heap memory but you might have reference some static object variable in there. They might already be destroyed after changing scene, etc.

Extra : NativeTouchTracker (EXPERIMENTAL)

Touches from Native Touch are very barebone. Often we want to interpret them further. Data in Unity's Input.touches is one such interpretation which discards a lot of data (you can learn about this in "VS Unity" topics in Callback Details page). I am also using Native Touch in my game, and I have a touch interpretation script called NativeTouchTracker. From Native Touch v2.1.0 onwards I have decided to separate it from my game and included as a bonus for you to use.

It is hidden in a zip file inside Extra folder because I do not want to maintain compatibility. It uses C# 7.0 (possible with Incremental Compiler package), using Unity.Mathematics and Unity.Collections package. It works inside C# Jobs and ECS as well so you could Burst compile the whole logic, making this touch processing code optimized nicely to each CPU architecture. How to use :

  1. Track : Collect native touch events when it arrives at the callback then feed them repeatedly in correct order into an instance of NativeTouchTracker, like ntt.Track(nativeTouchData).
  2. Using tracked data : Along the way ntt.Touches will already contain some processed touches linked up from all of your input data. You can iterate through this list because its .Length is also available, just like with Unity's Input.touches. Ended phase touches will be kept in this list until you "end set" 2 times. (Next point)
  3. Ending a set : When you are content (usually when Unity's frame actually arrives, when you get a chance to call Update, etc.), you call ntt.EndSet(). All touches in ntt.Touches will receive a "set ended" status. It is to "summarize" all previous touches received in one "set". This "set" concept is analogous to how you get only one touch data per finger from Unity even though there might be multiple movement that occurs inbetween a frame. Here's what's happening to each touch on ending a set.
  4. You are required to Dispose the tracker since it uses native containers. You also have to call ntt.Panic() after coming back from minimizing the game to handle errornous Cancelled state.

The code is quite documented, you can read through it for more information.

My tracked points TrackedNativeTouchData contains more information than Unity's Touch found inside Input.touces. Unity's discard data such that only one sample per finger is left per frame and try to link up the remaining touches, but my tracker uses ALL native touch data.

Imagine a very fast curved movement of 1 finger. 3 Moved events were produced before the upcoming Update (this is 1 "set"). For Unity's Input.touches, you will find only 1 Moved event. Its previous position linked to prior Update()'s touch. (2 of the 3 movement likely discarded) For my tracker, you will also find 1 Moved event, but it includes information derived from all nuance of movements that occured sequentially.

For instance in the case of my tracked data, there are 2 timestamps embeded in each tracked touch representing the time of first sample and the last sample. You are able to use the final position of this touch while using the timestamp from the first movement in this set. Additionally you can get the "true" squared magnitude that accurately describe this curved finger movement's length. In Unity if you try to do a squared magnitude it would be a length of straight line skipping some data point completely.

Back to the top