Categories
Articles

Kobold2D XCode 4 Introduction Tutorial Lesson 4 – Progressively Increase Moving Target Speed


<== Lesson 3 || Lesson 5 ==>

This is a short lesson that adds a tad of challenge to trying to intercept the moving target.

Learn cocos2D Game Development
Learn cocos2D Game Development

The challenge is to increase the speed of each iteration of the moving target. The movingTargetDuration property has the length of time to “fall” from the top of the screen to the bottom. Progressively decreasing that value will increase the rate of movement for each interval.

However you eventually need to tweak game challenges so they are not impossible while remaining doable with practice. In this case a moving target duration below 2 appears to be close to the limit of impossible for the IPad in landscape mode.

Your testing may conclude differently that the speed could get higher. This is just a lesson and hopefully opens you to thoughts for varying the challenge. For example the decrease in the duration could be random number from a range say 0.0 – 0.2. Or you could have a random hyper change in the duration for one iteration only.

One other item that can vary the challenge is game levels. Our tutorial is limited to one level at this lesson. If more levels are added, then variations on the moving target speed, direction and size could be possible properties to add challenge.

Finished Lesson Running on IPad2 Simulator

Lesson Downloads

  1. Game piece images for project
  2. IPhone images for project. Icons and splash screen.
  3. Completed Project. This is built in Kobold2d 1.0.1.

Step 1 – Build an Empty-Project Template

You can continue with the Lesson 3 project and make changes noted here. Otherwise the steps for creating this project from an Empty-Project template are the same as Lesson 1 except to substitute Lesson 4 for Lesson 1 in the project name and to include the red_ball.png and red_ball-hd.png files when you add the game pieces. Then you can use the code presented here.
[ad name=”Google Adsense”]

Step 2 – Set Properties in config.lua

There are no new configuration properties from Lesson 3. Complete config.lua file is included here for your copy convenience.

--[[
* Kobold2D™ --- http://www.kobold2d.org
*
* Copyright (c) 2010-2011 Steffen Itterheim. 
* Released under MIT License in Germany (LICENSE-Kobold2D.txt).
--]]


--[[
* Need help with the KKStartupConfig settings?
* ------ http://www.kobold2d.com/x/ygMO ------
--]]


local config =
{
    KKStartupConfig = 
    {
        -- load first scene from a class with this name, or from a Lua script with this name with .lua appended
        FirstSceneClassName = "GameLayer",

        -- set the director type, and the fallback in case the first isn't available
        DirectorType = DirectorType.DisplayLink,
        DirectorTypeFallback = DirectorType.NSTimer,

        MaxFrameRate = 60,
        DisplayFPS = YES,

        EnableUserInteraction = YES,
        EnableMultiTouch = NO,

        -- Render settings
        DefaultTexturePixelFormat = TexturePixelFormat.RGBA8888,
        GLViewColorFormat = GLViewColorFormat.RGB565,
        GLViewDepthFormat = GLViewDepthFormat.DepthNone,
        GLViewMultiSampling = NO,
        GLViewNumberOfSamples = 0,

        Enable2DProjection = NO,
        EnableRetinaDisplaySupport = YES,
        EnableGLViewNodeHitTesting = NO,
        EnableStatusBar = NO,

        -- Orientation & Autorotation
        DeviceOrientation = DeviceOrientation.LandscapeLeft,
        AutorotationType = Autorotation.None,
        ShouldAutorotateToLandscapeOrientations = NO,
        ShouldAutorotateToPortraitOrientations = NO,
        AllowAutorotateOnFirstAndSecondGenerationDevices = NO,

        -- Ad setup
        EnableAdBanner = NO,
        PlaceBannerOnBottom = YES,
        LoadOnlyPortraitBanners = NO,
        LoadOnlyLandscapeBanners = NO,
        AdProviders = "iAd, AdMob",	-- comma seperated list -> "iAd, AdMob" means: use iAd if available, otherwise AdMob
        AdMobRefreshRate = 15,
        AdMobFirstAdDelay = 5,
        AdMobPublisherID = "YOUR_ADMOB_PUBLISHER_ID", -- how to get an AdMob Publisher ID: http://developer.admob.com/wiki/PublisherSetup
        AdMobTestMode = YES,

        -- Mac OS specific settings
        AutoScale = NO,
        AcceptsMouseMovedEvents = NO,
        WindowFrame = RectMake(1024-640, 768-480, 640, 480),
        EnableFullScreen = NO,
    },
}

return config

Step 3 – GameLayer.h

No changes from Lesson 3. Complete GameLayer.h file is included here for your copy convenience.

/*
 * Kobold2D™ --- http://www.kobold2d.org
 *
 * Copyright (c) 2010-2011 Steffen Itterheim. 
 * Released under MIT License in Germany (LICENSE-Kobold2D.txt).
 */

#import "kobold2d.h"

@interface GameLayer : CCLayer
{
    CCSprite* player;
    CGPoint playerVelocity;
    CCSprite* movingTarget;
	float movingTargetMoveDuration;
}
@end

[ad name=”Google Adsense”]
Step 4 – Reduce Moving Target Animation Duration

Complete GameLayer.m file is included here for your copy convenience. We will just look at the code change up ahead for the endMovingTargetSequence method.

/*
 * Kobold2D™ --- http://www.kobold2d.org
 *
 * Copyright (c) 2010-2011 Steffen Itterheim. 
 * Released under MIT License in Germany (LICENSE-Kobold2D.txt).
 */

#import "GameLayer.h"

@interface GameLayer (PrivateMethods)
-(void) initMovingTarget;
-(void) movingTargetUpdate:(ccTime)delta;
-(void) startMovingTargetSequence;
-(void) endMovingTargetSequence;
@end

// Velocity deceleration
const float deceleration = 0.4f;
// Accelerometer sensitivity (higher = more sensitive)
const float sensitivity = 6.0f;
// Maximum velocity
const float maxVelocity = 100.0f;

@implementation GameLayer

-(id) init
{
	if ((self = [super init]))
	{
        // Enable accelerometer input events.
		[KKInput sharedInput].accelerometerActive = YES;
		[KKInput sharedInput].acceleration.filteringFactor = 0.2f;
        // Graphic for player
        player = [CCSprite spriteWithFile:@"green_ball.png"];
		[self addChild:player z:0 tag:1];
        // Position player        
        CGSize screenSize = [[CCDirector sharedDirector] winSize];
		float imageHeight = [player texture].contentSize.height;
		player.position = CGPointMake(screenSize.width / 2, imageHeight / 2);
		glClearColor(0.1f, 0.1f, 0.3f, 1.0f);
		// First line of title
		CCLabelTTF* label = [CCLabelTTF labelWithString:@"Kobold2d Intro Tutorial" 
                                               fontName:@"Arial"  
                                               fontSize:30];
		label.position = [CCDirector sharedDirector].screenCenter;
		label.color = ccCYAN;
        [self addChild:label];
        // Second line of title
 		CCLabelTTF* label2 = [CCLabelTTF labelWithString:@"Lesson 4"
                                                fontName:@"Arial"
                                                fontSize:24];
		label2.color = ccCYAN;
        label2.position = CGPointMake([CCDirector sharedDirector].screenCenter.x ,label.position.y - label.boundingBox.size.height);
        [self addChild:label2];
        // Seed random number generator
        srandom((UInt32)time(NULL));
        // Initialize our moving target.
        [self initMovingTarget];
        // Start animation -  the update method is called.
        [self scheduleUpdate];;
	}
	return self;
}
-(void) dealloc
{
#ifndef KK_ARC_ENABLED
	[super dealloc];
#endif // KK_ARC_ENABLED
}
#pragma mark Player Movement
-(void) acceleratePlayerWithX:(double)xAcceleration
{
    // Adjust velocity based on current accelerometer acceleration
    playerVelocity.x = (playerVelocity.x * deceleration) + (xAcceleration * sensitivity);
    // Limit the maximum velocity of the player sprite, in both directions (positive & negative values)
    if (playerVelocity.x > maxVelocity)
    {
        playerVelocity.x = maxVelocity;
    }
    else if (playerVelocity.x < -maxVelocity)
    {
        playerVelocity.x = -maxVelocity;
    }
}
#pragma mark update
-(void) update:(ccTime)delta
{
    // Gain access to the user input devices / states
    KKInput* input = [KKInput sharedInput];
    [self acceleratePlayerWithX:input.acceleration.smoothedX];
    // Accumulate up the playerVelocity to the player's position
    CGPoint pos = player.position;
    pos.x += playerVelocity.x;
    // The player constrainted to inside the screen
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    // Half the player image size player sprite position is the center of the image
    float imageWidthHalved = [player texture].contentSize.width * 0.5f;
    float leftBorderLimit = imageWidthHalved;
    float rightBorderLimit = screenSize.width - imageWidthHalved;
    // Hit left boundary
    if (pos.x < leftBorderLimit)
    {
        pos.x = leftBorderLimit;
        // Set velocity to zero
        playerVelocity = CGPointZero;
    }
    // Hit right boundary
    else if (pos.x > rightBorderLimit)
    {
        pos.x = rightBorderLimit;
        // Set velocity to zero
        playerVelocity = CGPointZero;
    }
    // Move the player
    player.position = pos; 
}  
#pragma mark MovingTarget

-(void) initMovingTarget
{
    NSLog(@"initMovingTarget");
    // This is the image
	movingTarget = [CCSprite spriteWithFile:@"red_ball.png"];
    // Add CCSprite for movingTarget
    [self addChild:movingTarget z:0 tag:2];
    // Set the starting position and start movingTarget play sequence
    [self startMovingTargetSequence];
    movingTargetMoveDuration = 4.0f;
   	// Unschedule the selector just in case. If it isn't scheduled it won't do anything.
	[self unschedule:@selector(movingTargetUpdate:)];
	// Schedule the movingTarget update logic to run at the given interval.
	[self schedule:@selector(movingTargetUpdate:) interval:0.1f];
}

-(void) startMovingTargetSequence
{
    NSLog(@"startMovingTargetSequence");
    // Get the window size
    CGSize screenSize = [[CCDirector sharedDirector] winSize];
    // Get the image size
    CGSize imageSize = [movingTarget texture].contentSize;
    // Generate a random x starting position with offsets for center registration point.
    int randomX = CCRANDOM_0_1() * (screenSize.width / imageSize.width);
    movingTarget.position = CGPointMake(imageSize.width * randomX  + imageSize.width * 0.5f, screenSize.height + imageSize.height);
    // Schedule the movingTarget update logic to run at the given interval.
    [self schedule:@selector(movingTargetUpdate:) interval:0.1f];
}

-(void) movingTargetUpdate:(ccTime)delta
{
    // CCSprite->CCNode no sequence of actions running.
    if ([movingTarget numberOfRunningActions] == 0)
    {
        NSLog(@"movingTargetUpdate");
        // Determine below screen position.
        CGPoint belowScreenPosition = CGPointMake(movingTarget.position.x, - ( [movingTarget texture].contentSize.height));
        // CCAction to move a CCNode object to the position x,y based on position. 
        CCMoveTo* moveEnd = [CCMoveTo actionWithDuration:movingTargetMoveDuration position:belowScreenPosition];
        // Call back function for the action.
        CCCallFuncN* callEndMovingTargetSequence = [CCCallFuncN actionWithTarget:self selector:@selector(endMovingTargetSequence)];
        // Create a sequence, add the actions: the moveEnd CCMoveTo and the call back function for the end position.
        CCSequence* sequence = [CCSequence actions:moveEnd,callEndMovingTargetSequence, nil];
        // Run the sequence.
        [movingTarget runAction:sequence];
    }
}
-(void) endMovingTargetSequence
{
    NSLog(@"endMovingTargetSequence");
    // Terminate running the moveTargetUpdate interval.
    [self unschedule:@selector(movingTargetUpdate:)];
    // Decrease the moving target move duration to increase the speed.
    movingTargetMoveDuration -= 0.1f;
    // Moving target move duration is below 2 then hold at 2.
    if (movingTargetMoveDuration < 2.0f)
    {
        movingTargetMoveDuration = 2.0f;
    }
    NSLog(@"movingTargetMoveDuration: %f",movingTargetMoveDuration);
    // Set the starting position and start movingTarget play sequence
    [self startMovingTargetSequence];
}

@end

The endMovingTargetSequence method is called when the moving target exits the bottom of the screen. That is set in the movingTargetUpdate method.

Line 173 of the endMovingTargetSequence method reduces the movingTargetMoveDuration class property by 0.1.

The movingTargetMoveDuration property starts at 4.0 in the initMovingTarget method.

Line 175 – 178 add a governor to prevent the duration from becoming less than 2.0.

As I mentioned earlier, you can be creative with ways to vary the moving target speed.

-(void) endMovingTargetSequence
{
    NSLog(@"endMovingTargetSequence");
    // Terminate running the moveTargetUpdate interval.
    [self unschedule:@selector(movingTargetUpdate:)];
    // Decrease the moving target move duration to increase the speed.
    movingTargetMoveDuration -= 0.1f;
    // Moving target move duration is below 2 then hold at 2.
    if (movingTargetMoveDuration < 2.0f)
    {
        movingTargetMoveDuration = 2.0f;
    }
    NSLog(@"movingTargetMoveDuration: %f",movingTargetMoveDuration);
    // Set the starting position and start movingTarget play sequence
    [self startMovingTargetSequence];
}



<== Lesson 3 || Lesson 5 ==>

[ad name=”Google Adsense”]