Vectors. Vectors Everywhere.

Defenderoids, nothing more than a name really. But lets see where that goes… So, last time, we left our little bitmap engine creating a glorified star field, to not especially great effect.

Time to add some pizazz.

Vectors are great, but they are very mathematically intense, especially on a system without functional floating points like the poor old NGPC.

To start with then, we need to think about what constitutes a vector, and how to model that in code in such a way that we can work on it in integer maths.

A vector is, to all intents and purposes, a line. It has an origin, a direction and a length. It could also be represented as a start and end point if we prefer.

But a line isn’t very interesting, and probably not what you or I think about when we imagine vectors. I want shapes. Triangles, Squares, Asteroids, SPACESHIPS. For that, we need a list of Vectors, and a way to join them up.

So, what does that mean in practise? If we think of that as a series of points and each line is a vector from the last point to the next, then we can envisage how we might draw the five lines in sequence from that list like so:

XY
030
2020
300
4010
100
030

So, five vectors, needs six points – as I said, in this case it’s easier to think of the vectors as having fixed start and end points rather than trying to work backwards from a direction and length). Each vector hops from one point to the next in sequence – vector 1 from (0,30) to (20,20), vector 2 from (20,20) to (30,0) right the way through to vector 5 which joins the shape up by going from (10,0) back to (0,30). (remember that our bitmap origin in the NGPC is the Top Left corner, so all points are relative to that. There’s no particular need to join it up I suppose, but it wouldn’t be a very good asteroid if it wasn’t closed now would it?

Luckily, as well as the humble SetPixel() our NGPC C Library contains a handy function to draw a line on a bitmap – DrawLine(). So, enough theory for now – lets see how that works if we inject that into our bitmap code for the above example.

// Create the bitmap
CreateBitmap((u16*)bmpLogo, 144, 112);
CopyBitmap((u16*)bmpLogo, bgTileBase);

// Copy the tiles into the screen RAM
iTile=0;
for (iLoopY=0;iLoopY<14;iLoopY++)
{
	for (iLoopX=0;iLoopX<18;iLoopX++)
	{
		PutTile(SCR_1_PLANE, 0, 1 + iLoopX, 1 + iLoopY, bgTileBase+iTile);
		iTile++;
	}
}

// Draw the lines
DrawLine((u16*)bmpLogo,0,30,20,20,1);
DrawLine((u16*)bmpLogo,20,20,30,40,1);
DrawLine((u16*)bmpLogo,30,40,40,10,1);
DrawLine((u16*)bmpLogo,40,10,10,0,1);
DrawLine((u16*)bmpLogo,10,0,0,30,1);

// Then copy the bitmap back into tile memory...
CopyBitmap((u16*)bmpLogo, bgTileBase);
Voila, our first “vector” object

So far so good, but not very flexible. We need to step back a bit from the DrawLine() functions and define a couple of structures that can represent our shapes.

typedef struct Point
{
	s16 x;
	s16 y;
} POINT;

typedef struct ColourPoint
{
	s16 x;
	s16 y;
	u8 colour;
} COLOURPOINT;

typedef struct VectorObject
{
	POINT Origin;
	POINT Position;
	u8 Points;
	COLOURPOINT VectorList[128];
	u8 Scale;
	s8 RotationAngle;
} VECTOROBJECT;

First, we need a “point” structure – I use two slightly different ones both with and without a colour attribute. I could probably use the COLOURPOINT for both purposes and just ignore the Colour, but I prefer to keep things focused.

The main part of the VECTOROBJECT structure is the list of COLOURPOINTS – I’ve coded this to a maximum of 128 points because we can’t have open ended arrays within a structure. There are then a few additional attributes around this big list of points (as in our example basically) which will be used to define the position of the object within our bitmap, the scale, and the rotation angle – more on that in a minute. As we move on, we’ll extend this to include behavioural attributes for the object such as speed, direction and rotation. Lets look at our example again but this time using the above structure.

// Create the bitmap
CreateBitmap((u16*)bmpLogo, 144, 112);
CopyBitmap((u16*)bmpLogo, bgTileBase);

// Copy the tiles into the screen RAM
iTile=0;
for (iLoopY=0;iLoopY<14;iLoopY++)
{
	for (iLoopX=0;iLoopX<18;iLoopX++)
	{
		PutTile(SCR_1_PLANE, 0, 1 + iLoopX, 1 + iLoopY, bgTileBase+iTile);
		iTile++;
	}
}

myVectorObject.Points=5;
myVectorObject.VectorList[0].x = 0;
myVectorObject.VectorList[0].y = 30;
myVectorObject.VectorList[0].colour = 1;
myVectorObject.VectorList[1].x = 20;
myVectorObject.VectorList[1].y = 20;
myVectorObject.VectorList[1].colour = 1;
myVectorObject.VectorList[2].x = 30;
myVectorObject.VectorList[2].y = 40;
myVectorObject.VectorList[2].colour = 1;
myVectorObject.VectorList[3].x = 40;
myVectorObject.VectorList[3].y = 10;
myVectorObject.VectorList[3].colour = 1;
myVectorObject.VectorList[4].x = 10;
myVectorObject.VectorList[4].y = 0;
myVectorObject.VectorList[4].colour = 1;
myVectorObject.VectorList[5].x = 0;
myVectorObject.VectorList[5].y = 30;
myVectorObject.VectorList[5].colour = 1;

myStartPoint = myVectorObject.VectorList[0];
for (iLoop=1;iLoop<=myVectorObject.Points;iLoop++)
{
	DrawLine((u16*)bmpLogo,(u8)myStartPoint.x,myStartPoint.y,(u8)myVectorObject.VectorList[iLoop].x,(u8)myVectorObject.VectorList[iLoop].y,(u8)myVectorObject.VectorList[iLoop].colour);
	myStartPoint = myVectorObject.VectorList[iLoop];
}

// Then copy the bitmap back into tile memory...
CopyBitmap((u16*)bmpLogo, bgTileBase);

If we run that, then we should get the same shape again – just with more lines of code (and yes, I know that the constructor can be simplified, at the expense of readability). But, crucially, we’ve now abstracted the shape definition away from the drawing – and we can now further abstract that to do more stuff. Remember we had a couple of attributes in the VECTOROBJECT structure for rotation and scale. After all, if they were just going to stay the same size and face in one direction, a sprite would be much easier.

I won’t dwell on the maths too much – there are far better places to go for that if you are interested – I’d recommend Ian Bell’s page “Maths for programmers” – it’s just brilliant. And yes, it is that Ian Bell.

So, the two things we want to do most will be to scale the shape – I won’t dwell on that, as essentially all we have to do is “multiply” the .x and .y coordinates by our scaling factor. For speed reasons, I will always try and use the boolean left and right shift operators for multiplication and division where it makes sense, so multiplying by factors of two.

Rotation is a bit more complex, and needs three things:

  • a point of origin, that we are going to rotate our object around
  • a rotation angle
  • Sine and Cosine functions.

The basic method to rotate a point is simply:

  1. Offset the point by the origin
  2. Transform the x-coordinate as point.x * Cosine(angle) – point.y * Sine(angle)
  3. Transform the y-coordinate as point.x * Sine(angle) + point.y * Cosine(angle)
  4. Offset the rotated point back to it’s relative position from the origin

So, putting both of those together, and further abstracting our “drawing” routine into a function gives us:

void DrawVectorObject(u16 * BitmapAddress, VECTOROBJECT VectorObject)
{
	s16 iStartX;
	s16 iStartY;
	s16 iEndX;
	s16 iEndY;
	s16 iTempX;
	s16 iTempY;
	u8 iPoint = 0;
	s8 cSin;
	s8 cCos;
	
	cSin = Sin(VectorObject.RotationAngle);
	cCos = Cos(VectorObject.RotationAngle);

	iStartX = VectorObject.VectorList[0].x;
	iStartY = VectorObject.VectorList[0].y;

	while (iPoint++<VectorObject.Points)
	{

		// Modifier here is to find the centre of rotation
		iStartX = (iStartX*VectorObject.Scale)-VectorObject.Origin.x;
		iStartY = (iStartY*VectorObject.Scale)-VectorObject.Origin.y;

		// rotate point.
		iTempX = ((iStartX * cCos)>>7) - ((iStartY * cSin)>>7);
		iTempY = ((iStartX * cSin)>>7) + ((iStartY * cCos)>>7);

		// translate point back to it's original position:
		// Modify back from the centre of rotation above, and then add the X & Y co-ordinates
		iStartX = (VectorObject.Position.x>>7)+iTempX+VectorObject.Origin.x;
		iStartY = (VectorObject.Position.y>>7)+iTempY+VectorObject.Origin.y;

		iEndX = (VectorObject.VectorList[iPoint].x*VectorObject.Scale)-VectorObject.Origin.x;
		iEndY = (VectorObject.VectorList[iPoint].y*VectorObject.Scale)-VectorObject.Origin.y;

		// rotate point
		iTempX = ((iEndX * cCos)>>7) - ((iEndY * cSin)>>7);
		iTempY = ((iEndX * cSin)>>7) + ((iEndY * cCos)>>7);

		// translate point back:
		iEndX = (VectorObject.Position.x>>7)+iTempX+VectorObject.Origin.x;
			//if (iEndX<0) iEndX=0;
		iEndY = (VectorObject.Position.y>>7)+iTempY+VectorObject.Origin.y;

		// Bounds check to ensure that rotated points are still within the bitmap boundary
		if (iStartX>=0&&iStartY>=0&&iEndX>=0&&iEndY>=0&&iStartX<=BitmapAddress[0]&&iStartY<=BitmapAddress[1]&&iEndX<=BitmapAddress[0]&&iEndY<=BitmapAddress[1])
		{
			DrawLine((u16*)BitmapAddress,(u8)(iStartX),(u8)(iStartY),(u8)(iEndX),(u8)(iEndY),VectorObject.VectorList[iPoint].colour);
		}
		iStartX = VectorObject.VectorList[iPoint].x;
		iStartY = VectorObject.VectorList[iPoint].y;
	}
	
}

We can now call that directly instead of our drawing code:

myVectorObject.Position.x = 0;
myVectorObject.Position.y = 0;
myVectorObject.RotationAngle = 0;
myVectorObject.Scale = 1;

DrawVectorObject((u16*)bmpLogo,myVectorObject);

Which will give us the same shape again, or we can change the scale, position and angle to do something more interesting.

myVectorObject.Points=5;
myVectorObject.VectorList[0].x = 0;
myVectorObject.VectorList[0].y = 3;
myVectorObject.VectorList[0].colour = 1;
myVectorObject.VectorList[1].x = 2;
myVectorObject.VectorList[1].y = 2;
myVectorObject.VectorList[1].colour = 1;
myVectorObject.VectorList[2].x = 3;
myVectorObject.VectorList[2].y = 4;
myVectorObject.VectorList[2].colour = 1;
myVectorObject.VectorList[3].x = 4;
myVectorObject.VectorList[3].y = 1;
myVectorObject.VectorList[3].colour = 1;
myVectorObject.VectorList[4].x = 1;
myVectorObject.VectorList[4].y = 0;
myVectorObject.VectorList[4].colour = 1;
myVectorObject.VectorList[5].x = 0;
myVectorObject.VectorList[5].y = 3;
myVectorObject.VectorList[5].colour = 1;
myVectorObject.Position.x = 2048;
myVectorObject.Position.y = 2048;
myVectorObject.RotationAngle = 16;
myVectorObject.Scale = 6;

DrawVectorObject((u16*)bmpLogo,myVectorObject);
It’s moved! Finally.

There are a couple of things to note here. Firstly, I use a larger scale for the position variables, so that objects can move at different paces, but still move at the right relative speeds.

You’ll also notice that the point definitions of the base object have changed slightly – this is just to make the vector definition as small as possible so that we have more flexibility to scale it up. There is no support here for scaling the object down to make it smaller, but there’s no reason why we couldn’t do that too I suppose.

Secondly, sine() and cosine() as defined in the library are integer values – remember how I mentioned that we don’t have a floating point on the poor old NGPC? So, we need to shift these down by 128 AFTER the multiplication in order to pretend that we’re multiplying by a decimal point. Also, we use a 256 degree circle for the angles – primarily for speed reasons. Everything is better when it’s a power of two.

I’m going to leave this post here, as I think I’ve waffled on plenty well enough about the theory for now. Tune in next time when I might actually start doing something about writing a game…

Advertisements

Some things come from nothing

There’s something rather absurd about coding games on obsolete platforms. They’re limited in weird ways – number of colours, sprites, controls, speed, you name it, it comes with limitations.

Life would be much easier with a copy of Unity of whatever, I could bring my ideas to life in moments rather than the weeks it takes to get a small NGPC sketch to the point where it can legitimately be called a game (months, or, lets be frank, years is probably more accurate). Hell, I might even be able to bung it up on one of these new-fangled stores and make a few pennies.

But where would be the fun in that?

You see, one of the things I find most enjoyable about coding on the NGPC are the limitations. It’s as though the machine is sitting there saying “Come on then, I have two limited colour planes for backgrounds, 64 tiny sprites, a joystick with two and a half buttons, a squawky buzzer and a 6.144MHz CPU. Go on then, make that do something amazing. I dare you.”

You know what little machine. Challenge accepted.

So, what’s the ultimate course of action with this approach? You’ve guessed it, why not take that already limited palette and try and use it for something that it patently is not suited for?

Enter – Bitmap mode. This was something I did for the C Framework library more than ten years ago now, because I wanted nice swoopy backgrounds for my Juno First game. It grew out of the times when you had to build tiles manually in hex editors and after I rolled my own very basic tile editor, I realised I could build tiles in engine just as easily.

I’m not going to go into details about how it works (basically applying boolean logic directly into the tilespace to set/reset the individual bits of an 8 byte tile, I’ll leave it as a challenge for the reader to work that out). Most of my games have some form of bitmap support – from the bootup “rug” pattern in Joust and Defender or the star background in Juno First, but generally all the heavy lifting is done with Sprites and Tiles as SNK intended, so I thought it would be “fun” to try and write a game entirely in bitmap mode.

Now, cards on the table, I tried this several years ago by trying to write a version of Qix but I got myself thoroughly tied up in knots doing that and couldn’t face going back to the code again, so this time I’m starting from scratch.

Buckle up, this is going to be a long one…

So far, all I really have is a name “Defenderoids” – which I came up with simply because I couldn’t decide whether to do a Defender clone or an Asteroids one. I have little to no idea how this might play at the moment, it is “just” a name*.

Bitmaps on the NGPC are created within a tileset which then gets echoed to the screen as an array. The basic approach is as follows :

void Defenderoids()
{
	u16 bmpPlayField[252][8];
	CreateBitmap((u16*)bmpPlayField, 144, 112);
	CopyBitmap((u16*)bmpPlayField, bgTileBase);
	//Copy the bitmap to SCR_1_PLANE
	// Watch the order...
	for (iLoopY=0;iLoopY<14;iLoopY++)
	{
		for (iLoopX=0;iLoopX<18;iLoopX++)
		{
			PutTile(SCR_1_PLANE, 0, 2 + iLoopX, 1 + iLoopY, bgTileBase+iTile);
			iTile++;
		}
	}
}

That won’t look like much – since we’re not actually putting anything into the bitmap. But that’s essentially the blank canvas that we can then use.

We can then use SetPixel() to write individual pixels to the screen. We’ll start with a star field…

void Defenderoids()
{
	// Variable declarations
	u8 iTile;
	u8 iLoopX;
	u8 iLoopY;
	u8 iLoopStar;
	u16 bmpPlayField[252][8];

	// Create a "bitmap" array
	CreateBitmap((u16*)bmpPlayField, 144, 112);
	// Copy the bitmap array into the tilespace
	CopyBitmap((u16*)bmpPlayField, bgTileBase);
	//Copy the bitmap to SCR_1_PLANE
	iTile = 0;
	for (iLoopY=0;iLoopY<14;iLoopY++)
	{
		for (iLoopX=0;iLoopX<18;iLoopX++)
		{
			PutTile(SCR_1_PLANE, 0, 2 + iLoopX, 1 + iLoopY, bgTileBase+iTile);
			iTile++;
		}
	}

	// Draw a "random" star  field
	for (iLoopStar=0;iLoopStar<25;iLoopStar++)
	{
        	iLoopY = QRandom()&gt;&gt;1;
	        iLoopX = QRandom()&gt;&gt;1;
        	SetPixel((u16*)bmpLogo,(u8)(iLoopX),(u8)(iLoopY),QRandom()&gt;&gt;7);
	}
}

I’ll leave this post here, next time I’ll dig into how to create and use some simple vector objects.

* Although names are important…

Seven Day Robotron+

Robotron is one of the finest videogames ever produced. I’ll accept no argument on that score.

So of course, back when we did the Eugenius issue of Way of the Rodent (we’re not dead, we’re just sleeping) celebrating the highs and very highs of the career of Eugene Jarvis, I foolishly thought it would be a jolly idea to try and write a clone of Robotron for the NGPC in a week. It was originally intended to a) allow me to waffle on about the game for a bit as well as b) give me something to aim at on the NGPC and c) show some “props” to the original developers (or for that matter anyone who ever actually manages to finish a game)

Anyway, that version of the game has been available for ages on various sites now, and, well, it’s a bit basic.

SevenDayRobotron

There’s a lot of the DNA of Robotron in there, the grunts zone in on the player, the family are all present and intact, and the base mechanisms for the game are largely there, but it was never going to set the world on fire like that.

But those 7 days weren’t the end of Robotron on the NGPC. I carried on developing it for a while as and when I could, and well, when I found my source code recently there it was. So, I thought it was about time I “finished” it. It’s not perfect, far from it, but it is functionally complete, playable and available for public download for the first time.

RobotronFirstWave

A little bit better, I hope you agree…

After sitting through the tutorial, I thought it would be worth having a deep-dive into a completed game. So, the full source is available on the download link at the bottom of the article.

I won’t bore you with a full code listing here, but a few things to watch out for.

Firstly main.c is not the main program. This “merely” contains the bootstrap and a relatively light loop which mainly checks for game over and level progression.

The main game logic is all held within robotron.c/h. All game objects are encapsulated in a series of structures:

  • Sprite
    • Generic sprite structure, shared between player, shots, family and the Robotrons
    • Laser, extends the generic sprite structure for the player’s shots
    • Player, extends the sprite for information about the player, including score and shot counters
    • Robotron, extends the sprite for information about your enemies, the evil Robotrons
  • Level, includes all enemy information for the current level
  • Game, contains meta information and parameters about the game itself

These objects are created as variables in rtDrawLogo(), rtCreateLevel() and rtCreatePlayer()

The Player, including all input and shot collision detection is handled in rtMovePlayer()

All Robotron movement, including the human family is handled by rtMoveRobotrons(). This branches out to enemy-type specific routines such as rtMoveGrunt(), rtMoveHlk() etc where applicable. Some of the individual robotron movements aren’t fully realised yet (Brains are a bit stupid, and Progs don’t want to move for some reason, so I’m still working on those)

All collisions between the Robotrons, Human Family and the player are handled in rtRobotronCollision(). This is probably the single piece of code that needs a little bit of explanation… Basically, I wanted to lock the game to as close to the VBLANK() as possible and there are a lot of potential collisions in Robotron:

  • Collisions between a shot and the Robotron
  • Collisions between a Robotron and the player
  • Collisions between certain Robotrons (Hulks and Brains) and the Last Human Family

The first two of these are pretty straightforward and relatively limited (one player, four or five shots at most) so even with a brute force collision method of testing every Robotron on screen against them you’ll only be spending a relatively consistent 300 distinct tests to figure out whether you’ve hit something.

The problem comes when you try and test Robotrons against the Family (or against each other in the cast of Electrodes) – there can be as many as 50 of these that need to be tested against each other – so at worst we could have 25×25=625 distinct brute force “have ‘these two’ things collided” tests.

So, what we do is to lock the collision detection into a single VBLANK, and attempt to pick up the collision detection loop on the next blank if we haven’t quite got round to it. It appears to work.

The controls also need a bit of explaining – unlike the arcade original, which requires a twin-joystick that the NGPC simply doesn’t have, the game will auto-fire. You move using the joystick and rotate the angle of fire by 45 degrees clockwise or counter-clockwise using the A and B buttons.

This is still a work in progress, but it’s playable, mostly functional and generally feels quite like Robotron. Which considering the limitations of the NGPC (specifically with the controls) I’m quite pleased with. Full source is available here and if you just want to play the game in an emulator of your choice you can grab it from here

Pocket Adventures

Firefly

We’re spoiled now, with our iPads and emulators running on inexpensive mobile phones. With the addition of a £10 bluetooth controller, we can play decent emulations of almost any gaming system that takes our fancy. But it wasn’t always thus…

Looking back on it now, mid to late 90’s era handheld gaming was, well, a bit crap really?

But that didn’t stop me falling absolutely head over heels in love with SNK’s Neo Geo Pocket Color when it managed to make it to the UK in 1999. I saw Sonic, Metal Slug, Pac Man and Crush Roller and I was all-in. The combination of “good enough” colour, decent sound and (still!) the best joystick on a handheld device was enough for me. I quickly burned a hole in my wallet and my heart getting hold of the best games for the system – a line up which still stands up today. I would still rank Card Fighter’s Clash in my top-3 games of all time…

And then… Within a year, SNK crashed and burned and the new owners Aruze pulled the plug on the NGPC and boom, my new favourite toy was suddenly defunct in the West, just when it felt like it was hitting it’s stride.

Well, what do you do in this situation? Of course, you go into mad completist mode and try and buy up everything you can as quickly as you can… I swiftly cleaned out my local shops of the few games that I didn’t already own, and then hit Ebay looking for other hits. Play-Asia became my new favourite website as I found myself looking for more and more obscure titles such as Gunbare Neo Poke-Kun, a game which I still don’t understand, but into which I sank 100’s of hours of my life.

Eventually, all too soon, of course, even the Japanese titles started to dry up – or simply became far too expensive to buy – something not helped when Edge ran an article on the NGPC and every collector in the UK suddenly seemed to wake up, causing the prices to rocket in the process. I started to look elsewhere for my gaming needs…

There was, however, one last chance for the NGPC to shine. The late purchase of a flash Linker, bought to play games I could no longer afford, and the timely expertise of a small handful of hardcore hackers, led me into the murky worlds of console development…

…to be continued…