I tell you my experience, I am not an expert in this, but have spent a time doing something similar.
Some months ago I started a project which allowed players together to draw, play music and simple games through wifi or 3G, and by far the most important stuff was to abstract and put everything in place. The basics worked as a charm. Once this had been done, adding games and utilities is just "normal" programming. Now I am improving it and hope to release it "soon" to the market.
For me there are two main differences between one-device-one-user apps and multi-player ones
* Things are distributed. In the first case, for instance you have an input and want to process it. So you call your function, passing arguments to it or even leaving the vars to be processed in global vars. the result may also be left in global vars...everything works easily. In the second case, the process unit is far and you must each time send all the needed data
* Transmission of information is not immediate nor reliable. In a one-device-one-player-app you get an input and "immediately" want to process it. So just call your function and it is done. Moreover, the next time you call that function the results can even depend on the previous call. This can be different in the second case since the first frame perhaps was lost.
As debugging in this scenario can be very tedious, I would recommend to start by doing absolutely everything in just one device.
- Two instances of a player class, each one with its own panel (half screen), and one instance of a "master game logic unit". No global vars shared, everything contained in the classes
- Each player instance sends its inputs to the "game logic unit". These inputs can be touch positions, button presses, the player has left the game,... (This helps to define which are the data flow needs and stablish the protocol)
- The "game logic unit" receives everything, processes, has some outputs and sends them to each one of the player instances. The outputs are not necessarily the whole screen drawings, but f.i. the position of the cars, the sound to play, or other higher-level states which the player can process or animate. Also, this helps to define what is needed in the protocol
- For simplicity, at first all the communication between "player instances" and "game control logic" can be done via direct function calls. But everything has to be passed as arguments, no shared global vars. Later, you can add a channel simulator which can be just a call dispatcher, but will allow you to add later some random delay, missing frames, delivering frames in wrong order, etc (you are simulating the channel, very very useful to simulate real conditions and test protocol strength if needed)
- When everythng works ok, do the same communication via internal IP
- The last step is to jump to more than one device. The "game logic unit" can be in any of the devices, and prepared to communicate with one player via an internal socket and the other via external IP
Other things
- For the comm I would use UDP and design the protocol and logic taking into account that small delays may exist and some frames may be lost.
- I would add local time information in the protocol frames, so that the "game logic unit" can know the real lapse between them when originated
- For the outputs, as there will be different screen resolutions, work with relative coordinates.
- Also for the inputs
Hope it can help