Search ObeyBrew.Com

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

A Crash Course In HuC

Part 4 - Playing With Yourself

That's about the most politically incorrect title I could choose for this one... but it got your attention, didn't it? :P

What You'll Learn

This tutorial's main purpose is to introduce another extremely important detail of coding with HuC (or any system, really)... user interaction

In this tutorial, we'll explore using the gamepad, dive into some background graphics, and introduce very important pseudoinstructions that will really let you show your stuff.

Getting Prepped

You're not going to the ball but there's one thing you'll need in preparation for this tutorial. Since we are going to start using external files, you're going to need... some external files. Luckily for you, I have some right here. Go ahead, take 'em.

Download The Files

Also, take a stroll over to Useful Source Code and find spr_make. Get ready to copy n paste!

Setting Up Our Program

Unzip the files and put them in the same place as your new source code. We'll be including them into this new program. Start up as usual, but this time, we're going to be adding some new stuff. Put the following lines before your main():
#incspr(bonk, "bonk.pcx", 0, 0, 2, 2);
#incpal(bonkpal, "bonk.pcx");
These are called pseudoinstructions. incspr is a way of including sprite data into your program without having to type out all those numbers. The first value is the filename. The next two values are the X and Y pixel positions inside the image. Since we're starting from the top left, we set these to 0 and 0. The next two aren't so obvious... these are the number of sprite blocks to copy. bonk.pcx is 32x32 pixels in size. Each sprite block is 16x16 pixels in size so we need 2 across and 2 down. Easy stuff! And next, we have incpal, which loads the palette data from bonk.pcx without having to type them in manually. You will use these two pseudoinstructions a lot. So now, we're going to do something a bit more fancy, using more pseudoinstructions. After your bonk lines, add these lines:
#incchr(scene_chr, "scene.pcx");
#incpal(scene_pal, "scene.pcx");
#incbat(scene_bat, "scene.pcx", 0x1000, 32, 28);
incchr will load all of the pixels in the given image as tile graphics. Yes! Background graphics at last. You already know what incpal does. incbat is sort of like a map... it will load values for the background attribute table or BAT for short. These three lines are going to be needed for a special function call later in the code.

Help Me Out Here!

Next, let's copy that spr_make function into our source. This is a useful utility function that lets us set up sprites a lot easier than usual. Just copy and paste it into your source code outside of your main function. Put it after it, before it, wherever... as long as it's not between the opening and closing brackets.

Gimme Bonk!

Let's get Bonk on the screen. We already have the sprite loaded above, so let's get him up here. Using the spr_make function will make this a lot easier on us. In your main function:
init_satb();
spr_make(0, 104, 144, 0x5000, FLIP_MAS|SIZE_MAS, NO_FLIP|SZ_32x32, 0, 1);
You know init_satb already. The next line is our spr_make mega-line. It contains all the information we need to set up the sprite in one line of code, saving lots of typing. Now, let's get Bonk displayed.
load_palette(16, bonkpal, 1);
load_vram(0x5000, bonk, 0x100);
satb_update();


That was ridiculously easy.

The World Around Us

Now, let's put Bonk in his own little world... literally. Those three pseudoinstructions from earlier will now be useful... and we'll only need a single line of code.
load_background(scene_chr, scene_pal, scene_bat, 32, 28);
load_background is a handy little function, or at least it can be... don't get too used to it as its limitations will become apparent really quickly. Anyway, we give it the graphics, the palette, and the bat map... and then tell it how many character across (32) and how many characters down (28). And voila... Bonk's in his world!

Bonk Is High!

There's no 'shrooms in Bonk's world but he's a bit high... I mean he's hovering in mid-air 9 pixels too tall. You can either modify the spr_make line or use spr_set and spr_y to adjust his position... either way works.

So Logical

Now that Bonk's in his world, it's time to make him move around. The first thing we're going to need to do is create two new int variables. call them j1 and bonkx. Now, assign a value of 104 to bonkx.

Now, we are going to create a new kind of for loop called an infinite loop. Infinite loops are... as they say... infinite... as in never-ending... like an American soap opera. Put this after your load_background line:
for(;;)
{

}
There you go... a complete infinite loop. If you run this, you'll notice... that nothing actually changed. That's ok. The loop is set up and that's the important part. Now's where we get funky. Just inside your loop, add the following lines of code:
vsync();
j1 = joy(0);
You know what vsync does... for the most part. As we saw in the previous tutorial, it can slow down the program. But its real purpose is to wait for the vertical blanking period, or VBL. Without an argument, it's the same as doing:
vsync(1);
This locks your program at 60 frames per second and ensures that everything is synchronized properly... nothing runs too fast.

Now... joy is a new one. This tiny little three-letter function call is the heart of your interaction scheme. In this case, we are reading the status of joypad 0 (or player 1) and storing its value in the variable j1. You'll see why we call it j1 later on.

So now that we have the value of the joypad, what do we do with it? Well, let's use it to move Bonk around. Let's add some lines of code to do this.
if(j1 & JOY_LEFT)
{
	bonkx--;
}
if(j1 & JOY_RGHT)
{
	bonkx++;
}
Welcome to Logic 101... the if comparison. This is as it says it is... if this is so, then do this. We're using a little bit of logical shorthand here so this is probably not the best way to introduce if but bear with me for now. Now, bonkx as you can probably tell is holding the X position of Bonk. In coding, it's helpful to use variable names that are relevant so you don't get confused. So what do we do with this? I'm sure you can figure it out by now...
spr_x(bonkx);
satb_update();
Note that I did not use spr_set... spr_make does this already and since we're not changing sprites at all, we don't have to do it again. Compile and run... you can now move Bonk left and right.

Mirror Mirror On The Wall

Ok so yeah, he moves back and forth but he never changes direction. Let's fix that. Since we set up the if blocks as... well, as blocks, we can add more code into them. Inside the JOY_LEFT if block, add this:
spr_ctrl(FLIP_X_MASK, FLIP_X);
and a similar line in the JOY_RGHT if block:
spr_ctrl(FLIP_X_MASK, NO_FLIP_X);
Now, Bonk will face in the direction he's moving in.

Exit Stage Left

You'll probably notice that if you move too far to the left or right, Bonk will go off the screen. Well, using a little if logic, we can prevent that from happening. Replace this line
bonkx--;
with this line
if(bonkx>-8) bonkx--;
and this line
bonkx++;
with this line
if(bonkx<232) bonkx++;
and now he can't go off the screen. What we're doing here is saying 'hey, is Bonk higher than -8 pixels? If so move him left... but not otherwise.' and something similar for the right.

So... What's Next?

You've now experienced everything you need to make a simple game. We've covered all the basics, from variables to user input. From here, we'll build on these concepts with more detail. The next tutorial will cover more input stuff, and I might even break into the animation vault. Be sure to save this source, as we'll be building on it in Tutorial 5.

Full Program Listing

#include "huc.h"

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

#incchr(scene_chr,"scene.pcx");
#incpal(scene_pal,"scene.pcx");
#incbat(scene_bat,"scene.pcx",0x1000,32,28);

main()
{
	int j1, bonkx;
	bonkx = 104;
	init_satb();
	spr_make(0,104,153,0x5000,FLIP_MAS|SIZE_MAS,NO_FLIP|SZ_32x32,0,1);
	load_palette(16,bonkpal,1);
	load_vram(0x5000,bonk,0x100);
	satb_update();
	load_background(scene_chr,scene_pal,scene_bat,32,28);
	for(;;)
	{
		vsync();
		j1 = joy(0);
		if (j1 & JOY_LEFT)
		{
			spr_ctrl(FLIP_X_MASK,FLIP_X);
			if (bonkx > -8) bonkx--;
		}
		if (j1 & JOY_RGHT)
		{
			spr_ctrl(FLIP_X_MASK,NO_FLIP_X);
			if (bonkx < 232) bonkx++;
		}
		spr_x(bonkx);
		satb_update();
	}
}

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 5 | incspr | incpal | incchr | incbat | load_background | joy | Useful Source Code
This site is ©2013 Eponasoft. This site is made in Dubley, eh?