Sign In Register

How can we help you today?

Start a new topic

Unreal Engine Real Time Third Person Tutorial

Hello,

I'm in the process of shifting my Unity game to Unreal Engine, and also shifting from using PlayFab + Photon (PUN) to using GameSparks, since GameSparks seem to be a more powerful tool after reading about it and testing it. So far so good.


What I'm trying to make a third person real time multiplayer. Now the problem is that I can't seem to find proper documentation for the unreal part for the real time multiplayer. I tried to see the Tetris Example, but I don't think it has what I need, or maybe it does but I can't seem to know what it is.


Can someone explain to me or guide me through the following:

After connecting to Gamesparks and logging in as 2 different users, and after matchmaking is complete, I start a session and I switch both clients to the game level (the game level consists of the normal "ThirdPersonExampleMap" that comes as a default with unreal engine's new third person project). When both clients join this level the third person character can't be controlled by the user. What I want to know is should I create a third person character manually for each player when the scene is loaded? and how can I make that character load on the other clients so they can see him, and how can I sync their position.


Thanks in advance!


Regards,


Elie


This took me a while to figure out, but this link should help.


The way it works is you use the Game Mode to send your own player information to GameSparks. Then GS sends that data to the other clients. When they receive that data, they have their own Game Modes produce replicas of the other clients on your screen based on the data relayed from GS, which could include character class, position, rotation, velocity, etc.


The part I'm still working on is how to keep track of each player's data. I'm not sure how to link the data received from GS with the character on the map.


2 people like this
Thank you Chris.

Well I'm stuck  right now. In the Tetris project there seem to be an "Id" variable that you can use to send the new created actor to other clients, but I can't seem to figure out from where that variable came from.

Also I can't seem to know when all players have connected so I start the game, in the Tetris demo it shows that Packet 100 will be sent when all players are connected, but I can't seem to know where is this packet sent.

Maybe I'm missing a key point to understanding how the workflow of updating clients with new locations/rotation/spawning/etc...

Figured out that the 100 OpCode is sent from the real time script.

Still can't figure out from where the "Id" variable came from.


Now I've attached a "Print String" to the custom event "OnDataDelegate" so I can see the incoming packets, but it seems I'm not getting any. The other 3 custom events (on player connected/disconnected, and the onready) are working so I don't think I have setup anything wrong here, so the OnDataDelegate should be working.


I've attached 2 pictures showing the delegates part. Did I set anything wrong? Or should they work normally?

The Id variable is generated from GS when the MatchmakingRequest is sent. That part you can see if you test it in the test harness. You can obtain the Id's like this (also attached): add a GSMatchmakingNode, break the resulting Match Details Response, from there add a ForEachLoop that takes in the Opponents array, which produces GSplayer structs which you can break to get the Id of the opponents.


You have the "receive" end set up properly. Now you just have to actually "send" the data from within your Blueprint (attached). Picture it like the game Battleship. You each have your own game board, and you're telling the other person where your next move is. You have to send the data to them, and they have to receive it. After it is received they have to interpret it in a way that results in a perfect replica of your character as you see it.


I'm looking into making use of the Id variable. I didn't realize the Tetris example utilized it. I'll look into that tonight.


Keep the questions coming. You're helping me understand this better by trying to explain it, and this should help other people that have similar questions since UE4+GS is relatively new still.

Send Data.PNG
(70.2 KB)

It looks like the Id in the Tetris example is a different variable than what I referred to. The Tetris example has its own custom variable called Id which is simply an integer that represents a shape. Each shape (I, L, and T) has a different integer so that integer can be passed to the other clients via the Send node. When the clients receive the data, the Blueprints should then recreate the shape based on the corresponding integer.

Hey Chris, sorry for the late reply. I agree that this post will be helping us better understand the gamesparks real time using unreal.


I was able to fix receiving the OpCode 100 from the cloud, turned out I forgot to add the "contains" function. Now the clients are receiving the OpCode 100 and the game is starting.


The new problem is that when the game starts, I'm sending an OpCode 0 so the clients know that I spawned a new player. But this packet doesn't seem to get to the clients. While the OpCode 1 is for sending the location of the client, I'm able to receive it on the other clients. Seems kind of weird I can't figure the problem out.

I've attached the sending part of my blueprint and reattached the new receiving part(added a print string for received OpCodes, and attached the Data variable to the Spawn Player and Set Network Player Location). Both strings "Spawn Sent" and "Location Sent" are being printed, but only the Location is being received.

Forgot to attach the blueprints

Which of these strings are getting printed when you test?


  1. Sending Player Spawn OpCode 0
  2. Spawn Sent
  3. Packet 0 Received

I think I found a way to keep track of players/actors without having to do tons of math and checks every time data is sent. I have a screenshot attached, but this is essentially the flow:


  1. Open the level blueprint.
  2. On BeginPlay, spawn your pawn's actor and a placeholder actor (I created a default actor blueprint to accomplish this), and Cast to Game Mode to add both actors to an array variable ("ActorList"). Be sure to add the placeholder first so it can be assigned Index 0.
  3. Open the Game Mode blueprint and create nodes to accomplish the following:
  4. Start the RT session.
  5. Grab the array of opponents from the match details.
  6. Send that through ForEachLoop.
  7. Within the loop, spawn an actor for the opponent and insert that actor into the ActorList array with the index being the player's peerId.

Now that this is set up, I can plug the Sender variable from the OnData delegate into the index of a Get node, which will instantly pull up the actor associated with that player. This should allow me to efficiently update actor locations and other data.

What do you think?

To answer your first post these are the strings that are being printed:
1. Sending Player Spawn
2. OpCode 0 Spawn Sent
3. Packet 1 Received

I've attached the Start Game blueprint.

As for the way you are tracking the actors/players, well it's a good idea to keep them in an arraylist and having the peer as the id so you know which player to change his position/data that was sent. Though what if a player drops out and another player join back? Then you'll have to know that you have to spawn his character. As I can see your method is made statically on start of the game by getting the list of players from the MatchDetailsRequest.
But i guess this can be solved by requesting the MatchDetails again when a player connects.

I'll give it a try but i'm still trying to receive the OpCode 0 for spawning players..

P.S.: In the start game blueprint I have attached, I can't seem to be able to control the actor that I have just spawned, although I have added the possess when he is spawned. Do you have any idea why?

Sorry the strings that are printed are the following:

1. Sending Player Spawn OpCode 0

2. Spawn Sent

3. Packet 1 Received


"Enter" mistake :p

BTW, all these blueprints are in the GameMode blueprint, should it be there or not?

It says that the GameMode blueprint is executed on the server, how is it possible?

Hi guys,


The ID variable can be found in the shapes actors, I shape, L shape and T shape. Each have an ID to help identify them in a simple way. The I shape has an ID of 0 which should be 1, I didnt realise this before I pushed the sample. The variable is inherited form the the Shape parent class. 

The OP Code 100 is sent from the cloud script when 2 players are ready to play. This is the script:


______________________________________________________________________________________

var playersJoined = []; // this is only used at the start to make sure each player is connected

var timeLeft = 60;

var playerData = {};



function contains(a, obj) { // this is a simple method that just checks if an element is in an array or not

    for (var i = 0; i < a.length; i++) {

        if (a[i] === obj) {

            return true;

        }

    }

    return false;

}


RTSession.onPacket("101", function(packet){

   

   var rtData = RTSession.newData().setNumber(1, packet.getData().getNumber(1)).setNumber(2, new Date().getTime());

   

   RTSession.newPacket().setData(rtData).setOpCode(101).setTargetPeers(packet.getSender().getPeerId()).send();

   

          // RTSession.newPacket().setOpCode(6).setTargetPeers().setData( RTSession.newData().setString(1, "testing from cloud")).send();


})


RTSession.onPlayerConnect(function(player){

    // first we check to see if the player has already joined the match

    if(!contains(player.getPeerId(), playersJoined)){

        playersJoined.push(player.getPeerId()); // and add them if not

    }

    

    RTSession.getLogger().debug(playersJoined.length);

    // next we check to see the max (or min) number of players has joined that match

    if(playersJoined.length === 2){

        RTSession.newPacket().setOpCode(100).setTargetPeers().send(); // send an empty pack back to all players

       

        RTSession.setInterval(function(){ // send current server time to all players every 1second

           // RTSession.getLogger().debug(new Date().getTime());

            RTSession.newPacket().setOpCode(102).setTargetPeers().setData( RTSession.newData().setNumber(1, new Date().getTime() )).send();


        }, 1000);

        

        RTSession.setInterval(function(){ // send current server time to all players every 1second

            

            if(timeLeft > 0){

            timeLeft--;

            RTSession.newPacket().setOpCode(103).setTargetPeers().setData( RTSession.newData().setNumber(1, timeLeft )).send();

            } else{

                RTSession.newPacket().setOpCode(104).setTargetPeers().send();

            }



        }, 1000);

        

    }

});

________________________________________________________________


Hope that helps. The reason why everything is in the GameMode is because every player will be the owner of their own world/level. The packets simply replicate what other players do in their own world. Server authority will have to be done through RT cloud script.


Cheers,

Omar

Hey Omar, Thank you for clearing some things up.


Everything seems good so far, but still missing the packet 0 that is not being received by the other clients, although it is being sent. Any idea why? I've got my blueprints attached in the above posts.

Login to post a comment