Search Unity

  1. Megacity Metro Demo now available. Download now.
    Dismiss Notice
  2. Unity support for visionOS is now available. Learn more in our blog post.
    Dismiss Notice

Companion apps and Unity

Discussion in 'Scripting' started by Studio_Akiba, Jan 5, 2016.

  1. Studio_Akiba

    Studio_Akiba

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    I am just gathering information for now, and have a few things planned in my head.
    But, has anyone here ever developed a companion app (that's the term the industry seems to be giving them anyway) that allows something like a mobile device or tablet (iOS or Android) to display game-relevant information on the fly?

    I am developing a game with a large map and multiple levels and thought it might be best to have a way to map out the areas visited on a second screen so players could continue playing while having the map up.

    Is something like this easily do-able in Unity?

    Any resources that can be provided are greatly appreciated.
     
  2. orb

    orb

    Joined:
    Nov 24, 2010
    Posts:
    3,037
    Extending a map seems like it should be among the simplest things, with both being Unity apps using the same assets and the same GUI code for maps/info. The only trick is discovering the running game from the mobile device (like Fallout 4 does). iOS and OS X have Bonjour built in, but this might help for other platforms: https://github.com/mono/Mono.Zeroconf/

    There are also various paid add-ons on the UAS. Some of the network add-ons might have automagical discovery functionality too.
     
  3. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    So companion apps, in the context of video games, are any range of apps that integrate with the game experience on various levels.

    Watch Dogs
    For example the companion app to 'Watch Dogs' allows players when away from the game to effect the games of those players actively online and playing. Challenging the full game players to combat where the mobile user commands forces of police against the players on the console/desktop.

    This example is more of like a 'multiplayer' game. It's structured the same way many multi-player games are done. Both communicating to a server to update gameplay. The only difference is that the mobile player has a completely distinct player experience. This would all be dealt with through a game server (note, a player can host the server themselves). Where one player sends commands to the server, which may result in a command being sent from the server to another player.



    Titan Fall
    Where as Titan Fall's companion app is merely a readout of lore and stats.

    Really, this app is more like a website documenting the lore, while also displaying the profile of your character. Sort of like a complex 'high scores' screen, combined with a wiki of the 'Titan Fall' universe. It's not real-time. There's probably a database (client side, or server side, no idea) that regularly gets written to with the current specs of the character.

    The only real-time deal it has is a real time map.

    This real time map is again, probably like a multiplayer game. Only that it's not server based, but instead peer-to-peer based (there is only ever 1 to 1 connection... server based is when you'd have many to many connection). The host (your main game) allows for connection, the client (your mobile game) connects to it over network. And the peer-to-peer connection probably has a very basic interface:

    Connect
    Disconnect
    RetrieveCurrentMapData

    And it just updates its map to mirror that map of the player on some interval.



    Does unity give you anything for this? Well, not directly... unity is more a game engine. It's networking options are mostly to synchronize scenes with one another. A companion app like this doesn't really need scene syncing, since the companion app doesn't necessarily show the scene of the game. Hell, the companion app probably isn't even written with Unity, and might just be a basic ol' Android/iOS app with no major graphics abilities.

    Thing is Unity allows scripting through Mono/.Net, which has many networking tools built in. You can create all sorts of networking connections through the Mono/.Net framework to facilitate your needs.

    You need to determine first and foremost how you'll structure it... since a companion app really is a broad term. What you do specifically may need very different ways to efficiently do what it's going to do.
     
  4. Studio_Akiba

    Studio_Akiba

    Joined:
    Mar 3, 2014
    Posts:
    1,421
    Effectively, what I want to do, is have an application on an iOS or Android device that allows the player to see their location overlaid on a map, which fills itself out as you move along and find more areas.

    As this is all indoors, there is no need for anything with advanced graphics, I may just need to get the player's position in order to do this, and have some artwork correctly scaled in the background.

    It's just how I get the player's location onto an external device in realtime that I'm lost on.
     
  5. lordofduct

    lordofduct

    Joined:
    Oct 3, 2011
    Posts:
    8,513
    The host (the actual game) should host up a network service that the client (the phone) can connect to.

    That network service should offer up a basic method "GetPlayerInfo" that hands back some data about the player... level, location, area of map uncovered. Format it in something easy to parse... json is good.

    The client then connects to that network service to read it.



    The task here is setting up a service, and also making it discoverable.

    There are many frameworks out there that streamline a lot of it, but often also bloated relative to what you really need.




    Another option is to roll your own with the built in socket server stuff in Mono/.Net, like UdpClient.
    https://msdn.microsoft.com/en-us/library/System.Net.Sockets.UdpClient(v=vs.110).aspx
    In this, what you'd probably do is this:


    Have an option in the game to start syncing a device (in the pause menu perse?).

    When chosen, spin up a thread, this will be your server. You NEED it to be threaded, since network messaging has variable durations of time. May you wait on a thread, or handle with asynchronous callbacks, you'll always need to jump back to the main thread. Tools like Thread Ninja (https://www.assetstore.unity3d.com/en/#!/content/15717) or my own RadicalCoroutine make this easy. I'd suggest ThreadNinja, it's very light weight.

    On the thread, you'd create a UdpClient, and start attempting to 'Receive'.

    You should have a timeout on this (use BeginReceive), so that way you can close the socket if no request is received after some duration. No reason clogging up the processor with unnecessary tasks if the player decides to not connect after selecting for whatever reason. But make sure the timeout is long enough to allow the client to open the app on their phone and attempt to connect.

    Now on the client (phone), there's an option to 'pair'. When the player presses this, it too creates a UdpClient and attempt to broadcast a request. You do this by flagging it the 'EnableBroadcast' property of UdpClient:
    https://msdn.microsoft.com/en-us/library/system.net.sockets.udpclient.enablebroadcast(v=vs.110).aspx

    The EndPoint should be IPAddress.Any, and use the predefined port that the game always uses (you could select 0 as the port for ANY port, on both client and host, if you want a variable port).

    Send the request, again with a timeout.

    If the host receives the request in the alotted time it should gather up the client information (ip address and port if variable), and send a response that includes its ip address and desired port.

    The client upon receiving the response will save that data.

    You're now paired.

    Now in a while(true) loop on the host, you just keep opening the Receive for the EndPoint that is the client. This way every time a request/response is finished, it just reopens back up waiting for the next request.

    On the client you also have a while(true) loop that repeatedly attempts to connect to the server, and request the information. Here if you have multiple different commands for different stuff (maybe a 'GetPlayerInfo' call, and a 'GetLevelStats' call), you include that in the request message.

    The host when it receives a message, parses out the command, and responds accordingly. Handing back the player data and what not. This is where you'll have to do the jumping back to the main thread bit to gather any information that might be accessed through the unity api.

    The client should sleep for a small duration between requests. Maybe do this every 100 milliseconds or so.




    This may seem like a lot of logic, but really it's not that much code. The actual server itself could be written in like 70 lines of code.

    Code (csharp):
    1.  
    2. //using ThreadNinja, this should be started through ThreadNinja on its own thread
    3. IEnumerator WorkerThread()
    4. {
    5.     //assumes we start on the thread
    6.     var server = new UdpClient(SOME_PORT); //this needs to be defined, can't be 0
    7.  
    8.     var asyncResult = server.BeginReceive(null, null);
    9.  
    10.     //we will block until a request is received, or the timeout passes
    11.     asyncResult.AsyncWaitHandle.WaitOne(TIMEOUT_DUR);
    12.     if(asyncResult.IsCompleted)
    13.     {
    14.         //we received a request in the the alotted time
    15.         IPAddress clientIP;
    16.         try
    17.         {
    18.             IPEndPoint endpoint = null;
    19.             byte[] data = server.EndReceive(asyncResult, ref endpoint);
    20.             var req = Encoding.ASCII.GetString(data); //here we presume the client sends ascii
    21.      
    22.             if(req == "MyGamePairingRequest")
    23.             {
    24.                 clientIP = endpoint.Address;
    25.                 var responseMsg = "THE NETWORK CRITERIA";
    26.                 var data = Encoding.ASCII.GetBytes(responseMsg);
    27.                 server.Send(data, data.Length, endpoint);
    28.             }
    29.             else
    30.             {
    31.                 yield break; //failed, stop paring attempt
    32.         }
    33.         catch
    34.         {
    35.             yield break; //failed, stop pairing attempt
    36.         }
    37.     }
    38.  
    39.     while(true)
    40.     {
    41.         asyncResult = server.BeginReceive(null, null);
    42.  
    43.         //this will wait a longer time, if too long passes, we assume that the client dropped out, repairing will be required
    44.         asyncResult.AsyncWaitHandle.WaitOne(EXTRA_LONG_TIMEOUT_DUR);
    45.         if(asyncResult.IsCompleted)
    46.         {
    47.             try
    48.             {
    49.                 IPEndPoint endpoint = null;
    50.                 byte[] data = server.EndReceive(asyncResult, ref endpoint);
    51.                 if(endpoint.Address != clientIP) continue; //we ignore unknown requests
    52.                 var req = Encoding.ASCII.GetString(data);
    53.          
    54.                 switch(req)
    55.                 {
    56.                     case "GetPlayerData":
    57.                     {
    58.                         yield return Ninja.JumpToUnity;
    59.                         var responseMsg = "GATHERED PLAYER INFO";
    60.                         yield return NInja.JumpBack;
    61.                         var data = Encoding.ASCII.GetBytes(responseMsg);
    62.                         server.Send(data, data.Length, endpoint);
    63.                     }
    64.                     case "GetLevelData":
    65.                     {
    66.                         //do similar to GetPlayerData...
    67.                     }
    68.                 }
    69.             }
    70.             catch
    71.             {
    72.             }
    73.         }
    74.         else
    75.         {
    76.             yield break;//client dropped out
    77.         }
    78.     }
    79. }
    80.  
    As for the client... well, if it's android or iOS, there should definitely be some UdpClient like thing for socket connections.

    Make sure you "Broadcast" your response to any ip address, looking for the port that the host expects to be connected on.

    And make sure to format your requests appropriately.

    Note, broadcasting can be slow... which is why you only do it once. When you attempt to 'pair' the 2 applications. All subsequent calls should be to the known address, so you're not broadcasting EVERY message.



    Do note, this example code is completely untested, written free-hand in a notepad, and it lacks any level of security or authentication.
     
    Last edited: Jan 6, 2016
  6. vectorCharlie_f

    vectorCharlie_f

    Joined:
    Sep 12, 2017
    Posts:
    10


    hi @lordofduct I am planning to do a similar thing to the OP but I'm struggling to understand how the networking works and how to implement the scripts. I have a few questions below if you have a moment to point me in the right direction.

    Firstly I create two separate Unity projects. One that builds for Mac and one that builds for iOS (the platforms I'm using).

    In the Mac project (the host), I create a C# script similar to what you have above and place it on an empty game object in the scene.
    - How do I decide which port to use in UdpClient?
    - How long should the timeout be, approximately? 1sec / 30sec?
    - Will this work in the editor or will I have to build the project and run it as a standalone?

    In the iOS project (the client), I create a C# script and put it on an empty game object in the scene. This script has a while(true) loop.
    - In your code example, the only while(true) loop is inside the WorkerThread IEnumerator which I thought was for the host project? Should they be separate pieces of code or is there only one script that is used for both the host and client?

    I expect all this happens with an iPhone and Mac connected to the same Wifi router. My project is only for my own use, on my own closed Wifi network, so I don't expect I will need any extra security built in.

    Any additional info appreciated.

    Thanks!
     
    mc_fragezeichen likes this.
  7. mc_fragezeichen

    mc_fragezeichen

    Joined:
    Aug 17, 2020
    Posts:
    14
    Are there any new insights on this issue? Is there an easy way to setup companion apps in 2023 using unity?