211. Making multiplayer games
The latest Codea release added sockets, which allow iPads to message each other and create multiplayer games. The Codea documentation doesn’t cover sockets, because they aren’t an official part of Codea – but we can use them, and this post shows how.
The code and example below were written by JakAttak , so all credit to him. I’m simply going to explain it the best way I can, so you can use it yourself.
The code comes in two parts. There is the socket “library” which does all the hard work, and then the example code that uses it. I’ll explain the example code, and if you want to know how the socket library works, you’ll find it is pretty clearly laid out.
Please note I haven’t shown all the example code below. I’ve skipped the parts that belong to the example game, so I can focus on the multi player code. You will find a link to the complete code at the end, so you can try it out.
So – let’s see how it works.
One player hosts the game, ie starts it and waits for another player to connect. So we need the option at the beginning to be the host or a guest, and some way of connecting to each other.
First, the setup function. It puts some parameter buttons on the screen so you can choose to be host or guest, and gets the game started when you make a choice.
function setup() --create a temporary function that will run *after* connecting local connectionMade = function() output.clear() --clear print area parameter.clear() --clear all parameters print("Connected!") --tell user the good news gameSetup() --set up the game end --create the multiplayer object --it needs two functions given to it --the first one handles data sent by the other player --the second is the function (above) that runs when we connect multihandler = Multiplayer(receiveData, connectionMade) --now the parameters that let us choose how to start the game --hosting option parameter.action("Host Game", function() multihandler:hostGame() end) --guest option that finds other player for you parameter.action("Find and Join Game", function() multihandler:findGame() end) --guest option where you enter ip address and port yourself parameter.action("Join Game", function() if other_ip then multihandler:joinGame(other_ip, other_port) else parameter.text("other_ip", "") parameter.text("other_port", "") print("Enter host ip and port, and click Join Game") end end) end
If you are playing on a local wifi network, the guest player should be able to just pick Find and Join Game, and Codea will find the other iPad. If you’re playing over the internet, you need to use Join Game, where you type in the ip address and port number of the other iPad. How? When you run the program and choose Host Game, it prints your ip address and port number, and then you need to share this somehow with the other user. I haven’t tested that this works.
Now you are connected, set up your game, as shown below. You can replace all this with your own code. Note how there is a “clear” parameter with a function attached, and that function clears the print area and then sends the message “clear” to the other iPad so it knows to clear its screen too. So you can send messages between you at any time using the function multihandler:sendData.
function gameSetup() canvas = image(WIDTH, HEIGHT) parameter.color("pen_col", color(0, 255, 0)) parameter.integer("pen_size", 2, 100, 10) --clear screen option parameter.action("clear", function() clear() --see below --tell other user to clear screen, --this is how to send messages multihandler:sendData("clear") end) pen_touch = nil last_point = vec2(0, 0) end function clear() --start drawing again canvas = image(WIDTH, HEIGHT) --clear image in memory end
If you aren’t used to seeing a function being defined in the middle of a parameter command, you can write like this if you prefer. The parameter is told to run the Restart function if it is pressed, and that function clears the screen and tells the other user to do the same.
parameter.action("clear", Restart) --somewhere else in your code, put this function function Restart() clear() multihandler:sendData("clear") end
Now we need to write a function that receives data from the other user. We called it receiveData above, when setting up the connection, so we need to use the same name here. Note how it clears the screen if it receives the word “clear”, then it has some very strange code, which I’ll explain underneath.
function receiveData(d) if d == "clear" then clear() else local tb = loadstring("return " .. d)() drawPoint(tb.point, tb.last_point, tb.drawing_line, tb.pen_size, tb.pen_col) end end
First, you’ll see the function has one parameter d, which is a text string sent by the other user. What is in that string is up to us. In this game, it can either be the word “clear” (to restart drawing), or it can be a table of data used to do some drawing. How do we convert a table of data to a string, and back again?
Here is the line (from below, we haven’t got to it yet) that sends data to the other user.
multihandler:sendData("{ point = " .. vec2ToStr(p) .. ", last_point = " .. vec2ToStr(lp) .. ", drawing_line = " .. tostring(d) .. ", pen_size = " .. ps .. ", pen_col = " .. colToStr(pc) .. " }")
It uses some functions (vec2Str, tostring, colToStr) to convert point positions, pen details etc to strings. Two of these three functions are defined below (the other, tostring, is a built in Codea function). I won’t explain them because they are very simple.
The important thing is that the final string looks like a line of code that defines a table. It has the squiggly brackets, commas between the items, etc. If only the receiving iPad could turn it from a string back into a table.
Well, it can. The loadstring function tells Codea to take the text inside its brackets, and treat it as code, ie run it. And the text inside the brackets is the word “return” followed by the table data sent by the other iPad. So the result is a table of data, which the code above puts in a variable called tb. The next line of code uses it to draw.
You don’t have to use fancy techniques like loadstring. You can send text however you want, and read it any way you want at the other end. But loadstring is a nice trick to know.
function draw() background(255, 255, 255, 255) --update the connection multihandler:update() --if we're still connected, sprite our drawing if multihandler.connected then --draw image on screen sprite(canvas, WIDTH/2, HEIGHT/2, WIDTH, HEIGHT) else --otherwise wait to be connected fill(0) text("Waiting for connection...", WIDTH / 2, HEIGHT / 2) end end
Finally, in this drawing game, any time we touch the screen, we do some drawing and we send the details to the other player.
function touched(t) --this code only runs if we are connected if multihandler.connected then --[drawing code skipped here] --this is the line we saw above --it sends the details of our touch to the other player multihandler:sendData("{ point = " .. vec2ToStr(p) .. ", last_point = " .. vec2ToStr(lp) .. ", drawing_line = " .. tostring(d) .. ", pen_size = " .. ps .. ", pen_col = " .. colToStr(pc) .. " }") end end
The complete code for the example (and multiplayer socket library) is here.
In my next post, I’ll (try to) explain the socket library, without getting too technical (there is little risk of that, because I don’t understand it too well myself).I won’t try explaining the socket library, but if you look at the code, you should find it’s well laid out for you to follow.
Trackbacks & Pingbacks