Let’s make things move.
If you’ve read my books or seen me speak, you’ll know I love balls. By which, I mean that I love to make circular objects for the purpose of demonstrating basic motion concepts. So I made a ball named “Ball.png” and added this to my Resources.
Now I’m thinking that this ball should be in its own layer. So I created a MotionLayer class. Interface:
[c]#import
#import “Layer.h”
@interface MotionLayer : Layer {
}
@end[/c]
We’ll get to the implementation soon. But first lets add the layer to the MainScene:
[c]#import “MainScene.h”
#import “Sprite.h”
#import “cocos2d.h”
#import “PictureLayer.h”
#import “MotionLayer.h”
@implementation MainScene
– (id) init
{
self = [super init];
if (self != nil) {
Sprite *background = [Sprite spriteWithFile:@”background.png”];
background.position = ccp(240, 160);
[self addChild:background];
PictureLayer *pictureLayer = [PictureLayer node];;
[self addChild:pictureLayer];
MotionLayer *motionLayer = [MotionLayer node];
[self addChild:motionLayer];
}
return self;
}
@end[/c]
Simple enough. The project should compile at this point, but won’t do anything different, since the MotionLayer doesn’t add anything to the picture. Let’s move on to the ball itself. We could just make a new sprite with the ball image and move that around. But let’s actually give the ball some behavior of its own. For that, we’ll need a Ball class. Create a new class Named “Ball” that extends Sprite, and add the following to its interface:
[c]#import
#import “Sprite.h”
@interface Ball : Sprite {
float vx;
float vy;
float gravity;
float bounce;
}
@property float vx;
@property float vy;
– (void)update:(ccTime)dt;
@end[/c]
Now, if you’ve read Making Things Move, or seen my talks or read my tutorials, or even if you’re just wicked smart, you’ll see where this is going. vx and vy are velocity on the x and y axes. bounce is going to be used to reverse velocity when the ball hits an edge, and gravity is going to pull the ball “down”. vx and vy are properties so they can be set from outside, and there’s an update method, which we’ll get to shortly. Here’s the implementation:
[c]#import “Ball.h”
#import “cocos2d.h”
@implementation Ball
@synthesize vx;
@synthesize vy;
– (id) init
{
self = [super init];
if (self != nil) {
gravity = -20.0;
bounce = -0.7;
Sprite *ball = [Sprite spriteWithFile:@”Ball.png”];
[self addChild:ball];
[self schedule:@selector(update:)];
}
return self;
}
– (void)update:(ccTime)dt
{
float x = self.position.x;
float y = self.position.y;
vy += gravity;
x += vx * dt;
y += vy * dt;
if(x + 20 > 480)
{
x = 460;
vx *= bounce;
}
else if(x < 20)
{
x = 20;
vx *= bounce;
}
if(y + 20 > 320)
{
y = 300;
vy *= bounce;
}
else if(y < 20)
{
y = 20;
vy *= bounce;
}
self.position = ccp(x, y);
}
@end[/c]
In the init method we are setting values for gravity and bounce, and calling this schedule method. schedule is kind of like enterFrame and a timer, all in one. If you just call schedule with a selector, that selector will be called repeatedly, at a rate set by the Director. This defaults to 60 times per second. There is an alternate version of the method with the signature:
schedule:(SEL)s interval:(ccTime)seconds
This allows you to set your own interval rate for the selector to get called. Here we are passing in our update method. Now let's look at the signature of that again:
- (void)update:(ccTime)dt
You'll see that it gets passed a value called dt, of type ccTime, which is just a typedef for float. This represents the amount of time that has elapsed since the last update. This is very useful for accurate timing of motion. Multiply your velocities by that value each time, and if the frame rate lags for some reason, your motion will continue along at the same perceived speed. This is because a lower frame rate will result in a higher elapsed time between frames and make each frame's velocity exactly that much faster to compensate. The other side of this is that you have to change the size of the numbers you use for velocity. If you are just adding velocity straight to position on each frame, then your velocity is basically in terms of "pixels per frame". If you are multiplying it by the elapsed time before adding it to position, then you are dealing with "pixels per second". 5 pixels per frame is the same as 300 pixels per second at 60 fps (60 x 5 = 300).
I'm not going to say any more about the update code. If you don't understand it, buy my book. Actually, just buy my book anyway. 🙂
Though I should note that true to OpenGL, coco2d's y coordinates are upside down from what you might be used to in Flash or even just plain Cocoa. In other words, positive y is up, negative y is down. The orgin is in the lower left corner, not the upper left. In other words, it's like regular Cartesian Coords.
OK... now back to the MotionLayer where we create this ball. Here's the implementation, finally:
[c]- (id) init
{
self = [super init];
if (self != nil) {
Ball *ball = [Ball node];
ball.position = ccp(200, 200);
ball.vx = 300;
ball.vy = 200;
[self addChild:ball];
}
return self;
}
@end[/c]
All we are doing here is creating a ball, positioning it, setting its initial velocity, and adding it to the layer.
Run this baby and behold the bouncing ball!
Now we talked a bit about frame rate, so I want to show you something cool. Go back to the main app delegate, and after all the other Director setup calls, add this line:
[[Director sharedDirector] setDisplayFPS:YES];
And look what we have here:
A nice little fps meter down in the lower left corner! Sweet. And that explains why we needed to add that fps_image.png to the project. This will show you the exact frame rate of your game at any given time. If you start adding too much motion and too many objects and you feel it’s getting a little choppy, this will tell you without guesswork.
Also, what if you don’t want to run at the default 60 fps? You set that in the Director as well. Say you wanted 30 fps. Add this line:
[[Director sharedDirector] setAnimationInterval:1.0 / 30.0];
And you’ll see in the fps meter, that’s what it’s running at.
Another neat thing about using schedule instead of NSTimer or some other animation method, is that you can now control ALL animation in the game directly through the Director, via the pause, resume, stopAnimation, and startAnimation methods. I’ll just show you the declarations for those methods to give you and idea of what they do:
[c]/** Pauses the running scene.
The running scene will be _drawed_ but all scheduled timers will be paused
While paused, the draw rate will be 4 FPS to reduce CPU consuption
*/
-(void) pause;
/** Resumes the paused scene
The scheduled timers will be activated again.
The “delta time” will be 0 (as if the game wasn’t paused)
*/
-(void) resume;
/** Stops the animation. Nothing will be drawn. The main loop won’t be triggered anymore.
If you wan’t to pause your animation call [pause] instead.
*/
-(void) stopAnimation;
/** The main loop is triggered again.
Call this function only if [stopAnimation] was called earlier
@warning Dont’ call this function to start the main loop. To run the main loop call runWithScene
*/
-(void) startAnimation;[/c]
Neato, huh?
Well that covers lesson 3. And that really is it for today. I’ll see what I can dig up for tomorrow.
Hi Keith, thanks for these great tutorials. Honestly I can’t believe you came with that just today, when I have to choose between cocos and unity. Both worlds are great and it’s not an easy decision. If you’d ask me yesterday I would tell you Unity is the way for me, mainly because I am completely lost in objc brackets. Well at the moment I am on the beginning of the quest again. So thanks man, you make it easier :]] Looking forward to the next parts. Cheers!
(and as a side note, I wouldn’t be surprised if you’ll end your flash carrier, same as what seems a next logical step for me)
I don’t see how a decision between cocos2d and Unity could be a tough one. They are two totally different things. Are you going to do a 2d or a 3d project?
cocos2d is 2d only. Unity is 3d. I imagine you can do 2d in Unity, but it’s such overkill. I can’t imagine paying that amount of money for Unity, and winding up with the huge file sizes it creates, just for 2d.
On the other hand, if you’re planning on doing 3d, there’s really no question. Unity.
Looks really good!
I’m excited to see how fast Cocos2D can draw multiple objects with the FPS shown.
I guess the next step to show us would be how to handle animated sprites/states properly. Keep teaching us and we’ll keep coming back 🙂
It looks like this is the best one that I’ve encountered. These tutorials are great resources. I’d like to see more of this.
What is the difference between moving a sprite by modifying the position in a scheduled selector vs moving via a scripted action? Very curious because I’m new to cocos2d/iphone dev and I’m used to traditional game programming where you update everything on a per frame basis.
great posts, thanks!
This is just fantastic! Loving these tutorials and neat tricks. Heading for part 4 now!
Great tutorial and thanks for the info on the cocos2d engine. Have you done anything with collisions or does it have that built in? I see in your new game you have some collisions but you could just be checking the x/y with height and width to make the bounding box. My question is, if you know if it has collision detection and how that affects assets in the Chimpunk physics simulation. Guess I’ll be having some fun this weekend!
Thanks for the great tutorials.
Just one quick question. When you create the ball sprite with: Sprite *ball = [Sprite spriteWithFile:@”Ball.png”];
Shouldn’t you send a retain message? Like: Sprite *ball = [[Sprite spriteWithFile:@”Ball.png”] retain];
Cheers,
Joe
Hey dude, great tutorials from the flash perspective – please keep them up 🙂
One question if you have time… Have you ever had issues building/running your cocos2d apps on device?
My app works great in the simulator but bums out when I try and install it on the device – with like 59 errors along the lines of “symbol(s) not found” and it doesn’t even install.
I’m guessing it’s some problem to do with the cocos2d classes not being found/included in the build but really I’ve got no idea 🙁
Cheers dude.
Keith, thanks for tutorials, and thanks again for your excellent workshop at Flash on Tap. Pleasure to meet you, and hope to see you there next year!
Thanks for the tutorials Keith! Makes it a lot easier for newbies like me! 🙂
I am having a crash in my MotionLayer class
@implementation MotionLayer
– (id) init
{
self = [super init];
if (self != nil) {
Ball *ball = [Ball node];
/*It crashes here.–>*/ ball.position = ccp(200, 200);
ball.vx = 300;
ball.vy = 200;
[self addChild:ball];
}
return self;
}
Problem solved.
i have the same problem as andrew, i can’t seem to find the problem.
hey Erik, I had the same problem.
make sure to
#import “cocos2d.h”
I am having an issue with the program crashing also.
For me, it seems to be crashing on the line:
Ball *ball = [Ball node];
It compiles fine but when running, it crashes when it gets to that line.
I have imported cocos2d, I can’t seem to figure out the problem.
Any suggestions?
#import “MotionLayer.h”
#import “Ball.h”
#import “cocos2d.h”
@implementation MotionLayer
– (id) init
{
self = [super init];
if (self != nil) {
Ball *ball = [Ball node];
ball.position = ccp(200, 200);
ball.vx = 300;
ball.vy = 200;
[self addChild:ball];
}
return self;
}
@end
Make sure that your image name is “Ball.png” and not “ball.png”
This solved my problem.
Great tutorials, sincerely Thank You
Bump to Daniel, same problem here, uncaught exception… The ball appears on screen but it doesn’t move and crashes immediately.
Thanks keith! Awesome tutorial btw!
Found the problem…
was missing a column in:
– (id) init
{
self = [super init];
if (self != nil) {
gravity = -20.0;
bounce = -0.7;
Sprite *ball = [Sprite spriteWithFile:@”Ball.png”];
[self addChild:ball];
[self schedule:@selector(update:)]; // (update:) <– right there
}
return self;
}
@ Daniel B :
make certain that your Sprite exists (i.e. it’s being created with a valid image file… i had my case mixed up for my ball and it threw an unhandled exception.
Thanks for this great tut, and for you amazing books they had been a huge learning experience
what is the “main loop” , which handle animation,shedule etc. multithreading invovled ?