nathandemick.com

My thoughts on Android phones

Even though Android is pretty much a giant iOS rip-off (see pre-iPhone UI screenshots), I appreciate its' existence. Healthy competition brings about innovation, and Apple's iOS devices are better due to having Android on the playing field. While my current carrier (Verizon) doesn't currently offer the iPhone (until tomorrow, I guess), I haven't had any interest in buying one of the many Android phones that they do offer. The reason is the "openness" that Google touts as being one of Android's advantages.

In practice, what "open" means for Android is that each phone manufacturer will take the base OS, then try to customize it as much as possible for their devices, in an attempt to differentiate themselves from other Android handset manufacturers. As far as I know, it's impossible to remove these customizations and revert back to Google's "vanilla" Android UI without significant hacking. The annoying thing here is that I would want to use a phone with no tacked-on cruft, but HTC, Motorola, etc. don't provide that option.

The other problem with Android phones is that you can't get official OS updates without going through the carrier/manufacturer. With iOS, this isn't a big deal; Apple releases a new version, and everyone can download and upgrade at the same time. With Android, Google releases a new version, and then the manufacturers have to tailor the upgrade to fit each of their devices. Of course, this takes time, and in some cases a manufacturer won't want to keep supporting their device, which means you are crap out of luck.

Both of these problems don't exist if you buy one of the "official" Android phones, the Nexus One or Nexus S. But bizarrely, the Nexus phones are only supported by T-Mobile in the US (you can run one on AT&T, sans 3G data connection). Of course, that means they don't work on Verizon's CMDA network at all. The unfortunate conclusion is that Google doesn't feel like giving people using carriers other than T-Mobile a choice on whether or not to user their official phone.

These three problems have prevented me from using Android, and since it looks like Verizon is going to start carrying the iPhone very soon, I don't think there's much hope for Android in my future.

· 1 comments

Name Brainstorming

I came across this file the other day, back when I was trying to figure out a name for my new game. I thought it was interesting, so figured I'd post it here. Basically what I did was make a list of words that described the core ideas of my game, then use a thesaurus to get some synonyms. Then I tried just randomly combining words until I hit on a combination that I liked. Not very scientific, but I really like the result. You can see how I immediately stopped after I found the one I settled on =]

Ball - balloon, drop, globoid, globule, orb, spheroid
Marble - globe, marble , orb, planetoid, sphere, terrene, wandering star, world
Gravity
Drop - descend, plummet, plunge, release
Fall - cascade, depreciate, dive, gravitate, plummet, plunge, sink, slip, slope, slump, spin, topple, tumble
Maze - Labyrinth, puzzle
Rotate - gyre, pivot, revolve, spin, swivel, twirl, twist, whirl

Ball, Marble, Globe, Orb, Sphere

Pivot Orb
Pivot Drop
Spherevolve
Marblevolve
Marble Maze
Gravity Orb
Gravity Maze
Terrene Orb
Sphere Spin
Revolve Ball

· 0 comments

Preview: Revolve Ball

So I figured that I'd do a quick preview of the next game I'm working on. It's called Revolve Ball, and it's a remake of a game that my friend Sam and I created in college, which is in turn a clone of an old Taito game called Camel Try. I was first introduced to Camel Try by another friend, Adam, who was a MAME enthusiast. Even though Camel Try's controls were a little bit janky, I really enjoyed the concept of the game. A while later, my friend Sam and I took a computer graphics course that emphasized OpenGL. We paired up for our final project, and decided that a game like Camel Try would be an excellent way to showcase the glRotate() function. Well, to be honest, such a game was beyond our capabilities at the time. We ended up creating something that mostly worked, but was pretty ugly. The collision detection was not robust at all, and the graphics were bare-bones. I think we maybe got a C, which (in retrospect) was generous.

Fast forward a few years to my renewed interest in game development. I learned some Actionscript and had released a Flash game or two, when I decided that I wanted to recreate my old game. I guess it was because I felt unsatisfied with the original result. I actually got a decent groundwork started for a Flash version; my program interpreted .png files to use as level layouts, and used a grid to hide/display the sprites that made up a level based on their proximity to the player. I even implemented the Separating Axis Theorem to use for collision detection. However, I never completed the game. I don't know if I just got bored, or discouraged from doing too much low-level programming (i.e. not making content), or what.

Once I finished Nonogram Madness, though, I decided I wanted to revisit this game concept yet again, this time on iOS. This time, however, I was able to stand on the shoulders of a few giants (in the form of cocos2d and Box2D). With the interface and physics frameworks already written, I was able to more quickly get my engine into a state where I could start creating content for an actual game.

The concept behind Revolve Ball is similar to many "maze"-type games, where the player must navigate a marble through an obstacle course. The difference is that in Revolve Ball, the ball is viewed from the side, and constantly falls "downward" due to gravity. The player then spins the maze around the ball in an effort to find the exit. While this type of game was fun to play with a mouse or keyboard, I think a touchscreen is the ideal controller. There's a real feeling of interaction as you spin each level around with your finger which is quite appealing.

Right now I'm in the middle of creating levels for the game. I'd like to publish with 40 levels, and increase the number as I push updates. One of the biggest things I've learned so far is that making game content is extremely time consuming. My next game is going to have automatically-generated content =] I'm not sure how long it's going to take me to finish, but it's going to be before April 10th... that's the day that Apple will remove my app entry in iTunes for not having the program actually uploaded =] Let me know in the comments or on Twitter if you're interested in testing out the app, since I'll be sending it out to a few people before the official launch.

· 1 comments

App Pricing Experiment

For the month of January, I'm running a pricing experiment with Nonogram Madness. For the first two weeks, I'm pricing it at $0.99, and for the last two, $1.99 (the normal price is $2.99). Since all my sales come from users searching the App Store, and not from any external source, I'm hoping to be able to find the "sweet spot" where I get the most downloads at the highest price. As a baseline for comparison, I'll be able to use the previous 5 months when the game was at its' original price.

What I'm trying to find is whether 5 people would buy Nonogram Madness for $2.99, or 30 people would buy it for $0.99 (on a daily basis). Obviously, if it's the latter, I'd get more revenue from pricing it at the "traditional" level. I'm not sure if this experiment will help me determine pricing for future games, but I figured I can't really damage sales of Nonogram Madness, since they've been more or less consistent since the month after the game was released. Whatever the results are, I'll be sure to post them here.

· 1 comments

cocos2d Game Tutorial – Multitouch Asteroids (Part 2)

Bokay, so in the previous tutorial we created a good base for an Asteroids-style game — we created a Ship object, and got it to respond to user input. But! There's no conflict yet, and therefore not much of a game. In this installment we're going to create Bullet and Asteroid classes, so you can actually shoot at something! The excitement is palpable.

Open up the project you previously created. If you guessed that we're going to start by adding two new sets of .h/.m files, then you are correct. Right-click on the Classes group in your project sidebar, and choose Add > New File. Use the "cocos2d Node class" template, have it subclass CCSprite, and name it "Asteroid.m". Do the same thing again, except name the second file "Bullet.m". These two classes will be very similar to the Ship class we already created, so don't worry too much.

So let's think about what we need to have our bullet and asteroid objects do. They both need to follow the same movement rules as the ship — when going off the edge of the screen, they re-appear on the other side. Well, that's easy... we can just re-use the code we wrote for the ship. The asteroid object also needs to know if it's "small," "medium," or "large." Remember that if you destroy a large asteroid, it splits into two medium asteroids, et cetera. The asteroid should also detect if it collides with a bullet or the ship, so we should have a collision detection method. As you can see from the following code, the "Asteroid.h" header is almost the same as "Ship.h," with the addition of the property/method that we discussed.

//  Asteroid.h

#import "cocos2d.h"

@interface Asteroid : CCSprite
{
	// Stores the size of the asteroid - values will be 1, 2, or 3
	int size;

	// A struct that holds X/Y values that will be used for the asteroid's speed
	CGPoint velocity;
}

// Declare properties so setters/getters can be automatically synthesized
@property int size;
@property CGPoint velocity;

// Declare methods
- (id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect;
- (void)update:(ccTime)dt;
- (bool)collidesWith:(CCSprite *)obj;

@end

The corresponding implementation file, "Asteroid.m," is again almost the same as "Ship.m". The only changes here are the inclusion of a line of code to make each asteroid spin as it moves, as well as the collision detection method. The collision detection uses some of Apple's built-in geometry functions to create bounding rectangles around two objects, then passes them through a function to detect intersections.

//  Asteroid.m

#import "Asteroid.h"

@implementation Asteroid

// Create setters/getters for these properties
@synthesize size, velocity;

- (id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect
{
	if ((self = [super initWithTexture:texture rect:rect]))
	{
		// Make sure to schedule the "update" method to run
		[self scheduleUpdate];
	}
	return self;
}

- (void)update:(ccTime)dt
{
	// Rotate (based on time interval between each frame)
	[self setRotation:self.rotation + (float)dt * 15];

	// Move
	[self setPosition:ccp(self.position.x + velocity.x, self.position.y + velocity.y)];

	// Get window size
	CGSize windowSize = [CCDirector sharedDirector].winSize;

	// If object moves off the bounds of the screen, make it appear on the other size
	if (self.position.x < 0)
		[self setPosition:ccp(windowSize.width, self.position.y)];
	else if (self.position.x > windowSize.width)
		[self setPosition:ccp(0, self.position.y)];

	if (self.position.y < 0)
		[self setPosition:ccp(self.position.x, windowSize.height)];
	else if (self.position.y > windowSize.height)
		[self setPosition:ccp(self.position.x, 0)];
}

// Super-basic AABB collision detection
- (bool)collidesWith:(CCSprite *)obj
{
	// Create two rectangles with CGRectMake, using each sprite's x/y position and width/height
	CGRect ownRect = CGRectMake(self.position.x - (self.contentSize.width / 2), self.position.y - (self.contentSize.height / 2), self.contentSize.width, self.contentSize.height);
	CGRect otherRect = CGRectMake(obj.position.x - (obj.contentSize.width / 2), obj.position.y - (obj.contentSize.height / 2), obj.contentSize.width, obj.contentSize.height);

	// Feed the results into CGRectIntersectsRect() which tells if the rectangles intersect (obviously)
	return CGRectIntersectsRect(ownRect, otherRect);
}
@end

Now let's take a look at the bullet object. Again, it's mostly the same as the asteroid and ship, but we want our bullets to disappear after they travel a certain distance... otherwise they'd just continue to float around forever until they hit something. I guess that would be true to life, but it would make the game too easy! So for each bullet, we'll store the total distance it's traveled since it was created, as well as store an "expired" flag. Whenever the bullet moves, we'll check to see if the accumulated distance is greater than a certain number (I'm using half of the screen width), and if it is, then the bullet is considered expired. It'll then be up to the game loop to look at that flag and remove the bullet.

//  Bullet.h

#import "cocos2d.h"

@interface Bullet : CCSprite
{
	// Stores how far the bullet has moved!
	float distanceMoved;

	// How fast the bullet moves
	CGPoint velocity;

	// Whether or not the bullet has traveled so far that it disappears
	bool expired;
}

// Declare properties so setters/getters can be automatically synthesized
@property float distanceMoved;
@property CGPoint velocity;
@property bool expired;

// Declare methods
- (id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect;
- (void)update:(ccTime *)dt;

@end

In the "update" method of the bullet, we'll perform the movement and distance checks. If you remember your geometry, this code should seem familiar. Also, in the "initWithTexture" method, we'll also initialize the variable that stores the distance the bullet has traveled, setting it to zero.

//  Bullet.m

#import "Bullet.h"

@implementation Bullet

// Create setters/getters for these properties
@synthesize distanceMoved, velocity, expired;

- (id)initWithTexture:(CCTexture2D *)texture rect:(CGRect)rect
{
	if ((self = [super initWithTexture:texture rect:rect]))
	{
		// Schedule update for this object
		[self scheduleUpdate];

		// Initialize the distance the bullet has moved
		distanceMoved = 0;
	}
	return self;
}

- (void)update:(ccTime *)dt
{
	// Get window size
	CGSize windowSize = [CCDirector sharedDirector].winSize;

	// Move
	[self setPosition:ccp(self.position.x + velocity.x, self.position.y + velocity.y)];

	// Increment the distance moved by the velocity vector
	distanceMoved += sqrt(pow(velocity.x, 2) + pow(velocity.y, 2));

	// Determine if bullet is expired -- check to see if its gone at least half the width of the screen
	if (distanceMoved > windowSize.width / 2)
		expired = TRUE;

	// If object moves off the bounds of the screen, make it appear on the other size
	if (self.position.x < 0)
		[self setPosition:ccp(windowSize.width, self.position.y)];
	else if (self.position.x > windowSize.width)
		[self setPosition:ccp(0, self.position.y)];

	if (self.position.y < 0)
		[self setPosition:ccp(self.position.x, windowSize.height)];
	else if (self.position.y > windowSize.height)
		[self setPosition:ccp(self.position.x, 0)];
}

@end

Pretty easy, right? There shouldn't be anything in these two classes that should surprise you too much; perhaps just some of the wonky Objective-C syntax, which you will get used to over time. OK, so now that we've created all three of the objects our game is going to use, let's go back to the GameScene class and actually add the bullets and asteroids. We'll also create write convenience methods that we'll use for creating bullets and asteroids, as well as add some Objective-C arrays that will be used to keep track of all the bullets and asteroids that are on screen. Add the following variable declarations in "GameScene.h" between the brackets after @interface GameLayer : CCLayer.

	// Arrays used to keep track of all visible asteroids/bullets
	NSMutableArray *asteroids;
	NSMutableArray *bullets;

	// Used to determine the number of asteroids that appear
	int currentLevel;

Then add the following method declarations after the + (id)scene; declaration. You can see that these method names are very verbose... some people don't like that, but I think it makes reading Objective-C code very easy (as long as you name your methods well). We'll put code in these two methods that create bullets and asteroids. Since asteroids can be created anywhere on the screen and have multiple sizes, the "createAsteroid" method has two arguments, "position" and "size." Bullets can only be shot by the ship, the position of which is pre-determined, so "createBullet" doesn't need any arguments. To end it up, we'll create some additional methods which will be used to reset the game and start new levels.

- (void)createAsteroidAt:(CGPoint)position withSize:(int)size;
- (void)createBullet;
- (void)startLevel;
- (void)resetShip;

Now let's actually code these methods in "GameScene.m." As you can see, you'll have to create three asteroid graphics (small, medium, and large) as well as a bullet graphic and add them to your project. If you don't add the files to the project (for example, if you just put the files in the project directory structure), the game will crash when you call these methods.

- (void)createAsteroidAt:(CGPoint)position withSize:(int)size
{
	// Decide which image file to use for the new asteroid
	NSString *imageFile;
	switch (size)
	{
		default:
		case kAsteroidLarge:
			imageFile = @"asteroid-large.png";
			break;
		case kAsteroidMedium:
			imageFile = @"asteroid-medium.png";
			break;
		case kAsteroidSmall:
			imageFile = @"asteroid-small.png";
			break;
	}

	// Create a new asteroid object using the appropriate image file
	Asteroid *a = [Asteroid spriteWithFile:imageFile];

	// Set the size and position
	a.size = size;
	a.position = position;

	// Random numbers - see http://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c
	a.velocity = ccp((float)(arc4random() % 100) / 100 - 1, (float)(arc4random() % 100) / 100 - 1);

	// Add asteroid to organizational array
	[asteroids addObject:a];

	// Add asteroid to layer
	[self addChild:a];
}

- (void)createBullet
{
	// Create a new asteroid object using the appropriate image file
	Bullet *b = [Bullet spriteWithFile:@"bullet.png"];

	// Set the bullet's position by starting w/ the ship's position, then adding the rotation vector, so the bullet appears to come from the ship's nose
	b.position = ccp(ship.position.x + cos(CC_DEGREES_TO_RADIANS(ship.rotation)) * ship.contentSize.width, ship.position.y - sin(CC_DEGREES_TO_RADIANS(ship.rotation)) * ship.contentSize.height);

	// Set the bullet's velocity to be in the same direction as the ship is pointing, plus whatever the ship's velocity is
	b.velocity = ccp(cos(CC_DEGREES_TO_RADIANS(ship.rotation)) * 2 + ship.velocity.x, -sin(CC_DEGREES_TO_RADIANS(ship.rotation)) * 2 + ship.velocity.y);

	// Add bullet to organizational array
	[bullets addObject:b];

	// Add bullet to layer
	[self addChild:b];
}

- (void)startLevel
{
	// Reset the ship's position, which also removes all bullets
	[self resetShip];

	// Get window size
	CGSize windowSize = [CCDirector sharedDirector].winSize;

	// Create asteroids based on level number
	for (int i = 0; i < (currentLevel + 2); i++)
	{
		// Random numbers - see http://stackoverflow.com/questions/160890/generating-random-numbers-in-objective-c
		CGPoint randomPointOnScreen = ccp((float)(arc4random() % 100) / 100 * windowSize.width, (float)(arc4random() % 100) / 100 * windowSize.height);

		[self createAsteroidAt:randomPointOnScreen withSize:kAsteroidLarge];
	}
}

- (void)resetShip
{
	// Reset ship position/speed
	CGSize windowSize = [CCDirector sharedDirector].winSize;
	ship.position = ccp(windowSize.width / 2, windowSize.height / 2);
	ship.velocity = ccp(0, 0);

	// Remove all existing bullets from layer
	for (Bullet *b in bullets)
		[self removeChild:b cleanup:NO];

	// Empty out bullet-storing array
	[bullets removeAllObjects];
}

That's a lot of code to break down, but the comments and descriptive method names should help in understanding what each block does. The first question you probably have would be, "what's up with kAsteroidLarge, kAsteroidMedium, and kAsteroidSmall?" Those are constants that we will define in a configuration file. They represent the integers 1, 2, and 3, but are more readable and make more sense than plain ol' numbers. Open the "GameConfig.h" file that's in your project, and add the following definitions to the end of it:

#define kAsteroidLarge 1
#define kAsteroidMedium 2
#define kAsteroidSmall 3

Now make sure to import that config file into GameScene.m by adding #import "GameConfig.h" at the top, near the other import declarations. Also, make sure to import the header files for the asteroid and bullet classes as well. In total, your import list should look like this:

#import "GameScene.h"
#import "Ship.h"
#import "Asteroid.h"
#import "Bullet.h"
#import "GameConfig.h"

Whew! That's a lot of nonsense to plow through. Let's make two more small changes before we check our progress so far. The first is to add a bit more code into our "init" method:

// Initialize arrays that will be used to store other game objects
asteroids = [[NSMutableArray array] retain];
bullets = [[NSMutableArray array] retain];

// Call method which positions the ship and creates asteroids
[self startLevel];

Warning: most cocos2d objects are auto-released from memory when they are no longer needed, but if you use other Objective-C data structures, you'll have to manually release them from memory. You can see that we initialize and "retain" the two arrays in memory, so we'll have to deallocate them when the GameScene object is deallocated. Create a "dealloc" method at the end of "GameScene.m" and put the following into it:

- (void)dealloc
{
	// Release all the pointers that have been retained in this class
	[asteroids release];
	[bullets release];

	[super dealloc];
}

The next is to make a call to the "createBullet" method in the appropriate place, so that the ship can shoot. In the "ccTouchesEnded" method, find this block of code, and add [self createBullet];:

// If the distance moved (in pixels) is small enough, consider the gesture a tap
if (distance < 5)
{
	// Shoot!
	[self createBullet];
}
// Otherwise, it's a swipe
else
{
	// Use distance of swipe as a multiplier for the ship velocity (longer swipe, go faster)
	ship.velocity = ccp(cos(CC_DEGREES_TO_RADIANS(ship.rotation)) * distance / 100, -sin(CC_DEGREES_TO_RADIANS(ship.rotation)) * distance / 100);
}

Save, build and run the project, and you should see some asteroids roaming around the screen. And you should be able to tap the screen to shoot in the direction your ship is facing. The last thing we need to do for this part of the tutorial is add collision detection between the asteroids, ship, and bullets. This is where those arrays that we created come in handy. We can iterate over the arrays and use them to check for collisions. To do that, we'll create an "update" method that will get called at each frame.

// Mostly handles collision detection
- (void)update:(ccTime)dt
{
	// If there are no more asteroids left, increment the level counter and start the new level
	if ([asteroids count] == 0)
	{
		currentLevel++;
		[self startLevel];
	}

	// Check for collisions vs. asteroids
	for (Asteroid *a in asteroids)
	{
		// Check if asteroid hits ship
		if ([a collidesWith:ship])
		{
			// Reset ship position
			[self resetShip];

			// Remove the asteroid the ship collided with
			[asteroids removeObject:a];

			// Remove asteroid sprite from layer
			[self removeChild:a cleanup:NO];

			// This asteroid is gone, so go to the next one - no need to check if a bullet has also hit it
			continue;
		}

		// Check if asteroid hits bullet, or if bullet is expired
		for (Bullet *b in bullets)
		{
			if (b.expired)
			{
				// Remove the bullet from organizational array
				[bullets removeObject:b];

				// Remove bullet sprite from layer
				[self removeChild:b cleanup:NO];
			}
			else if ([a collidesWith:b])
			{
				// Remove the asteroid the bullet collided with
				[asteroids removeObject:a];

				// Remove asteroid sprite from layer
				[self removeChild:a cleanup:NO];

				// Remove the bullet the asteroid collided with
				[bullets removeObject:b];

				// Remove bullet sprite from layer
				[self removeChild:b cleanup:NO];

				// Create two new asteroids in the place of the destroyed one, if the destroyed one wasn't already the smallest
				if (a.size < kAsteroidSmall)
				{
					for (int i = 0; i < 2; i++)
						[self createAsteroidAt:a.position withSize:a.size + 1];
				}

			}	// End bullet/asteroid collision check
		}	// End bullet loop
	}	// End asteroid loop
}

The first thing "update" does is check to see if the asteroid array is empty; if it is, then the next level starts. If the asteroid array is not empty, it loops through each asteroid, checking to see if the asteroid hits the ship (reset ship and remove asteroid if so), or any of the bullets on screen (remove bullet and asteroid, then create two more asteroids if the asteroid size != small). The only thing we need to do now is tell the layer that we want the "update" method to be scheduled to run every frame. It's pretty simple... in the "init" method of the layer, just add [self scheduleUpdate]; and you're good to go.

Build and run the project again, and all the objects should react to each other in the correct way. If not, perhaps you (or I) have made a mistake somewhere... let me know in the comments, and I'll try to help out. You can also download a .zip with all the project files for reference.

Edit: check out part three of the tutorial series!

· 4 comments