Constantly moving object with Cocos2d - cocos2d-iphone

I'm looking for some guidance on how, using Cocos2d, I can have an object (let's say a rocket) that is in constant motion at a constant speed, and I have two buttons to simply change its direction.
I've found a few pieces of information on how to change the orientation of the rocket, however I'm stuck on just having the rocket be in constant motion.
I'll later want to make the rocket be able to fly off the screen and re-appear on the other side of the screen, but that's for another question (I've found some help on this already).
I'm new to Cocos2d and have searched for a few hours, but haven't been able to find what I'm after.
Could somebody point me in the right direction in being able to get my rocket constantly moving, and responding to changes in direction?

So, here's a first pass at how you could do something like that:
// ProjectileTest holds a projectile class that simply knows how to
move itself
// WrapBoundaryTest is the example scene showing potential usage of
ProjectileTest
ProjectileTest.h
#import "cocos2d.h"
#interface ProjectileTest : CCNode
{
CCSprite *sprite;
CGPoint velocity;
CGRect bounds;
BOOL isActive;
float steerAmount;
}
#property CCSprite *sprite;
#property CGPoint velocity;
#property CGRect bounds;
#property BOOL isActive;
#property float steerAmount;
+(id) projectileWithBounds:(CGRect) boundary
andVelocity:(CGPoint) v
andSteeringAmount:(float) steeringAmount;
-(void) step:(CCTime) dt;
-(void) steerLeft;
-(void) steerRight;
#end
ProjectileTest.m
#import "ProjectileTest.h"
#implementation ProjectileTest
#synthesize sprite, velocity, bounds, isActive, steerAmount;
+(id) projectileWithBounds:(CGRect) boundary andVelocity:(CGPoint) v andSteeringAmount:(float) steeringAmount
{
ProjectileTest *proj = [[self alloc] init];
proj.bounds = boundary;
proj.velocity = v;
proj.steerAmount = steeringAmount;
proj.isActive = YES;
[proj calculateAngleForVelocity: v];
return proj;
}
-(id) init
{
if(( self = [super init]))
{
sprite = [CCSprite spriteWithImageNamed:#"Icon.png"];
[self addChild:sprite];
[sprite setScale:0.50f];
bounds = CGRectZero;
velocity = CGPointZero;
isActive = NO;
steerAmount = 1.0f;
}
return self;
}
-(void) calculateAngleForVelocity:(CGPoint) v
{
float rads = ccpToAngle(v);
float degs = -CC_RADIANS_TO_DEGREES(rads);
sprite.rotation = degs + 90.0f;
}
-(void) steerLeft
{
[self steer: YES];
}
-(void) steerRight
{
[self steer: NO];
}
-(void) steer:(BOOL) left
{
if(left)
{
velocity = ccpRotateByAngle(velocity, CGPointZero, -CC_DEGREES_TO_RADIANS(-steerAmount));
}
else // right
{
velocity = ccpRotateByAngle(velocity, CGPointZero, -CC_DEGREES_TO_RADIANS(steerAmount));
}
}
-(void) step:(CCTime)dt
{
if(isActive)
{
if(CGRectContainsPoint(bounds, self.position))
{
self.position = ccpAdd(self.position, velocity);
[self calculateAngleForVelocity: velocity];
}
else
{
float nudge = 0.5f;
if(self.position.x >= bounds.size.width && velocity.x > 0.0f)
{
self.position = ccp(bounds.origin.x + nudge, self.position.y);
}
else if(self.position.x <= bounds.origin.x && velocity.x < 0.0f)
{
self.position = ccp(bounds.size.width - nudge, self.position.y);
}
if(self.position.y >= bounds.size.height && velocity.y > 0.0f)
{
self.position = ccp(self.position.x, bounds.origin.y + nudge);
}
else if(self.position.y <= bounds.origin.y && velocity.y < 0.0f)
{
self.position = ccp(self.position.x, bounds.size.height - nudge);
}
}
}
}
#end
WrapBoundaryTest.h
#import "cocos2d.h"
#import "cocos2d-ui.h"
#interface WrapBoundaryTest : CCScene
{
NSMutableArray *projectiles;
CCButton *left;
CCButton *right;
}
#property __strong NSMutableArray *projectiles;
+(id) scene;
#end
WrapBoundaryTest.m
#import "WrapBoundaryTest.h"
#import "ProjectileTest.h"
#implementation WrapBoundaryTest
#synthesize projectiles;
+(id) scene
{
return [[self alloc] init];
}
-(id) init
{
if(( self = [super init]))
{
projectiles = [NSMutableArray array];
CGRect projectileBounds = CGRectMake(
0.0f,
0.0f,
[CCDirector sharedDirector].designSize.width,
[CCDirector sharedDirector].designSize.height
);
CGPoint origin = ccp(200.0f, 150.0f);
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp( 1.0f, 5.0f) andSteeringAmount:2.0f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp(-1.0f, -5.0f) andSteeringAmount:1.0f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp(-1.0f, 1.0f) andSteeringAmount:0.5f atStartingPosition:origin];
[self addProjectileWithBoundsRect:projectileBounds andVelocity:ccp( 8.0f, 1.0f) andSteeringAmount:7.5f atStartingPosition:origin];
left = [CCButton buttonWithTitle:#"Left" fontName:#"Arial" fontSize:16.0f];
left.positionType = CCPositionTypeNormalized;
left.position = ccp(0.1f, 0.1f);
[self addChild:left];
right = [CCButton buttonWithTitle:#"Right" fontName:#"Arial" fontSize:16.0f];
right.positionType = CCPositionTypeNormalized;
right.position = ccp(0.9f, 0.1f);
[self addChild:right];
}
return self;
}
-(void) addProjectileWithBoundsRect:(CGRect) boundsRect
andVelocity:(CGPoint) velocity
andSteeringAmount:(float) steeringAmount
atStartingPosition:(CGPoint) startingPosition
{
ProjectileTest *proj = [ProjectileTest projectileWithBounds: boundsRect
andVelocity: velocity
andSteeringAmount: steeringAmount
];
proj.position = startingPosition;
[self addChild:proj];
[projectiles addObject:proj];
}
-(void) update:(CCTime)delta
{
for(ProjectileTest *p in projectiles)
{
if(left.touchInside)
{
[p steerLeft];
}
if(right.touchInside)
{
[p steerRight];
}
[p step:delta];
}
}
#end
// HTH

Related

Cocos2d Using 1 class for multiple enemies Score error

Thank you for taking the time to look at my question, As I am very new to programming any help or pointers in the right direction is much appreciated.
Overview...
I have a simple game I am trying to create and it includes a class for 'Ball' sprite that is fired from the left of the screen to the right. The speed at which this "Ball" sprite is created is random and each time the "Ball" moves past the right of the screen a point is added to the score.
Problem....
The problem I have is when 2 "Balls" fire at almost the same time (which happens some times as its random) they pass the right hand of the screen less then a second apart and when this happens it only seems to add 1 when it should be adding 2 to the score (as 2 balls has passed the right side).
Below is the code for my Ball launcher class (use this class to call the ball randomly).
If you have used or gone through 'Ray Wenderlichs' Learn Cocos2D then allot of this will look familiar as after going through his book I have tried to edit his code to do what I want (to me after reading the book its the best way to learn, by messing with the code etc).
#implementation BL
#synthesize delegate;
#synthesize lauchingAnim;
#synthesize afterlauchingAnim;
-(void) dealloc {
delegate = nil;
[lauchingAnim release];
[afterlauchingAnim release];
[super dealloc];
}
//--The below shootPhaser method takes the current direction the launcher is facing and asks the delegate (gameplaylayer) to create the ball moving in that direction.
//--The below createPhaserWithDirection method was declared (created) in the GamePlayLayerDelegate protocol.
-(void)shootPhaser {
CGPoint phaserFiringPosition;
PhaserDirection phaserDir;
CGPoint position = [self position];
float xPosition = position.x + position.x * 0.51f;
float yPosition = position.y + position.y * 0.045f;
{
phaserDir = kDirectionRight;
}
phaserFiringPosition = ccp(xPosition, yPosition);
[delegate createPhaserWithDirection:phaserDir andPosition:phaserFiringPosition];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
id action = nil;
characterState = newState;
switch (newState) {
case kStatespawning:
// CCLOG(#“launcher->Changing State to Spwaning");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#“lancher_1.png"]];
break;
case kStateIdle:
// CCLOG(#“laucher->Changing state to idle");
[self setDisplayFrame:
[[CCSpriteFrameCache sharedSpriteFrameCache]
spriteFrameByName:#"lancher_1.png"]];
break;
case kStateFiring:
// CCLOG(#“launcher->Changing State to firing");
action = [CCSequence actions:
[CCAnimate actionWithAnimation:lauchingAnim],
[CCCallFunc actionWithTarget:self
selector:#selector(shootPhaser)],
[CCAnimate actionWithAnimation:afterlauchingAnim],
[CCDelayTime actionWithDuration:2.0f],
nil];
lauchingAnim.restoreOriginalFrame = NO;
afterlauchingAnim.restoreOriginalFrame = NO;
[self changeState:kStateIdle];
break;
case kStateDead:
CCLOG(#“launcher->changing state to dead");
break;
default:
CCLOG(#"unhandled state %d in launcher", newState);
break;
}
if (action !=nil) {
[self runAction:action];
}
}
-(void)updateStateWithDeltaTime: (ccTime)deltaTime andListOfGameObjects:(CCArray*)listOfGameObjects {
if (characterState == kStateFiring) {
// 5
if (characterState != kStateFiring) {
// If RadarDish is NOT already taking Damage
[self changeState:kStateFiring];
return;
}
}
if ((([self numberOfRunningActions] == 0) && (characterState != kStateDead)) ) {
// CCLOG(#"launcher Going to Idle!!!");
[self changeState:kStateIdle];
return;
}
}
-(void)initAnimations {
[self setLauchingAnim:[self loadPlistForAnimationWithName:#"lauchingAnim" andClassName:NSStringFromClass([self class])]];
[self setAfterlauchingAnim:[self loadPlistForAnimationWithName:#"afterlauchingAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Laauncher initialized");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kBallLaucher;
// 3
[self changeState:kStateIdle];
}
}
return self;
}
#end
Below is the code for my "Ball" class...
#import “Ball.h"
#implementation Ball
#synthesize delegate;
#synthesize myDirection;
#synthesize travelingAnim;
#synthesize ScoreAnim;
-(void) dealloc {
delegate = nil;
[travelingAnim release];
[ScoreAnim release];
[super dealloc];
}
-(void)changeState:(CharacterStates)newState {
[self stopAllActions];
[self setCharacterState:newState];
CGSize screenSize1 = [CCDirector sharedDirector].winSize;
CGPoint position = [self position];
CGPoint endPosition = ccp(screenSize1.width*1.5f, screenSize1.height*0.20f);
id action = nil;
//id action1 = nil;
switch (newState) {
case kStatespawning:
CCLOG(#“Spawning Ball");
[self setDisplayFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
break;
case kStateTravelling: {
movementAction = [CCMoveTo actionWithDuration:5.0f
position:endPosition];
}
break;
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
break;
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
}
if (action !=nil)
[self runAction:action];
}
-(void)removeSelf{
CCLOG(#"Removing Ball Object Has Scored.");
[self setVisible:NO];
[self removeFromParentAndCleanup:YES];
return;
}
-(void)updateStateWithDeltaTime:(ccTime)deltaTime andListOfGameObjects:(CCArray *)listOfGameObjects {
CGPoint currentSpitePosition = [self position];
CGSize screenSize = [CCDirector sharedDirector].winSize;
if (currentSpitePosition.x > screenSize.width*1.1f) {{
[self changeState:kStateScore];
}
}
return;
}
if ([self numberOfRunningActions] == 0) {
if (characterState == kStatespawning) {
[self changeState:kStateTravelling];
return;
}
}
}
-(void)initAnimations {
[self setTravelingAnim:[self loadPlistForAnimationWithName:#"travelingAnim" andClassName:NSStringFromClass([self class])]];
[self setScoreAnim:[self loadPlistForAnimationWithName:#"ScoreAnim" andClassName:NSStringFromClass([self class])]];
}
-(id) initWithSpriteFrameName:(NSString*)frameName{
if ((self=[super init])) {
if ((self = [super initWithSpriteFrameName:frameName])) {
CCLOG(#"### Ball Initialised");
[self initAnimations];
characterHealth = 3.0f;
gameObjectType = kEnemyTypeBall;
[self changeState:kStatespawning];
}
}
return self;
}
#end
Below is the code for my "GameplayLayer" class...
#import "GamePlayLayer.h"
#import “Ball.h"
#import “BL.h"
#implementation GamePlayLayer
#synthesize delegate;
-(void) dealloc {
delegate = nil;
[super dealloc];
}
-(void) update:(ccTime)deltaTime {
CCArray *listOfGameObjects =
[sceneSpriteBatchNode children];
for (GameCharacter *tempChar in listOfGameObjects) {
[tempChar updateStateWithDeltaTime:deltaTime andListOfGameObjects:listOfGameObjects];
}
GameCharacter *tempChar = (GameCharacter*)[sceneSpriteBatchNode
getChildByTag:kEnemyTypeBall];
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
return;
}
}
-(void)addPoint
{
score = score + 1;
[scoreLabel setString:[NSString stringWithFormat:#"$%i", score]]; <===ADDING THE POINT TO THE SCORE
return;
}
-(void) createObjectOfType: (GameObjectType)objectType
withHealth:(int)initialHealth atLocation:(CGPoint)spawnLocation withZValue:(int)ZValue {
if (kBallLaucher == objectType) {
CCLOG(#"Creating launcher Object");
BL *ballLauncher = [[[BL alloc] init] initWithSpriteFrameName:#“launcher_1.png"];
[ballLauncher setCharacterHealth:initialHealth];
[ballLauncher setPosition:spawnLocation];
[sceneSpriteBatchNode addChild:tBT z:ZValue tag:kBallLaucher ];
[ballLauncher setDelegate:self];
[ballLauncher release];
}
}
**BELOW IS HOW I CONTROL WHEN THE BALL IS FIRED**
-(void)ChangeStateLaucher:(int)brandnewState withState:(CharacterStates)newState andObject:(GameObjectType)objectType; {
BL *bL = (BL*)
[sceneSpriteBatchNode getChildByTag:kBallLaucher];
int x = (arc4random() % 2);
if (x==0) {
CCLOG(#"Start Laucher Firing");
[bL changeState:kStateFiring];
count = 0;
}
if (x==1) {
CCLOG(#"No Laucher Firing");
count = count + 1;
if (count == 2) {
CCLOG(#"No Laucher Firing x2 - Start Laucher Firing");
[bL changeState:kStateFiring];
} else if (count > 3) {
CCLOG(#"No Laucher Firing x3 - Start Laucher Firing");
[bL changeState:kStateFiring];
}
}
[delegate ChangeStateLaucher:x withState:kStateFiring andObject:objectType];
}
-(void)createPhaserWithDirection:(PhaserDirection)phaserDirection andPosition:(CGPoint)spawnPosition {
CCLOG(#"Creating Ball from Gameplay Layer");
Ball *ballSprite = [[Ball alloc]initWithSpriteFrame:[[CCSpriteFrameCache
sharedSpriteFrameCache]
spriteFrameByName:#"Ball_1.png"]];
[ballSprite setPosition:spawnPosition];
[ballSprite setMyDirection:phaserDirection];
[ballSprite setCharacterState:kStatespawning];
[ballSprite setCharacterHealth:3.0f];
[sceneSpriteBatchNode addChild:ballSprite z:20 tag:kEnemyTypeBall];
[ballSprite release];
}
-(id)init {
self = [super init];
if (self !=nil) {
CGSize screenSize = [CCDirector sharedDirector]. winSize;
self.TouchEnabled = YES;
srandom(arc4random()); // Seeds the random number generator
[[CCSpriteFrameCache sharedSpriteFrameCache]
addSpriteFramesWithFile:#"scene1atlas.plist"]; // 1
sceneSpriteBatchNode =
[CCSpriteBatchNode batchNodeWithFile:#"scene1atlas.png"]; // 2
[self createObjectOfType:kBallLaucher withHealth:3 atLocation:ccp(screenSize.width * 0.05f, screenSize.height * 0.822f) withZValue:10];
[gameBeginLabel setPosition:ccp(screenSize.width/2,screenSize.height/2)]; // 2
[self addChild:gameBeginLabel]; // 3
id labelAction = [CCSpawn actions:
[CCFadeOut actionWithDuration:2.5f],
nil]; // 4
[gameBeginLabel runAction:labelAction];
lives = 3;
scoreLabel = [CCLabelBMFont labelWithString:#"$0"
fntFile:#“BallTest.fnt"];
scoreLabel.position = ccp(screenSize.width * 0.5f, screenSize.height * 0.9f);
[self addChild:scoreLabel
z:-1 tag:kNewScoreTagValue];
[self scheduleUpdate];
}
return self;
}
#end
Once again I would like to thank anyone that has taken the time to look at this question regardless if you have posted a pointer or answer or not.
Thanks.
I think that when there are more than one Ball objects on the screen, the GamePlayLayer update method is unable to pick up on the state change on one of the balls at times because that ball is removed as soon as it enters kStateScore.
If you add a certain delay before removing the ball , the update method should pick up the changes in all the balls and your problem should be resolved.
In your Ball.m -(void)changeState:(CharacterStates)newState function , modify the action on state kStateScore like
action = [CCSequence actions:[CCDelayTime actionWithDuration:0.2],
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
Try this out and let me know if this worked for you!
EDIT
The above solution would end up adding more than one point as the ball would remain in kStateScore state for a long time due to delay. In order to counter that problem , we can introduce a new state call kStateDead and change the ball state to kStateDead after adding the point. This will ensure a point is added only once and also we can put the responsibility of removing the ball in kStateDead state. The new code would like as follows :
Ball.m
case kStateScore:
PLAYSOUNDEFFECT(SCORE);
CCLOG(#“Ball Past Left Of Screen => Add 1 to Score");
break;
case kStateDead:
action = [CCSequence actions:
[CCCallFunc actionWithTarget:self selector:#selector(removeSelf)],
nil];
default:
CCLOG(#“Ball -> Unknown CharState %d",
characterState);
break;
and in your Gameplayer.m
if ([tempChar characterState] == kStateScore) <==HERE I AM SEEING IF THE BALL HAS SCORED - THIS IS ALSO WHERE I THINK I MAY BE GOING WRONG SOMEHOW.
{
CCLOG(#"Add 1 Points To Score");
[self addPoint];
[tempChar changeState:kStateDead]; //change the state to dead after the point is added
return;
}

How to move a CCSprite inside CCNode In cocos2d

I have a Bird which is a (CCSprite) and i want it to be flying in the Top of screen off course, So i add CCNode and add the bird as child on it but it's still flying outside of the CCNode!
MY CODE IS:
#implementation GamePlay {
CCSprite *myBird;
CCNode *theFlyingArea;
}
-(id)init {
if(self=[super init]) {
theFlyingArea = [CCNodeColor nodeWithColor:[CCColor colorWithRed:0.2f green:0.2f blue:0.2f alpha:0.4f] width:viewSize.width height:viewSize.height*60/100];
theFlyingArea.position = ccp(0, 0);
theFlyingArea.positionType = CCPositionTypeMake(CCPositionUnitPoints, CCPositionUnitPoints, CCPositionReferenceCornerTopLeft);
theFlyingArea.anchorPoint = ccp(0, 1);
[self addChild:theFlyingArea z:1];
float RandomY = arc4random() % (int)theFlyingArea.contentSize.height;
// Create MY BIRD
myBird = [CCSprite spriteWithImageNamed:#"myBird.png"];
myBird.scale = myBird.scale / 2;
myBird.position = ccp(-10, RandomY);
[theFlyingArea addChild:myBird z:2];
}
return self;
}
- (void)update:(CCTime)delta {
id moveTo = [CCActionMoveTo actionWithDuration:100.f position:ccp(((float)rand() / RAND_MAX) * theFlyingArea.contentSize.width , ((float)rand() / RAND_MAX) * theFlyingArea.contentSize.height)];
[myBird runAction:moveTo];
}
#end
Thank you very much.

COCOS2D - Removing a child if it collides with another sprite

I am working on a game that has the player controlling a spaceShip. I want to make it such that if the spaceShip sprite collides with spaceJunk the junk disappears.
Here is my code so far.
HelloWorldLayer.h
#import <GameKit/GameKit.h>
// HelloWorldLayer
#interface HelloWorldLayer : CCLayerColor
{
CCSprite *_starShip;
CCSprite *_Paddle1;
CCSprite *_paddle2;
}
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
HelloWorldLayer.M
// Import the interfaces
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#pragma mark - HelloWorldLayer
// HelloWorldLayer implementation
//Import SpaceThings
#import "SpaceThings.h"
#implementation HelloWorldLayer{
}
// Helper class method that creates a Scene with the HelloWorldLayer as the only child.
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(void)gameLgoic:(ccTime)dt
{
[self addSpaceThings];
}
-(void) addSpaceThings
{
// _junk = [CCSprite spriteWithFile:#"catplaceholder.png"];
SpaceThings * spaceThings = nil;
if (arc4random() % 2 == 0){
spaceThings = [[[Astroids alloc] init] autorelease];
}else{
spaceThings = [[[SpaceJunk alloc]init] autorelease];
}
//determine where to spawn the space junk along the X-axis
CGSize winSize = [CCDirector sharedDirector].winSize;
int minX = spaceThings.contentSize.width/2;
int maxX = winSize.width - spaceThings.contentSize.width/2;
int rangeX =maxX - minX;
int actualX = (arc4random() % rangeX) + minX;
//spawn the sprite slightly above the screen
spaceThings.position = ccp(actualX, winSize.height+_starShip.contentSize.height/2);
[self addChild:spaceThings];
//set the speed of junk
int minDuration = spaceThings.minMoveDirection;
int maxDuarton = spaceThings.maxMoveDirection;
int rangeDuration = maxDuarton - minDuration;
float actualDuraton = (arc4random() % rangeDuration)+ minDuration;
//move junk logic
CCMoveTo * actionMove = [CCMoveTo actionWithDuration:actualDuraton position:ccp(actualX, -spaceThings.contentSize.height/2)];
CCCallBlockN *actionMoveDone = [CCCallBlockN actionWithBlock:^(CCNode *node){[node removeFromParentAndCleanup:YES];}];
[spaceThings runAction:[CCSequence actions:actionMove, actionMoveDone, nil]];
if (CGRectIntersectsRect(_starShip.boundingBox, spaceThings.boundingBox)){
[self removeChild:spaceThings cleanup:YES];
}
}
// on "init" you need to initialize your instance
-(id) init
{
if (self =[super initWithColor:ccc4(255,255,255,255)]){
CGSize winSize = [CCDirector sharedDirector].winSize;
_starShip = [CCSprite spriteWithFile:#"theShip.gif"];
_starShip.position = ccp(winSize.width/2, 1.25*_starShip.contentSize.height);
//make the starship appear
[self addChild:_starShip];
//make the paddles
//bottom left one
_Paddle1 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomOfScreenX = 0 + _Paddle1.contentSize.width/2;
int bottomOfScreenY = 0+_Paddle1.contentSize.height/2;
_Paddle1.position = ccp(bottomOfScreenX,bottomOfScreenY);
[self addChild:_Paddle1];
//bottom right one
_paddle2 = [CCSprite spriteWithFile:#"spritePaddle.jpeg"];
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/2;
_paddle2.position = ccp(bottomRightOfScreenX, bottomOfScreenY);
[self addChild:_paddle2];
//make thingsInSpace spawn at a set interval
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
//enable touch
[self setIsTouchEnabled:YES];
}
return self;
}
// 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];
}
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
//make the screens and create events
CGSize winSize = [CCDirector sharedDirector].winSize;
NSSet *allTouches = [event allTouches];
UITouch *touch = [allTouches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
//check to see if the left paddle is being pressed
if (CGRectContainsPoint([_Paddle1 boundingBox], location)){
int bottomLeftOfScreenX = 0 + _Paddle1.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship must move parallel to it.
id action = [CCMoveTo actionWithDuration:2 position:ccp(bottomLeftOfScreenX,1.25*_starShip.contentSize.height) ];
//have starship call this action
[_starShip runAction:action];
[action setTag:1];
}
//check to see if the right paddle is being pressed
if (CGRectContainsPoint([_paddle2 boundingBox], location)){
int bottomRightOfScreenX = winSize.width - _paddle2.contentSize.width/1.25;
//make an action
//the Y-coordinate of starShip must be the same since the ship moves parallel to it
id action2 = [CCMoveTo actionWithDuration:2 position:ccp(bottomRightOfScreenX,1.25*_starShip.contentSize.height) ];
//starShip is now running this specified action
[_starShip runAction:action2];
[action2 setTag:2];
}
}
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//if the player stops touching the pads, all the aciton should stop.
//left button stop
[_starShip stopActionByTag:1];
//right button stop
[_starShip stopActionByTag:2];
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
spaceThings.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#interface SpaceThings : CCSprite
#property (nonatomic, assign) int minMoveDirection;
#property (nonatomic, assign) int maxMoveDirection;
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection;
#end
#interface Astroids : SpaceThings
#end
#interface SpaceJunk : SpaceThings
#end
SpaceThings.M
#import "SpaceThings.h"
#implementation SpaceThings
-(id)initWithFile:(NSString *)file minMoveDirection:(int)minMoveDirection maxMoveDirection:(int)maxMoveDirection
{
if ((self =[super initWithFile:file])){
self.minMoveDirection = minMoveDirection;
self.maxMoveDirection = maxMoveDirection;
}
return self;
}
#end
#implementation Astroids
-(id)init
{
if (self = [super initWithFile:#"astroid.jpeg" minMoveDirection:2 maxMoveDirection:5]){
}
return self;
}
#end
#implementation SpaceJunk
-(id)init
{
if (self = [super initWithFile:#"blueDot.jpg" minMoveDirection:4 maxMoveDirection:7]){
}
return self;
}
#end
I'm wondering as to where to put CGRectInteresectRect part of the code. As I've currently tried placing within the SpaceJunk alloc init part and it doesn't remove the child when the ship collides with the junk.
int setInterval = (arc4random() % 3);
[self schedule:#selector(gameLgoic:) interval:setInterval];
Make setInterval value to float and give small value for schedular.
Also not do [spaceThings runAction inside loop. if required then do some condition check and run only if required.
Then your CGRectIntersectsRect will fire when intersect each other.
Happy Coding.

EXC_BAD_ACCESS while using box2d GetUserData and SetUserData

I've been straggling with this problem for a few days now and I'm desperate for your help. As I've been following Ray Wenderlich's tutorials and I still get EXC_BAD_ACCESS error when I'm trying to getUserData from b2Body inside a ContactListener
so WHContactListener.h:
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
class WHContactListener : public b2ContactListener
{
private:
void BeginContact(b2Contact* contact);
void EndContact(b2Contact* contact);
};
WHContactListener.mm
#import "WHContactListener.h"
#import "cocos2d.h"
#import "ExSprite.h"
void WHContactListener::BeginContact(b2Contact *contact)
{
b2Body *bodyA = contact->GetFixtureA()->GetBody();
b2Body *bodyB = contact->GetFixtureB()->GetBody();
/*The problem occurs here, while it returns a non-null value it just crashes when I implement any method here, such as NSLog */
ExSprite *spriteB = (ExSprite*)bodyB->GetUserData();
NSLog(#"output %#", spriteB);
}
void WHContactListener::EndContact(b2Contact *contact)
{
}
Sprite class ExSprite.h
#import <Foundation/Foundation.h>
#import "cocos2d.h"
#import "Box2D.h"
#interface ExSprite : CCSprite {
BOOL _exploded;
b2Body *_explBody;
}
#property (nonatomic, readwrite) BOOL exploded;
#property (nonatomic, assign) b2Body *explBody;
-(void)setPhysicsBody:(b2Body*)body;
#end
ExSprite.mm
#import "ExSprite.h"
#import "HelloWorldLayer.h"
#pragma mark - ExSprite
#implementation ExSprite
#synthesize exploded = _exploded;
#synthesize explBody = _explBody;
-(void)setPhysicsBody:(b2Body *)body
{
_explBody = body;
_explBody->SetUserData(self);
}
// this method will only get called if the sprite is batched.
// return YES if the physics values (angles, position ) changed
// If you return NO, then nodeToParentTransform won't be called.
-(BOOL) dirty
{
return YES;
}
// returns the transform matrix according the Chipmunk Body values
-(CGAffineTransform) nodeToParentTransform
{
b2Vec2 pos = _explBody->GetPosition();
float x = pos.x * PTM_RATIO;
float y = pos.y * PTM_RATIO;
if ( ignoreAnchorPointForPosition_ ) {
x += anchorPointInPoints_.x;
y += anchorPointInPoints_.y;
}
// Make matrix
float radians = _explBody->GetAngle();
float c = cosf(radians);
float s = sinf(radians);
if( ! CGPointEqualToPoint(anchorPointInPoints_, CGPointZero) ){
x += c*-anchorPointInPoints_.x + -s*-anchorPointInPoints_.y;
y += s*-anchorPointInPoints_.x + c*-anchorPointInPoints_.y;
}
// Rot, Translate Matrix
transform_ = CGAffineTransformMake( c, s,
-s, c,
x, y );
return transform_;
}
-(void) dealloc
{
//
[super dealloc];
}
#end
Finally the layer that I'm using HelloWorld.h
#import <GameKit/GameKit.h>
// When you import this file, you import all the cocos2d classes
#import "cocos2d.h"
#import "Box2D.h"
#import "GLES-Render.h"
#import "WHContactListener.h"
//Pixel to metres ratio. Box2D uses metres as the unit for measurement.
//This ratio defines how many pixels correspond to 1 Box2D "metre"
//Box2D is optimized for objects of 1x1 metre therefore it makes sense
//to define the ratio so that your most common object type is 1x1 metre.
#define PTM_RATIO 32
// HelloWorldLayer
#interface HelloWorldLayer : CCLayer <GKAchievementViewControllerDelegate, GKLeaderboardViewControllerDelegate>
{
CCTexture2D *spriteTexture_; // weak ref
b2World* world; // strong ref
GLESDebugDraw *m_debugDraw; // strong ref
WHContactListener* contactListener;
CCRenderTexture *target;
CCSprite *brush;
b2Body *groundBody;
NSMutableArray *shapeVert;
}
#property (nonatomic, readwrite)b2Body *groundBody;
#property (nonatomic, assign)NSMutableArray *shapeVert;
// returns a CCScene that contains the HelloWorldLayer as the only child
+(CCScene *) scene;
#end
and HelloWorld.mm
#import "HelloWorldLayer.h"
// Needed to obtain the Navigation Controller
#import "AppDelegate.h"
#import "PhysicsSprite.h"
#import "ExSprite.h"
enum {
kTagParentNode = 1,
};
#pragma mark - HelloWorldLayer
#interface HelloWorldLayer()
-(void) initPhysics;
-(void) addNewSpriteAtPosition:(CGPoint)p;
#end
#implementation HelloWorldLayer
#synthesize groundBody = groundBody_;
#synthesize shapeVert = shapeVert_;
+(CCScene *) scene
{
// 'scene' is an autorelease object.
CCScene *scene = [CCScene node];
// 'layer' is an autorelease object.
HelloWorldLayer *layer = [HelloWorldLayer node];
// add layer as a child to scene
[scene addChild: layer];
// return the scene
return scene;
}
-(id) init
{
if( (self=[super init])) {
// enable events
self.isTouchEnabled = YES;
self.isAccelerometerEnabled = YES;
CGSize s = [CCDirector sharedDirector].winSize;
// init physics
[self initPhysics];
target = [CCRenderTexture renderTextureWithWidth:s.width height:s.height pixelFormat:kCCTexture2DPixelFormat_RGBA8888];
[target retain];
[target setPosition:ccp(s.width/2, s.height/2)];
[self addChild:target z:0];
brush = [CCSprite spriteWithFile:#"bird.png"];
[brush retain];
[self scheduleUpdate];
}
return self;
}
-(void) dealloc
{
delete world;
world = NULL;
delete contactListener;
contactListener = NULL;
delete m_debugDraw;
m_debugDraw = NULL;
[super dealloc];
}
-(void) initPhysics
{
CGSize s = [[CCDirector sharedDirector] winSize];
b2Vec2 gravity;
gravity.Set(0.0f, -10.0f);
world = new b2World(gravity);
// Do we want to let bodies sleep?
world->SetAllowSleeping(true);
world->SetContinuousPhysics(true);
contactListener = new WHContactListener();
world->SetContactListener(contactListener);
m_debugDraw = new GLESDebugDraw( PTM_RATIO );
world->SetDebugDraw(m_debugDraw);
uint32 flags = 0;
flags += b2Draw::e_shapeBit;
// flags += b2Draw::e_jointBit;
// flags += b2Draw::e_aabbBit;
// flags += b2Draw::e_pairBit;
// flags += b2Draw::e_centerOfMassBit;
m_debugDraw->SetFlags(flags);
// Define the ground body.
b2BodyDef groundBodyDef;
groundBodyDef.position.Set(s.width/2/PTM_RATIO, s.height/2/PTM_RATIO); // bottom-left corner
//SET USERDATA
//groundBodyDef.userData = groundBody;
// Call the body factory which allocates memory for the ground body
// from a pool and creates the ground box shape (also from a pool).
// The body is also added to the world.
groundBody = world->CreateBody(&groundBodyDef);
// Define the ground box shape.
b2EdgeShape groundBox;
float32 x1 = 2*cosf(0.0f * b2_pi/180);
float32 y1 = 2*sinf(0.0f * b2_pi/180);
for(int32 i=1; i<=18; i++){
float32 x2 = 2*cosf((i*20) * b2_pi / 180);
float32 y2 = 2*sinf((i*20) * b2_pi / 180);
groundBox.Set(b2Vec2(x1,y1), b2Vec2(x2,y2));
groundBody->CreateFixture(&groundBox, 0);
x1 = x2;
y1 = y2;
}
}
-(void) draw
{
//
// IMPORTANT:
// This is only for debug purposes
// It is recommend to disable it
//
[super draw];
ccGLEnableVertexAttribs( kCCVertexAttribFlag_Position );
kmGLPushMatrix();
world->DrawDebugData();
kmGLPopMatrix();
}
-(void) addNewSpriteAtPosition:(CGPoint)p
{
//CCLOG(#"Add sprite %0.2f x %02.f",p.x,p.y);
CCNode *parent = [self getChildByTag:kTagParentNode];
//We have a 64x64 sprite sheet with 4 different 32x32 images. The following code is
//just randomly picking one of the images
int idx = (CCRANDOM_0_1() > .5 ? 0:1);
int idy = (CCRANDOM_0_1() > .5 ? 0:1);
//PhysicsSprite *sprite = [PhysicsSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32 * idx,32 * idy,32,32)];
ExSprite *sprite = [ExSprite spriteWithTexture:spriteTexture_ rect:CGRectMake(32*idx, 32*idy, 32, 32)];
[parent addChild:sprite];
sprite.position = ccp( p.x, p.y);
// Define the dynamic body.
//Set up a 1m squared box in the physics world
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(p.x/PTM_RATIO, p.y/PTM_RATIO);
//USERDATA set
//bodyDef.userData = sprite;
b2Body *body = world->CreateBody(&bodyDef);
// Define another box shape for our dynamic body.
b2PolygonShape dynamicBox;
dynamicBox.SetAsBox(.5f, .5f);//These are mid points for our 1m box
// Define the dynamic body fixture.
b2FixtureDef fixtureDef;
fixtureDef.shape = &dynamicBox;
fixtureDef.density = 1.0f;
fixtureDef.restitution = 0.0f;
//fixtureDef.density = rand() * 20.0f;
fixtureDef.friction = 0.3f;
body->CreateFixture(&fixtureDef);
[sprite setPhysicsBody:body];
//NSLog(#"here %#", body->GetUserData());
}
-(void) update: (ccTime) dt
{
//It is recommended that a fixed time step is used with Box2D for stability
//of the simulation, however, we are using a variable time step here.
//You need to make an informed choice, the following URL is useful
//http://gafferongames.com/game-physics/fix-your-timestep/
int32 velocityIterations = 8;
int32 positionIterations = 1;
// Instruct the world to perform a single step of simulation. It is
// generally best to keep the time step and iterations fixed.
world->Step(dt, velocityIterations, positionIterations);
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
//Add a new body/atlas sprite at the touched location
for( UITouch *touch in touches ) {
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
[self addNewSpriteAtPosition: location];
}
}
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
/*UITouch *touch = (UITouch*)touches.anyObject;
CGPoint start = [touch locationInView:[touch view]];
start = [[CCDirector sharedDirector] convertToGL:(start)];
CGPoint end = [touch previousLocationInView:[touch view]];
end = [[CCDirector sharedDirector] convertToGL:end];
[target begin];
float distance = ccpDistance(start, end);
for(int i =0; i < distance; i++){
float difx = end.x - start.x;
float dify = end.y - start.y;
float delta = (float)i / distance;
[brush setPosition:ccp(start.x + (difx*delta), start.y + (dify * delta))];
[brush visit];
}
[target end];
*/
}
#pragma mark GameKit delegate
-(void) achievementViewControllerDidFinish:(GKAchievementViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
-(void) leaderboardViewControllerDidFinish:(GKLeaderboardViewController *)viewController
{
AppController *app = (AppController*) [[UIApplication sharedApplication] delegate];
[[app navController] dismissModalViewControllerAnimated:YES];
}
#end
Any help will be appreciated on the matter.
I've found the problem. It seems that I've left CCNode undeclared, and when I attach body with the UserData to the unavailable CCNode, it then crashes when I retrieve it in the contactlistener class

How to use openGL line drawing in cocos2d

I like to draw a line, but cocos2d inside ccDrawLine serrate, how to draw a blurring of the line, who can help me?
ccDrawLine( ccp(StartP.x, StartP.y), ccp(EndP.x, EndP.y) );
I did not use ccDrawLine but I created a line with a Sprite and I animated it (see code below). In this way I was able to use custom line images made (but nevermind. that's how I did).
If you want to stick to primitives I guess you should set the opacity of the line primitive (see this post which explains how) and then you could create a sequence of action that set the opacity level (e.g. start with opacity at 100%, then 75%, then back to 100% like I did with my images but using the method in the link above) to get the blurring effect..
Code using images:
CCSprite * string = [self getChildByTag:tag];
[string setOpacity:100];
NSMutableArray* frames = [[NSMutableArray alloc]initWithCapacity:3];
NSString*lineFrame = [NSString stringWithString:#"line0.png"];
CCSpriteFrame* frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line1.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
lineFrame = [NSString stringWithString:#"line0.png"];
frame = [[CCSpriteFrameCache sharedSpriteFrameCache]spriteFrameByName:lineFrame];
[frames addObject:frame];
CCAnimation* anim = [CCAnimation animationWithFrames:frames delay:0.1f];
CCAnimate* animate = [CCAnimate actionWithAnimation:anim];
//CCRepeatForever* repeat = [CCRepeatForever actionWithAction:animate];
[string runAction:animate];
[string setOpacity:75];
Hope that this helps..
#import "cocos2d.h"
#interface CClineSprite : CCLayer
{
CCRenderTexture * renderTarget;
NSMutableArray * pathArray;
CCSprite * pathBrush;
CGPoint prePosition;
}
-(void)setLinePosition:(CGPoint)position;
-(void)setLineOpacity:(GLubyte) anOpacity;
-(void)setLineScale:(float) scale;
-(void)setLineColor:(ccColor3B) color;
#end
#import "CClineSprite.h"
#implementation CClineSprite
- (id) init
{
self = [super init];
if (self)
{
CGSize s = [[CCDirector sharedDirector] winSize];
renderTarget = [CCRenderTexture renderTextureWithWidth:s.width height:s.height];
[renderTarget setPosition:ccp(s.width/2, s.height/2)];
[self addChild:renderTarget z:1];
pathBrush = [CCSprite spriteWithFile:#"dots.png"];
pathBrush.color = ccWHITE;
[pathBrush setOpacity:100];
[pathBrush setScale:0.5];
pathArray = [[NSMutableArray alloc]init];
}
return self;
}
-(void)setLineOpacity:(GLubyte) anOpacity
{
[pathBrush setOpacity:anOpacity];
}
-(void)setLineScale:(float) scale
{
[pathBrush setScale:scale];
}
-(void)setLineColor:(ccColor3B) color
{
[pathBrush setColor:color];
}
-(void)setLinePosition:(CGPoint)position
{
[pathArray addObject:[NSValue valueWithCGPoint:position]];
[self renderPath];
}
- (void) renderPath
{
[renderTarget clear:0 g:0 b:0 a:0];
[renderTarget begin];
for (int i = 0; i < pathArray.count-1;i++)
{
CGPoint pt1;
CGPoint pt2;
[[pathArray objectAtIndex:i] getValue: &pt1];
[[pathArray objectAtIndex:i + 1] getValue: &pt2];
float distance = ccpDistance(pt1, pt2);
if (distance > 1)
{
int d = (int)distance;
for (int i = 0; i < d; i += 10)
{
float difx = pt2.x - pt1.x;
float dify = pt2.y - pt1.y;
float delta = (float)i / distance;
[pathBrush setPosition:ccp(pt1.x + (difx * delta), pt1.y + (dify * delta))];
[pathBrush visit];
}
}
}
[renderTarget end];
}
#end
USAGE:
CClineSprite* streak = [CClineSprite node];
[self addChild:streak z:999];
[streak setLinePosition:associatedTurtleObject.position];
[streak setLineScale:0.4];
To dynamically update or extend the line just use
[streak setLinePosition:ccp(x,y)];
Hope this will give you more flexibility for use