I have a sprite that runs and jumps (like Mario), I am using a state machine to help with running the animation actions, I am using a texture atlas, and everything works fine.
- (void)jumpingphysics {
if (_JumpBool && self.onGround) {
_doubleJumpBool = YES;
_JumpBool = NO;
[self catJump];
self.physicsBody.velocity = CGVectorMake(0.0, 600);
}
// double jump
if (_JumpBool && _doubleJumpBool) {
self.physicsBody.velocity = CGVectorMake(0.0, 600);
[self catDoubleJump];
_doubleJumpBool = NO;
_JumpBool = NO;
}
}
this is the method thats called.
- (void)catJump {
if (_actionState == kActionStateIdle || _actionState == kActionStateWalk || _actionState == kActionStateJump) {
[self removeAllActions];
[self runAction:_jumpAction];
SKAction *jumpFx = [SKAction playSoundFileNamed:#"jump.wav" waitForCompletion:NO];
[self runAction:jumpFx];
_actionState = kActionStateJump;
}
}
this is where I created the frames array animation.
- (void)catJumpAnimation {
int i;
NSMutableArray *jumpFrames = [NSMutableArray arrayWithCapacity:9];
for (i = 1 ; i < 9; ++i) {
NSString *frames = [NSString stringWithFormat:#"jump%d.png",i];
SKTexture *temp = [catAtlas textureNamed:frames];
[jumpFrames addObject:temp];
self.jumpAction = [SKAction animateWithTextures:jumpFrames timePerFrame:1.0 / 16.0 resize:YES restore:NO];
}
}
The problem is when running on an iPad mini, when I jump the first time, my frame rate dips to 40, only that first time, and only on my iPad mini, not my iPhone 5 c.
I know its the [self catJump] call that causes the frame rate dip, but I'm confused why only that call causes a dip, I have other animations which do more work yet don't cause a dip.
Any ideas would be greatly appreciated. :D
In my ActionSprite.h class which handles the the methods that controls the animations and sounds.
#interface ActionSprite : SKSpriteNode
#property (nonatomic,strong)SKAction *jumpFx;
#property (nonatomic,strong)SKAction *DjumpFx;
#property (nonatomic,strong)SKAction *hurtFx;
#property (nonatomic,strong)SKAction *dashFx;
#property (nonatomic,strong)SKAction *defeatFx;
#property (nonatomic,strong)SKAction *powerFx;
#property (nonatomic,strong)SKAction *bounceFx;
#property (nonatomic,strong)SKAction *rocketFx;
In my ActionSprite.m
#implementation ActionSprite
- (id)init {
if (self = [super init]) {
}
return self;
}
- (void)preLoadSound {
_jumpFx = [SKAction playSoundFileNamed:#"jump.wav" waitForCompletion:NO];
_DjumpFx = [SKAction playSoundFileNamed:#"dJump.wav" waitForCompletion:NO];
_hurtFx = [SKAction playSoundFileNamed:#"cat hurt.wav" waitForCompletion:NO];
_defeatFx = [SKAction playSoundFileNamed:#"defeat.wav" waitForCompletion:NO];
_bounceFx = [SKAction playSoundFileNamed:#"bounce.wav" waitForCompletion:NO];
_powerFx = [SKAction playSoundFileNamed:#"power.mp3" waitForCompletion:NO];
_rocketFx = [SKAction playSoundFileNamed:#"rocketFx.wav" waitForCompletion:NO];
}
preload sounds in the cat class
#implementation Cat
SKTextureAtlas *catAtlas;
- (id)init {
if (self = [super initWithImageNamed:#"idle.png"]) {
catAtlas = [SKTextureAtlas atlasNamed:#"idle.plist"];
[self preLoadSound];
}
return self;
}
no more lag! :D
Related
I've been having problems with xcode in general. Example, NSLog not printing, case statements not working, etc. I did created this project before xcode 6 came out and thought if i switched it to a new file made in xcode 6 that these things would have been fixed. So far, they have been fixed. However, more problems have happened. One major one is that the didBeginContact method is not being called now. I've tried doing everything to fix it. I changed the category masks, their values, the collisionBitMask, contactBitMask, put different foundations in, but nothing has worked yet. It works in the file made before xcode 6 came out, but not in xcode 6.
There is supposed to be contact detected between the player and the opponent, but it doesn't work anymore.
I put breakpoints at the didBeginContact method and at the method it is supposed to call when there is contact between the player and opponent, but program didn't exit.
Thanks in advance!
GameScene.m:
#import "GameScene.h"
#interface GameScene ()
#end
#implementation GameScene
- (void)didMoveToView:(SKView *)view
{
if (!self.contentCreated)
{
[self createSceneContents];
self.contentCreated = YES;
[self addplatform];
[self addPlayer];
[self addButtons];
[self addOpponent];
}
}
typedef NS_OPTIONS(uint32_t, CollisionCategory) {
CollisionCategoryPlayer = 1 << 0,
CollisionCategoryOpponent = 1 << 1,
CollisionCategoryPlatform = 1 << 2,
CollisionCategoryPlatformBorder = 1 << 3,
};
//static inline CGFloat skRandf() {
// return rand() / (CGFloat) RAND_MAX;
//}
- (void)createSceneContents
{
self.backgroundColor = [SKColor purpleColor];
self.scaleMode = SKSceneScaleModeAspectFit;
self.multipleTouchEnabled = YES;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.physicsWorld.contactDelegate = self;
}
return self;
}
- (SKSpriteNode *)addPlayer {
self._player = [SKSpriteNode spriteNodeWithTexture: [SKTexture textureWithImageNamed:#"TurnipSmall"]];
self._player.position = CGPointMake(100,450);
self._player.anchorPoint = CGPointMake(0.5,0.5);
self._player.physicsBody = [SKPhysicsBody bodyWithTexture:self._player.texture size:self._player.texture.size];
self._player.name = #"player";
self._player.physicsBody.dynamic = YES;
self._player.physicsBody.allowsRotation = FALSE;
self._player.physicsBody.affectedByGravity = TRUE;
self._player.physicsBody.friction = 5;
self._player.physicsBody.mass = 10;
self._player.physicsBody.usesPreciseCollisionDetection = YES;
self._player.physicsBody.restitution = 0.0;
self._player.physicsBody.categoryBitMask = CollisionCategoryPlayer;
self._player.physicsBody.collisionBitMask = CollisionCategoryPlayer | CollisionCategoryOpponent | CollisionCategoryPlatform;
self._player.physicsBody.contactTestBitMask = CollisionCategoryPlayer | CollisionCategoryOpponent | CollisionCategoryPlatform;
self.playerPunching = false;
[self addChild:self._player];
return self._player;
}
- (SKSpriteNode *)addOpponent {
self._opponent = [SKSpriteNode spriteNodeWithTexture: [SKTexture textureWithImageNamed:#"Tomato"]];
self._opponent.position = CGPointMake(300, 450);
self._opponent.anchorPoint = CGPointMake(0.5, 0.5);
self._opponent.physicsBody = [SKPhysicsBody bodyWithTexture:self._opponent.texture size:self._opponent.texture.size];
self._opponent.name = #"opponent";
self._opponent.physicsBody.dynamic = YES;
self._opponent.physicsBody.allowsRotation = NO;
self._opponent.physicsBody.affectedByGravity = YES;
self._opponent.physicsBody.friction = 5;
self._opponent.physicsBody.mass = 10;
self._opponent.physicsBody.density = 5;
self._opponent.physicsBody.usesPreciseCollisionDetection = YES;
self._opponent.physicsBody.restitution = 0;
self._opponent.physicsBody.velocity = CGVectorMake(0, 0);
self._opponent.physicsBody.categoryBitMask = CollisionCategoryOpponent;
self._opponent.physicsBody.collisionBitMask = CollisionCategoryPlatform| CollisionCategoryPlayer;
self._opponent.physicsBody.contactTestBitMask = CollisionCategoryPlayer | CollisionCategoryPlatform;
[self addChild:self._opponent];
return self._opponent;
}
- (void)didBeginContact:(SKPhysicsContact *)contact
{
SKPhysicsBody *firstBody, *secondBody;
if (contact.bodyA.categoryBitMask < contact.bodyB.categoryBitMask)
{
firstBody = contact.bodyA;
secondBody = contact.bodyB;
}
else
{
firstBody = contact.bodyB;
secondBody = contact.bodyA;
}
if ((firstBody.categoryBitMask & CollisionCategoryPlayer) != 0 && (secondBody.categoryBitMask & CollisionCategoryOpponent))
{
[self playerTouchingOpponent];
}
}
GameScene.h:
//
// GameScene.h
// TEST
//
// Copyright (c) 2014 G Hui. All rights reserved.
//
#import <SpriteKit/SpriteKit.h>
#interface GameScene : SKScene <SKPhysicsContactDelegate>
#property bool multipleTouchEnabled;
#property BOOL contentCreated;
#property SKSpriteNode * _donut;
#property SKSpriteNode * _player;
#property SKSpriteNode * _opponent;
#property SKSpriteNode *platform1Scene1;
#property BOOL movementBegins;
#property NSArray *level1;
#property BOOL playerPunching;
#property bool alreadyPunching;
#property float characterNumber;
#property SKSpriteNode *platform2Scene1;
#property SKSpriteNode *platform3Scene1;
#property float playerHealth;
#property SKSpriteNode *_healthBar;
#property SKSpriteNode *rightplatformBorder;
#property SKSpriteNode *mask;
#property SKNode *_playerHealthBar;
#property SKNode *_opponentHealthBar;
#property SKNode *_playerPowerUpBar;
#property SKNode *_opponentPowerUpBar;
#property int _playerHP;
#property int _opponentHP;
#property const int MaxHP;
#property int _playerPowerUp;
#property int _opponentPowerUp;
#property const int MaxPowerUp;
#property const float healthBarWidth;
#property const float healthBarHeight;
#property const float powerBarWidth;
#property const float powerBarHeight;
#property bool touchingPlatform;
#property SKSpriteNode *sideBorder;
#property SKSpriteNode *frontBorder;
#property BOOL playerOpponentContact;
#property float distanceBetweenPlayerAndOpponent;
#property float distanceBetweenOpponentAndPlayer;
#end
Fixed!
It was because the contact delegate was in initWithSize, which wasn't being called for some reason...
I am trying to stop emitter playing endlessly in my Sprite Kit project. The Lifetime setting is set to Start=1 and End=0 and it shows correct result that I want (emits only once).
Problem comes when I call it in update method it keeps emitting endlessly, I tried using removeFromParent and removeAllAction but no luck. Can you please help me with the solution here?
-(void)update:(NSTimeInterval)currentTime {
[self updateheartPositionFromMotionManager];
[self matchIt];
if ( _heart.position.x == 512 && _heart.position.y == 484 ){
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:
[[NSBundle mainBundle] pathForResource:#"sparkly" ofType:#"sks"]];
emitter.position = CGPointMake(self.frame.size.width/2,
CGRectGetMidY(self.frame)+100);;
emitter.name = #"exhaust";
emitter.targetNode =_heart;
[self addChild:emitter];
}else{
[self removeFromParent];
}
}
//step 1 take a global variable
#property BOOL sparklyAdded; in your .h file
inside init or instance method of your .m file
sparklyAdded=FALSE;
-(void)particleAdded
{
}
-(void)update:(NSTimeInterval)currentTime {
[self updateheartPositionFromMotionManager];
[self matchIt];
if (( _heart.position.x>= 512 && _heart.position.y=< 484 ) && !sparklyAdded){
SKEmitterNode *emitter = [NSKeyedUnarchiver unarchiveObjectWithFile:
[[NSBundle mainBundle] pathForResource:#"sparkly" ofType:#"sks"]];
emitter.position = CGPointMake(self.frame.size.width/2,
CGRectGetMidY(self.frame)+100);;
emitter.name = #"exhaust";
emitter.targetNode =_heart;
[self addChild:emitter];
sparklyAdded=TRUE;
}else{
[self removeEffect];
}
}
-(void) removeEffect
{
[self enumerateChildNodesWithName:#"exhaust" usingBlock:^(SKNode *node, BOOL *stop) {
[node removeAllActions];
[node removeFromParent];
sparklyAdded=FALSE;
}];
}
//remember few thighs while adding particle
1) they are expenisive cpu call so add and remove them only once from update loop
i have two layers control layer which contains CCLabelTTF for score, and first layer in which i detect collision btwn my objects. i want to update score (i.e CCLabelTTF value) in control layer from my first layer.
here is my code...
my scene.m
-(id)init{
self=[super init];
FirstLayer *fl=[FirstLayer node];
[self addChild:fl];
controlLayer *cl=[controlLayer node];
[self addChild:cl z:3 tag:1];
return self;
}
control layer.h
#interface controlLayer : CCLayer{
CCLabelTTF * score ;
int score_value;
}
#property(nonatomic,retain)CCLabelTTF * score ;
#property int score_value;
-(void)displayScore;
#end
controlLayer.m
-(id)init{
// my code..
[self displayScore];
}
return self;
}
-(void)displayScore{
CGSize screenSize=[[CCDirector sharedDirector]winSize];
CCLabelTTF * score_lbl = [CCLabelTTF labelWithString:#"Score" fontName:#"Arial" fontSize:16.0];
score_lbl.position=ccp(screenSize.width*0.10,screenSize.height*0.90);
[self addChild:score_lbl z:99];
score =[CCLabelTTF labelWithString:[NSString stringWithFormat:#"test:%d",score_value] fontName:#"Arial" fontSize:16.0] ;
NSString *str = [score string];
NSLog(#"SCORE control:%#",str);
score.position=ccp(screenSize.width*0.20,screenSize.height*0.90);
[self addChild:score];
}
firstLayer.h
#interface FirstLayer : CCLayer
{
controlLayer *cl;
}
#property(nonatomic,retain)controlLayer *cl;
#end
firstLayer.m
#implementation FirstLayer
#synthesize cl;
-(id)init{
---
cl=[controlLayer new];
[self schedule:#selector(tick:)];
return self;
}
-(void)tick:(ccTime)dt{
bool blockFound=false;
world->Step(dt, 10, 10);
std::vector<MyContact>::iterator pos;
for(pos = _contactListener->_contacts.begin();
pos != _contactListener->_contacts.end(); ++pos) {
MyContact contact = *pos;
b2Body *bodyA = contact.fixtureA->GetBody();
b2Body *bodyB = contact.fixtureB->GetBody();
if (bodyA->GetUserData() != NULL && bodyB->GetUserData() != NULL) {
CCSprite *spriteA = (CCSprite *) bodyA->GetUserData();
CCSprite *spriteB = (CCSprite *) bodyB->GetUserData();
// Sprite A = ball, Sprite B = Block
if (spriteA.tag == 1 && spriteB.tag == 2) {
cl.score_value=cl.score_value+5;
[cl.score setString:[NSString stringWithFormat:#"%d",cl.score_value]];
NSString *str = [cl.score string];
NSLog(#"SCORE in GAME:%#",str);
// [cl displayScore];
if (std::find(toDestroy.begin(), toDestroy.end(), bodyB)
== toDestroy.end()) {
toDestroy.push_back(bodyB);
}
}
// Sprite B = block, Sprite A = ball
else if (spriteA.tag == 2 && spriteB.tag == 1) {
cl.score_value=cl.score_value+5;
[cl.score setString:[NSString stringWithFormat:#"%d",cl.score_value]];
NSString *str = [cl.score string];
NSLog(#"SCORE in GAME:%#",str);
// [cl displayScore];
}
}
}
What is going Wrong? , i'm always getting score:test 0 !:(
Your statement cl=[controlLayer new]; is not getting the reference of the controlLayer that was added to your game scene. As your game scene.m code indicates, both FirstLayer and controlLayer instances are initalized and added as children to the CCScene. To get a proper reference to your controlLayer from inside FirstLayer, do it like this:
FirstLayer.m
-(void) onEnter {
[super onEnter];
cl = (controlLayer*)[self.parent getChildByTag:1];
}
The reason you have to put this in onEnter is because self.parent will not be valid within the FirstLayer's init method. onEnter is called after initialization is complete. This should give you the reference to the actual controlLayer that was added to your game scene.
I think by the looks of it, you are sending a new score value to your control layer, but you are not updating the label, as you only set it once. It won't automatically update if score_value changes. I think you need to schedule an update method in your control layer that will update the score label.
Please, I need help in making this code work. I intend to do an animation with a sprite I added using cocos2d and box2d in xcode. But for some odd reasons I cannot get the sprite to animate repeatedly.
This code builds successfully but animates only once. Can anyone help and tell me what I am not doing right?
The implementation file are as follows:
#import "Mosquito.h"
#import "Box2DHelpers.h"
#implementation Mosquito
#synthesize flyingAnim;
- (void) dealloc{
[flyingAnim release];
[super dealloc];
}
-(void)initAnimations {
flyingAnim = [self loadPlistForAnimationWithName:#"flyingAnim"
andClassName:NSStringFromClass([self class])];
[[CCAnimationCache sharedAnimationCache] addAnimation:flyingAnim
name:#"flyingAnim"];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
// id flyingAction = nil;
//CGPoint newPosition;
[self setCharacterState:newState];
switch (newState) {
case kStateIdle:
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"Mosquito_anim_1.png"]];
break;
case kStateFlying:
action = [CCAnimate actionWithAnimation:flyingAnim
restoreOriginalFrame:NO];
break;
case kStateTakingDamage:
action = [CCBlink actionWithDuration:1.0 blinks:3.0];
break;
default:
//CCLOG(#"Unhandled state %d in Mosquito", newState);
break;
}
if (action != nil) {
[self runAction:action];
}
}
- (id)initWithWorld:(b2World *)theWorld atLocation:(CGPoint)location {
if ((self = [super init])) {
world = theWorld;
[self setDisplayFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Mosquito_anim_1.png"]];
gameObjectType = kMosquitoType;
characterHealth = 100.0f;
[self createBodyAtLocation:location];
[self initAnimations];
}
return self;
}
- (void) updateStateWithDeltaTime:(ccTime)deltaTime
andListOfGameObjects:(CCArray *)listOfGameObjects {
//CGPoint oldPosition = self.position;
if ((characterState == kStateDestroyed) &&
([self numberOfRunningActions] > 0)) {
return;
}
if (characterState != kStateFlying &&
[self numberOfRunningActions] == 0) {
[self changeState:kStateFlying];
}
}
#end
Thanks.
id repeatAnimation = [CCRepeatForever actionWithAction:action];
To repeat forever, you need to do that, otherwise you need to just do:
[self runAction:action];
again.
Also, you might want to consider not reassigning action to CCBlink and make another action and call
[self stopAllActions];
id blinkAction = [CCBlink actionWithDuration:1.0 blinks:3.0];
[self runAction:blinkAction];
This may Help you.
One of the easiest ways for sprite animation.
https://sites.google.com/site/rajanallathambi1/cocos2d-tutorials/sprite-animation-without-plist-file
I am obviously making a game that has a score. How do I call an update method and have the integer actually displayed in the Top-Right corner?
Here, this might work
In the .h file:
#interface HelloWorld : CCLayer {
int score;
CCLabelTTF *scoreLabel;
}
- (void)addPoint;
In the .m file:
In the init method:
//Set the score to zero.
score = 0;
//Create and add the score label as a child.
scoreLabel = [CCLabelTTF labelWithString:#"8" fontName:#"Marker Felt" fontSize:24];
scoreLabel.position = ccp(240, 160); //Middle of the screen...
[self addChild:scoreLabel z:1];
Somewhere else:
- (void)addPoint
{
score = score + 1; //I think: score++; will also work.
[scoreLabel setString:[NSString stringWithFormat:#"%#", score]];
}
Now just call: [self addPoint]; whenever the user kills an enemy.
That should work, tell me if it didn't because I have not tested it.
in header file:
#interface GameLayer : CCLayer
{
CCLabelTTF *_scoreLabel;
}
-(void) updateScore:(int) newScore;
in implementation file:
-(id) init
{
if( (self=[super init])) {
// ..
// add score label
_scoreLabel = [CCLabelTTF labelWithString:#"0" dimensions:CGSizeMake(200,30) alignment:UITextAlignmentRight fontName:#"Marker Felt" fontSize:30];
[self addChild:_scoreLabel];
_scoreLabel.position = ccp( screenSize.width-100, screenSize.height-20);
}
return self;
}
-(void) updateScore:(int) newScore {
[_scoreLabel setString: [NSString stringWithFormat:#"%d", newScore]];
}
EDIT: if you don't want to use an ivar, you can use tags:
[self addChild:scoreLabel z:0 tag:kScoreLabel];
// ...
CCLabelTTF *scoreLabel = (CCLabelTTF*)[self getChildByTag:kScoreLabel];
EDIT 2: For performance reasons you should switch to CCLabelAtlas or CCBitmapFontAtlas if you update the score very frequently.
Also read the cocos2d programming guide about labels.
Using UILabel
UILabel.text = [NSString stringWithFormat:#"%lu",score];
Move the UILabel in the top of the view using interface builder
you could also create it programmatically
UILabel *label = [[UILabel alloc] initWithFrame:CGRectMake(0,0,500,30)];
[[self view] addSubview:label];
[label release]; // dont leak :)