Help develop this game using Photon (Turnbase vs Realtime)

Hello everyone,
I am Puneet from ReFocus Tech.

We recently took part in a 48 hour gamejam and came up with this really promising concept for our next game DUEL.

Here is a gameplay video from our game prototye:
http://youtu.be/HmZ7RH7ul_U

We have already worked out how to run everything in the offline scenario, and even implemented the more advanced features such as game modes, levels, physics, etc.

We would like to develop this game for online play using Photon networking. And we need your help to do so.

We have gone through quite a few Photon tutorials and videos and we now have some questions specific to our game. For simplicity's sake, consider the gameplay requirement to be EXACTLY as in our video.

1. For the given game, which solution is better- PUN, Turnbased, or Realtime? Ideally I think turnbased is good, but I believe only PUN is available for Unity 3D and not the other two. I also read on the forums that turnbased documentation is not up to the mark as is realtime or PUN. Please clarify this.

2. Using whichever solution, could you just generalize what steps I would need to have this game work over the network? For example, what things would I need to send in an RPC, what would I need to see using PhotnView, etc.? I have a general idea after reading some tutorials but would like clarity and more views on this.

3. The gravity points (both blue and red) are randomly positioned at the start of a game. When the game is online, how would the same random positions be sent to both players? Which player would run the random placement script? And how could I tell the other player not to randomly place them and just use what the other player has created.

Thanks a lot everyone, your help is really appreciated.

Comments

  • 1. This game can be created in Unity. Regarding PUN, Turnbased, or Realtime, it's the same underlying technology. For example, one can make a turnbased game using PUN with Unity.

    2. This is a big question regardless of how one generalizes it. The best way is to take a look at the PUN examples. As well, check out the Marco Polo tutorial. It gives a nice overview of the interactions of sending data and serialize view. For me, I use PUN as my starting point. It makes everything so much easier!

    3. You'd use an RPC call.
  • 1. Does the game have asynchronous gameplay? Then use Turnbased. Else use PUN and the "Realtime" Photon Cloud subscription.
    2. You basically have to send anything the other side needs to know and you should skip anything that you can trigger by the data you send already. This is very general but a more concrete help would go into analyzing the game itself and that's beyond the limits of the free support here.
    3. You can use and sync a "seed" to initialize your map generation process. This way you only sync a tiny value and still get all maps generated the same way. Of course you have to make sure it's deterministic on the supporter platforms but that should be doable.
    You can take a look at Custom Properties for the seed. Store it in the Room and you're good.
  • Tobias wrote:
    1. Does the game have asynchronous gameplay? Then use Turnbased. Else use PUN and the "Realtime" Photon Cloud subscription.
    2. You basically have to send anything the other side needs to know and you should skip anything that you can trigger by the data you send already. This is very general but a more concrete help would go into analyzing the game itself and that's beyond the limits of the free support here.
    3. You can use and sync a "seed" to initialize your map generation process. This way you only sync a tiny value and still get all maps generated the same way. Of course you have to make sure it's deterministic on the supporter platforms but that should be doable.
    You can take a look at Custom Properties for the seed. Store it in the Room and you're good.

    Hi Tobias, thank you very much for the reply.

    If you could help me out a bit more, I would appreciate it. I know you're the one handling everything on the forum, so you would be the best person to work this out with.

    I have a player (ball), one belongs to team BLUE and one belongs to team RED. When the ball is in the LAUNCH AREA (extreme left or right) the positions need to be synced constantly. When the balls are shot out of the launch area, they enter the playing field, where the Physics code on the Gravity points take care of the movements, interactions, etc.

    Also, I need to manage the following: Whose turn is it, What are the scores, How many balls remaining per player.

    I need to know the best implementation (possibly with sample code). I've been through all your sample codes and nothing is helping me out. Thank you so much, I would appreciate it.

    1 more question: Is there a way to Remove the 'observed' from photonview?
  • You could observe a script and in that script, you can detect if you want continuous updates or not. Simply stop adding content to the PhotonStream when you don't want updates.

    If the players can shoot simultaneously, each shot will take a moment to reach the other client(s). This means that the local physics situation is differing from the one of the remote client. In this case, even little lag can make a big difference in the end-result!
    If only one player can shoot, you could send the end-position of a ball before the other can shoot. This way, at least the start/end of a turn is guaranteed to be the same situation.

    If you want turns and score, please have a look at the Pickup Demo from more recent PUN versions. This includes scripts that do player score. It's not extremely well explained but should be easy to use.
    Also there is a RoundTimer script. Search for "Timer" in the project. It uses a room property to mark the time when the game started. Based off that, the current round can be computed. Alternatively use a property to store whos turn it is and how many balls were used.
  • Tobias wrote:
    You could observe a script and in that script, you can detect if you want continuous updates or not. Simply stop adding content to the PhotonStream when you don't want updates.

    If the players can shoot simultaneously, each shot will take a moment to reach the other client(s). This means that the local physics situation is differing from the one of the remote client. In this case, even little lag can make a big difference in the end-result!
    If only one player can shoot, you could send the end-position of a ball before the other can shoot. This way, at least the start/end of a turn is guaranteed to be the same situation.

    If you want turns and score, please have a look at the Pickup Demo from more recent PUN versions. This includes scripts that do player score. It's not extremely well explained but should be easy to use.
    Also there is a RoundTimer script. Search for "Timer" in the project. It uses a room property to mark the time when the game started. Based off that, the current round can be computed. Alternatively use a property to store whos turn it is and how many balls were used.

    Hi thanks a lot.
    Again a few questions.

    1. Is the Score solution used in the pickup demo an out-of-the-box working solution?
    2. When you mean to use a property to store whose turn it is and how many balls were used- Could you explain that a bit? It'd be great if you could write say 2-3 lines of sample code to help me move forward in this direction.
  • 1. It depends on your needs. Just have a look.
    2. There is sample code in the RoundTimer script. Check it.
  • Tobias wrote:
    1. It depends on your needs. Just have a look.
    2. There is sample code in the RoundTimer script. Check it.

    Hi,

    Just checked the documentation as well as the sample you suggested. Thanks for that.

    So I understand I need to use PhotonNetwork.room.Setcustomproperties
    But honestly, the implementation of it is not very clear- how exactly do I set a custom property, where is the property data stored, etc? Also, how would I use this to set a property for score and balls, etc

    EDIT: Trying a basic implementation for counting the no. of moves made since the starting.
    void OnJoinedRoom()
    	{
    		Debug.Log("Connected to Room");
    		if(PhotonNetwork.isMasterClient)
    		{
    			PhotonNetwork.Instantiate("BlueBall", new Vector3(0, -4.67f, 0), Quaternion.identity, 0, null);
    			this.StartCounting();
    		}
    		else
    		{
    			PhotonNetwork.Instantiate("RedBall", new Vector3(0, 4.67f, 0), Quaternion.identity, 0, null);
    			Quaternion temp = new Quaternion(0,0,180,0);
    			//Camera.main.transform.rotation = temp;
    		}
    	}
    
            private const String MovesKey = "mk";
    	void StartCounting()
    	{
    		ExitGames.Client.Photon.Hashtable countMoves = new Hashtable();  // only use ExitGames.Client.Photon.Hashtable for Photon
    		countMoves[MovesKey] = ballCount;
    		PhotonNetwork.room.SetCustomProperties(countMoves);
    	}
    
    	public void OnPhotonCustomRoomPropertiesChanged(Hashtable propertiesThatChanged)
    	{
    		if (propertiesThatChanged.ContainsKey(MovesKey))
    		{
    			moveCount = (int)propertiesThatChanged[MovesKey];
    		}
    	}
    

    Now, I guess the Moves Hashtable is set up. Now my problem is that, I have 2-3 other scripts that change the MovesCount. How would I access the HT I just made from those scripts? Also, let me know if this implementation is correct or not for a moves counter
  • Hmm, I should then take another look at the docs. Might be unclear - sorry.

    All properties are cashed in a Hashtable PhotonPlayer.customProperties. From this Hashtable you only READ on any client.
    When you use SetCustomProperties(Hashtable propertiesToSet) the key-values in the propertiesToSet will be sent to the other clients (and customProperties gets updated) and locally, the new values are immediately applied to customProperties.

    Your count code looks OK. I don't know if it does what you want but technically, you got it right.

    As said, there are PunPlayerScores and in that a class "ScoreExtensions" in PUN, which should do the score counting.
  • Tobias wrote:
    Hmm, I should then take another look at the docs. Might be unclear - sorry.

    All properties are cashed in a Hashtable PhotonPlayer.customProperties. From this Hashtable you only READ on any client.
    When you use SetCustomProperties(Hashtable propertiesToSet) the key-values in the propertiesToSet will be sent to the other clients (and customProperties gets updated) and locally, the new values are immediately applied to customProperties.

    Your count code looks OK. I don't know if it does what you want but technically, you got it right.

    As said, there are PunPlayerScores and in that a class "ScoreExtensions" in PUN, which should do the score counting.

    Hi Tobias,

    The documentation isn't clear, but I believe the ScoreExtensions class is doing what I was trying to do and it would serve my purpose.

    But just to put things into perspective,
    For now what I've done is created a static class GameManager that handles all my variables such as MasterScore, ClientScore, MasterCurrentBalls, ClientCurrentBalls, etc.

    Now, whenever I need to update them I am doing something like this,
    void Shoot()
    	{
    		if(PhotonNetwork.isMasterClient)
    		{
    			photonView.RPC("MasterBallChange", PhotonTargets.AllBuffered, false);
    			photonView.RPC ("MasterOnfieldChange", PhotonTargets.AllBuffered, true);
    			photonView.RPC("ReleaseBall", PhotonTargets.AllBuffered, transform.position, new Vector2(0,velocity));
    		}
    		else
    		{
    			photonView.RPC("ClientBallChange", PhotonTargets.AllBuffered, false);
    			photonView.RPC ("ClientOnfieldChange", PhotonTargets.AllBuffered, true);
    			photonView.RPC("ReleaseBall", PhotonTargets.AllBuffered, transform.position, new Vector2(0,-velocity));
    		}
    	}
    
    	[RPC]void ClientBallChange(bool increment)
    	{
    		if(increment)
    			NetworkGameManager.Instance.AddClientBall();
    		else
    			NetworkGameManager.Instance.RemoveClientBall();
    	}
            //All the other RPCs are similar so just putting 1 for example
    

    Do let me know if there are any drawbacks to using this implementation, and if I had to use a similar implementation to the ScoreExtensions, how would it match with my current scenario?