Tutorial: How to start a Muvslide connection

Note

This tutorial is for Unity, but Muvslide works in any software that can include a DLL library.

Objective

Follow this tutorial to understand the two ways to start a Muvslide connection between smartphones and your application. After completing the tutorial you will be able to connect your phone and your application starting the connection from the phone, or starting the connection from the application.

Setup your environment

Go to the downloads page. Intall Muvslide App in your Android and download the API to your computer. The API is a .DLL library you need to include in your project.

Create a new Unity project

This tutorial starts from an empty Unity project.

  1. Create a Plugins folder and pull the Muvslide API files.

  2. Add a cube. We will control this cube to see easily when the connection is working by rotating the cube based on the smartphone orientation.

  3. Save the scene in a new Scenes folder and create an empty Scripts folder that we will use later.

Screenshot

Base code

Let's create some of the basic code we will use independently of the connection type:

1) Create a new script called Connection in your Scripts folder, and then add it to a new empty object in your scene. I called the object ConnectionManager.

Screenshot

2) Since we don’t want our connection to get lost when moving through scenes, we can use Object.DontDestroyOnLoad. Following Unity’s documentation that would be:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
using UnityEngine;

public class Connection : MonoBehaviour {

    private static bool created = false;

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
        }
    }
}

3) Create the MuvslideConnection object by adding ForestIndieGames.Muvslide library and creating the new object inside the Awake method. Also, add OnApplicationQuit Unity method to make sure the network resources are freed when your game stops running, failing to do this could lead Unity to freeze.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
using UnityEngine;
using ForestIndieGames.Muvslide;

public class Connection : MonoBehaviour {

    private static bool created = false;

    private MuvslideConnection muvslideConn;

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn!= null)
            muvslideConn.Close();
    }

}

4) Override the Update method to monitor the connection. If there is new input available, mark a Boolean variable to indicate that, and provide a method for other objects to know that there is input available.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
using UnityEngine;
using ForestIndieGames.Muvslide;

public class Connection : MonoBehaviour {

    private static bool created = false;

    private bool newInputAvailable;
    private MuvslideConnection muvslideConn;

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn != null)
            muvslideConn.Close();
    }

    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
            newInputAvailable = true;
    }

    public bool IsNewInputAvailable()
    {
        bool result = newInputAvailable;
        newInputAvailable = false;
        return result;
    }

}

5) Add a method to return a Vector3 object with the angles from the phone:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using UnityEngine;
using ForestIndieGames.Muvslide;

public class Connection : MonoBehaviour {

    private static bool created = false;

    private bool newInputAvailable;
    private MuvslideConnection muvslideConn;

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn != null)
            muvslideConn.Close();
    }

    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
            newInputAvailable = true;
    }

    public bool IsNewInputAvailable()
    {
        bool result = newInputAvailable;
        newInputAvailable = false;
        return result;
    }

    public Vector3 GetAngles()
    {
        float[] angles = muvslideConn.GetInputManager().GetOrientationDegrees();
        return new Vector3(angles[0], angles[1], angles[2]);
    }

}

6) Create a new script called PhoneInputHandler and add it to your cube.

Screenshot

7) Add the code to find the ConnectionManager object on Start, then read its data to rotate the object:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using UnityEngine;

public class PhoneInputHandler : MonoBehaviour {

    Connection conn;

    // Use this for initialization
    void Start () {
        conn = GameObject.Find("ConnectionManager").GetComponent<Connection>();
    }

    // Update is called once per frame
    void Update () {
        if (conn.IsNewInputAvailable())
            transform.localEulerAngles = conn.GetAngles();
    }
}

Connect from the smartphone

1) In the Connection class, make the MuvslideConnection object to listen for connection messages by adding the following line:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
            muvslideConn.WaitForConnectionFromBroadcast();
        }
    }

2) Run the project. Open the Muvslide App in your phone and connect using the button in the app. The cube should start moving when you rotate your phone.

Cube moving

Tip

If the cube is not rotating, make sure your firewall is not blocking Unity and that your phone and your computer are in the same network. If you are in a public or corporate network, there are chances that the broadcast messages are blocked. In that case, you need to create a separate local network between your phone and your computer.

Allow reconnections

If the App is closed, the connection will be lost and the game won't be listening to broadcast messages anymore, so the user won't be able to reconnect. To fix this you can start listening for connections again if after some time the game does not receive any input.

1) In the Connection class, add a float value to store the time from the last message, and define a variable for the time you want to wait before the object starts listening from connections again.

2) In the Update method, store the time from the last message.

3) In the Update method, check if you need to start listening for connections again.

4) Add a couple debugging lines to notice when the Connection object is waiting for the phone.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using UnityEngine;
using ForestIndieGames.Muvslide;

public class Connection : MonoBehaviour {

    private static bool created = false;

    private bool newInputAvailable;
    private bool waiting;
    private MuvslideConnection muvslideConn;
    private float lastMessageTime;
    private float waitTimeToReconnect = 1; //seconds

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
            muvslideConn.WaitForConnectionFromBroadcast();
            Debug.Log(Time.time + ": Waiting for phone");
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn != null)
            muvslideConn.Close();
    }

    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
        {
            newInputAvailable = true;
            lastMessageTime = Time.time;
            waiting = false;
        }
        /*Avoid checking for reconnection if the connection has
        never happened or is already waiting for the phone*/
        else if (lastMessageTime != 0 && !waiting)
        {
            bool timeoutWaitingMessages = Time.time - lastMessageTime > waitTimeToReconnect;
            if (timeoutWaitingMessages)
            {
                waiting = true;
                muvslideConn.WaitForConnectionFromBroadcast();
                Debug.Log(Time.time + ": Waiting for phone");
            }
        }
    }

    public bool IsNewInputAvailable()
    {
        bool result = newInputAvailable;
        newInputAvailable = false;
        return result;
    }

    public Vector3 GetAngles()
    {
        float[] angles = muvslideConn.GetInputManager().GetOrientationDegrees();
        return new Vector3(angles[0], angles[1], angles[2]);
    }

}

Connect from the game

If you prefer to make the connection process more transparent for the user, you may want to start the connection from the game itself. For the sake of clarity, we are going to create a separate class, starting from the current code in the Connection class.

1) Create a new script called AutoConnection. Remove the Connection component from the ConnectionManager object in the scene and add AutoConnection instead.

Screenshot

2) Copy the content from the Connection class and modify/delete the following lines. Note that multiple lines were commented so you could easily identify them, but you can remove those lines:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
using UnityEngine;
using ForestIndieGames.Muvslide;

public class AutoConnection : MonoBehaviour {

    private static bool created = false;

    private bool newInputAvailable;
    //private bool waiting;
    private MuvslideConnection muvslideConn;
    private float lastMessageTime;
    private float waitTimeToReconnect = 1; //seconds

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
            //muvslideConn.WaitForConnectionFromBroadcast();
            //Debug.Log(Time.time + ": Waiting for phone");
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn != null)
            muvslideConn.Close();
    }

    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
        {
            newInputAvailable = true;
            lastMessageTime = Time.time;
            //waiting = false;
        }
        /*Avoid checking for reconnection if the connection has
        never happened or is already waiting for the phone*/
        //else if (lastMessageTime != 0 && !waiting)
        //{
        bool timeoutWaitingMessages = Time.time - lastMessageTime > waitTimeToReconnect;
        if (timeoutWaitingMessages)
        {
            //waiting = true;
            //muvslideConn.WaitForConnectionFromBroadcast();
            //Debug.Log(Time.time + ": Waiting for phone");
        }
        //}
    }

    public bool IsNewInputAvailable()
    {
        bool result = newInputAvailable;
        newInputAvailable = false;
        return result;
    }

    public Vector3 GetAngles()
    {
        float[] angles = muvslideConn.GetInputManager().GetOrientationDegrees();
        return new Vector3(angles[0], angles[1], angles[2]);
    }

}

3) Modify the Update method to try to start a connection every time the connection seems to be lost. Note the wait time was increased to 2 seconds; otherwise the game could try to reconnect during the connection setup process, cancelling the connection each time.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
using UnityEngine;
using ForestIndieGames.Muvslide;

public class AutoConnection : MonoBehaviour {

    private static bool created = false;

    private bool newInputAvailable;
    private MuvslideConnection muvslideConn;
    private float lastMessageTime;
    private float waitTimeToReconnect = 2; //Changed so to avoid the reconnection to start during the connection process

    private void Awake()
    {
        if (!created)
        {
            DontDestroyOnLoad(this.gameObject);
            created = true;
            muvslideConn = new MuvslideConnection();
        }
    }

    private void OnApplicationQuit()
    {
        if (muvslideConn != null)
            muvslideConn.Close();
    }

    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
        {
            newInputAvailable = true;
            lastMessageTime = Time.time;
        }
        /*If no messages received in the specified time,
          clean the connection and try to start a new one*/
        bool timeoutWaitingMessages = Time.time - lastMessageTime > waitTimeToReconnect;
        if (timeoutWaitingMessages)
        {
            muvslideConn.StartConnection();
            lastMessageTime = Time.time; //To avoid the program to retry during the connection process
        }
    }

    public bool IsNewInputAvailable()
    {
        bool result = newInputAvailable;
        newInputAvailable = false;
        return result;
    }

    public Vector3 GetAngles()
    {
        float[] angles = muvslideConn.GetInputManager().GetOrientationDegrees();
        return new Vector3(angles[0], angles[1], angles[2]);
    }

}

4) Finally, modify the PhoneInputHandler class to use the new script:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
using UnityEngine;

public class PhoneInputHandler : MonoBehaviour {

    AutoConnection conn;

    // Use this for initialization
    void Start () {
        conn = GameObject.Find("ConnectionManager").GetComponent<AutoConnection>();
    }

    // Update is called once per frame
    void Update () {
        if (conn.IsNewInputAvailable())
            transform.localEulerAngles = conn.GetAngles();
    }
}

Test it. The game should connect automatically to your phone if the Muvslide App is open.

Test moving through scenes

1) Create a new scene called "second" with an object that also contains the behavior PhoneInputHandler:

Screenshot

2) Add the scenes in your project's Build Settings so you can load them from code.

Screenshot

3) Only for testing purposes, in the AutoConnection class add the following code to move to the new scene (include the using UnityEngine.SceneManagement for this to work):

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
    private void Update()
    {
        if (muvslideConn.GetInputManager().IsNewMotionAvailable())
        {
            newInputAvailable = true;
            lastMessageTime = Time.time;
        }
        /*If no messages received in the specified time,
          clean the connection and try to start a new one*/
        bool timeoutWaitingMessages = Time.time - lastMessageTime > waitTimeToReconnect;
        if (timeoutWaitingMessages)
        {
            muvslideConn.StartConnection();
            lastMessageTime = Time.time; //To avoid the program to retry during the connection process
        }

        if (Input.GetKey(KeyCode.Space)) //Adding this for testing purposes only
        {
            SceneManager.LoadScene("second");
        }
    }

4) You should see the object in the new scene moving with your phone as soon as you navigate to it hitting the space bar.

Cube and capsule moving