I’m currently looking for freelance work as a pixel artist / Construct 2 programmer. Read more about it here:
You can check out some of my games here:
Some examples of my work:
– Disclaimer: I’m no programming master, I’m only a self-taught student. I’m sorry if any info here is wrong and I won’t be getting too much into the actual room creation. I’m also not responsible if your computer blows up or anything. –
First things first; if you’d like to follow along and read the code, you can download the .capx here. You’re free to use it however you want, but if you do use it for anything, if possible, credit R0J0hound and I (AndreYin).
Optimizing R0J0’s random room generation code to accept Tilemaps
I’ve been studying a lot about random room generation and the new multiplayer object in Construct 2. It all began early this year, when I found this example by R0J0hound on the Scirra forums. At the time the Tilemap object didn’t exist (or did it? If it did it was really archaic). R0J0 basically had 30 different layouts, each with their own objects, then he went on each layout at the beginning of the game and stored each one on an array, together with some info of which exits it had.
After that he generated the map using a small piece of code that’s just too much for me, since I suck at math. He used a small mini-map to create the random room layout, and based on that, he created the “rooms” (which were just a bunch of objects placed like a room) themselves.
This code was already pretty amazing, but I decided to try and optimze it a bit to accept Tilemaps instead of objects. This would increase performance a lot, make it a little bit easier to mess with rooms and things like that.
The way it works (apart from the actual room generation) is pretty simple. The game starts on the “init” layout and it has every possible room, each on a separate instance of the Tilemap object. Each instance also has 4 boolean variables that correspond to their own exits. The original code from R0J0 has a separate text object that held these vars. I also ran a loop on each tilemap and check if there are tiles on the exits; then I change the boolean. This is much better than having to set each variable manually every time you create a new room.
Also, on the original code R0J0 stored all the walls on an array for each room. I decided to keep using one array for each room, but now it only stores the .TilesJson from each tilemap on it. Each array also has a variable that keeps which exists the room is supposed to have. This probably could be a little more optimized.
After all the rooms have been stored on different arrays (which could probably be optimized by a lot if you used just one array maybe?) and the player has typed anything and pressed the button, the game goes to the next layout (game).
At the “mapGen” group (which turns on once if the player is the host) is where the magic happens. This first part of the code is R0J0’s; First there’s a small loop using a variable that repeats 30 times, and each time he creates a new room. Using some math he was able to generate a small maze of rooms and doors, and then he creates a mini-map based on that. Then he proceeds to check which doors are overlapping which rooms, picking a room where the “doors” variable on the array is equal to the doors on the actual room that was generated. I’ve left some of R0J0’s comments. For each X element on the array (that meaning, for each “room” on the array), it will create a new tilemap based on the mini-map room position, then load the TilesJSON based on which exits the mini-map has.
There’s a small piece of commented out code here; this is a loop that checks if the tilemap has a certain tile on it. If it does, it erases the tile and places an object on it’s place. IIRC it works and it could be useful creating objects on each room, so I left it there. I haven’t tested it in a while though.
When the room creation is done, it adds 1 to the temp. loop variable (making the room generation stop), then adds 1 to the “checkMapDone” variable (meaning that the map has loaded).
The next events are only important to the multiplayer part – if the map has been loaded, since ONLY THE HOST can generate the map, it sets the “howManyRooms” variable on the player to the current Tilemap.count, and sets the “isHost” to 1, meaning that player is the host. After that there’s a small loop – it gets the state of each tilemap object and saves it as json (which is a string containing all the info about that object – it’s position, the tiles it’s supposed to have, etc) together with an asterisk. It will end up looking somewhat like this:
This is enough for the map to be created and the game starts. After I got this down I decided to try out implementing the mutliplayer object to study it a little.
Implementing the multiplayer object
I have to admit, I was really scared by the multiplayer object. I’m not a good programmer so I thought it would be hard as hell to implement, but at the end it wasn’t so bad. Just to be clear, this part is probably not optimized AT ALL; I’ve used a few workarounds to get some things going and it probably could have been made way better.
I’ve used the “Ghost Shooter” multiplayer example as a base to get it going; created similar groups to the ones on it and much of the code is the same. I’ll try to explain from what I understand.
First, the signalling. This tells the server which variables it’s supposed to keep an eye on. I’m seriously not sure if there’s a better way to do this since the events say “client input value” and apart from the actual input I’m also using the signalling to store how many rooms have been generated. The most important thing to keep in mind here though is that we’re asking the server to sync the “player” object’s position, then it’s inputs. This way the server can tell both the movement the player should be making and the position he’s supposed to be at. After that we contact the signalling server.
When connected, we use the username the player first typed in the first layout. You can also have different game rooms if you’d like so players could have specific rooms to play with whoever they wanted; on the “init” layout there’s a text box that I’ve left out of the layout.
Before we keep going I forgot to mention something important; there are 3 really important global variables at the beggining of the event sheet. They’re GAME_NAME, INSTANCE_NAME and ROOM_NAME.
The GAME_NAME var tells the server which game you’re running. IT’S REALLY IMPORTANT THAT YOU CHANGE THIS VARIABLE if you make any modification in the game at all. Otherwise people with other versions will be able to join your modified version and things will not go as expected. Yes this has happened to me. The other 2 vars are pretty simple to understand.
Continuing, the game checks if someone has entered the game room. If they’re the host, it activates the “Host” and “mapGen” groups, sets the player variable “peerID” to your own ID. If the player joining is not the host, it only activated the “Peer” group and destroys the player object that currently is on screen – this is so only the host has the player objects created and synched (I think?).
When the “Host” group is activated, whenever a new peer connects to the game it will create a new “player” object and set his variable “PeerID” to the current PeerID of the player that just connected. He also associates that player to the current PeerID of whoever just connected. This way that peer will have control only over that player object.
Then comes the controls. There’s a small variable called “changingScreen” that is supposed to see if the player is changing rooms, and if it is, it’s supposed to make him stop while the camera lerps to the right place, but this is currently not working. I’m not sure why, either.
After that it’s pretty simple; since we’re on the “Host” group it checks if your own ID matches one of the players on the game. If it does and you press a keyboard button, it simulates the 8 direction behavior.
Under that, we check if the player object does not match our own ID – this means that it checks for each player object that isn’t the host. If it isn’t, it fetches the “inputs” variable from the current player.peerID directly from the server. Then it compares it to see which movement the peer should be doing; it’s a simple boolean check – if the input is 1 then the key is being pressed. If it’s 0, it’s not.
For a quick reference, in this case we’re using 0 for “left”, 1 for “up”, 2 for “right” and 3 for “down”. So for instance, if we check if “input 0 is 1” we’re checking if “left” is being pressed.
Onto the “Peer” group – whenever a new “player” object is created (by the host), we set it’s peerid to the current ID of the peer that just connected then associate that object to that ID. We also turn on “input prediction” for the player, but I haven’t messed around with that too much yet (this is probably what’s causing a bug where peers won’t stop moving after entering a room).
We check again if the player is changingScreens, and if he isn’t, on every client update where the player.peerID is the same as your own ID (you being the peer), we check each key to see if it’s being pressed. The way it’s set it means that if LEFT is pressed, it sends the info that “input 0” is 1 (which means that “input 0” is being pressed). Else, you have to specify that if it’s not being pressed, “input 0” is 0. This code is straight from the Ghost Shooter example so I’ve left the comments. We do this for each key, then we send this info over to the host using the “set client input state” to match the current input being pressed. There’s a comment here that explains why we’re going to compare the player.inputs and move them accordingly.
The common group is where I’ve set the tilemaps to load on peers too. When a new peer connects to the room, the Host broadcasts a message; this message contains the state of every single room that was generated on the “mapGen group” in JSON format, in a single string, separated by an asterisk. Remember the part where we set the host player to receive all the rooms on his “loadrooms” variable? That’s the message that’s broadcasted. Broadcasting a message means that every peer in the room will receive it.
Under that we check if the “loadRooms” message has been received, if it has, the global var “currentRoomsAsJson” is set to the message string, then a small loop creates every room and sets it to the .JSON state using TokenAt and the asterisk as a separator.
That’s pretty much it! I won’t be getting too much into some things but hopefully this is enough.
Again, you can download this .capx here. You are free to use it however you want, but if possible please credit R0J0hound and me. You don’t have to, but it would be nice.
Thank you for reading!
A couple days before the jam actually had started I decided to warm up and created a simple mock up of this little duck dude, with some rocks around him. The rock ended up looking a bit like teeth, like the camera was inside of a giant worm mouth or something. I first decided to expand this concept, and make a simple game where you went around as the duck trying to rescue eggs from the worm.
I quickly discarded that though because it didn’t seem fun. After the jam had started, I drew some more concepts and thought that maybe he could be an archeologist; maybe I could pull off a La-Mulana concept on a top-view dungeon crawler.
So I began working on it. I considered having the main character be able to attack somehow and even drew a couple moving enemies, but then I decided to not have him attack at all and make this an exploration-only game. I remembered of Goof Troop for the SNES, one of my favorite games of all time, where the characters couldn’t attack directly but had to avoid enemies and kick blocks/throw jars at them, so I decided to go with it.
Programming the blocks to be pushed wasn’t very hard but I did run into some trouble; blocks in old games are always sort of “fixed to a grid”, but you hardly even notice. I tried to use a Custom Movement behavior on the blocks and attach them to an invisible grid, but to no avail. I didn’t have enough time to spend learning how to do it, so in the end I just added a Bullet behavior.
The events are also simple enough; if a button is pressed it checks if the player is overlapping the block by an certain offset. If he is, the bullet movement turns on and the block is moved to the direction. Each block also has a “initialXY” variable and at the start of the layout it saves its own position to it. Whenever the player leaves the screen, the block is set to that position (like old games).
Then I decided to make moving spike blocks. The concept was pretty simple, it checks if the player is on the same Y position as itself, and if it is, it moves. Obviously I also had to check if the spiked blocks were on the same screen as the player, but that was pretty easy; C2 has an event called “is on-screen”. Basically I only had to say that if the spiked blocks were on screen, the bullet behaviour would be on, else it would be off. Simple enough and nothing exploded, thankfully.
After that I created wall arrows and buttons. Buttons were a bit more tricky, but I did something simple though; whenever a block is over a button the block sets a boolean var called “isOnButton” to true, turns its own bullet behavior off and lerps into the nearest button. Each button also has a text variable called “whichFunc”, this way I could create an event that says that when a button is pressed, it should call the function “whichFunc”, so I could have buttons do different things, like create keys and activate arrows.
I also created text dialogues using the sprite font object and had to wrestle a bit with different sprite font generators around the web. The only one who worked for me and didn’t blur the font too much was the old version of this Sprite Font Generator by blackhornet at the Scirra forums.
Most of the jam time went to creating the tilemaps though. Each room is a different instance of the tilemap object. I made it this way so I could re-arrange rooms more easily, and it ended up pretty much saving my ass! C2 tilemap support isn’t very good so you can’t select and copy a certain area, re-arranging rooms would take ages. Thankfully though I found out that Scirra included hotkeys so whenever you’re drawing on a tilemap you can press Z and X to flip/rotate the tile.
By then, everything was doing pretty good. Until I decided to test out the game as a whole. It was really boring. All you did was to push a block onto a button, get a key to open a door and that was it. There was no thrill and it just wasn’t very fun at all.
I was getting kinda worried over that, so I talked to a friend and she came up with the idea of a timer. This way the player would have to run through everything while trying to keep alive. I absolutely LOVED the idea and tried it out right away. Since by then I was kinda used to the whole room layout, I thought it was really fun. It was now fast-paced and not nearly as boring as before. Unfortunately what I didn’t seem to notice at the time was that to first-time players it was nothing but frustrating.
But I was completely in love with the concept, and decided to make “breathing rooms” – rooms where the main character could breathe. I came up with the concept that since he was inside of a cave, the air was too thin so you had to reach some checkpoints to breathe. At the time I also didn’t want to include saving in the checkpoints because I thought it would be too easy; maybe the game would have been much better by then if I had though. I made the checkpoint rooms just far enough that you could reach them before just running out of air, too.
Then I finished the last rooms, and noticed that something was missing; the whole exploration aspect wasn’t there at all. Time was running out so I did something that would definetely doom the game; I hid the last key and the last egg under the ground and added the shovel mechanic so you have do dig them out. I don’t think that anyone has played the game enough to actually get to this part, but if you did, oh boy. The only hint is on a skeleton on a hardly reachable place, and he says that “the key is hidden under the moon”. The moon is a single tile on another room, and you have to dig under it. There was absolutely NO WAY of knowing in which room it was unless you really paid attention to the rooms you were in. Apart from the hard difficulty, this made the game almost impossible to beat unless you played it over and over again.
Honestly, that made me quite happy. It’s not like I didn’t want people to beat my game, but I knew deep down that if someone DID beat it, it would be satisfying as hell. So I uploaded it to Gamejolt thinking that.
After that I talked about the game a bit on 4chan, Facebook and Twitter. The feedback was NOT good. People complained that the random spikes were bullshit, played it for less than 5 minutes and dropped it. No one seemed to notice that you could actually see the places that had spikes. I didn’t want to make it too obvious because, well, if you were an expert archeologist you’d see it. They also thought the whole “air” thing was awful, and some complained that they did not know what to do; they didn’t know you could push the block over the button. One dude went as far as telling me that it was typical that I didn’t know anything about game design since I seemed to be more of an artist and that I had ruined the game.
That made me feel really awful, obviously. But I kept thinking to myself that no matter what anyone said, I enjoyed it. I couldn’t even beat it properly, but hell, I really enjoyed it. It felt like you HAD to speedrun the game to beat it. It was good concept that could have been expanded on if only I had more time. It also made me feel a little better that whoever enjoyed it REALLY enjoyed it. Some friends and colleagues were getting addicted to it, trying to reach the end. It became somewhat of a race to see whoever could get farther. That made me feel accomplished.
As for now, I have another version incoming. It has save in checkpoints, it takes a while longer for your air to deplete and you can obviously see where there are spikes now (I drew leaves on top of them). I’ve also fixed some bugs, like a door that was showing off-screen. I’m not sure why am I even working on it, but still, I’d rather have a beatable version up.
And that’s the whole story! Thanks for reading, playing, and everything else.