How do I create a multiplayer "text game" ?

Just started using Construct 2? Post your questions here

Post » Tue Dec 20, 2016 4:03 pm

I would just have the host broadcast one single message that will start a timer for the peers.


But when would I send this message ? If someone joins after the message was sent, this peer won't have a timer. And for the scoring system I will need to be sure everyone send their answers upon host's signal (on the precise tick when timer reaches 0). Well anyway, this part is working for now. So that's not the most important.

try to test whether messages are actually received with a simple debugging text object before changing the actions/methods.


I tried to do that after including "int(value)", but it stills doesn't work for the group value. The peer never gets their message upon connection, so there must be a problem on host side.

Another try with the .capx: https://www. dropbox.com/s/palq7rp46p2k687/ChatProject.capx?dl=0
B
7
Posts: 18
Reputation: 311

Post » Tue Dec 20, 2016 5:43 pm

But when would I send this message ? If someone joins after the message was sent, this peer won't have a timer.

Earlier, you said that the timer is to give each player a timelimit on how long he can take to make a pick.
I would generally say that you start the game no earlier than when all players joined a room (room is full).
This will prevent you from needing to synchronize the timer every second or so.

If a player disconnects during a game, I wouldn't let him join until the current round is over. That makes the whole process easier-

_______________________________

I will need to be sure everyone send their answers upon host's signal

Alright, that's quite a requirement.

I would take the following approach:

Since you are playing over LAN, latency shouldn't be much of an issue.
This means that the timer's current times wouldn't be very different for each peer anyways.
Do this:
  • When the round starts, the host gives each player the signal to start the timer
  • the timer for the peers is used to control the displayed timelimit only.
That means that they will have visual feedback on their time left. (You probably wanted to do that anyways)
But when the peer's timer reaches 0, they won't send their message yet.
They will wait until the host broadcasts a message, that his timer is over.
This will probably cause a slight difference delay the peer's visual timer reaching 0 and the host's message arriving, but this will guarantee that each player sends his message at the same time.
Long story short:
  • The peer's timer is started at the same time as the host's timer
  • The peer's timer is only used for displaying the time left, not to send the message when the timer reaches 0
  • When the host's timer reaches 0, he broadcasts a message (e.g. "timer"). When the peers receive this message, they send their answer (synchronized)

__________________________________

I tried to do that after including "int(value)", but it stills doesn't work for the group value. The peer never gets their message upon connection, so there must be a problem on host side.

Alright.
Try to set the "Send message" Peer ID to Multiplayer.PeerIDAt(Multiplayer.PeerCount-1).
(I'll explain that if it actually works)
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Wed Dec 21, 2016 4:53 pm

Try to set the "Send message" Peer ID to Multiplayer.PeerIDAt(Multiplayer.PeerCount-1).
(I'll explain that if it actually works)


It does work :D. So what is the difference between "Multiplayer.PeerIDAt(Multiplayer.PeerCount-1)" and "Multiplayer.FromID" ?
B
7
Posts: 18
Reputation: 311

Post » Wed Dec 21, 2016 5:19 pm

Apparently, quite a big one.
No, I was just wrong when I thought that FromID returns the ID of the recently connected peer when put under a "On peer connected" event.
Cause, you know, that would make sense.
But apparently, that's not the case, so I tried something else (PeerIDAt) and that worked.

Now, to explain Multiplayer.PeerIDAt:
it takes the index of a player and returns his ID.
The index is different for each player.
So, when there were two players in a room, me and you and I used PeerIDAt(0), it would return my ID while PeerIDAt(1) would return yours.
The other way around, if you used the expression, (0) would be your ID and (1) would be mine.

When a player joins though, he will always be added on the last position of this table.
That's why we use PeerCount.
We substract 1 because PeerCount starts as 1 and the PeerIDAt expression needs an index, which starts at 0.
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Thu Dec 22, 2016 7:32 pm

Oh ok I get it. (Well it's still weird that ".FromID" doesn't work though).

Well I actually managed to implement the scoring system too and changed the way the "timer" works to what you explained earlier. So now I could say the game is "fully operational". Here is the .capx : https://www. dropbox.com/s/gv08nlsdwl1xbcx/ChatProject.capx?dl=0

There is still place for improvement though, here is how it works now :
- When the peercount = "the number of player I want", host broadcasts his timer, then everybody substract 1 every second (it's only visual for peers).
- When host's timer reaches 0, he broadcasts "timer0" to everyone. He also registers his answer (if not empty) on a dictionary with value 1. And puts "timer" back to 10 seconds.
- When peers get "timer0", they send their answers to host (if not empty, tag "Answer"). They put their "timer" to 10 seconds too.
- When host gets "Answer", he adds this to the dictionary (either a new key with value 1 or +1 to an already existing key).
- When host's "timer" is 9, he checks if the dictionary has a key corresponding to his "Answer.Text". If so, add key value -1 to your score (you don't get a point for yourself). He then broadcasts the dictionary as JSON and peers do the same.

Note that everybody saves their own answers only with the "Spawn" value for later analyzes purposes. But this has nothing to do with the scoring system.

My problem here is the last step has to happen at host's timer = 9, which is already 1 second in the next round (so there is a delay between the end of the round and the actual scoring). But when trying with timer=10, it seemed that the host did not have time to get the peers' "Answer" messages. Which is weird, since this should happen at timer = 0, thus, one frame earlier than timer = 10. Also, this would be a problem for scoring the last round (well, it's not really hard to solve that). Do you think this is actually just because it takes time to exchange all those messages ? Do you have a better idea to improve that ? I thought about asking the host to count the answers he gets and set timer to 10 only when that number reaches peercount-1. But then I should allow peers to send empty answers and find another way to exclude those from the dictionary, so nobody get points for not answering.
B
7
Posts: 18
Reputation: 311

Post » Fri Dec 30, 2016 1:14 am

Sorry for the late answer, I was on Christmas holidays.

________________________________

When peers get "timer0", they send their answers to host (if not empty, tag "Answer")

Don't know whether you added this yet or not, but it may be sensible to catch the case that every player submits an empty answer, by accident.
So that it doesn't display "", but "No answer given" for example.

________________________________________

I thought about asking the host to count the answers he gets and set timer to 10 only when that number reaches peercount-1.

Yep, that is exactly what I would do.
Don't start a new round before the host didn't receive all answers.
You could also add a timeout, so that when the timer is 0, he waits 30 seconds and after that, he uses the answers he received no matter whether there are some missing (which should be unlikely anyways).

________________________________________________

find another way to exclude those from the dictionary, so nobody get points for not answering.

Just compare the content of the received message.
If "Multiplayer.Message" equals "", then it is empty.
If you invert this condition, it isn't.

Easy as that.

_____________________________________


I'm happy you are making progress.
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Tue Jan 03, 2017 11:07 am

Hey ! No problem, I was on holydays too.

Actually, I will be quite busy with other stuffs until the middle of february, so I may have to put the game aside for a while. But I think it is already in good shape (thanks to your good advices).

The next thing I will have to think about is this : the "previous creatures" hint.

I want it to be a page that appears when "previous creatures" button is clicked. It would show the previous creatures sprite (in reduced size) + the answer given by the player and the points he got for that round. I am sure there is a quite simple way to do this. I just haven't had time to think about it yet. If you have any advice...
B
7
Posts: 18
Reputation: 311

Post » Wed Jan 04, 2017 1:45 am

I want it to be a page that appears when "previous creatures" button is clicked. It would show the previous creatures sprite (in reduced size) + the answer given by the player and the points he got for that round. I am sure there is a quite simple way to do this. I just haven't had time to think about it yet. If you have any advice...

In my opinion, the hardest thing while doing this, is to find a proper way to layout this to not make it obliterate the whole program haha.
But that's up to you.

Well, again, I'll have to refer to Dictionaries for this case (they are so useful).
Since every peer receives the current creature anyways, you can simply submit that creature to a "creature"-dictionary when receiving the creature for the current round. Choose the current round's number as the key.
(Don't forget to add "str()" around the number, since dictionaries only take strings as keys)
(And to use "int()" when parsing the dictionary's contents)

The same goes for the answer and the points. When you submit your answer/get your points, add the answer/points to a "myAnswers"/"myPoints" dictionary with the current round as a key (-> "str()").

Now when a new round starts (and it isn't the first round), get the creature, answer and points from the previous round using a "For each key" condition (from the dictionary).
Under that condition, you can retrieve the current dictionary's contents with the expression "[Dictionary].currentValue".

Use these steps for the three dictionaries (creature, answer, points) and voilà, you have your previous answers at your fingertip(s).
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

Post » Wed Jan 04, 2017 3:50 pm

Yeap, dictionnaries for life lol. My first idea was to use families though. Something like creating 30 sets of 3 items (at the beginning, only "?" would appear) : a sprite, a "answer" text and a "score" text. Put the 3 items in 3 different families with an instance variable (corresponding to the creature count). When timer reaches 0 :

Pick sprite (currently a question mark picture) by instance variable = creature count => Replace with sprite of current creature
Pick "answer" text (currently a question mark) by instance variable = creature count => Replace it with Answer.Text
Pick "score" text (currently a question mark) by instance variable = creature count => Replace it with "HostAnswerList.get(Answer.Text)-1"

Do you think I can do something along those lines ?

Don't know whether you added this yet or not, but it may be sensible to catch the case that every player submits an empty answer, by accident.
So that it doesn't display "", but "No answer given" for example.


I don't understand what would be the problem if everybody submit an empty answer. So far, empty answer = you don't send anything to host and you don't score anyway when getting the answer list.

Also, I notice a little problem with the way the scoring system works now : if someone always answer the same thing, like "ZZZZZ" for example, even if it is nonsense, it will add 1 to "ZZZZZ" value every round. So this player would score round number-1 every round. I should find a way to prevent previous rounds to influence later ones. I thought about 2 solutions :

- Reset HostAnswerList every round => cool, but a complete list of all answers could have been interesting data to analyze for my research, so I'd rather not.
- Learning to use array, then I can have x = Answer (text), y = Value for this answer, z = round number. And players would score y points only if there Answer.Text = x and round number = z. => I have to learn how to use array T_T
B
7
Posts: 18
Reputation: 311

Post » Wed Jan 04, 2017 5:59 pm

Pick sprite (currently a question mark picture) by instance variable = creature count => Replace with sprite of current creature
Pick "answer" text (currently a question mark) by instance variable = creature count => Replace it with Answer.Text
Pick "score" text (currently a question mark) by instance variable = creature count => Replace it with "HostAnswerList.get(Answer.Text)-1"

Do you think I can do something along those lines ?

Yup, that should work too. (I'm just too much of a fan of Dictionaries to not propose them).

_____________________________________

So far, empty answer = you don't send anything to host

Ah ok, if you included that already, then you won't have to worry about it.

___________________________________

Also, I notice a little problem with the way the scoring system works now : if someone always answer the same thing, like "ZZZZZ" for example, even if it is nonsense, it will add 1 to "ZZZZZ" value every round. So this player would score round number-1 every round. I should find a way to prevent previous rounds to influence later ones.

Well, we both know that we need a way to differentiate between answers given in different rounds.

The way I'd probably go (because I find 3d-Arrays too tedious to use, though they are very powerful), is to include the "round" number in the actual answer.
So for example, the dictionary-entry for the first answer could look like this:
· Key: "1" / · Value: "1//[answer]"
The second answer would look like this:
· Key: "2" / · Value: "2//[answer]"

Now you can see which answer comes from which round.
Of course, you will need to extract the plain answer from the key.
If you know, that the number of rounds will never] go above 9, you simply use "left(3,length([string]))".
Otherwise, you will have to use regex. (RegexMatchAt([string],"\d\/{2}([\d\D]+)","",0)

__

Or, you can learn 3d-arrays. :D
"We can't solve problems by using the same kind of thinking we used when we created them."
- Albert Einstein
B
27
S
11
G
8
Posts: 528
Reputation: 7,091

PreviousNext

Return to Beginner's Questions

Who is online

Users browsing this forum: No registered users and 0 guests