Search ObeyBrew.Com

OBEYBREW.COM | Tutorials | A Crash Course In HuC Part 6

A Crash Course In HuC

Part 6 - Of Bonks And Magic Scrolls

Static screens are fine for some kinds of games but for most, you're going to have to do mapped backgrounds. Lucky for you, mapped backgrounds are almost as easy as sprites. Almost.

What You'll Learn

You're gonna learn tiles whether you want to or not. :P Yeah seriously... this one's all about tiles and mapping. There's way more material to cover than sprites though, which is why I saved this for later... hey look, it's now later. :)

Getting Prepped

Okay, now you're going to have to start investing hard drive space in tools and such. We're going to start small... yer basic map editor. For the purposes of this tutorial, I am utilizing mappy, which HuC has built-in support for. It's free to download and runs in Windows and WINE.

mappy website

Since you're going to learn about tiles, having a few tiles kicking around can't hurt, eh? Oh, and I've already made a map for you. Ain't I nice?

Yes I am!

Oh, and be sure you've got the bonkwalk.pcx file floating around somewhere from the previous tutorial, as we'll reuse it here. We won't be reusing the old code though... we're starting anew mostly... keep the old code around though, as we'll copy a few things out of it.

Get This Party Started

Start up a new source the same way you've done before, and be sure to include the bonkwalk.pcx sprites. Let's keep calling it bonk so we don't get confused. Now, we're going to add a few new pseudoinstructions. Our first one is going to be loading the map we're about to use. It's a single pseudoinstruction:
#incbin(levelmap, "tut6map.fmp");
incbin is like a general purpose pseudoinstruction... normally, it will just load any old file unmodified into the program. However, when we give it an .FMP file, HuC says 'hey lookie here, a map file... just gimme map data mmmkay kthx'. A normal .FMP map file also contains tile graphics and we can't use the ones it stores anyway. So for future reference, remember... incbin will load a file unmodified unless it's an .FMP map file, in which case it only loads the map data. Dig?

Now, we need to load our tiles. Remember the last tutorial where we used incchr? Well, there's another version of that called incchr_ex and we're going to use it here. It's going to be a little weird-looking and it takes quite a few arguments.
#incchr_ex(leveltiles, "tut6pal0.pcx", 0, 0, 69, 2, 0,\
			"tut6pal1.pcx", 0, 0, 51, 1, 1};
Yeah, that's a lot to chew on. You should be able to figure out the first two arguments on your own. From there, we give the starting X and Y coordinates of the image file. Next comes the columns and rows. Finally, we assign a palette number to these tiles. And then... the backslash at the end of the first line means keep going on the next line. Fortunately, we don't have to give it any new names... we just tell it to keep loading tiles. You can have up to 16 lines of tiles like this.

Ok so that was a handful! What's next? Well, we have a map and tiles but we need palette data too. You already know how to use incpal... name them levelpal0 and levelpal1.

We're going to set up a few variables now but we're not going to do it inside of main. And why not? Because they're going to be globals... meaning they will be accessible from anywhere in the program. Now, some of you more experienced C coders will be moaning and groaning over this, as it's traditional wisdom in C coding to avoid using globals unless necessary. However, in HuC land, globals are not only faster than local variables, they also tend to be more reliable. To make a global variable, we simply define it outside of a function. It's that simple.

So then... we make one int variable... call it bonkx. Then, we're going to need a couple of char variables. As in the prior tutorial, we can call them tics and frame. As you've probably noticed from the source listings and whatnot from prior tutorials, you can put multiple variables of the same type on one line to save yourself some unneeded extra typing. Pretty darn convenient. Also, as a general rule, try to use char whenever possible, as it's the machine's native type and is a lil faster than int. Use int when you need a larger range of numbers, as char can only be 0 to 255 but int can be anywhere from -32768 to 32767.

Care To Comment?

Okay, we should probably hit an important detail at this point. Experienced C coders can skip this part but for you newcomers, pay 'tension. As you may have noticed, your programs are starting to get long. When your program gets big and contains a lot of functions and doodads, it can become easy to forget what stuff does. We're only human after all, so we need to be able to give ourselves lil reminders here and there. In coder jargon these are called comments and they look a bit like this:
/* this is a comment */

/* this is a multi-
 line comment */
Now, for you C gurus out there who are reading this section for the fudge of it or to find technical errors, nitpicks, or hoohaw plugs, you know that in most C compilers, C++-style comments are often allowed in C program source. However, HuC doesn't know wtf to do with C++-style comments so don't use them; only use the C-style comment lines and HuC will be a happy bugger. Anyway, remember that lil detail about comments... it will save your bacon later on when that pesky real-life thing calls you away from your epic game design and you don't get to work on it again for a month... and then you come back and are all like 'wtf does this do, well damn I don't freakin' remember'. The comments you write might just remind you what that code does. Write them a lot and make them all good-like.

Bonk Is Back!

Okay so let's get Bonk going again. The spr_make code from our last tutorials should serve you well here also, so copy n paste it. And again, do what ya need to do to get ole Bonk back on the screen... copy from the last tutorial if you don't remember what to do... the code will be the same here.



Really not much else needs to be stated here... just make sure you remember to load_vram and satb_update so we can see the lil... ok big... guy.

Bonk's World Revisited

Now for the real fun... we're going to create Bonk's world... or a small portion of it, really... remember that sick pseudoinstruction up at the top, incchr_ex? Well, time to put it and the map to good use. The first thing we do is tell HuC that we're gonna be using a map.
set_map_data(levelmap, 64, 28);
This tells HuC 'yo I gots a map, here it is, and this is how big it is'. set_map_data takes three arguments: the map data, the width in tiles, and the height in tiles. So now, we should feed the map some tiles. Here it is:
set_tile_data(leveltiles);
Yep... that's pretty easy to do. So now it knows that we're going to use the data at leveltiles for the tile graphics. We still have to tell it to load the tiles, which we do like this:
load_tile(0x1000);
load_tile loads the graphics into VRAM. We'll stick 'em all at 0x1000, which seems to be like the unwritten standard for level tiles. We just have two more minor details to see to.

Now, we told HuC that we've got a map and we told it how big it is. Now we have to tell HuC's mapper to draw the map. This is also pretty easy to do...
load_map(0, 0, 0, 0, 64, 28);
You can compile it now and see what it looks like... you might see something like this:



Wtf okay... here's the deal: HuC automatically sets up some of the palette colors for writing text. It's kind of foolish but it is what it is. So anyway, what do we do? What are we missing? Oh yeah... background palettes. This time, we're loading two of them, and you should be able to figure out not only how to do this but also which palettes to assign. If you do it right, you will end up seeing this:



That's more like it!

What We Did

Okay, without getting overly technical here... what we've done up to this point is load the entire map into the BAT (the Background Attribute Table). Of course, we can only see about half of it, so we need a way to scroll this map. How convenient for us that there's a function to do that for us, so let's see if we can make the whole map scroll for us with a little demo code...
for(bonkx=0; bonkx<256; bonkx++)
{
	scroll(0, bonkx, 0, 0, 223, 0xc0);
	vsync();
}
If you run that, you'll see bonk slide along the ground a bit and then stop...



So, what did we do? Well, we used the scroll function to... well, scroll the screen. It's really that simple. Okay, it's not that simple... but weve just seen the simplest way of doing it.

scroll has a number of arguments that we need to be aware of. The first one is the 'window', so to speak... in HuC, we can define up to four of these... 0 to 3. We're using the first one here. The second argument is the X location of the virtual window and the third is the Y location of the virtual window. This might seem confusing at first... and it can be. But basically... think of it like this: you have a picture drawn onto a piece of paper. And then, you have a small box frame that you put over it. Now, assume that you are only looking at the part of the picture that you can see in the box frame. If you move the box frame around, you see different parts of the picture. That's exactly what these two arguments of scroll are doing for you... moving that box frame around. Now... the next two arguments are the top and bottom of the scroll... no, these have nothing to do with where the box frame is, but these tell us how tall that box frame is. 0 here tells us start at the very top and 223 tells us end at the very bottom. Technically, 223 isn't the real very bottom, but it's the acceptable bottom for most TVs and we wanna make sure people can see everything right now. The last argument is a weird one... it's a value that tells HuC what to display in this window. When we set it for 0xC0 (this is decimal 192 for you purists out there), it says 'show me both sprites and tiles'. We can turn these off independently by changing the value, but we have to set it to specific values... we can't just throw it numbers willy-nilly and expect to get meaningful results. We can set it to 0x0, 0x40, 0x80, or as we've seen, 0xc0. This is 0, 64, 128, and 192 respectively. 0x0 obviously turns everything off. 0x40 will make it so only sprites are displayed, and 0x80 will make it so only the background is displayed.

Bonk Is Not An Ice-Skater

Okay, so Bonk looks kinda foolish just sliding around the ground as if he's trying to be Gilbert Fuchs. Remember the animation code from the previous tutorial? For kicks, let's add it to this tutorial too. I won't go into much more description as to what we're doing here, as not much else needs to be described. Update the loop with the scroll function in it with this code:
for(bonkx=0; bonkx<256; bonkx++)
{
	scroll(0, bonkx, 0, 0, 223, 0xc0);
	tics++;
	if (tics > 4)
	{
		tics = 0;
		frame++;
		if (frame > 5) frame = 0;
		if (frame == 0) spr_pattern(0x5100);
		if (frame == 1) spr_pattern(0x5200);
		if (frame == 2) spr_pattern(0x5300);
		if (frame == 3) spr_pattern(0x5200);
		if (frame == 4) spr_pattern(0x5100);
		if (frame == 5) spr_pattern(0x5000);
	}
	satb_update();
	vsync();
}
Now he really looks like he's walking through the map!

So... What's Next?

By now, you should understand the basics of scrolling. It might seem as if this tutorial series is moving a little slow at this point but there really is a lot of stuff to cover and you gotta get it all before you get balls-deep into a project and then realize 'wtf I'm lost here!'. A few things you might have noticed... our map is limited to 64 tiles wide, or two screens. Not very impressive! We didn't do any controls here... I'm sure you wanted Bonk to walk manually through the map, right? If you've looked at the graphics, you'll notice that even though we told it to load 69 tiles wide, the image is actually 70 tiles wide and on the second set of tiles, we told it 1 high, but the image is really 2 high. What gives? Well, HuC likes graphics in multiples of 16 pixels. Don't even try to get around this. You'll fail. Even if the additional pixels aren't used, they still have to be part of the image file. The unused graphics will not be compiled into your project, so don't worry... there won't be any wasted space. If you're one of those people who likes to put a one-pixel border around every tile... break that ridiculous habit right now... HuC does not like this and you'll never be able to convince it to like it... it'll just stick its fingers in its ears and say 'nyah nyah I can't hear you!'

So if you've made it this far, you're doing great. Feel free to open the map file in mappy and have at some editing to get the hang of it. Next, we're going to look at a more advanced concept of game design... the state machine. This is an essential building block for many many types of games. Also, we're going to get into just how those other games have maps so big by delving into scroll wrapping. After the next tutorial, you will literally have enough firepower to make a basic scrolling game of your own. So, pull up your asbestos underpants and get ready. :P

Full Program Listing

#include "huc.h"

#incspr(bonk,"bonkwalk.pcx",0,0,2,8);
#incpal(bonkpal,"bonkwalk.pcx");

#incbin(levelmap,"tut6map.fmp");
#incchr_ex(leveltiles,"tut6pal0.pcx",0,0,69,2,0,\
			"tut6pal1.pcx",0,0,51,1,1);

#incpal(levelpal0,"tut6pal0.pcx");
#incpal(levelpal1,"tut6pal1.pcx");

int bonkx;
char tics, frame;

main()
{
	init_satb();
	tics = 0;
	frame = 0;
	bonkx = 104;
	spr_make(0,104,153,0x5100,FLIP_MAS|SIZE_MAS,NO_FLIP|SZ_32x32,0,1);
	load_palette(16,bonkpal,1);
	set_color(256,0);
	load_vram(0x5000,bonk,0x400);
	satb_update();  
	set_map_data(levelmap,64,28);
	set_tile_data(leveltiles);
	load_tile(0x1000);
	load_map(0,0,0,0,64,28);
	load_palette(0,levelpal0,1);
	load_palette(1,levelpal1,1);
	for (bonkx = 0; bonkx < 256; bonkx ++)
	{
		scroll(0,bonkx,0,0,223,0xC0);
		tics++;
		if (tics > 4)
		{
			tics = 0;
			frame++;
			if (frame > 5) frame = 0;
			if (frame == 0) spr_pattern(0x5100);
			if (frame == 1) spr_pattern(0x5200);
			if (frame == 2) spr_pattern(0x5300);
			if (frame == 3) spr_pattern(0x5200);
			if (frame == 4) spr_pattern(0x5100);
			if (frame == 5) spr_pattern(0x5000);
		}
		satb_update();
		vsync();
	}
}

spr_make(spriteno,spritex,spritey,spritepattern,ctrl1,ctrl2,sprpal,sprpri)
int spriteno,spritex,spritey,spritepattern,ctrl1,ctrl2,sprpal,sprpri;
{
	spr_set(spriteno);
	spr_x(spritex);
	spr_y(spritey);
	spr_pattern(spritepattern);
	spr_ctrl(ctrl1,ctrl2);
	spr_pal(sprpal);
	spr_pri(sprpri);
}

See Also

A Crash Course In HuC - Part 7 | incchr_ex | set_map_data | set_tile_data | load_tile | load_map | scroll
This site is ©2013 Eponasoft. Everything is good in moderation. Except moderation.