We have not discussed much about the nature of the game. Believe me it is relatively routine. The green player will try to intercept a falling red target.
The falling red target will start at random horizontal positions at the top of the screen and drop to the bottom of the screen. Only one red target falls at a time. The goal is to get the green player to intercept the falling red target.
In this lesson, we will add the falling red target but will not handle the collision. However, you can get some practice trying for later when you add a collision with sound and points.
[ad name=”Google Adsense”]
Lesson Downloads
- Game piece images for project
- IPhone images for project. Icons and splash screen.
- Completed Project. This is built in Kobold2d 1.0.1.
Step 1 – Build an Empty-Project Template
You can continue with the Lesson 2 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 3 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.
Step 2 – Set Properties in config.lua
There are no new property changes from lesson 2. All are included here for you to follow along.
--[[ * 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.LandscapeRight, 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 – Add Moving Target Properties to GameLayer.h File
A CCSprite Cocos2d object is responsible for our target shown on line 14. CCSprite represents a 2d image and inherits from CCNode.
The movingTargetDuration float is the speed of the target fall from the top of the screen.
/* * 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
Step 4 – Integrate Moving Target Into to GameLayer.m File
I am going to discuss specific sections of the GameLayer.m file in this post. Meantime for the impatient, like me, here here is the full GameLayer.m source code for your copy and paste convenience.
/* * 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 3" 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:)]; // Set the starting position and start movingTarget play sequence [self startMovingTargetSequence]; } @end
These methods are private and so we are declaring them in the implementation file. They are for controlling your moving target. We will talk more about them one at time.
#import "GameLayer.h" @interface GameLayer (PrivateMethods) -(void) initMovingTarget; -(void) movingTargetUpdate:(ccTime)delta; -(void) startMovingTargetSequence; -(void) endMovingTargetSequence; @end
Step 5 – Integrate Moving Target into GameLayer.h File init method
To keep the eye clear which lesson you are viewing, change the subtitle on line 49.
You have the initMovingTarget method to handle creating our moving target. Line 58 is added to call the initMovingTarget.
You need to place the moving target at various horizontal positions on the top of the screen. We are using CCRANDOM_0_1() for the randomization. CCRANDOM_0_1() needs a one time seeding shown on line 56.
@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 3" 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; }
[ad name=”Google Adsense”]
Step 6 – Add the initMoving Target Method
You can see on line 123 the red_ball.png is the graphic image for the moving target.
On line 125 the moving target is added. The z order is zero for this lesson. The tag value I mentioned in Lesson 1 as another way to get references to sprite objects. We are not using tags but since the game player has a tag value of 1, the moving target needed a different number. How about 2?
One line 127 the startMovingTargetSequence method for the initial start position of the moving target is called.
Line 128 sets the moving target moving duration value. For now the value never changes. But to add some variety to the game in a future lesson, we will decrease the value to speed up the moving target drop speed. The point is there needs to be a place to set the initial value.
One line 130 a standard safety precaution cancels the movingTargetUpdate method from being called. This method gets the target moving and we will look at it in a bit. At this point it is not possible to call initiMovingTarget more than once. However later it might happen this initMovingTarget method is called more than once and we have stopped any sequence from running.
Finally line 132 gets the moving target into motion. The movingTargetUpdate method defines that motion.
-(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]; }
Step 7 – Add the startMovingTargetSequence Target Method
This method through line 144 provides the starting point for the moving target object.
Then on line 146, the movingTargetUpdate method is scheduled for call. That is where the animation sequence for the moving target is created.
-(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]; }
Step 8 – Add the movingTargetUpdate Target Method
The movingTargetIUpdate method creates two CCAction objects.
The first is CCMoveTo on line 158. This is the destination of the moving target. The current y position and the belowScreenPosition y are traversed over the movingTargetMoveDuration value.
Line 160 creates the second CCCallFuncN to identify calling the endMovingTargetSequence method to handle the end of the animation.
These two CCAction objects are then bundled on line 162 into a CCSequence CCAction. CCSequence is essentially an array.
Line 164 runs the actions in the CCSequence.
So the CCMoveTo CCAction is processed over the movingTargetMoveDuration and on its conclusion the CCCallFuncN CCAction invokes the endMovingTargetSequence method for cleanup.
-(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]; } }
[ad name=”Google Adsense”]
Step 9 – Add the endMovingTargetSequence Target Method
This is the end of the moving target animation. The movingTargetUpdate method is removed as a scheduled method on line 171,
Line 172 then sets up for the next moving target for a repeat performance.
-(void) endMovingTargetSequence { NSLog(@"endMovingTargetSequence"); // Terminate running the moveTargetUpdate interval. [self unschedule:@selector(movingTargetUpdate:)]; // Set the starting position and start movingTarget play sequence [self startMovingTargetSequence]; }