Cocos2D - Moving Sprites with the Accelerometer

In this cocos2d tutorial we will move a sprite on the screen with the accelerometer if the device.To start, we are going to use the cocos2d ARC templates, which are created by Steffen Itterheim, the author of the “Learn Cocos2d” book. Download the templates at GitHub. Unpack the templates and make a copy of the cocos2d-2.x-ARC-iOS folder.

Open the cocos2d-2.x-ARC-iOS project file. Now you can rename the project from within Xcode, which will also rename the .xcodeproj file. In the Project Navigator select the cocos2d-2.x-ARC-iOS project  and edit it with a delayed double-click. Enter movingSpriteWithAccelerometer as the name for the project. Xcode will ask you to confirm renaming several items. Click the rename button.

In the template there is already a HelloWorldLayer scene created, we are going to create a new scene and remove the HelloWorlLayer later.  Add a new File -> iOS -> cocos2d v2.x -> CCNode class. Make it a subclass of CCLAyer and name it GameLayer.

In GameLayer.h add the scene method definitition

@interface GameLayer : CCLayer { }

+(id)scene;

@end

In GameLayer.m implement the scene and init methods.

+(id)scene 
{
CCScene *scene = [CCScene node];
CCLayer *layer = [GameLayer node];
[scene addChild:layer];

return scene;
}

- (id)init
{
if ((self = [super init]))
{
}

return self;
}

Now, remove the HelloWorldLayer class from the Project. In appDelegate.m are still two references to the HelloWorldLayer class, replace these lines with GameLayer.

#import "GameLayer.h"
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions
{
..
[director_ pushScene:[GameLayer scene]];
}

Build and Run, you will see a blank scene.

blank_scene_cocos2d

blank_scene_cocos2d

Next, we'll add a sprite to the screen. Download the following zip-file and extract the spaceship files. There is a normal and a retina image of a spaceship. Files with the hd suffix are used automatically by cocos2d on retina devices, otherwise the normal image is used.

Change the init method to

 

- (id)init 
{
if ((self = [super init]))
{
self.isAccelerometerEnabled = YES;

player = [CCSprite spriteWithFile:@"spaceship.png"];
[self addChild:player];

CGSize screenSize = [CCDirector sharedDirector].winSize;
player.position = CGPointMake(screenSize.width / 2, screenSize.height / 2);
}

return self;
}

We enable the accelerometer of the device and we put the spaceship in the center of the screen. We need a velocity variable to keep track oft the current speed of the movement. In GameLayer.h, add the following variable.

@interface GameLayer : CCLayer 
{
..
CGPoint playerVelocity;
}

Next, in GameLayer.m implement the accelerometer:didAccelerate method. This method will be called when a accelerometer event occurs.

- (void)accelerometer:(UIAccelerometer *)accelerometer didAccelerate:(UIAcceleration *)acceleration 
{
float deceleration = 1.0f;
float maxVelocity = 50;

playerVelocity.x = playerVelocity.x * deceleration + acceleration.x;
playerVelocity.y = playerVelocity.y * deceleration + acceleration.y;

if (playerVelocity.x > maxVelocity)
{
playerVelocity.x = maxVelocity;
}
else if (playerVelocity.x < -maxVelocity)
{
playerVelocity.x = -maxVelocity;
}


if (playerVelocity.y > maxVelocity)
{
playerVelocity.y = maxVelocity;
}
else if (playerVelocity.y < -maxVelocity)
{
playerVelocity.y = -maxVelocity;
}
}

The velocity will be adjusted with the current acceleration. The deceleration variable will be used to keep the acceleration movement fluid. In the if..else loops we check if the velocity don't exceed the maximum. Next implement the update method.

- (void)update:(ccTime)delta 
{
CGPoint pos = player.position; pos.x += playerVelocity.x;
pos.y += playerVelocity.y;

CGSize screenSize = [CCDirector sharedDirector].winSize;

float imageWidthHalved = player.texture.contentSize.width * 0.5f;
float imageHeightHalved = player.texture.contentSize.height * 0.5f;

float leftBorderLimit = imageWidthHalved;
float rightBorderLimit = screenSize.width - imageWidthHalved;
float topBorderLimit = imageHeightHalved;
float bottomBorderLimit = screenSize.height - imageHeightHalved;

if (pos.x < leftBorderLimit)
{
pos.x = leftBorderLimit;
playerVelocity.x = 0;
}
else if (pos.x > rightBorderLimit)
{
pos.x = rightBorderLimit;
playerVelocity.x = 0;
}

if (pos.y < topBorderLimit)
{
pos.y = topBorderLimit;
playerVelocity.y = 0; }
else if (pos.y > bottomBorderLimit)
{
pos.y = bottomBorderLimit;
playerVelocity.y = 0;
}

player.position = pos;
}

We define the current border limits and then we check the current player position so the sprite cannot be moved offscreen. Only one more thing left to do. Add the following line to the end of the init method

[self scheduleUpdate]

Build and Run, tilt the device to test the movement.