Water drums with big hands?

hand

Okay, so the post title does sound a little weird. But it’s not. Actually, it’s really simple. What is the biggest problem with designing video games? For me personally, it is making sure that it looks exactly the way you want it too on any sized screen.

In my new water drum game, I’ve drawn a cool little hand for the mouse cursor. It is 200 x 200 pixels. On my 1280×1024 screen, that seems appropriate for this game. However, when I want to show it to my buddy who only has a 1920×1600, the size of the hand becomes disproportionately smaller than it was on my screen. On his screen it is only 1.3% of the screen size, but on mine it is doubled at 2.8% of the screen size.

What if someone tried to play the game on a 640×480 screen? Well, aside from consoling them for having such a tiny, poor resolution screen, the game could actually get a bit difficult to play as the mouse pointer would take up nearly 14%  (almost 1/6th) of the screen! What can you do? Then your game would have to be purposely designed for each screen size.

There are several options.

  1. Purposely build and distribute the game for a set screen size. You would then need to name that size as a dependency of minimum resolution, and use a window for larger screens.
  2. Build your game to check for common screen resolutions, and have multiple artwork and if/then statements to check which ones to use. (I’ve done this in the past with some Android games.)
  3. Write code to scale your images to be appropriate for the screen resolution.
  4. Some combination of the above. (My Dad always picked “D – all of the above” if it was an option, so I’m putting it in here.)

Each of these has drawbacks as well as perks. If you do the first choice, your game will look absolutely best on that resolution. This can be limiting to those with lower res systems, or will limit you to make a low res game that will look bad or tiny on hi res systems if in a window.

If you do the second, you will increase the size of your application by including bigger and better art for each resolution. Also, this gets tedious. What if you change the player icon? You now have to change it 18 times to support the change across each resolution, because currently there are 18 different “HD” resolutions. Not to mention lower res systems, which you probably just wont support anyways.

If you try the third, that of code to scale your graphics, it will always look proportional on any resolution. However, scaling may make the art look pixelated or stretched if scaled too far. Generally, this is the easiest, but may cause graphical quality problems.

I tend to think that most big games (at least on Android) use a combination of both. You select a few common screen sizes and make art for those, scaling anything in between with the closest matching resolution art.

What did I choose? Well, I chose to scale my art. I’m not too worried about quality of the art, since I drew it in a few minutes with pretty low quality to begin with. Here’s how I set the scale:

// Hide the mouse pointer and replace it with drum hand
window.setMouseCursorVisible(false);
Texture textureDrumHand;
textureDrumHand.loadFromFile(“graphics/hand.png”);
Sprite spriteDrumHand;
spriteDrumHand.setTexture(textureDrumHand);
spriteDrumHand.setOrigin(100, 100);
spriteDrumHand.setScale((resolution.x*.04)/200,(resolution.x*.04)/200);

By making the size of the hand a math problem derived from the resolution, it *should* be right on different sized screens. In theory, it should always be about 4% of the screen width. It is 4% of screen width divided by 200 (the size of the png file), which will make the scale, which is multiplied by the actual size of the png file. Mathematically, that means we can take out the divide by 200 and multiply by 200, so it will remain as 4% of the screen resolution!

Similarly, I did the same for the water drums themselves, except I want them to be 20% of the screen resolution width:

// And the water drums themselves.
Sprite spriteWD1;
spriteWD1.setTexture(textureWaterDrum_still);
spriteWD1.setPosition(resolution.x/15*2,resolution.y/7*2);
spriteWD1.setScale((resolution.x*.2)/500,(resolution.x*.2)/500);

Again, the formula is simple: (resolution.x * {desired percentage}) / {original art size in pixels}. This way it will always be a uniform size no mater what screen resolution you use!

You can check out the full commit on my GitLab if you like!

Linux – keep it simple.

Critical Velocity – an open source libGDX game that was crashing on small screens is now fixed!

Critical Velocity is a fun little open source arcade style scrolling game that I made after finishing my Android developer class. It was my first released game using the libGDX engine, an open source graphics engine that is great for making games. Due to my inexperience, however, I didn’t take into account that all screens are not created equal. Some screens are bigger than others, and some have lower resolution than the phone I built the game on.

This caused a problem. After releasing the game, I quickly was alerted to the issue through the GitHub issue tracker, where several players let me know that it was crashing on startup with their phones. It took a little bit of time to figure out, but I came to realize that the app was crashing on phones that were smaller than 800×480 screen resolution.

At first I thought there were all sorts of problems with graphic asset sizes, and memory issues. I tried numerous things to fix it, but nothing worked. Finally, I gave up. But, a few days ago, I decided to take another look. By God’s grace, this time I got it figured out! Before, I was working off of the information provided by people with smaller resolution screens, but I decided that I should load up the game in an emulator with a 320×240 resolution screen, and watch the logs. That’s when I found the problem.

The issue didn’t have anything to do with libGDX, the graphic assets, or the amount of memory the game takes (about 15 MB). As it turns out, the math just didn’t add up. Literally!

In the game, the upper and lower walls are separated by a gap. The size of the screen and the size of the gap is used to determine where the next set of walls opening can be (up or down) from the previous wall. The idea was that as you went faster, an opening at the top of the screen followed by an opening at the bottom of the screen is technically impossible to reach, so this math would keep it “near” the other opening within reason, but still allow it to be randomly placed, up or down from the previous one.

The math looked like this:

Place of the next wall opening = randomly based off of (1/2 of the screen height – the size of the gap) from the center of the screen.

This worked great on my test phone that had a screen resolution of 1920×1080. With the standard gap that I set of 450, the math looked like this:

Place of the next wall opening = randomly based off of (960 – 450 = 510) from the center of the screen.

But it didn’t work for a screen as small as 320×240 because:

Place of the next wall opening = randomly based off of (160 – 450 = -290) from the center of the screen.

For a random number, you can have a negative outcome, but you cannot put in a negative input. The random number is based on starting at 0, and create UP TO X random numbers. So if X is negative, then the random number generator doesn’t know what to do!

So I had to invent a way to fix this problem. Fortunately, that was easy. I used an if/then statement to ask what the screen size was, and then changed the gap size to be less than 1/2 of the screen size. I also had to edit a few parameters, such as thrust, speed, and distance also based on screen size. You can check out my code and commits here and here, if you’d like.

Long story short: Don’t make assumptions, check the logs and they WILL lead right to the problem! If I had run the emulator myself before, not having a small screen phone anymore, I hopefully would have seen this right away. Here are some screen shots of the game on different phones. Unfortunately, I still have a lot to learn to make sure that the game LOOKS the same on different sized screens, but at least now it doesn’t crash!

Linux – keep it simple.