I have a cocos2d application for mac, with two scenes. In scene one, when I push a button it loads the second scene by using:
[[CCDirector sharedDirector] replaceScene:[CompetitiveScene node]];
Now, on this second CCScene, in the init method I load a texture using:
CCSpriteBatchNode *parentNodeBullet = [CCSpriteBatchNode batchNodeWithFile:#"bullet.png" capacity:100];
This makes the application crash on CCTextureCache.m : 276
dispatch_sync(_dictQueue, ^{
tex = [_textures objectForKey: path];
});
Apparently _textures has 0 key/values
I have no idea why is this happening, it's such a simple code! In scene1 I simply have one button and one label ...
It gets more strange because if I change my workflow and load the CompetitiveScene first, it works perfectly..
Any idea?
EDIT1
If I perform
CCSprite *player = [CCSprite spriteWithFile:#"bullet.png"]; instead of using the CCSpriteBatchNode, I have the exact same problem :/
EDIT2
If I perform CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:#"bullet.png"];, same problem
EDIT3
Here is the code of the project
First layer
// CompetitiveLayer.m
#import "CompetitiveLayer.h"
#import "cocos2d.h"
#import "CollectableIncreaseAmmo.h"
enum {
kTagParentNode = 1,
};
#interface CompetitiveLayer()
-(void) addNewSpriteAtPosition:(CGPoint)p;
#end
#implementation CompetitiveLayer
-(id) init
{
if( (self=[super init])) {
CGSize s = [CCDirector sharedDirector].winSize;
// enable events
self.mouseEnabled = YES;
// Use batch node. Faster
/*
CCSpriteBatchNode *parentNodeBullet = [CCSpriteBatchNode batchNodeWithFile:#"bullet.png" capacity:10];
spriteTextureBullet_ = [parentNodeBullet texture];
*/
NSImage *img = [NSImage imageNamed:#"bullet.png"];
if(img == nil) // log image is nil
{
CCLOG(#"NIL");
}
CCSpriteBatchNode *parentNodeBullet = [[[CCSpriteBatchNode batchNodeWithFile:#"bullet.png" capacity:15] retain] autorelease];
spriteTextureBullet_ = [parentNodeBullet texture];
[self addChild:parentNodeBullet z:0 tag:kTagParentNode];
[self addNewSpriteAtPosition:ccp(s.width/2, s.height/2)];
}
return self;
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
/*
CCSprite *player = [CCSprite spriteWithFile:#"bullet.png"];
player.position = ccp(p.x, p.y);
[self addChild:player];
*/
CollectableIncreaseAmmo *sprite2 = [CollectableIncreaseAmmo spriteWithTexture:spriteTextureBullet_];
[sprite2 setPosition: ccp( p.x, p.y)];
[self addChild:sprite2];
/*
CCTexture2D *texture = [[CCTextureCache sharedTextureCache] addImage:#"bullet.png"];
CollectableIncreaseAmmo *sprite2 = [CollectableIncreaseAmmo spriteWithTexture:texture];
[sprite2 setPosition: ccp( p.x, p.y)];
[self addChild:sprite2];
*/
}
- (BOOL) ccMouseDown:(NSEvent *)event
{
CGPoint location = [(CCDirectorMac*)[CCDirector sharedDirector] convertEventToGL:event];
[self addNewSpriteAtPosition: location];
return YES;
}
- (void) dealloc
{
[super dealloc];
}
#end
Second layer
// TitleLayer.m
#import "TitleLayer.h"
#import "CompetitiveScene.h"
#import "cocos2d.h"
#interface TitleLayer()
-(void) createButtons;
-(void) createTitle;
#end
#implementation TitleLayer
-(id) init
{
if( (self=[super init])) {
// enable events
#ifdef __IPHONE_OS_VERSION_MAX_ALLOWED
self.touchEnabled = YES;
self.accelerometerEnabled = YES;
#elif defined(__MAC_OS_X_VERSION_MAX_ALLOWED)
self.mouseEnabled = YES;
#endif
// Create buttons
[self createButtons];
// Create title
[self createTitle];
//[self scheduleUpdate];
}
return self;
}
-(void) createButtons
{
CCMenuItemLabel *competitiveGameButton = [CCMenuItemFont itemWithString:#"Fight" block:^(id sender){
/*
CCScene *s = [CCScene node];
id child = [CompetitiveLayer node];
[s addChild:child];
[[CCDirector sharedDirector] replaceScene: s];
*/
// [[CCDirector sharedDirector] replaceScene: [CCTransitionZoomFlipX transitionWithDuration:1 scene:[CompetitiveScene node] ]];
[[CCDirector sharedDirector] replaceScene:[CompetitiveScene node]];
// We can also push and pop the scene
}];
CCMenuItemLabel *coperativeGameButton = [CCMenuItemFont itemWithString:#"Coperate" block:^(id sender){
/*
CCScene *s = [CCScene node];
id child = [TitleLayer node];
[s addChild:child];
[[CCDirector sharedDirector] replaceScene: s];
*/
}];
CCMenu *menu = [CCMenu menuWithItems:competitiveGameButton, coperativeGameButton, nil];
[menu alignItemsVertically];
CGSize s = [[CCDirector sharedDirector] winSize];
menu.position = ccp(s.width/2, s.height/2);
[self addChild: menu z:-1];
}
-(void) createTitle
{
CGSize s = [CCDirector sharedDirector].winSize;
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Select type of game" fontName:#"Marker Felt" fontSize:32];
[self addChild:label z:0];
[label setColor:ccc3(0,0,255)];
label.position = ccp( s.width/2, s.height-50);
}
- (void) update:(ccTime) time {
CCLOG(#"Testing update");
}
- (void) dealloc
{
[super dealloc];
}
#end
Related
I am trying to make a vehicle (in this case a train) move based on player input. If I move the train via an SKAction, the wheels do not rotate. I could use the applyForce method on its physics body, but it I need more control. I need to make it move a certain distance over a certain amount of time. How can this be accomplished?
-(void)didMoveToView:(SKView *)view {
SKTexture *trainBodyTexture = [SKTexture textureWithImageNamed:#"levelselect_trainbody"];
SKSpriteNode *trainBody = [[SKSpriteNode alloc] initWithTexture:trainBodyTexture];
trainBody.zPosition = 0;
trainBody.position = CGPointMake(300, 500);
trainBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:trainBody.size];
[self addChild:trainBody];
SKTexture *trainWheelTexture = [SKTexture textureWithImageNamed:#"levelselect_trainwheel"];
SKSpriteNode *trainWheel1 = [[SKSpriteNode alloc] initWithTexture:trainWheelTexture];
trainWheel1.zPosition = 1;
trainWheel1.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:trainWheel1.size.width/2];
trainWheel1.physicsBody.allowsRotation = YES;
trainWheel1.position = CGPointMake(220, 400);
[self addChild:trainWheel1];
SKSpriteNode *trainWheel2 = [[SKSpriteNode alloc] initWithTexture:trainWheelTexture];
trainWheel2.zPosition = 1;
trainWheel2.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:trainWheel2.size.width/2];
trainWheel2.physicsBody.allowsRotation = YES;
trainWheel2.position = CGPointMake(380, 400);
[self addChild:trainWheel2];
SKShapeNode *dot = [SKShapeNode shapeNodeWithCircleOfRadius:10];
dot.zPosition = 2;
dot.fillColor = [NSColor redColor];
dot.position = CGPointMake(0, -20);
[trainWheel1 addChild:dot];
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:trainBody.physicsBody bodyB:trainWheel1.physicsBody anchor:trainWheel1.position];
SKPhysicsJointPin *pin2 = [SKPhysicsJointPin jointWithBodyA:trainBody.physicsBody bodyB:trainWheel2.physicsBody anchor:trainWheel2.position];
[self.scene.physicsWorld addJoint:pin];
[self.scene.physicsWorld addJoint:pin2];
//[trainWheel1 runAction:[SKAction moveByX:300 y:0 duration:3]];
[trainBody.physicsBody applyForce:CGVectorMake(3000, 0)];
}
UPDATE: Implemented With A Train Class (suggested by Jaffer Sheriff)
Train.h
#import <SpriteKit/SpriteKit.h>
#interface Train : SKSpriteNode
-(void) createPhysics;
-(void) moveLeft;
-(void) moveRight;
#end
Train.m
#import "Train.h"
#interface Train()
#property SKSpriteNode *trainBody, *trainWheelFront, *trainWheelRear;
#property SKPhysicsWorld *physicsWorld;
#end
#implementation Train
-(instancetype) init {
if (self = [super init]) {
[self initTrainBody];
[self initWheels];
}
return self;
}
-(void) initTrainBody {
SKTexture *trainBodyTexture = [SKTexture textureWithImageNamed:#"levelselect_trainbody"];
_trainBody = [[SKSpriteNode alloc] initWithTexture:trainBodyTexture];
_trainBody.zPosition = 0;
_trainBody.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_trainBody.size];
[self addChild:_trainBody];
}
-(void) initWheels {
SKTexture *_trainWheelTexture = [SKTexture textureWithImageNamed:#"levelselect_trainwheel"];
_trainWheelFront = [[SKSpriteNode alloc] initWithTexture:_trainWheelTexture];
_trainWheelFront.zPosition = 1;
_trainWheelFront.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_trainWheelFront.size.width/2];
_trainWheelFront.physicsBody.allowsRotation = YES;
_trainWheelFront.position = CGPointMake(-80, -82);
[self addChild:_trainWheelFront];
_trainWheelRear = [[SKSpriteNode alloc] initWithTexture:_trainWheelTexture];
_trainWheelRear.zPosition = 1;
_trainWheelRear.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:_trainWheelRear.size.width/2];
_trainWheelRear.physicsBody.allowsRotation = YES;
_trainWheelRear.position = CGPointMake(80, -82);
[self addChild:_trainWheelRear];
//dot used to see if wheels are rotating, no other point
SKShapeNode *dot = [SKShapeNode shapeNodeWithCircleOfRadius:10];
dot.zPosition = 2;
dot.fillColor = [NSColor redColor];
dot.position = CGPointMake(0, -20);
[_trainWheelFront addChild:dot];
}
//this method is called after the train node is added to the scene in GameScene otherwise will get error adding joints before node is in scene
-(void) createPhysics {
SKPhysicsJointPin *pin = [SKPhysicsJointPin jointWithBodyA:_trainBody.physicsBody bodyB:_trainWheelFront.physicsBody anchor:_trainWheelFront.position];
SKPhysicsJointPin *pin2 = [SKPhysicsJointPin jointWithBodyA:_trainBody.physicsBody bodyB:_trainWheelRear.physicsBody anchor:_trainWheelRear.position];
[self.scene.physicsWorld addJoint:pin];
[self.scene.physicsWorld addJoint:pin2];
}
-(void) moveLeft {
SKAction *rotateLeft = [SKAction rotateByAngle:6*M_PI duration:0.2];
[_trainWheelFront runAction:rotateLeft];
[_trainWheelRear runAction:rotateLeft];
}
-(void) moveRight {
SKAction *rotateRight = [SKAction rotateByAngle:-6*M_PI duration:0.2];
[_trainWheelFront runAction:rotateRight];
[_trainWheelRear runAction:rotateRight];
}
#end
GameScene.m
#import "GameScene.h"
#import "Train.h"
#interface GameScene()
#property Train *train;
#end
#implementation GameScene
-(void)didMoveToView:(SKView *)view {
[self initTrain];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(keyPressed:) name:#"KeyPressedNotificationKey" object:nil]; //using notifications and custom view class to handle key presses
}
-(void) initTrain {
_train = [[Train alloc] init];
_train.position = CGPointMake(500, 300);
[self addChild:_train];
[_train createPhysics];
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
-(void) keyPressed:(NSNotification*)notification {
NSNumber *keyCodeObject = notification.userInfo[#"keyCode"];
NSInteger keyCode = keyCodeObject.integerValue;
NSLog(#"keycode = %lu", keyCode);
switch (keyCode) {
case 123:
[self leftArrowPressed];
break;
case 124:
[self rightArrowPressed];
break;
}
}
-(void) leftArrowPressed {
SKAction *moveLeft = [SKAction moveByX:-200 y:0 duration:0.2];
[_train runAction:moveLeft];
[_train moveLeft];
}
-(void) rightArrowPressed {
SKAction *moveRight = [SKAction moveByX:200 y:0 duration:0.2];
[_train runAction:moveRight];
[_train moveRight];
}
#end
Note: This solution causes the entire train to flip and freak out when the left/right keys are pressed. It seems like my pin joints are incorrect, but they seem correct to me :/
Default Initializer of SkSpriteNode is - (instancetype)initWithTexture:(SKTexture *)texture color:(SKColor *)color size:(CGSize)size; Try this,
#interface Train : SKSpriteNode
- (instancetype)initTrainWithColor:(UIColor *) color andSize:(CGSize) size;
-(void) animateWheelsWithTime:(float) time;
#end
#interface Train ()
{
SKSpriteNode *wheel1;
SKSpriteNode *wheel2;
NSMutableArray *wheelsArray;
}
#end
#implementation Train
- (instancetype)initTrainWithColor:(UIColor *) color andSize:(CGSize) size
{
self = [super initWithTexture:nil color:color size:size];
if (self)
{
[self addWheels];
}
return self;
}
-(void)addWheels
{
wheelsArray = [[NSMutableArray alloc]init];
wheel1 = [SKSpriteNode spriteNodeWithImageNamed:#"wheel"];
[wheel1 setPosition:CGPointMake(-(self.frame.size.width/2.0f-wheel1.frame.size.width/2.0f), - (self.frame.size.height/2.0f - wheel1.frame.size.height/2.0f))];
[self addChild:wheel1];
wheel2 = [SKSpriteNode spriteNodeWithImageNamed:#"wheel"];
[wheel2 setPosition:CGPointMake((self.frame.size.width/2.0f-wheel2.frame.size.width/2.0f), - (self.frame.size.height/2.0f - wheel2.frame.size.height/2.0f))];
[self addChild:wheel2];
[wheelsArray addObject:wheel1];
[wheelsArray addObject:wheel2];
}
-(void) animateWheelsWithTime:(float) time
{
for (SKSpriteNode *wheel in wheelsArray)
{
SKAction *act = [SKAction rotateByAngle:3*M_PI duration:time];
[wheel runAction:act];
}
}
#end
In GameScene.m add train like this,
-(void) addTrain
{
Train *train1 = [[Train alloc]initTrainWithColor:[UIColor yellowColor] andSize:CGSizeMake(150, 100)];
[train1 setPosition:CGPointMake(self.size.width/2.0f, self.size.height/2.0f)];
[self addChild:train1];
SKAction *act = [SKAction moveTo:CGPointMake(self.size.width/2.0f, self.size.height - 50) duration:2];
[train1 runAction:act];
[train1 animateWheelsWithTime:2];
}
I tried and it works.
I have a Menu ("myMenu") containing CCMenuItemImages. I would like this menu to detect finger swipes and slide accordingly.
My problem is that the CCMenuItemImages seem to absorb the touch event. The swipe works fine when the user touches the menu outside of the CCMenuItemImages, but not when the touch happens on these.
I have tried to put my menu items in a layer to detect touches (refering to answer Scrollable menu using MenuItem's), but this doesn't seem to work either. Any idea why ?
+(id) scene
{
CCScene *scene = [CCScene node];
ModeMenuScene *layer = [ModeMenuScene node];
[scene addChild: layer];
return scene;
}
-(id) init
{
if( (self=[super init] )) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite *background = [CCSprite spriteWithFile:#"bg.png"];
background.position=ccp(winSize.width/2,winSize.height/2);
[self addChild:background];
mode1 = [CCMenuItemImage itemFromNormalImage:#"Mode1.png" selectedImage: #"Mode1.png" target:self selector:#selector(goToMode1:)];
mode1label = [CCLabelTTF labelWithString:[NSString stringWithFormat:#"Level 1 %d", n] dimensions:CGSizeMake(220,53) alignment:UITextAlignmentCenter fontName:#"Arial" fontSize:20.0];
mode1label.color = ccc3(167,0,0);
mode1label.position=ccp(55,-30);
[mode1 addChild:mode1label];
// here same kind of code to define mode2,mode3,mode4 (taken out to reduce size of code)
myMenu=[CCMenu menuWithItems:mode1,mode2,mode3,mode4,nil];
[myMenu alignItemsHorizontallyWithPadding:25];
myMenu.position=ccp(winSize.width/2+40,180);
menuLayer = [CCLayer node];
[menuLayer addChild:myMenu];
[self addChild:menuLayer];
[self enableTouch];
}
return self;
}
-(void) disableTouch{
self.isTouchEnabled=NO;
menuLayer.isTouchEnabled=NO;
}
-(void) enableTouch{
self.isTouchEnabled=YES;
menuLayer.isTouchEnabled=YES;
}
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
if(location.y>100 && location.y<260) {
draggingMenu=1;
x_initial = location.x;
}
else draggingMenu=0;
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
if(draggingMenu==1) {
CGSize winSize = [[CCDirector sharedDirector] winSize];
int x = myMenu.position.x+location.x-x_initial;
x = MAX(0,x);
x = MIN(x,winSize.width/2+40);
myMenu.position=ccp(x,180);
x_initial=location.x;
}
}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
draggingMenu=0;
}
- (void)dealloc {
[super dealloc];
}
#end
Solved it by adding :
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:INT_MIN+1 swallowsTouches:NO];
}
the problem was that CCMenuItemImage swallows the touches and has a high priority set at -128. Thus the need to set priority at INT_MIN+1
I animate my character like that :
-(void) createHero
{
_batchNode = [CCSpriteBatchNode batchNodeWithFile:#"Snipe1.png"];
[self addChild:_batchNode];
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"Snipe1.plist"];
//gather list of frames
NSMutableArray *runAnimFrames = [NSMutableArray array];
for(int i = 1; i <= 7; ++i)
{
[runAnimFrames addObject:
[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:
[NSString stringWithFormat:#"run000%d.png", i]]];
}
//create sprite and run the hero
self.hero = [CCSprite spriteWithSpriteFrameName:#"run0001.png"];
_hero.anchorPoint = CGPointZero;
_hero.position = self.heroRunningPosition;
//create the animation object
CCAnimation *runAnim = [CCAnimation animationWithFrames:runAnimFrames delay:1/30.0f];
self.runAction = [CCRepeatForever actionWithAction: [CCAnimate actionWithAnimation:runAnim restoreOriginalFrame:YES]];
[_hero runAction:_runAction];
[_batchNode addChild:_hero z:0];
}
This works fine an my character is running, but now i want a second animation when he jumps. At the moment i make it like that:
-(void)changeHeroImageDuringJump
{
[_hero setTextureRect:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"run0007.png"].rect];
}
But now i want a second plist with a second png, so i get a whole new animation when the character jumps. How can i implement that ?
In my case, i implemented an AnimatedSprite class that will handle this for me. This way, I add files like so:
NSDictionary* anims = [NSDictionary dictionaryWithObjectsAndKeys:
#"Animations/Character/idle_anim.plist", #"Idle",
#"Animations/Character/walk_anim.plist", #"Walk",
#"Animations/Character/run_anim.plist", #"Run", nil];
CCNode* myNode = [[AnimatedSprite alloc] initWithDictionary:anims
spriteFrameName: #"character_idle_01.png"
startingIndex:#"Idle"];
Changing the animation is as simple as:
[myNode setAnimation: #"Run"];
Heres my implementation This is the .h
#interface AnimatedSprite : CCSprite
{
NSMutableDictionary* _spriteAnimations;
CCAction* _currentAnimation;
NSString* _currentAnimationName;
bool _initialized;
}
- (id) initWithTextureName:(NSString*) textureName;
- (id) initWithArray: (NSArray*) animList spriteFrameName: (NSString*) startingSprite startingIndex: (int)startingIndex;
- (id) initWithDictionary:(NSDictionary *)anims spriteFrameName:(NSString *)startingSprite startingIndex:(NSString *)startingAnim;
- (void) addAnimation: (NSString*) animationName andFilename: (NSString*) plistAnim;
- (void) setAnimationIndex: (int) index;
- (void) setAnimation: (NSString*) animationName;
#end
And this is the .m
#import "AKHelpers.h"
#implementation AnimatedSprite
NSMutableDictionary* _spriteAnimations;
- (id) initWithTextureName:(NSString*) textureName
{
CCTexture2D* texture = [[CCTextureCache sharedTextureCache] addImage:textureName];
CCSpriteFrame* frame = [CCSpriteFrame frameWithTexture:texture rect: CGRectMake(0, 0, 1, 1)];
if ((self=[super initWithSpriteFrame:frame]))
{
_currentAnimationName = nil;
_currentAnimation = nil;
_spriteAnimations = [[NSMutableDictionary alloc] init ];
_initialized = true;
}
return self;
}
- (id) initWithArray: (NSArray*) animList spriteFrameName: (NSString*) startingSprite startingIndex: (int)startingIndex
{
_initialized = false;
_spriteAnimations = [[NSMutableDictionary alloc] init];
// Add animations as numbers from 0 to animList.count
int i = 0;
for (NSString* anim in animList)
{
[self addAnimation: [NSString stringWithFormat:#"%d", i] andFilename:anim];
i++;
}
if ((self = [super initWithSpriteFrameName:startingSprite]))
{
_currentAnimationName = nil;
_currentAnimation = nil;
[self setAnimationIndex: startingIndex];
_initialized = true;
}
return self;
}
- (id) initWithDictionary:(NSDictionary *)anims spriteFrameName:(NSString *)startingSprite startingIndex:(NSString *)startingAnim
{
_initialized = false;
_spriteAnimations = [[NSMutableDictionary alloc] init];//[[NSMutableArray alloc] init];
// Add animations
for (NSString* key in anims)
{
[self addAnimation: key andFilename: [anims objectForKey: key] ];
}
if ((self = [super initWithSpriteFrameName:startingSprite]))
{
_currentAnimationName = nil;
_currentAnimation = nil;
[self setAnimation: startingAnim];
_initialized = true;
}
return self;
}
- (void) dealloc
{
[_currentAnimationName release];
[_spriteAnimations release];
[super dealloc];
}
- (void) setAnimation: (NSString*) animationName
{
if (![_currentAnimationName isEqualToString:animationName])
{
[_currentAnimationName release];
_currentAnimationName = [animationName copy];
// Stop current animation
if (_currentAnimation != nil)
[self stopAction:_currentAnimation];
//[self stopAllActions];
// Apply animation
NSDictionary* clip = [_spriteAnimations objectForKey: animationName];
if (clip)
{
_currentAnimation = [AKHelpers actionForAnimationClip:clip];
if (_currentAnimation)
[self runAction:_currentAnimation];
}
else
{
_currentAnimation = nil;
}
}
}
- (void) setAnimationIndex: (int) index
{
[self setAnimation: [NSString stringWithFormat:#"%d", index]];
}
- (void) addAnimation: (NSString*) animationName andFilename: (NSString*) plistAnim
{
NSDictionary *clip = [AKHelpers animationClipFromPlist:plistAnim];
if (clip)
{
[_spriteAnimations setObject:clip forKey:animationName];
if (_initialized && [_spriteAnimations count] == 1)
{
[self setAnimation:animationName];
}
}
}
#end
Create two different animation actions for running and jumping. Run those actions on need basis.
I made an App using cocos2D that has a hierarchical structure.
And I used a UIButton on glView in a child layer.
When I get back to parent layer, the button still on it's position.
I want to terminate that.
How can I do this?
Here's the code.
MainHallLayer.m
- (id)init
{
self = [super init];
if (self != nil)
{
CCMenu *menuLists = [CCMenu menuWithItems: nil];
menuLists.position = ccp(screenSize.width/2, screenSize.height/2);
[self addChild:menuLists z:10];
NSString *fontName = [NSString stringWithFormat:#"Chalkboard SE"];
CGFloat fontSize = 28;
{
CCLabelTTF *label = [CCLabelTTF labelWithString:#"Touch Field : Getting
touches coordinates" fontName:fontName fontSize:fontSize];
[label setColor:ccc3(0.0, 0.0, 0.0)];
label.anchorPoint = ccp(0, 0.5f);
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id
sender)
{
CCScene *scene = [TouchField1Layer node];
[ReturningNode returnToNodeWithParent:scene];
[[CCDirector sharedDirector] replaceScene:scene];
}];
[menuLists addChild:menuItem];
}
}
return self;
}
ReturnToNode.m
- (id)initWithParentNode:(CCNode *)parentNode
{
self = [super init];
if (self != nil)
{
CGSize screenSize = [[CCDirector sharedDirector]winSize];
CCLabelTTF *label = [CCLabelTTF labelWithString:#" <= Return"
fontName:#"Gill Sans"
fontSize:30.0f];
label.color = ccMAGENTA;
id tint_1 = [CCTintTo actionWithDuration:0.333f red:1.0 green:.0f blue:.0f];
id tint_2 = [CCTintTo actionWithDuration:0.333f red:.5f green:.5f blue:.0f];
id tint_3 = [CCTintTo actionWithDuration:0.333f red:.0f green:1.0 blue:.0f];
id tint_4 = [CCTintTo actionWithDuration:0.333f red:.0f green:.5f blue:.5f];
id tint_5 = [CCTintTo actionWithDuration:0.333f red:.0f green:.0f blue:1.0];
id tint_6 = [CCTintTo actionWithDuration:0.333f red:.5f green:.0f blue:.5f];
id sequence = [CCSequence actions:tint_1,tint_2,tint_3,tint_4,tint_5,tint_6, nil];
id repeatAction = [CCRepeatForever actionWithAction:sequence];
[label runAction:repeatAction];
CCLayerColor *labelBackground = [CCLayerColor layerWithColor:ccc4(0.0, 0.0, 70, 40) width:label.contentSize.width + 20 height:label.contentSize.height + 20];
[label addChild:labelBackground z:-1];
CCMenuItem *menuItem = [CCMenuItemLabel itemWithLabel:label block:^(id sender)
{
[self removeFromParentAndCleanup:YES];
[[CCDirector sharedDirector] replaceScene:[TouchControllerLayer scene]];
}];
CCMenu *menu = [CCMenu menuWithItems:menuItem, nil];
[menu alignItemsVertically];
menu.position = CGPointMake(label.contentSize.width/2 , screenSize.height - 20);
[self addChild:menu];
[parentNode addChild:self z:1000];
}
return self;
}
+ (id)returnToNodeWithParent:(CCNode *)parentNode
{
return [[[self alloc] initWithParentNode:parentNode] autorelease];
}
TouchField1Layer.m
- (id)init
{
self = [super init];
if (self != nil)
{
BackgroundLayer *background = [BackgroundLayer node];
TouchField3Layer *layer = [TouchField3Layer node];
[self addChild:background z:3];
[self addChild:layer z:2];
[self preparingTools];
}
return self;
}
- (void)preparingTools
{
UIButton *button = [UIButton buttonWithType:UIButtonTypeRoundedRect];
button.frame = CGRectMake(0, 0, 100, 40);
UIView *glView = [CCDirector sharedDirector].openGLView;
glView.tag = TAG_GLVIEW;
[glView addSubview:button];
}
Any advices, helps are welcome. Always thanks. Bless You.
If you add UIKit views you will have to remove them manually at the appropriate point in time. Cocos2D does not manage the lifetime of UIKit views, only classes derived from CCNode.
For example when changing scenes you will have to remove the UIButton from the glView either in -(void) dealloc or in -(void) cleanup.
This one has been doing my head in for months - So time to swallow my pride and reach out for a little help. At the moment this is being done in UIWebView as HTML5/JS controlled system. But UIWebview frankly sux and looking to make this last component native too.
I have a collection of videos and at specific timed points during the video, I am calling a page of instructions that relate to the timed period in the video. The video controls also act as a controller for the instructions pages. So whatever timed point is reached, the corresponding page is animated into place.
I've looked in many, many options, with the closest coming in with http video streaming and using timed metadata to initiate a view, but I am containing the videos locally on the device. And, as yet cannot find anything that looks like it will work. Seems simple enough in principle, but I'll be damned if I can find a decent solution...
Any ideas / pointers?
Here's the last attempt at going native with this before the remainder of my hair fell out - I think I may be seeing where I was heading in the wrong direction, but if you can spare a few moments, I'd really appreciate it!
OBJECTIVE is to have a shoutOut that lives below the video that contains a page of instructions. At x seconds, the content will be refreshed to correspond to that portion of the video and persist until the next shoutOut for fresh content. This I have managed to achieve successfully. Where I have been falling down (a lot) is when I scrub the video back to a previous section, the shoutOut content remains at the position from which I scrubbed and remains there permanently. Or as the code is below, simply doesn't re-apear as it is set to a timed visible duration.
Anyway, here's the code...
Header:
// START:import
#import <UIKit/UIKit.h>
// START_HIGHLIGHT
#import <MediaPlayer/MPMoviePlayerController.h>
#import "CommentView.h"
// END_HIGHLIGHT
// START:def
// START:wiring
#interface MoviePlayerViewController : UIViewController {
UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
MPMoviePlayerController *player;
// END_HIGHLIGHT
// START:wiring
UILabel *onScreenDisplayLabel;
UIScrollView *myScrollView;
NSMutableArray *keyframeTimes;
NSArray *shoutOutTexts;
NSArray *shoutOutTimes;
}
#property (nonatomic, retain) IBOutlet UIView *viewForMovie;
// END:wiring
// START_HIGHLIGHT
#property (nonatomic, retain) MPMoviePlayerController *player;
// END_HIGHLIGHT
#property (nonatomic, retain) IBOutlet UILabel *onScreenDisplayLabel;
#property (nonatomic, retain) IBOutlet UIScrollView *myScrollView;
#property (nonatomic, retain) NSMutableArray *keyframeTimes;
// START_HIGHLIGHT
-(NSURL *)movieURL;
- (void)timerAction:(NSTimer*)theTimer;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification;
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer;
- (IBAction) getInfo:(id)sender;
- (void)removeView:(NSTimer*)theTimer;
// END_HIGHLIGHT
// START:wiring
#end
// END:def
// END:wiring
// END:import
Main:
#implementation MoviePlayerViewController
// START:synth
#synthesize player;
#synthesize viewForMovie;
#synthesize onScreenDisplayLabel;
#synthesize myScrollView;
#synthesize keyframeTimes;
// END:synth
// Implement loadView to create a view hierarchy programmatically, without using a nib.
// START:viewDidLoad
// START:viewDidLoad1
- (void)viewDidLoad {
[super viewDidLoad];
keyframeTimes = [[NSMutableArray alloc] init];
shoutOutTexts = [[NSArray
arrayWithObjects:#"This is a test\nLabel at 2 secs ",
#"This is a test\nLabel at 325 secs",
nil] retain];
shoutOutTimes = [[NSArray
arrayWithObjects:[[NSNumber alloc] initWithInt: 2],
[[NSNumber alloc] initWithInt: 325],
nil] retain];
self.player = [[MPMoviePlayerController alloc] init];
self.player.contentURL = [self movieURL];
// END:viewDidLoad1
self.player.view.frame = self.viewForMovie.bounds;
self.player.view.autoresizingMask =
UIViewAutoresizingFlexibleWidth |
UIViewAutoresizingFlexibleHeight;
[self.viewForMovie addSubview:player.view];
[self.player play];
// START_HIGHLIGHT
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self selector:#selector(timerAction:) userInfo:nil repeats:YES];
// END_HIGHLIGHT
// START:viewDidLoad1
[self.view addSubview:self.myScrollView];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(movieDurationAvailable:)
name:MPMovieDurationAvailableNotification
object:nil];
}
// END:viewDidLoad
// END:viewDidLoad1
// START:movieURL
-(NSURL *)movieURL
{
NSBundle *bundle = [NSBundle mainBundle];
NSString *moviePath =
[bundle
pathForResource:#"BigBuckBunny_640x360"
ofType:#"m4v"];
if (moviePath) {
return [NSURL fileURLWithPath:moviePath];
} else {
return nil;
}
}
// END:movieURL
int position = 0;
- (void)timerAction:(NSTimer*)theTimer {
NSLog(#"hi");
int count = [shoutOutTimes count];
NSLog(#"count is at %d", count);
if (position < count) {
NSNumber *timeObj = [shoutOutTimes objectAtIndex:position];
int time = [timeObj intValue];
NSLog(#"time is at %d", time);
if (self.player.currentPlaybackTime >= time) {
CommentView *cview = [[CommentView alloc]
initWithText:[shoutOutTexts objectAtIndex:position]];
[self.player.view addSubview:cview];
position++;
[NSTimer scheduledTimerWithTimeInterval:4.0f target:self selector:#selector(removeView:) userInfo:cview repeats:NO];
}
}
}
- (void)removeView:(NSTimer*)theTimer {
UIView *view = [theTimer userInfo];
[view removeFromSuperview];
}
/*
// Implement viewDidLoad to do additional setup after loading the view, typically from a nib.
- (void)viewDidLoad {
[super viewDidLoad];
}
*/
// Override to allow orientations other than the default portrait orientation.
- (BOOL)shouldAutorotateToInterfaceOrientation:(UIInterfaceOrientation)interfaceOrientation {
return YES;
}
- (void)didReceiveMemoryWarning {
// Releases the view if it doesn't have a superview.
[super didReceiveMemoryWarning];
// Release any cached data, images, etc that aren't in use.
}
- (void)viewDidUnload {
// Release any retained subviews of the main view.
// e.g. self.myOutlet = nil;
}
- (void)dealloc {
[super dealloc];
}
- (void) movieDurationAvailable:(NSNotification*)notification {
MPMoviePlayerController *moviePlayer = [notification object];
int duration = [moviePlayer duration];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(playerThumbnailImageRequestDidFinish:)
name:MPMoviePlayerThumbnailImageRequestDidFinishNotification
object:nil];
NSMutableArray *times = [[NSMutableArray alloc] init];
for(int i = 0; i < 20; i++) {
[times addObject:[NSNumber numberWithInt:5+i*((duration)/20)]];
}
[self.player requestThumbnailImagesAtTimes:times timeOption: MPMovieTimeOptionNearestKeyFrame];
}
int p = 0;
int ll=0;
- (void) playerThumbnailImageRequestDidFinish:(NSNotification*)notification {
NSDictionary *userInfo;
userInfo = [notification userInfo];
NSNumber *timecode;
timecode = [userInfo objectForKey: #"MPMoviePlayerThumbnailTimeKey"];
[keyframeTimes addObject: timecode];
UIImage *image;
image = [userInfo objectForKey: #"MPMoviePlayerThumbnailImageKey"];
int width = image.size.width;
int height = image.size.height;
float newwidth = 75 * ((float)width / (float)height);
self.myScrollView.contentSize = CGSizeMake((newwidth + 2) * 20, 75);
UIImageView *imgv = [[UIImageView alloc] initWithImage:image];
[imgv setUserInteractionEnabled:YES];
[imgv setFrame:CGRectMake(ll, 0, newwidth, 75.0f)];
ll+=newwidth + 2;
UITapGestureRecognizer *tapRecognizer = [[UITapGestureRecognizer alloc]
initWithTarget:self action:#selector(handleTapFrom:)];
[tapRecognizer setNumberOfTapsRequired:1];
[imgv addGestureRecognizer:tapRecognizer];
[tapRecognizer release];
[myScrollView addSubview:imgv];
}
- (void) getInfo:(id)sender
{
MPMovieMediaTypeMask mask = self.player.movieMediaTypes;
NSMutableString *mediaTypes = [[NSMutableString alloc] init];
if (mask == MPMovieMediaTypeMaskNone) {
[mediaTypes appendString:#"Unknown Media Type"];
} else {
if (mask & MPMovieMediaTypeMaskAudio) {
[mediaTypes appendString:#"Audio"];
}
if (mask & MPMovieMediaTypeMaskVideo) {
[mediaTypes appendString:#"Video"];
}
}
MPMovieSourceType type = self.player.movieSourceType;
NSMutableString *sourceType = [[NSMutableString alloc] initWithString:#""];
if (type == MPMovieSourceTypeUnknown) {
[sourceType appendString:#"Source Unknown"];
} else if (type == MPMovieSourceTypeFile) {
[sourceType appendString:#"File"];
} else if (type == MPMovieSourceTypeStreaming) {
[sourceType appendString:#"Streaming"];
}
CGSize size = self.player.naturalSize;
onScreenDisplayLabel.text = [NSString stringWithFormat:#"[Type: %#] [Source: %#] [Time: %.1f of %.f secs] [Playback: %.0fx] [Size: %.0fx%.0f]",
mediaTypes,
sourceType,
self.player.currentPlaybackTime,
self.player.duration,
self.player.currentPlaybackRate,
size.width,
size.height];
}
- (void)handleTapFrom:(UITapGestureRecognizer *)recognizer {
NSArray *subviews = [myScrollView subviews];
for (int i = 0; i < 20; i++) {
if (recognizer.view == [subviews objectAtIndex:i]) {
NSNumber *num = [keyframeTimes objectAtIndex:i];
self.player.currentPlaybackTime = [num intValue];
return;
}
}
}
#end
The Comment View Header:
#import <UIKit/UIKit.h>
#interface CommentView : UIView {
}
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text;
- (id)initWithText:(NSString *) text;
#end
The Comment View Main:
#import "CommentView.h"
#implementation CommentView
- (id)initWithFrame:(CGRect)frame andText:(NSString *) text {
if ((self = [super initWithFrame:frame])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (id)initWithText:(NSString *) text {
if ((self = [super init])) {
UIImage *image = [UIImage imageNamed:#"comment.png"];
UIImageView *imageView = [[UIImageView alloc] initWithImage:image];
[self addSubview:imageView];
CGRect rect = CGRectMake(20, 20, 200.0f, 90.0f);
UILabel *label = [[UILabel alloc] initWithFrame:rect];
label.text = text;
label.numberOfLines = 3;
label.adjustsFontSizeToFitWidth = YES;
label.textAlignment = UITextAlignmentCenter;
label.backgroundColor = [UIColor clearColor];
[self addSubview:label];
}
return self;
}
- (void)dealloc {
[super dealloc];
}
#end
Thoughts anyone?
Cheers!
What's wrong with monitoring currentPlaybackTime at regular intervals (assuming you are using an instance that implements MPMediaPlayback for playback).