Can the cocos2d Director return time? - iphone

I am currently using the cocos2d Director for controlling my animation with the pause, resume, and stopAnimation methods. Is it also possible to use the Director to return the time that the animation has played?
I am currently using this method:
-(void)stopAnimation:(id)sender {
//Timer initialized elsewhere: startTimer = [NSDate timeIntervalSinceReferenceDate];
//Do other method stuff here
[[Director sharedDirector] stopAnimation];
stopTimer = [NSDate timeIntervalSinceReferenceDate];
elapsedTime = (stopTimer - startTimer);
NSLog(#"elapsedTime = %f", elapsedTime);
}

I looked through the Director source and didn't see anything that would help you. I did notice that your code, as written, doesn't take into account the time your animation was paused or when other scenes were playing.
If that is a concern you can keep track of the elapsed time in a tick method that you schedule in your Scene or Layer.
MyLayer.h
#interface MyLayer : Layer {
ccTime totalTime;
}
#property (nonatomic, assign) ccTime totalTime;
MyLayer.m
-(id)init
{
if( (self = [super init]) )
{
[self schedule:#selector(update:)];
}
return self;
}
// deltaTime is the amount of running time that has passed
// since the last time update was called
// Will only be called when the director is not paused
// and when it is part of the active scene
-(void)update:(ccTime)deltaTime
{
totalTime += deltaTime;
}

Related

Quartz 2D MVC Drawing

all. I'm trying to follow a tutorial on making a ball bounce around the screen of an iPhone. The tutorial constructs the application in a MVC scheme. I'm having trouble wrapping my head around this concept when it comes to the drawRect method in the View implementation.
This is my Model header file:
#import <Foundation/Foundation.h>
#import "TestView.h"
#define BALL_SIZE 20.0
#define VIEW_WIDTH 320.0
#define VIEW_HEIGHT 460.0
#interface TestModel : NSObject
{
TestView* ball;
CGPoint ballVelocity;
CGFloat lastTime;
CGFloat timeDelta;
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp;
- (void) checkCollisionWithScreenEdges;
#property (readonly) TestView* ball;
#end
The tutorial instructs me the user to override the init method of NSObject. I've also included the methods for controlling the "animation" logic:
- (id) init {
self = [super init];
if (self) {
ball = [[TestView alloc] initWithFrame: CGRectMake(0.0, 0.0, BALL_SIZE, BALL_SIZE)];
// Set the initial velocity for the ball
ballVelocity = CGPointMake(200.0, -200.0);
// Initialize the last time
lastTime = 0.0;
}
return self;
}
- (void) checkCollisionWithScreenEdges {
// Left Edge
if (ball.frame.origin.x <= 0) {
ballVelocity.x = abs(ballVelocity.x);
}
// Right Edge
if (ball.frame.origin.x >= VIEW_WIDTH - BALL_SIZE) {
ballVelocity.x = -1 * abs(ballVelocity.x);
}
// Top Edge
if (ball.frame.origin.y <= 0) {
ballVelocity.y = abs(ballVelocity.y);
}
// Bottom Edge
if (ball.frame.origin.y >= VIEW_HEIGHT - BALL_SIZE) {
ballVelocity.y = -1 * abs(ballVelocity.y);
}
}
- (void) updateModelWithTime:(CFTimeInterval) timestamp {
if (lastTime == 0.0) {
// initialize lastTime if first time through
lastTime = timestamp;
} else {
// Calculate time elapsed since last call
timeDelta = timestamp - lastTime;
// Update the lastTime
lastTime = timestamp;
[self checkCollisionWithScreenEdges];
// Calculate the new position of the ball
CGFloat x = ball.frame.origin.x + ballVelocity.x * timeDelta;
CGFloat y = ball.frame.origin.y + ballVelocity.y * timeDelta;
ball.frame = CGRectMake(x, y, BALL_SIZE, BALL_SIZE);
}
}
The View implementation file is the following:
#import "TestView.h"
#implementation TestView
- (id)initWithFrame:(CGRect)frame
{
self = [super initWithFrame:frame];
if (self) {
// Initialization code
}
return self;
}
// Only override drawRect: if you perform custom drawing.
// An empty implementation adversely affects performance during animation.
- (void)drawRect:(CGRect) rect {
}
#end
Finally, my View Controller:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
gameModel = [[TestModel alloc] init];
[self.view addSubview:gameModel.ball];
// Set up the CADisplayLink for the animation
gameTimer = [CADisplayLink displayLinkWithTarget:self selector:#selector(updateDisplay:)];
// Add the display link to the current run loop
[gameTimer addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
}
- (void) updateDisplay:(CADisplayLink *) sender {
[gameModel updateModelWithTime:sender.timestamp];
}
OK, so now that I've provided a look at the structure of the code (hopefully I've given enough) I can get to my question. So when I add anything to drawRect a new object is drawn and does not get "animated" by the model logic methods.
Right now I have a bouncing square. When I try to fill the square with an ellipse in drawRect, I get a new object, drawn how I want, that just sits at 0,0 while the bouncing square is still active.
I'm sure I'm missing something really big here, but I've been banging my head against the wall for hours and can't figure it out. Any help would be greatly appreciated!
A couple of things here:
1- Have you overriden the view class in SB to TestView?
2- Looking at your code, I do not see how your model is connected to your View through your controller. Recall from the MVC model, that the Controller is on top and talks down (pyramid style) to the model and the view and so, somewhere in your view controller:
a) you need to instantiate your model
b) get values from your model and pass them to your view variables - which would be called in drawrect
Last but not least, lookup setNeedsDisplay which is the way to call and refresh the view.
Hope this helps without spoiling the assignment.

Move Image in CocoS2d

I am developing game in Cocos2d. In it background of scene moving constantly with x - axis.
I want to understand how can i do it because i am new in cocos2d.
thanks in advance
Take a look to my code - here are the sources. Find the "moving" technique in HelloWorldLayer.m. I have taken a 360 panorama picture and put it to move continuously from right to left, in fullscreen.
In your YourClass.h file:
#import "cocos2d.h"
#interface YourClass : CCLayer
+(CCScene *) scene;
#end
In your YourClass.m file:
#import "YourClass.h"
#implementation YourClass
CCSprite *panorama;
CCSprite *appendix;
//_____________________________________________________________________________________
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
YourClass *layer = [YourClass node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
//_____________________________________________________________________________________
// on "init" you need to initialize your instance
-(id) init
{
// always call "super" init
// Apple recommends to re-assign "self" with the "super" return value
if( (self=[super init])) {
panorama = [CCSprite spriteWithFile: #"panorama.png"];
panorama.position = ccp( 1709/2 , 320/2 );
[self addChild:panorama];
appendix = [CCSprite spriteWithFile: #"appendix.png"];
appendix.position = ccp( 1709+480/2-1, 320/2 );
[self addChild:appendix];
// schedule a repeating callback on every frame
[self schedule:#selector(nextFrame:)];
}
return self;
}
//_____________________________________________________________________________________
- (void) nextFrame:(ccTime)dt {
panorama.position = ccp(panorama.position.x - 100 * dt, panorama.position.y);
appendix.position = ccp(appendix.position.x - 100 * dt, appendix.position.y);
if (panorama.position.x < -1709/2) {
panorama.position = ccp( 1709/2 , panorama.position.y );
appendix.position = ccp( 1709+480/2-1, appendix.position.y );
}
}
// on "dealloc" you need to release all your retained objects
- (void) dealloc
{
// in case you have something to dealloc, do it in this method
// in this particular example nothing needs to be released.
// cocos2d will automatically release all the children (Label)
// don't forget to call "super dealloc"
[super dealloc];
}
//_____________________________________________________________________________________
#end
Play a little with this code and you will get what you need.

How to increase interval over time?

I was wondering on how do I increase the interval over time so I can add target. I am still new to cocos2d.
[self schedule:#selector(gameLogic:) interval:0.7];
-(void)gameLogic:(ccTime)dt {
[self addTarget];
}
Why not declare a simple property (int, float, etc.) to hold the number of times your method has been called, and increment it when you call the method itself? That way, it's just a multiplication problem:
//.h
...
#property (nonatomic, assign) int iterations;
//.m
#synthesize iterations = iterations_;
[self schedule:#selector(gameLogic:) interval:0.7*iterations_];
-(void)gameLogic:(ccTime)dt {
[self addTarget];
iterations_++;
}
float interval = .7;
-(id)init{
...
[self scheduleOnce:#selector(gameLogic:) delay:interval]; //Check the name of the method, I'm not 100% sure about it
...
}
-(void)gameLogic:(ccTime)dt {
[self addTarget];
interval += dt; //Or whatever you want to increase it by
[self scheduleOnce:#selector(gameLogic:) delay:interval]; //Check the name of the method, I'm not 100% sure about it
}

accessing instance variable in cocos2d scheduled method crashes

fresh to objC and cocos2d :)
i'm following "learn cocos2d game development with iOS5", in chapter4, there is a "DoodleDrop" game.
define some variable in GameScene.h like this
#interface GameScene : CCLayer
{
CCSprite *player;
CGPoint playerVelocity;
CCArray *spiders;
CGSize screenSize;
int dropedSpidersCount;
float duration;
}
+ (CCScene *)scene;
#end
in GameScene.m the init method looks like this
- (id)init
{
if (self = [super init]) {
duration = 4.0;
[self createPlayer];
[self createSpiders]; // spiders were inited here.
[self resetSpiders];
[self schedule:#selector(chooseSpider:) interval:0.7];
}
return self;
}
while in chooseSpider, i cannot access spiders, xcode broke
in other methods, spiders or duration just behave normally, why does this happens?
gist code added
https://gist.github.com/2940466
After inspecting your code, I suggest you to try this fix:
- (void)createSpiders
{
CCSprite *tempSpider = [CCSprite spriteWithFile:#"spider.png"];
CGSize spiderSize = [tempSpider texture].contentSize;
int spiderCount = screenSize.width / spiderSize.width;
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
for (int i = 0; i < spiderCount; i++) {
CCSprite *spider = [CCSprite spriteWithFile:#"spider.png"];
[self addChild:spider];
[spiders addObject:spider];
}
}
where the only difference is in the line:
spiders = [[CCArray arrayWithCapacity:spiderCount] retain];
Indeed, if you do not retain you spiders object, it will be autoreleased at the next run loop iteration.
OLD ANSWER:
Without seeing more code it is not possible to say exactly what is happening, but it seems that in the interval between creating the spiders and the actual execution of chooseSpiders, your spiders array gets deallocated.
As a quick try, I would suggest adding:
[spiders retain];
before calling
[self schedule:#selector(chooseSpider:) interval:0.7];
and see wether the crash keeps happening.
if you provide more code, it could be possible to help you further.

Cocos2D Character animation

I started playing around with Cocos2D and I figured out to do a sprite animation with sprite sheets.
Now I have a little robot walking around on my screen. But I am wondering how I can put one animation into another.
For example, my robot has a walking animation and I want to move his arms separately. But his shoulders will rise and fall during walking and the arms should move relative to his shoulder position.
My walk animation has five sprites and I have nine different arm positions. Now, I could add the arms to every single walking sprite for all nine arm positions. but then I would have 45 Images instead of 14.
That's a good question (finally!).
What I would do is separate your character into different sprites (you already did this) that are animatable on their own.
Then, whenever a frame of that animation is presented, I would have a block of code executed that modified the position of the arms animation, so it matches the shoulders.
To execute that block of code, you'd need Cocos 1.1 or better, since they added the CCAnimationFrame there, however, those frames can only execute code via an NSNotification, so I made an improvement so we can set a block on a frame, and that block would be executed whenever that frame is displayed.
Just find CCAnimation.h and modify the CCAnimationFrame interface to look like this:
typedef void(^FrameBlock)(CCSprite *sprite);
/** CCAnimationFrame
A frame of the animation. It contains information like:
- sprite frame name
- # of delay units.
- offset
#since v1.1
*/
#interface CCAnimationFrame : NSObject <NSCopying>
{
CCSpriteFrame* spriteFrame_;
float delayUnits_;
NSDictionary *userInfo_;
FrameBlock frameBlock;
}
/** CCSpriteFrameName to be used */
#property (nonatomic, readwrite, retain) CCSpriteFrame* spriteFrame;
/** how many units of time the frame takes */
#property (nonatomic, readwrite) float delayUnits;
/** A CCAnimationFrameDisplayedNotification notification will be broadcasted when the frame is displayed with this dictionary as UserInfo. If UserInfo is nil, then no notification will be broadcasted. */
#property (nonatomic, readwrite, retain) NSDictionary *userInfo;
/** If the block is not NULL, it will be executed when the frame becomes visible **/
#property (nonatomic, readwrite, copy) FrameBlock frameBlock;
/** initializes the animation frame with a spriteframe, number of delay units and a notification user info */
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo;
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits block:(FrameBlock)block;
#end
And then open CCAnimation.m and make sure the CCAnimationFrame implementation looks like this:
#implementation CCAnimationFrame
#synthesize spriteFrame = spriteFrame_, delayUnits = delayUnits_, userInfo=userInfo_;
#synthesize frameBlock;
-(id) initWithSpriteFrame:(CCSpriteFrame *)spriteFrame delayUnits:(float)delayUnits userInfo:(NSDictionary*)userInfo
{
if( (self=[super init]) ) {
self.spriteFrame = spriteFrame;
self.delayUnits = delayUnits;
self.userInfo = userInfo;
}
return self;
}
-(id) initWithSpriteFrame:(CCSpriteFrame*)spriteFrame delayUnits:(float)delayUnits block:(FrameBlock)block{
self = [self initWithSpriteFrame:spriteFrame delayUnits:delayUnits userInfo:nil];
if(self){
[self setFrameBlock:block];
}
return self;
}
-(void) dealloc
{
CCLOGINFO( #"cocos2d: deallocing %#", self);
[spriteFrame_ release];
[userInfo_ release];
[super dealloc];
}
-(id) copyWithZone: (NSZone*) zone
{
CCAnimationFrame *copy = [[[self class] allocWithZone: zone] initWithSpriteFrame:[[spriteFrame_ copy] autorelease] delayUnits:delayUnits_ userInfo:[[userInfo_ copy] autorelease] ];
return copy;
}
-(NSString*) description
{
return [NSString stringWithFormat:#"<%# = %08X | SpriteFrame = %08X, delayUnits = %0.2f >", [self class], self, spriteFrame_, delayUnits_ ];
}
#end
Then, when creating the animation frames, add a block to them in which you set the position of the arms to match the shoulders.
I hope it helps.