Let’s Talk about Masks Baby

It’s been over three months since I started on Innkeep!  I’m pretty happy with the pace of development so far. I often find a bit of time after coming back from work each day, sometimes a little in the morning, and typically larger blocks on weekends. Slowly, I’m getting a better idea of how to do this “make a game” thing. I’m doing a poor job slowly, but slowly ideas are turning into actual features of a prototype, and that’s really really cool.

stein

Unfortunately you can easily go from “I worked for 10 minutes and now my man can hold a beer! Coding is fun!”, to “So I bashed my head against a wall for 15 hours and I -still- can’t get this smooth zoom feature to work with my shading system!!” Whole weekends can just go up in smoke with nothing to show. That’s demoralizing when you’re struggling to find the free time for your project to begin with. It seems that a big chunk of my time is spent trying to get something to work as it should, so that then you can build some features on top of that thing. If the foundation is all wobbly you won’t be getting anywhere in the long term.

Anyway, given that I am in the middle of setting up another system at the moment, the conversation system, and the release of my next development diary video has stalled while I hack away at a few more systems, I thought I would write a little on one of the systems that is already in and working quite nice. This concerns the use of masks effectively to create the 2D environment of the game.

First though, what is a mask in game maker? A simple explanation is due for non-developers. It might help if we first compare masks with sprites. spr_PlayerBack Here is the current stand-in sprite I am using for the player character, when he is facing away from the screen. A sprite is one, or multiple images, comprised of pixels. It can be still, or animated (for example when walking). By comparison, a mask delimits a certain area in two-dimensional space, which can be used for the purposes of checking if something (like a cursor) is over it, or if something is running into it, i.e. functioning as a “bounding box”. They are not visible in-themselves when playing the game. spr_PlayerBack Suppose that the box we see here is the mask. Using this, we can do some useful things. For example, we can:

  • Use it to run a check to see if the object has run into another object, such as a table.

or

  • Use it to run a check to see if a mouse is over it (a check that can be run in conjunction with clicking, to see if you have clicked on the guest.

In an angled perspective 2D environment such as used in Innkeep!, it should be fairly clear why the mask is only covering the bottom part of the character, as opposed to say, a platformer, where most masks would be covering the entire player. Objects can be near or far away, behind or in-front of each other. If the mask was the whole of an object such as the innkeeper, then when trying to walk up to a table you would be bumping your head into it. Instead we get something like this (minus the white box around you of course): spr_PlayerBack Only when those boxes run into-each other does the game prevent you from moving further “up”. Likewise you can go “behind” objects. The order in which the sprites are drawn depends on how high they are on the Y axis. A lower value (i.e. being higher on the Y axis), means you will be drawn before something with a higher number. The point being checked is a specific pixel (the so-called origin point) of the object, which you can select yourself, and typically needs to be inside the objects mask. So this is how we get the layering, and a nice fake 2.5D perspective effect is born. So far, so good. But in Innkeep! you don’t simply control a single character with a keyboard, walking around and interacting with things using space etc. Rather I have a Don’t Starve esq mouse-based control system that allows me to click on the environment in order to interact with it. But wait. As mentioned before, it is also the bounding box which we use to run our checks such as to see if the mouse is “over” an object. The bounding box is the physical extension of the object in 2D space, with the sprite in a sense just being the clothing it wears. And if the bounding box for an object is smaller than the sprite, then you end up with this weird situation where you can only click on a table or a guest if you click on a certain specific part of that table or guest, a part which is actually invisible. Not good. It seemed that I would actually need two masks. One for clicking, such as above, and one for running into things: spr_PlayerBack The problem is that an object can’t have two masks at once. What to do? My initial attempt at a solution was to have the object swap its masks (by swapping the sprite the mask is mapped to), for just a split second whenever a check for clicking was being done. Everything in the game which could be clicked on, would constantly be checking to see if the mouse was clicked. If it was, it swapped its mask to a version that was as large as itself, in order to see if the player wanted to click on it. A split second later, it was back to its normal “mini” bounding-box mask. This turned out to be complicated to implement, and rather buggy. When you are constantly swapping things in such a manner you need to have a very good understanding of the sequence in which the game is running things. There were too many situations in which it wasn’t functioning as it should, and I wasn’t sure how to fix it. The solution turned out to be a bit more straight forward than that (although maybe a bit silly sounding for experienced coders, I admit): Two objects! Each with their own purpose mask.  commonermask In Innkeep!, clickable objects in the world such as tables or guests, actually have a secret body-double, a unique black-and-white doppelganger instance which is linked to its owner’s ID when created. You never see it, as it is perfectly behind the original object holding all the real code, and its mask, though covering its whole body, is not counted as “solid”, so ghost-like it perfectly moves around wherever the original object goes. For checks on clicking a table or a guest, it is this second object which is doing the checking. If it is clicked on, it tells its partner “hey, you’ve been clicked on mate!”. So far, so good. But as we know, it is possible for these ghost objects to overlap. What if this is what we end up with? tablemask Unless I click only on his head, clicking on this guest mask would also mean I’m clicking on the table mask at the same time. But I only want one, the one which is “in front” to be selected. What to do? If you are running your checks inside the “step” event of your object (an event which runs code every “step” (typically 1/30 or 1/60th of a second, the equivalent of a frame of animation), then you might end up with a random outcome. The solution, it turns out, is to run the check within the “draw” function of the object, the function which is responsible for.. you get it, drawing the object. As we mentioned above, this is done in order, according to where the object is on the Y axis. So, say we had a global variable called “Selected.” When we click, we are not actually clicking on two static objects, we are clicking on objects which are constantly refreshing themselves, re-drawing themselves in the correct order. Game maker will run the draw function of the object which is higher on the Y first. So actually, when we click: …first we are clicking on the table, which is being drawn first… Selected = ID of the Table …and next we are clicking on the guest, which is being drawn next (still the same click as before) … Selected = ID of the Guest The global variable has just been over-written. For a fraction of a second, you DID click on the table, but that was quickly overwritten just as fast as the guest was then drawn in-front. It’s pixel perfect, and works every time. drink The above system is one of several which I’ve had to hammer-out in order to get the basic frame-work of this game up and running (another ongoing nightmare for a newbie programmer like me being good dynamic path-finding). Most of my development time so far has been taken up problem solving and studying for these systems, with a lot of the more simple visible features actually being very quick to implement. It is certainly not linear when it comes to what is actually visible for the player. What I’m hoping to see happen during the rest of this year is now a shift more towards development of game-play that relies on already established mechanics and systems being extended, fleshed out, adjusted, rather than building much from scratch. Especially once the conversation system is up and running properly, I will be able to pour lots of time into writing up stories and guest-tweets, adding content at a decent pace. That should be a lot of fun and I’m really looking forward to it. Towards the end of the year and running into next though, I do foresee a pretty long stretch of more painful coding once again for making the game capable of handling all sorts of monitors and machines, fixing weird bugs and so on. That’s enough writing for this time. Let me just say thank you to everybody who has been following me on Twitter, or watching my YouTube development diaries. I’m very excited that in the not too distant future I could have people playing and enjoying this game. I have a lot of ideas written down that are slowly turning into real content day by day, and now and then I’d like to write a few more posts about what these are and how they are being implemented.

Liked it? Take a second to support Daniel Burke on Patreon!

Comments are closed.