Adventures with infinite parallax scrolling on Cocos2d

A new game that I am developing involves an infinitely wrapping parallax scrolling background. This is an iOS game made on top of Cocos2d. I had a bit of trouble performance wise implementing it but here’s how I did it.

First, we make a layer which handles all the backgrounds.

Next, we make an class just for each object in the parallax scrolling layer. Each object should have overridden the draw method and make sure it draws the wrapping image. Here’s what I had:

@implementation FeiZhuBackgroundObject
+(id)backgroundFromFile:(NSString*)filename atPoint:(CGPoint)pos withSpeed:(float)spd {
	return [[[self alloc]initWithTexture:[[CCTextureCache sharedTextureCache] addImage:filename] atPoint:pos withSpeed:spd] autorelease];
}
-(id)initWithTexture: (CCTexture2D*)tex atPoint:(CGPoint)pos withSpeed:(float)spd {
	if( (self=[self init])) {
		texture = tex;
		[texture setAliasTexParameters]; //ZOMG, MUST INCLUDE.
		position = pos;
		speed = spd;
		[self schedule:@selector(step:)];
	}
	return self;
}
-(void) step:(ccTime) deltaTime
{
	 position = ccp(position.x - speed, position.y);
	 if (position.x <= -texture.contentSizeInPixels.width) position = ccp(DEVICE_WIDTH, position.y);
}
-(void)draw {
	glDisableClientState(GL_COLOR_ARRAY);
	[texture drawAtPoint:position];
	if (position.x > 0) [texture drawAtPoint:ccp(position.x-DEVICE_WIDTH, position.y)];
	if (position.x < 0) [texture drawAtPoint:ccp(position.x+DEVICE_WIDTH, position.y)];
	glEnableClientState(GL_COLOR_ARRAY);
	[super draw];
}
@end

Then, in the background layer, you can simply add all the backgrounds

@implementation MainBackgroundLayer
-(id)init {
	if( (self=[super init])) {
		[self addChild:[FeiZhuBackgroundObject backgroundFromFile:@"sky.png" atPoint:ccp(0,0) withSpeed: 0.0f]];
		[self addChild:[FeiZhuBackgroundObject backgroundFromFile:@"skytwo.png" atPoint:ccp(0,0) withSpeed: 2.0f]];
		[self addChild:[FeiZhuBackgroundObject backgroundFromFile:@"skyone.png" atPoint:ccp(0,0) withSpeed: 1.0f]];
		[self addChild:[FeiZhuBackgroundObject backgroundFromFile:@"mone.png" atPoint:ccp(0,0) withSpeed: 1.0f]];
		[self addChild:[FeiZhuBackgroundObject backgroundFromFile:@"mtwo.png" atPoint:ccp(0,0) withSpeed: 2.0f]];
	}
	return self;
}
@end

One thing that had me stumped for a long time was how slow it ran. The FPS would drop to 30 almost every time I ran the game. Turned out that the background textures were being antialiased.

As soon as I added [texture setAliasTexParameters];, all of my performance issues were gone. Something to take into consideration.

Circular collision detection

I know this is already super well known to most people but it was so easy to implement, I just HAD to share it.

Basically, find the distance between two points and if that distance is shorter than the the two radii added together, they intersect.

To implement in your favorite programming language, simply subtract two, 2D vectors to get the height and width of a right-angled triangle. Then use Pythagoras to derive the length.

Add the two radii together and compare it to the length. If length is less than the sum of the radii, assume they intersect and a collision has happened.

Basically, this psuedocode will do it

double dx, dy, rt;
rt = radius1 + radius2;

if (dx*dx + dy*dy < rt*rt) {
    //There is collision
}else{
    //There is no collision
}

NSpire game development framework

Update: Open Source’d! https://code.google.com/p/nspire-gamekit/

Been busy working on a homebrew game development framework for NSpire calculators. On top of that, I’m building a Plane port. So far, it works and I’m quite happy with it :D

Features (so far):

  • Super easy to use – handles the game loop for you efficiently. Just set FPS and callbacks and go!
  • Supports loading sprites and images from files (need to be converted first).
  • Everything images related is bitmap based.
  • Very basic memory leakage detection (which has actually saved me once).

Todo:

  • Need to open source ASAP :D
  • Documentation
  • User defined timers
  • Drawing to a negative coordinate glitches
  • Somehow integrate a OOP language (C++ comes to mind) into it so it doesn’t need disgusting static variable declarations all over the place.

Some sexy pictures:

Collision detection on iOS

I started developing plane for iOS recently. It’s a hacked up port of the original game I made when I was young. It was an excellent way to learn the iOS SDK and Objective-C.

Anyway, down to business… The initial collision detection I implemented was a very crude one that simply checks if the bounding boxes of the sprites intersect.

-(void)checkCollision {
	CGRect frame = sprite.frame;
	if (frame.origin.y < -32 || frame.origin.y > 320) {
		[parent collision];
	}

	NSMutableArray* baddies = parent.baddies;
	UIImageView* object;
	CGRect frame2;
	for (object in baddies) {
		frame2 = object.frame;
		if (CGRectIntersectsRect(frame,frame2)) {
			[parent collision];
		}
	}
}

Obviously it wasn’t very accurate and players would die before even hitting the objects – however, this was fast and simple. I tried to compensate for this by making the bounding box smaller – of course that didn’t work too well either.

There was no way around it, I had to use some more advanced collision checking solutions.

One solution I had was to draw a line around the edges of the sprite and have it check whether the lines intersect. But that would mean for every sprite, I had to somehow generate those lines. This was too complicated and too messy so I kept looking.

The solution I came to, albeit perhaps a bit slow, was to first get a bitmap and draw the two objects onto it with 0.5 alpha. Then, it’s simply just iterate through the bitmap and search for alpha values larger than 128. If there is one, it means a collision has occurred.

I dumped one such bitmap through the debugger and converted it to a PGM and it looks something like this:

There are 3 colors, black means an alpha value of 0, grey means an alpha value of less than 0.5 and white is an alpha value of more than 0.5. Obviously, no collision has occurred yet since there’s no white overlapping but you can see how the concept works.

Without further ado, here’s the code I used. You’ll have to make adjustments to make it work in your program but it’s here to demonstrate how it works. It’s also very bad code at the moment and constantly being optimized so watch out.

-(void)checkCollision {
	if (!doCollisionCheck) return;
	CGRect frame = sprite.frame;
	if (frame.origin.y < -32 || frame.origin.y > 320) {
		[parent collision];
	}

	NSMutableArray* baddies = parent.baddies;
	UIImageView* object;
	CGRect frame2;
	for (object in baddies) {
		frame2 = object.frame;
		if (CGRectIntersectsRect(frame,frame2)) {
			[self checkCollisionExpensive:object withSpritePoints:frame.origin withOtherPoints:frame2.origin];
		}
	}
}
#define device_w 480
#define device_h 320
-(void)checkCollisionExpensive:(UIImageView*) image withSpritePoints:(CGPoint)spritePos withOtherPoints:(CGPoint)otherPos  {
	CGContextRef bitmap = CGBitmapContextCreate(NULL,
												 device_w,
												 device_h,
												 8,
												 device_w,
												 NULL,
												 kCGImageAlphaOnly);
	if (!bitmap) { NSLog(@"Cannot alloc bitmap"); return; }
	CGImageRef otherImageRef = image.image.CGImage;
	CGImageRef selfImageRef = sprite.image.CGImage;
	CGRect rect;
	
	CGContextSetAlpha(bitmap, 0);
	rect = CGRectMake(0, 0, device_w, device_h);
	CGContextFillRect(bitmap, rect);
	
	CGContextSetAlpha(bitmap, 0.5);
	
	rect = CGRectMake(otherPos.x, device_h-otherPos.y-image.frame.size.height, image.frame.size.width, image.frame.size.height);
	CGContextDrawImage(bitmap, rect, otherImageRef);
	rect = CGRectMake(spritePos.x, device_h-spritePos.y-sprite.frame.size.height, sprite.frame.size.width, sprite.frame.size.height);
	CGContextDrawImage(bitmap, rect, selfImageRef);
	
	unsigned char* data = CGBitmapContextGetData(bitmap);
	int collided = 0;
	NSLog(@"%p",data);
	
	for (size_t i = 0; i<device_w*device_h; i++) {
		//NSLog(@"%x", (int)data[i]);
		if (data[i] > 128) {
			NSLog(@"collidepoint %d",(int)data[i]);
			collided = 1;
			break;
		}
	}
	
	if (collided) [parent collision];
	CGContextRelease(bitmap);
	
}

As the method name suggests, it’s a expensive process and calls to it should be reduced to a minimum. I used a combination of bounding box collision detection and the bitmapping to reduce calls.

Happy programming.

Making a small, quiet print server

I had a spare laptop laying around gathering dust since the hard drive died. I did reuse it at one point for a media center and server but it was too high maintenance and I already had a PS3 to stream media.

So, I decided it was going to be a print server now. Unfortunately, it doesn’t have a hard drive and I don’t want to install it onto a USB drive (performance issues and the computer cannot boot directly from USB). It also has to be low power and cheap. Storage isn’t an issue.

The best balance I could strike up was to use a CompactFlash card along with a CF to IDE adapter. A true SSD would be too expensive and a traditional harddrive would use too much power. CompactFlash also has built in wear leveling – an extra bonus.

All of this hardware cost me AUD$27 – pretty damn cheap SSD combo. You can find them all on eBay (though I highy suggest you get the CompactFlash card from a reputable source and brand rather than eBay even though it may cost much more – most of the memory cards on eBay are fakes and won’t last).

And finally, when it’s put together, we have our ghetto SSD!

Before you go booting your installation discs, remember to enter the BIOS and disable things you don’t need. This will save power.

Excited, I booted up my Debian 6.0 disc but unfortunately it simply froze. I hit Ctrl+Alt+Del and ran expert mode and found the problem. The kernel was frozen at “Probing EDD (edd=off to disable)”

The fix is to simply add the said string to the boot parameters.

Finally, I got to the familiar Debian install screen! Yay!

I’m not going to go into too much details about the rest of the installation. It’s pretty much follow the prompts from there.

One thing I would recommend though is when you get to the partitioning screen, make sure to set the noatime options. This stops the last access times from being written to disk – saving precious write cycles.

Finally, I rebooted and I was amazed at how quickly the system booted! From the GRUB prompt disappearing to the login prompt only took 3 seconds!

I was very excited and ran a speed test on the drive immediately and was very pleased with the results! It gave me values between 5 to 10MB/s. For an old computer that’s pretty good!

From here, things were pretty straightforward. I was amazed at what Debian 6.0 had to offer. Wireless drivers were already included and I even had the graphics driver installed :P

So, I set up a SSH server, then hooked the printer up to the computer and tucked it away under the desk.

I won’t go into too much detail on installing CUPS and setting up the correct permissions. There are numerous guides that tell you this.

The hard bit after setting up the print server was getting my Mac to print to it. Adding it through the printers interface in System Preference won’t work.

You have to go to http://localhost:631/ on your Mac and select the “Administration” tab. From there, you add a new printer.

Select “(http) Internet Printing Protocol” and the URL should be something like this: http://yourprintserverip:631/printers/your_printer

Select the print drivers appropriate for your printer and click Add Printer. Print a test page to check it works.

For Windows, it’s really easy. Just add a printer as you normally would but select Network Printer and select the option to print to the internet and use the same URL as above.

At the end of the day, you’ll (hopefully) have a working print server! Enjoy printing from anywhere in the house.

Sorting out CSS float collapses

As an amateur web developer, one of the first problems I encountered was that floated elements with non-floated parent elements will cause the parent to collapse. This can cause headaches later on.

There are many solutions to this problem – a small list can be found in this thread on StackOverflow.

The solution I used (which I will now be using in all future projects) is to simply add this to your CSS files:

div {
  overflow:hidden;
}

After that, you’ll never need to worry about the issue again.

As an extra note, if you use Firefox to test your websites, I highly suggest you download Firebug (if you haven’t already). This has helped me find problems with my code countless times.

A proper movie player for TI NSpire calculator

Update5:

Yeah, found the problems in libmpeg2. It was nothing wrong with the library (well, except some signedness issues), it was my linker which didn’t correctly update the function pointers in the arrays.

A quick workaround was to convert everything into switch statements.

The signedness issue was to do with the state enumeration. STATE_INTERNAL_NORETURN was supposed to be a negative value but since the enumeration was an unsigned type, when -1 was cast to it, it’s value was represented incorrectly. Casting it to a signed integer appears to have fixed the problem.

Update4:

I am really a bad programmer :\ Maybe it’s my lack of knowledge or maybe it’s my laziness but I also gave up on libtheora.

I’m having another go at libmpeg2

Update3:

Yeah, I gave up on libmpeg2 (I was so close though :-( ) since I couldn’t identify the bug and the mailing lists seemed to be empty. The code wasn’t well commented and documentation was far too lacking. To an amateur programmer, that’s already too big of a challenge.

Instead, I’ve started using libogg and libtheora. It took over an hour and a half worth of Makefile hacking and manipulating configure options but I got there in the end. I now have the static libraries in my ndless lib folder ready for linking against. I’ve already begun reading documentation and I’ll start with libtheora tomorrow.

Obvious advantages over other video codec libraries:

  • Ogg and Theora are liberal formats which means they aren’t encumbered by patents so I can be sure I’ll be able to use them freely.
  • It’s less complex than libav which means less Makefile hacking and less clutter.
  • It’s better documented than libmpeg2

Basically, to build these libraries, you need to configure it in a way that only the library is built. Anything else (including tests and documentation) should be avoided since they usually fail to build (for example, there was a C program used to build the documentation that refused to build so I had to hack the Makefile to skip it).

This project is finally starting to look workable now :D But, as always, assistance is appreciated if you can spare it.

Goodnight.

Update2:

There seems to be a bug somewhere in the library (or maybe my code is just wrong). This will set me back a long way :\. Anyhow, debugging has begun.

The library keeps claiming I have an invalid stream even when I run the same file on the same library on my computer.

Update:

This morning, I went through the sample code for the libmpeg2 library and I think I’ve got the main jist of it.

I wrote up some code based on the samples and it’s going through testing now.

Original post:

I’m in the mood for writing code for the calculator and (after not bothering with Linux anymore) came up with an idea for playing movies on the calculator.

So far, I’ve been trying to build various libraries for video decoding but the only one I succeeded in building was libmpeg2. I have a static library that I can link to and some header files so I can get started.

Unfortunately, libmpeg2 has almost no documentation and at the moment, I’m just trying to work out what each function does. If anyone would like to help me write this program, please contact me.

If you want to do some independent work, here is the build options I used to get it to build:

CFLAGS="-mcpu=arm926ej-s -I \"/path/to/ndless/trunk/include\"  -fpic -fno-builtin " ./configure --host=arm-none-eabi --enable-shared=no --prefix="/path/to/ndless/trunk" && echo "install: \

all:
" > src/Makefile

make

make install

The test programs won’t compile so we won’t bother (hence overriding the Makefile in the src folder).

After that, you should be able to use the header files included and it should link properly. Again, if you know or have worked out how to decode a MPEG stream into bitmaps, please tell me.

Rick Roll on the TI-NSpire calculator

It’s the school holidays, and again, I have been left really bored. So, I dug out my CAS calculator, put Ndless on and began writing programs. Eventually, I came up with calculator RickRoll.

I hacked up some programs and put it on the calculator that allowed to me to watch videos (though they are extremely horrible). I soldered a piezoelectric buzzer onto a spare USB AVR I had lying around and programmed it to sing the melody of Never Gonna Give You Up. Put the two together and you have RickRoll on the TI-Nspire with sound and all!

To convert to the video to a playable format, I simply converted the video to a series of grayscale pixmaps, extracted the 8bit color information and converted it to 4bit color and stored each frame sequentially into a giant file. The video player simply reads each frame into the framebuffer, delays and loads the next frame and so on.

The audio was done on an AVR since the NSpire doesn’t have any audio output. I simply coded it so play a melody on the piezoelectric buzzer.

Things that need improving:

  • Video compression – I have none. A one minuteish video takes up roughly 13MB which is basically all of the remaining free space on the Nspire.
  • Proper controls on the player (the player was hacked together at the last minute).
  • The converting process – The current way I do it is terrible.

As promised, here is the source code to all my work (sound part excepted because that source code is not in a distributable state). All code is licensed under the GPLv3.

A timetable application for the TI-NSpire

Recently, I got into developing programs for the TI-NSpire calculators. One program I made was a timetable application. I spent a long time writing the console library to output text (since the calculator’s printf function prints to the serial output rather than a console on the screen). But after that, it was easy writing the rest of the program.

Licensed under the GPLv3 License.

So, enjoy the application. Download Source Code and Binary.