How to Program Dpad Joystick Touch Controls for iPhone Games

Screenshot of QuexlorLite showing the joystick aka D-pad
A screenshot of the iPhone game QuexlorLite showing it’s D-pad / joystick controls

A Walkthrough Tutorial for Objective C / iPhone SDK Beginners

So you want to learn how to program an up, down, left, right D-pad for your iPhone game, eh? All right. Let’s get started.

The Graphics

First thing to do is make the graphics. Use your favorite graphical editor (mine is Photoshop) and come up with something like this:

The two images we will use to build a joystick D-pad

This is a picture of the two elements of the joystick laid out next to each other. The first element is the D-pad background, which has arrows for up/north, right/east, down/south, and left/west. The second is the joystick’s button that moves with your thumb. If you are artistically challenged, then feel free to crop these elements from the image (I promise I won’t tell anybody).

Loading the Graphics

At this point, you’re going to need a working game engine. I highly recommend Cocos2D because it is easy to use and has plenty of support from it’s author (his name is Ricardo) and the Cocos2D community.

Once you have a game engine you are also going to need some basic game code to start with. If you are new to all this, you can download the source code to the iPhone game QuexlorLite and use it as a base for your own game.

Now that you have a game to work with, the first thing we need to do is add a few member variables to our Heads-Up Display (HUD) Layer:


// in Hud.h
@interface HudLayer : CCLayer
	{
		CCSprite* joypad,*joybtn,
		CGPoint touchPos;
		float joybtnDistSquared,joybtnAngle;
		BOOL isMovingJoybtn;
	}

Then we load the graphics:


-(id) init
{
	self = [super init];
	if(self != nil)
	{
		// set this so we can register with touch dispatcher
		isTouchEnabled = YES;
		
		// load our joystick background
		joypad = [[CCSprite spriteWithFile:@"joypad.png"] retain];
		joypad.position = ccp(70,70);
		joypad.opacity = 0;
		[self addChild:joypad z:1];
			
		// load joypad button
		joybtn = [[CCSprite spriteWithFile:@"joybtn.png"] retain];
		joybtn.position = ccp(70,70);
		joybtn.opacity = 0;
		[self addChild:joybtn z:2];
	}
	return self;
}

The graphics are loaded using Cocos2D’s handy spriteWithFile method. This method returns a CCSprite pointer that we retain (and release in the dealloc).

The joystick button is placed on top of the joystick background, ready for us to move it with our thumb.

The Professional Touch

We set the opacity of each joystick element to zero (effectively making it transparent) and then nicely fade it in and make it blink when the game starts using code like this:


[joypad runAction:[CCSequence actions:
	[CCDelayTime actionWithDuration:1.5f],
	[CCFadeTo actionWithDuration:1.5f opacity:192],
	[CCBlink actionWithDuration:0.66f blinks:3],
	nil]];

This launches a sequence of actions: 1) delay a second and a half, 2) fade in to 75% opacity over a second and a half, and 3) blink three times over two thirds of a second.

Responding to Touch Events

In order to move the joystick button, we need to respond to touch events. Here’s a little blast of code which does that:


-(BOOL) ccTouchBegan:(UITouch*)touch withEvent:(UIEvent*)event
{
	// started touching somewhere in the joystick pad?
	if( !isMovingJoybtn && [self isTouchingJoybtn:touch] )
	{
		touchPos = joypad.position;
			
		// initially move the joystick button to touch point
		[self moveJoybtn:touch];

		// we are now moving the joystick button
		isMovingJoybtn = YES;
	}
		
	// returning YES means we claim the touch
	// (always return YES because we want to claim
	// touching the whole screen when player dies)
	return YES;
}

-(void) ccTouchMoved:(UITouch*)touch withEvent:(UIEvent*)event
{
	// keep moving the joystick button
	if( isMovingJoybtn )
		[self moveJoybtn:touch];
}
	
-(void) ccTouchEnded:(UITouch*)touch withEvent:(UIEvent*)event
{
	// stop moving joystick
	[self stop];
}

-(void) ccTouchCancelled:(UITouch*)touch withEvent:(UIEvent*)event
{
	// stop moving joystick
	[self stop];
}

When we first touch the iPhone’s screen, we get the ccTouchBegan message. As long as we are still touching the screen, we continue to get the ccTouchMoved message. When we finally let up, we get ccTouchEnded. Pretty self-explanatory, eh?

Our ccTouchBegan method is where most of the magic happens. We basically check to see if the touch point is within the boundaries of the joystick background using the isTouchingJoybtn method, and if so we store information on the touch and begin the joystick button’s movement.

The Pythagorean Theorem

A squared plus B squared equals C squared

Since the joystick background is a circle, we need some special code to determine whether our thumb is touching it. A rectangle test would be far too square. Thankfully, we can return to our roots and deploy a simple geometrical equation to help us. Anybody remember A squared plus B squared equals C squared?

In our case, A is the X distance from the thumb to the joystick center, B is the Y distance, and C is the diagonal distance (or radius).

The distanceFromJoypadSquared method simply calculates this radius. We then use it in the isTouchingJoybtn method to finally determine YES or NO whether the thumb is touching the joystick:


-(CGFloat) distanceFromJoypadSquared:(CGPoint)p
{
	// remember a squared + b squared = c squared?
	return ccpLengthSQ(ccpSub(p, joypad.position));
}

-(BOOL) isTouchingJoybtn:(UITouch*)touch
{
	// the joypad is a circle, so return yes if
	// distance from center is less than radius
	CGFloat c2 = [self distanceFromJoypadSquared:touchToPoint(touch)];
	return (c2 < kJoypadRadiusSquared);
}

Moving the Joystick Button

When we move the joystick button, we need to 1) make sure the joystick button does not move outside the boundaries of the joystick background, and 2) store the angle and distance of the joystick button so that we can use it to control our main character.


-(void) moveJoybtn:(UITouch*)touch
{
	// get previous touch point and determine offset
	CGPoint prev = (isMovingJoybtn ? touchToPreviousPoint(touch) : joybtn.position);
	CGPoint offset = ccpSub(touchToPoint(touch), prev);

	if( offset.x || offset.y )
	{
		// get new purported joybtn position and delta to joypad center
		touchPos = ccpAdd(touchPos, offset);
		CGPoint delta = ccpSub(touchPos, joypad.position);
		CGPoint newPos = touchPos;

		// get its angle and distance
		joybtnAngle = ccpToAngle(delta);
		joybtnDistSquared = [self distanceFromJoypadSquared:newPos];

		// clamp it inside the joypad
		if( joybtnDistSquared > kJoypadRadiusSquared )
		{
			newPos = ccpAdd(joypad.position,
				ccpMult(ccpForAngle(joybtnAngle), kJoypadRadius));
			joybtnDistSquared = kJoypadRadiusSquared;
		}
			
		// set it to the new position
		[joybtn setPosition:newPos];
	}
}

-(void) stop
{
	isMovingJoybtn = NO;
	joybtn.position = joypad.position;
}

The moveJoybtn method starts by calculating the previous touch position and the offset to the current touch position. It then adds the offset to the existing joystick button position to calculate the delta, or the desired new joystick button position. The angle of this delta is then saved for later and the joystick button is clamped within the boundaries of the joystick background.

Controlling the Main Character

We now have one particular member variable which will come in quite handy when we want to move the main character around the screen. It is called joybtnAngle. This angle can be used to smoothly move the character around the screen (like in the video below) or it can be "snapped" to one of the eight compass directions in order to give your game a more retro D-pad feel.

You can download the entire working source code to the game QuexlorLite to see this joystick code in action, complete with the snapping of the joystick angle to one of the eight compass directions.

Enjoy coding!

  • luckysing

    Hi Nat thanks for the joy pad tutorial.
    I wanted to ask is the source code for QuexlorLite available for free.
    Thanks again for the wonderful tutorials

  • Nat

    Hi luckysing. The full source code to QuexlorLite is available as part of purchasing the iPhone Game Kit. Thanks for your thanks, :)
    Nat

  • -->

    Featured Posts


    Hero Bash: the party moba!

    iOS 7 + Cocos2d 2.1 Update

    Making Cross-Platform iPhone & Android Games – Don’t Get Left Behind! (Part 3)

    Recent Posts


    Tuning the Mind

    From Programmer To Artist 10 of 10 – The Beginning

    From Programmer To Artist 9 of 10 – Animation

    The Ultimate Cocos2d-x Platformer Engine

    A cross-platform, parallaxing game engine based on cocos2dx. Includes procedural level generation, scripting, dynamic texturing, realtime multiplayer, virtual economy (in-app purchase) and all the basics like moving platforms, boxes, slopes, water, ladders and artificial intelligence. Write your game code in C++ and level scripts in Javascript. Games built with this kit can be deployed to iOS, Mac, Windows, Android and any of the other platforms supported by cocos2d-x.
    More Info...


    Action-RPG Engine Code & Tutorial

    Create your own RPG, RTS, action or adventure game with this source kit. Learn how to manage tilemaps, characters, AI and more. This kit includes a flexible RPG engine built on cocos2d iphone (now called cocos2d swift), along with a sample RPG project called Quexlor Lite. Also included is a PDF ebook tutorial and bonus royalty-free art package. Written in Objective C, games built with this kit can be deployed to iPhone, iPad and iPod Touch.
    More Info...


    iPhone Game Creation for Beginners

    This game source kit is a hands-on introduction to making games for the absolute beginner. No programming experience is required. The included PDF book walks you through all the basics of how to code with Objective C and will familiarize you with the tools you need to develop an iPhone game. It includes source code and Xcode project files to a simple checkers game built with cocos2d iphone (now cocos2d swift). Also included is documentation, a game template project, support and a bonus artwork package to start your own creation. Games built with this kit can be deployed to iPhone, iPad and iPod Touch.
    More Info...