Category: development

AutoFullScreen and MiniMap scripts for Game Maker 8

I came up with some handy scripts for Game Maker 8 projects.

  • AutoFullScreen(Border_W,Border_H,objFollow):  This script automatically sets the game window to full screen mode, and sizes View0 to the pixel dimensions of the display. If the room is not as large as the display, the room is scaled up to fit the display, and will stretch to fill the display in both the horizontal and vertical dimension, which can result in distort the room if it has a different height:width ratio than the display.
  • AutoFullScreen2(Border_W,Border_H,objFollow):  This script automatically sets the game window to full screen mode, and sizes View0 to the pixel dimensions of the display. If the room is not as large as the display, the room is scaled up to fit the display, but does not get distorted by a different height:width ratio.
  • MiniMap(minimap_width,minimap_height,top,left): Sets up View1 as a minimap view of the room. Takes parameters which set the height and width size of the minimap in pixels, and whether the minimap appears in the top/bottom or left/right corner of the window.
  • MiniMap2(scale_factor,top,left):  Like MiniMap, but with a slightly different argument for sizing the minimap. Instead of sizing the height and width using absolute pixel dimensions, you provide a scaling value. The map will be sized to a proportion of the display size divided by the scale factor (e.g., a scale factor of 8 means you’ll get a minimap that is 1/8 the size of the main window.)

The .gmk file is pretty well documented, contains example rooms with reference implementations of the scripts, and can be downloaded from releases.

AI_targeting debrief

First, who would have thought that one small component of AI behavior for my game would have taken so long to get working?

I was on a good roll, making steady progress on my project for most of December. Then the holidays hit and I couldn’t work on the project as much as I wanted. I had also just started to run into some stuff that was a little tricky (not that it was really hard, just that it was new to me) around this time, so the lack of putting time into it also made me feel nervous that I’d get stuck. There’s no way I’m ever giving up this project until I complete it, and that’s that, but I’ve run into problems in the past with projects where I get stuck, don’t know where to turn, and it sucks a lot. Oftentimes that puts the entire project at risk. But this is a project that I’ll never accept failure on — I’m working on an idea I had 30 years ago, and if it’s been in my head that long, and not gone away, it never will.

So, into January, I had less time than I hoped to get back into the project. When I did, I wanted to make the time productive, so I tended to pick things that I knew I could do, and that needed doing, but not necessarily the thing I’d gotten stuck on. That’s OK, but normally when you see something is going to be hard for you to figure out, you should wade into it and tackle the problem. I didn’t do this with myself, so much as I tried an idea a little bit, and when it didn’t do what I was expecting, I put it aside again and worked on something where I had more traction. I had a fatalistic sense of “When I am ready for this to make sense to me, it will.”

Also, during a lot of this time I was spending a lot of my project time on reading documentation, not coding. It was a struggle to make sense of what I was reading. My mind kept tripping up on something that didn’t make sense to me, and which in the end turned out to be inaccurate (unless I *still* misunderstand something, but I don’t think so). So that wasn’t too helpful.

In the reading that I did, I discovered a lot of things that merited further reading, and had to trace down a lot of avenues that potentially could have led to my solution, but didn’t. This wasn’t wasted time, though, because a lot of that stuff may end up becoming useful later, and having a clue that it’s out there is going to be helpful down the road.

Ultimately, I was able to prevail over my problem, get un-stuck, and deliver a working proof of concept. I need to do some further work to turn this proof of concept into an extension that I can import into any future Game Maker project that I work on, and from there I still need to bring it into my game project. But that’s all academic, and I have no doubt that I will get it done, and so I’m able to confidently declare victory at this point.

My initial attempts to implement the solution I was after focused on doing it directly in the current game project. I’ll call that a mistake now. For one, the existing game already has a lot of stuff in it, and the complexity of it makes it difficult to see (or think about) any new problems clearly. I had several false starts which ended up failing, trying this way.

Eventually, I got to the point where I recognized that what I needed to be able to solve the problem was simplicity. So to get that, I started a new project, and threw into it just enough bare bones to provide me with the building blocks I needed to test out the AI code that I was trying to figure out how to write.

So I did that. Twice. The first time was almost right, the second time was right, at least so far as it went, and I’d figured out enough to know that what I’d built there would work for what I need, but I need to do the rest of it back in the main project. The first attempt help me to figure out what I was doing wrong, or rather, what I needed to do.

So, that exercise was very beneficial. The second attempt only took me about 5-6 hours of hacking away at it to get it to work, which is about par for every other feature that I’ve committed in the project so far. So the fact that it took a few weeks of thinking, procrastinating, reading, and trying various things doesn’t worry me so much. I know the next time I get stuck with a problem like this, I’ll get to the solution that much sooner because I can take this general approach to it.

What was the most useful for me in solving this was the stuff I built into the project to provide me with feedback so I had something to diagnose. I strongly recommend building instrumentation and logging capabilities into whatever code you write. Otherwise, you’re only able to see what you can observe from the outside, which often ain’t much, and is apt to be very confusing when the application is behaving in some bizarre, unexpected way that you can’t figure out based on what you thought your instructions were saying to the compiler or interpreter.

Refactor often

If you dread or fear refactoring, it’s because your code is a big ugly mess.

If your code is a big ugly mess, it’s because you didn’t refactor soon enough or often enough.

To make your code not be a big ugly mess, you need to refactor it.

So, refactor every time you see the opportunity. Keep the code lean and pretty. Then refactoring stays simple and easy and doesn’t take long or hurt.

I say do this even if you feel mounting deadline pressure. In the long run, the gains in maintainability will far outweigh perceived slowdown of productivity. Working on maintainable code is far more productive than working on unmaintainable code.

Don’t ship messy code. Messy code hides many flaws. Flaws that you’ll need to fix sooner or later anyway. When you fix a mess, it’s a lot more work than fixing something that’s clean.

Yummy GameMaker Goodness…

Good technical documentation is a beautiful, beautiful thing. Almost as good as bug-free code.

Ultimate DdD to GML Converter

It occurred to me recently that my Actions could be easier to understand and debug if I refactored to convert as much of it from Drag-and-Drop actions into GML scripts. It’s a LOT easier to understand a script call to “Player_Eats_Fish” than it is to read a dozen or more DnD actions that do “Player Eats Fish” and figure out what they’re doing. Plus, you can call the scripts elsewhere if you need to.

So I started doing this, and realized that there were certain things that I didn’t know how to do. Reading the language reference in the GameMaker Help helped, but at times I still got confused… Then I googled and found this. UDnD2GML does a nice job of converting Drag and Drop actions into valid and correct GML. It’s actually written in GameMaker, too; that’s right, it’s a GameMaker project that converts GameMaker drag-n-drop actions into GML. Very slick! When you need to work out a bit of tricky syntax, or are dealing with some function you don’t use a whole lot, it’s indespensible.

Drag and Drop Icons and Their GML Equivalents for Version 7.0

Basically the same thing, only a big hypertext reference document. I liked it so much, I printed it to PDF format and am keeping it with my other GameMaker docs. Although this is for GML7, there doesn’t seem to be a whole lot that has changed in GML8. Could be a bit clearer in places, and incomplete in some regards (I had to use UDND2GML to figure out how to apply instance_destroy() to other; this document was silent on that point.)

Official GameMaker 8 Help File as a PDF

Not hard to find by any means, but if you don’t have it, it’s the best place to start. A lot nicer than searching through the Help Menu index.

Still more reflections from CodeRetreat

I’ve had some decent response to the posts about CodeRetreat. I had some more thoughts come to me in the days following, mostly on pair programming, but no time to write it until now. Here’s a brain dump:

Assert(Two_Heads>1);

This was my first opportunity to try it out, and I instantly liked pair programming. Much of my frustration in trying to learn how to program has come from trying to learn in isolation, getting stuck, and having no one to go to for help. Pairing means you have someone right there who you can talk to, and who can also catch mistakes that you might not have caught until much later.

Be Likeable

I think pairing is a great idea, but with the caveat that it requires you to like your partner. Establishing rapport is critical. I would say that if you are trying to program with someone and have yet to click on a personal level, it might be a good idea to stop trying to program and start trying to connect. Find some way to do this. Play a game that fosters working together with a common goal, like Jenga. If you can’t connect, then it might well be better to not try to pair up, or even not try to program together at all. Divorce is greater than the sum of its parting.

There’s only so much you can do about your partner, but you can do a lot to be likeable. Don’t focus immediately on the problem, but on the partner first. Smile. Be cheerful and enthusiastic. Remember names. Listen. Avoid negativity. Try out your partner’s ideas. When you think something, explain why you think it. When you want to try something, explain why you want to do it and what you hope to accomplish. Then don’t spend a lot more time talking about it — do it. What works becomes a lot more apparent at runtime than any other time, and the quicker you get there the better.

Assert(IsEqual(Life.Spice, Variety));

That’s not to say that you should never pair up with someone who has a very different style from yours. Very often that is precisely the type of person who you should be looking to pair with, because their differences will be the things that you learn from. Still, someone who you can communicate with easily is a must. You’re not going to enjoy learning from seeing a different style if the person demonstrating it is incapable of explaining it in a way that you can understand. Programming style is not the same thing as personality.

If you don’t get along well, or have very different styles, there’s a risk of ego clashes. Frankly, there’s always going to be a risk of ego clashes. Fortunately, this did not come up at CodeRetreat — the people who attended were there because they wanted to be there, which I think makes a huge difference. If your partner isn’t at least pair-curious, if not outright interested, they’re apt not to be a very good partner.

Swing your partner

Much like… uh… square dancing, pairing is even better if you can do it with more people. Try to find more people to pair with. Put on a snazzy shirt, go to bars, wear a lot of jewelry, buy someone a drink, and ask them if they code. See where that leads.

Good pairings are unequal

Intuition suggests the best pairing combinations are unequal. The value two great programmers contribute to a pair is not as great as the value created by pairing a great programmer with an average programmer. The great-great pair may be more productive, but the great-average pair will produce another great programmer in time.

Pairing isn’t just about mentoring and learning, it’s also about collaboration, error catching, and creativity. But mentoring and learning is a hugely valuable part of it — in most cases, probably more valuable than the resulting product.

Dance like no one’s watching, Code like your repository is /dev/null

Being willing to give things a try is a huge factor to pair success. I think what made CodeRetreat so easygoing was that it was deliberately structured to be coding without consequences. We threw out what we’d written at the end of each session, so there was no goal of getting it done, no stake in one person’s approach “winning”.

In the real world, there are consequences. But if you can, try to structure the project workflow in such a way that you can code as though there were no consequences. Be willing to take risks and experiment. Don’t just stick with what you know; grow.

If the established code base is getting in the way of this, don’t be afraid to cut it. From working on numerous development projects over the past five years, I’ve learned that the actual coding takes hardly any time at all, relative to the rest of the project. Requirements gathering, design, and testing all take considerably more time in my shop. Therefore, starting over rewriting code should not be intimidating.

In fact, going back over 20 years, to the bad old days when operating systems didn’t have protected memory and applications didn’t have automatic data recovery, in every instance I can think of when I was writing something and lost it, whether it was prose or code, the re-write was always faster and the results better. So don’t be afraid to take a clean-slate approach at times.

Start over

If CodeRetreat had taught me only one thing, it would have been that it’s not just OK to start over sometimes, sometimes it’s quite helpful. Too often, though, we attach value to code that has already been written and are reluctant to throw it out. Even if it was written as a short term stopgap, if it basically works no one wants to let you throw it out. “Reusable” code is something of a mantra in a lot of shops, as well it should — rewriting the same thing over and over again sucks. But if it wasn’t the right code to begin with, reusing code is about as appealing as reusing toilet paper. Before you try to re-use code, be sure that it is in fact re-usable.

Addendum to Things I learned at CodeRetreat

The most memorable moment for me was in the afternoon, when I spoiled a rant that Mark was about to launch into about a perceived problem he had with TDD “forcing” the programmer to code improperly by requiring them to expose inner members of classes that should properly be kept private.

Instantly, I blurted out, “So write the test method inside your class!” It was kindof a “from the mouth of babes” moment. I barely even thought before the words came out of my mouth. I wasn’t even sure if that was the right answer or not, but I guess it must have been, because the look on Mark’s face was priceless.

There were so many messages in that look: 1) that I was right with my guess; 2) that Mark had been stuck on this mental error for some time; 3) that it might’ve humbled him a little bit that he hadn’t seen it before.

I don’t mean to embarrass him by recounting this — if I thought this would, I’d never be using his real name. It wasn’t that I’d really shown him up — he’s got way more experience than I do, plenty of success under his belt, and he’s someone I like personally as well as look up to. But that was the first moment I felt like I was capable of making a tangible (if relatively tiny) contribution, that it wasn’t just a one-way knowledge transfer from the more experienced programmers at the event to me.

What made it a moment I’ll never forget was that I saw that I wasn’t the only person who gets mentally stuck in my own problems when it comes to programming. If a guy like Mark can still make a mistake like that once in a while, it makes me feel a lot better about doing it too, now and then.

But the bigger lesson is, it totally reinforced the value that we all get from being in the same room, talking to each other, making mistakes in front of each other, and learning from each other what lessons we might. With enough eyes, all bugs become shallow.

Things I learned at CodeRetreat

I went to a local CodeRetreat event hosted at Lean Dog, an agile development studio in downtown Cleveland. I had a great time, and learned a few things. There were a great many more things there to learn than I was able to learn, but that just means I’ll have an equally great time at the next one.

We broke the day up into 5 sessions, working in pairs on a test-driven development exercise. At the end of each exercise we deleted what we coded and paired with someone else and repeated the exercise. It was interesting to see both how different pairs would approach the problem, and how I approached the problem differently with each successive pairing. One of the interesting things I got out of this was how my own thought process and approach to solving the problem improved each time. Even though just 45 minutes had passed, each successive attempt was markedly better than the last. I’m intrigued by this and will be less reluctant to throw out code and start over in the future. I also gained greater appreciation for how important it is to pick good names for your variables, objects, properties, and methods. Good names really make it easier to think clearly about both the problem and your code.

I’ve heard of, and seen examples of, test driven development before, but never done it myself. I got an introduction to NUnit, which I’ll definitely be making greater use of in the future, courtesy of Mark W. Schumann of criticalresults.com. I got to pair with Jeff “cheezy” Morgan, who demonstrated some very keen insights on the essence of TDD, and gave me a little exposure to the Ruby language.

I’m really happy I went, and can’t wait to find more events like this to take part in. Lots of thanks to Corey Haines and everyone at Lean Dog for putting the event together.

GameMaker community forum gripe

Ok, I realize that GameMaker isn’t a hard core developer environment. It does serve a purpose though: to provide a more accessible means for aspiring game designers who are not primarily programmers to be able to give life to their ideas. This is a truly noble purpose, in my view and I will never say anything bad about GameMaker’s technical limitations as long as they’re limitations which must exist in order to achieve that goal.

That said, I am really starting to get frustrated with the GameMaker community. Not its members, mind you — the handful of interactions that I’ve had with GamMaker devs have been helpful and positive. But what is really driving me nuts in the community forums is the way people will post links to filehosting sites, and the link is dead because it’s a few months later. This is totally counterproductive to the goal of making a game dev environment that’s welcoming and forgiving to newbie programmers and non-techie types.

The forum thread is there, the tantalizing discussion about how awesome a solution is to just the exact problem I needed to solve is there, but the .gmk or .gex file that would make my day is gone. And not enough discussion around the gaping hole to figure out what the solution was that was demonstrated in the downloadable file.

But it’s not just an everyday occurrence. It’s damn near mandatory. And super frustrating.

.gmk files *usually* take up very little space, and if you don’t need all the resources embedded to create a full-fledged game, you can make *extremely* lean .gmk files that can provide a reference implementation of your nifty solution…. AND, we live in an age where 2TB hard drives cost under $100.

So, WTF? The GameMaker community would be so much better serving its users if it provided permanent hosting space so that discussion threads could remain relevant.

I really don’t understand it, I mean if you go to yoyogames they have graphic and sound resource packs that you can download. They’re of poor quality, and searching for anything in them is atrocious, but they at least have links that still point to extant resources. They even host all the games that people submit to the site.

Clearly they have the technology, why are they not using it in this way?

Fixed that bug

Haha, I figured out what was causing that bug I ran into the other day.

While reading the GameMaker documentation, I read that the order in which objects are drawn is determined by their Depth, and inferred that it is not just the *drawing* order but the construction order when a new room is instantiated. (In GameMaker, Depth is a property that all Objects have, which helps determined which Object is drawn “on top” when their sprites overlap.)

I happened to have my Fish objects at a higher depth than the Player, because if the Player was overlapping the Fish, I wanted the Player to be “on top” so that the Player would always be visible. As an unintended consequence, the Fish objects got drawn and created first, and since Fish.size depends on the value of Player.size, and so were throwing exceptions when the Player did not yet exist.

For some reason this bug was always latent in my game project, but it only manifested after I added a Background to the Room, which is a bit odd. I am guessing that a Background has an extremely high depth so that it appears “behind” everything else in the room, but why it seems to trigger the creation order bug is mysterious to me.

What’s more, I am also confused as to why one of the things I tried to prevent the error did not work. In the Fish.Create() event, I added a check to make sure that the Player object exists before assigning a value to Fish.size that is based on Player.size. If !Player.exists, Fish.size is set to a safe value that is not based on Player.size. Yet, for some reason, the check was successfully able to determine that Player existed for purposes of Player.exists() check, but yet was not able to get the value of Player.size in order to calculate a randomized value for Fish.size. Apparently, the Player object exists at this point but its size attribute does not, or perhaps contains an undefined value.

Figuring this out was rather tricky and it never would have occurred to me had I not been thinking about this bug when I happened to be reading about how the Depth property determines drawing order. Hopefully this writeup will help some other GameMaker developer avoid making a similar mistake.

Developing in GameMaker

One of the first things I did with my gmk project is figure out how to do file I/O so I can write out to an event log so I can see what’s going on in the game. This is highly useful, since it allows me to track every action that happens, every value of every variable if I want to.

Once I figured out how to write a file I/O routine in gamemaker language, I put logging on a bunch of things in the game that I wanted to track so I could see it working and make sure that it was working the way I wanted it to. Mostly this was a check to verify that the way I understood the environment to work was correct.

After I’d written a logging routine on just about everything I’d built so far, I discovered that you can call scripts. So I got excited by the prospect of being able to write a reusable script that would save me from having to write out the same code again and again, like I’d just did. So I built my script, proved it worked, then went and ripped out all that redundant work that I’d just put in. I didn’t mind at all because it meant moving forward that I’ll have a lot easier time with building up code, and I’d discovered it way early in the project.

(I probably could have learned it even earlier had I read the manual cover to cover before starting, but I find it easier to engage with something if I play with it as I learn and read as I play with it whenever I get stuck.)

###

The next interesting thing I did was a feature I wanted to implement early. Trying to do the early stuff the right way is important, but you can’t second-guess yourself so much that you don’t make progress at all because you can’t decide what approach is truly best.

The feature in question has to do with the graphics. I wanted a way to graphically show the relative strength of the enemies to the player. Basically, we have a “strength” variable, and if you compare it to the player’s current strength, an enemy is either weaker, equal, or stronger than the player. I want to make this apparent to the player visually.

On my first development iteration, I simply implemented a separate object for weaker, equal, and stronger enemies, which all inherited from a base enemy class.

On my second development iteration, I figured out that I could have a single enemy class, and for the initial value of the strength variable, I can just assign a random number, and then based on this random number, compare it to the strength value of the player, and assign the appropriate graphic to the enemy. I got this to work pretty easily, and it eliminated the need for having separate objects to represent the weaker, equal, and stronger variant enemies. This might be a mistake, as it might make things more complicated to have a single object doing all three things. But I think it is the right way for now.

On my third development iteration, I needed to make the enemies update their graphics. The player’s strength increases during play, so as the player’s strength changes, the enemy’s relative strength might change from stronger to equal, and finally to weaker. I wanted to figure out a way to do this efficiently, and figured that having an event that fires whenever the player’s strength changes would be a good solution, and the “right way” to do it. I haven’t been able to figure out how to do this in GameMaker yet, however. It’s not an easy thing to do, and probably requires a bit more understanding of GML than I have at present. GameMaker handles instances of objects in a kindof kludgy way, and this quirk in the way the language works makes it harder for me to do what I want to do.

After trying to figure it out for about 4-5 hours, and not getting anywhere, I opted to go for something cruder but simpler, and found that it works just fine, at least for now. Rather than trying to fire an event that checks the relative strength of every existing enemy in the current room whenever the player’s strength changes, I simply created an event for the enemy class which checks its strength relative to the player each and every step of the game engine, and updates the enemy’s graphic. Obviously, this would not scale as well (I’m basically “polling” the objects 30 times/second, instead of simply calling a method as needed, which would be a much more efficient way of doing it.) So this approach may not work out so well if the game gets larger. But at the scale I’m currently working at, it performs just fine. I’ll have to revisit at some point if I have too many objects in play, but I’m not going to worry about that for now.