How to group sprites in cocos2d for touch detection? - ios

I use this code example to detect whether sprite is touched.
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedlocation = [[CCDirector sharedDirector] convertToGL: location];
CGPoint convertedNodeSpacePoint = [self convertToNodeSpace:convertedlocation];
if (CGRectContainsPoint([_sprite boundingBox],convertedNodeSpacePoint))
{}
I have many sprites, and I must copy that code many times for each of them just changing sprite names. How can I make this process a bit shorter ? Making a NSArray, maybe?

If I have get your question right, you can use for loop and check all children. Something like this-
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
for (CCSprite *spr in self.children) {
if ([spr isKindOfClass:[CCSprite class]]) {
float distance = pow(spr.position.x - location.x, 2) + pow(spr.position.y - location.y, 2);
distance = sqrt(distance);
if (distance1 <= 150) {
//do something with your sprite
}
}
}

Related

Sprite Kit: How to make a node "jump" and also move it left or right, dependant on node position and length of touch?

This is my first post so go easy haha.
I'm new to 'iOS' 'coding', 'Xcode' and 'spritekit'. I'm looking to make an image node "jump" a distance on the positive y-axis if I touch anywhere on the screen, although if I touch somewhere to the left or right if the image and hold for a certain time, it moves in the respective left or right direction, a distance respective to the length of the touch.
Not sure if that's very clear, but any help would be appreciated! Thanks!
You could move a node like this
In touchesEnded: or touchesBegan: method:
{
node.position.y += 50;
}
In order for sprite to move somewhere you could also use actions, there is a family of actions like moveTo action.
I would suggest picking upa tutorial or a book about sprite kit. Good site with free tutorials is raywenderlich.com
Please Try this code :
a sprite move : left/right according to your touch direction anywhere on screen
// CGPoint initialPos; // in .h file
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchPt = [touch locationInView:touch.view];
initialPos = [[CCDirector sharedDirector] convertToGL:touchPt];
}
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch=[touches anyObject];
CGPoint currentPos=[myTouch locationInView:[myTouch view]];
currentPos=[[CCDirector sharedDirector] convertToGL:currentPos];
float diffX = currentPos.x - initialPos.x;
float diffY = currentPos.y - initialPos.y;
CGPoint velocity = ccp(diffX, diffY);
initialPos = currentPos;
[Sprite setPosition:ccpAdd([SmileBall position], velocity)];
}
- (void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch=[touches anyObject];
CGPoint currentPos=[myTouch locationInView:[myTouch view]];
currentPos=[[CCDirector sharedDirector] convertToGL:currentPos];
float diffX = currentPos.x - initialPos.x;
float diffY = currentPos.y - initialPos.y;
CGPoint velocity = ccp(diffX, diffY);
initialPos = currentPos;
[Sprite setPosition:ccpAdd([SmileBall position], velocity)];
}
a sprite jump up: according to your touch on screen
-(void) ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
CCSprite *RunningChar;
RunningChar=(CCSprite *)[self getChildByTag:10];
UITouch *touch = [touches anyObject];
CGPoint touchPt = [touch locationInView:touch.view];
touchPt = [[CCDirector sharedDirector] convertToGL:touchPt];
float radian = ccpToAngle(ccpSub(touchPt, prevPoint));
float degrees = CC_RADIANS_TO_DEGREES(radian);
if (degrees >= 22.5 && degrees <= 112.5) // for upward direction :). otherwise you can write only code without ant condition
{
CCJumpTo *jump=[CCJumpTo actionWithDuration:0.7 position:CGPointMake(RunningChar.position.x, 87) height:110 jumps:1];
[RunningChar runAction:seq];
}
else
{
}
}
Try this :)

What does userInteractionEnabled = NO mean and how does it work?

Cocos 2d-iphone 3.0. I am using this code to detect whether a single sprite is touched
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedlocation = [[CCDirector sharedDirector] convertToGL: location];
CGPoint convertedNodeSpacePoint = [self convertToNodeSpace:convertedlocation];
if (CGRectContainsPoint([_sprite boundingBox],convertedNodeSpacePoint))
{
// Remove sprite
}
}
I have some code inside that should remove some other sprites from parent. Logically, when _sprite
is touched second time, application will crash, because other sprites'v been already removed.
I was trying to make _sprite untouchable using _sprite.userInteractionEnabled = NO;, but this has no effect.
What does userInteractionEnabled mean exactly and how would one use this to facilitate touch detection on sprites.
What is the optimal way of handling touches on sprites in my scene?
There is no way to disable interactions on a ccsprite. What you should do is:
bool firstTimeClicked=false;
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if(firstTimeClicked==true)
{
return NO;
}
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedlocation = [[CCDirector sharedDirector] convertToGL: location];
CGPoint convertedNodeSpacePoint = [self convertToNodeSpace:convertedlocation];
if (CGRectContainsPoint([_sprite boundingBox],convertedNodeSpacePoint)) {
firstTimeClicked=true;
}
}
What userInteractionEnabled does
When you set userInteractionEnabled=YES on a sprite that mean that user interaction callbacks are going to get called.
So for example in your scene if you would have userInteractionEnabled=NO your -(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event would not get called.
What you we're doing
You were detecting the touches in your scene. And then you we're checking what was under that touch location, so if anything was under there it would be detected.
How to accomplish this properly - subclass your CCSprite
You want to handle these kind of behaviours in custom CCSprite classes, because if not your scenes touchBegan is going to turn into a huge buggy if else switch statement.
Believe me, I have been there.
Here is a code example of what this class could look like :
The .h file
#interface SubclassedSprite : CCSprite
#end
And the .m file
#implementation SubclassedSprite
- (void) onEnter
{
[super onEnter];
self.userInteractionEnabled = YES;
}
- (void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CCLOG(#"Touch began on sprite");
// Your code when the Sprite was touched [self removeFromParentAndCleanup:YES];
}
#end
This is the proper, scalable way to handle this.
Below code is without taking an extra BOOL variable and it should work for you:
-(void) touchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint location = [touch locationInView: [touch view]];
CGPoint convertedlocation = [[CCDirector sharedDirector] convertToGL: location];
CGPoint convertedNodeSpacePoint = [self convertToNodeSpace:convertedlocation];
if (CGRectContainsPoint([_sprite boundingBox],convertedNodeSpacePoint)) {
if(_sprite.userInteractionEnabled)//Here you can remove the children of _sprite. e.g.
{
//Here run some action or do what you want to do. For e.g.
CCFadeTo *fadeOut = [CCFadeTo actionWithDuration:0.5f opacity:0];
CCCallfuncN *clean = [CCCallFuncN actionWithTarget:self SEL:#selector(cleanSpriteChildren:)];
for(id item in _sprite.children)
if(item)
[item runAction:[CCSequence runActions:fadeOut, clean]];
}
else{//Do your own task while _sprite is untouchable}
}
}
-(void)cleanSpriteChildren:(id)sender
{
[item removeFromParentAndCleanUp:YES]
if(([_sprite children] count) == 0)
_sprite.userInteractionEnabled = YES; //Here make the _sprite touchable again
}

CCSprite rotation like Car Steering in Cocos2D

Is there any way to rotate a CCSprite to behave like a car Steering?If i release my touch with the CCSprite,it should automatically return to its original position in a smooth way exactly like a steering!Im using the below code,
-(void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch=[touches anyObject];
//acquire the previous touch location
CGPoint firstLocation = [touch previousLocationInView:[touch view]];
location = [touch locationInView:[touch view]];
CGPoint touchingPoint = [[CCDirector sharedDirector] convertToGL:location];
CGPoint firstTouchingPoint = [[CCDirector sharedDirector] convertToGL:firstLocation];
CGPoint firstVector = ccpSub(firstTouchingPoint, Steering.position);
CGFloat firstRotateAngle = -ccpToAngle(firstVector);
CGFloat previousTouch = CC_RADIANS_TO_DEGREES(firstRotateAngle);
CGPoint vector = ccpSub(touchingPoint, Steering.position);
CGFloat rotateAngle = -ccpToAngle(vector);
CGFloat currentTouch = CC_RADIANS_TO_DEGREES(rotateAngle);
dialrotation+=currentTouch-PreviousTouch;
sprite.rotation=dialrotation;
}
This rotates my CCSprite Smoothly,but i don't know how to return back to its original position like Car Steering.Is there any way to do so in Cocos2D?
In ccTouchesBegan method store the value of sprite.rotation somewhere, in ccTouchesEnded run CCRotateTo action on your sprite using this stored value.

How can i drag a CCSprite with velocity/speed in cocos2d by using CCTouchMoved?

I have an sprite, i want to move it by using my finger to move around the screen ~ drag.
I want my sprite move with velocity, it means do not as fast as my finger move.
it seem like this video: http://www.youtube.com/watch?v=Vair3CIxZEw (from 0:12 to 0:53)
Here is my ccTouch code. How can i fix to make it move look smoother?
Thank you!!! :)
simply return TRUE
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
return TRUE;
}
and
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
if (CGRectContainsPoint(_car.boundingBox, touchLocation)) {
CGPoint newPos = ccpAdd(_car.position, translation);
_car.position = newPos;
}
}
Try using CCMoveTo action for smooth moving
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
if (CGRectContainsPoint(_car.boundingBox, touchLocation)) {
CGPoint newPos = ccpAdd(_car.position, translation);
id moveAction = [CCMoveTo actionWithDuration:0.5f position:newPos];
[_car runAction:moveAction];
}

How to disable touch on a CGRect / Sprite after one touch

How can I disable touches for a CCRect / sprite after it has been touched?
I have in my init method to set the sprite:
[[CCSpriteFrameCache sharedSpriteFrameCache] addSpriteFramesWithFile:#"testAtlas_default.plist"];
sceneSpriteBatchNode = [CCSpriteBatchNode batchNodeWithFile:#"testAtlas_default.png"];
[self addChild:sceneSpriteBatchNode z:0];
dinosaur1_c = [CCSprite spriteWithSpriteFrameName:#"dinosaur1-c.png"];
[sceneSpriteBatchNode addChild:dinosaur1_c];
[dinosaur1_c setPosition:CGPointMake(245.0, winSize.height - 174.0)];
I then create a CGRect using the position and size of the sprite as its parameters in:
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
dinosaur1 = CGRectMake(dinosaur1_c.position.x - (dinosaur1_c.contentSize.width / 2), dinosaur1_c.position.y - (dinosaur1_c.contentSize.height / 2), dinosaur1_c.contentSize.width, dinosaur1_c.contentSize.height);
if( CGRectContainsPoint(dinosaur1, touchLocation) )
{
CCLOG(#"Tapped Dinosaur1_c!");
PLAYSOUNDEFFECT(PUZZLE_SKULL);
// Code to disable touches??
// Tried to resize CGRect in here to (0, 0, 1, 1) to get it away from the original sprite, but did not work. Still was able to tap on CGRect.
// Tried [[CCTouchDispatcher sharedDispatcher] setDispatchEvents:NO];, but disables ALL the sprites instead of just this one.
}
}
I'm able to successfully tap on the sprite to make it play the sound, however I just can't figure out how to disable the CGRect after it is touched. I have tried different methods as commented in the code above. Any ideas or tips are appreciated!
this will also help you
- (void)selectSpriteForTouch:(CGPoint)touchLocation {
for (CCSprite *sprite in _projectiles) {
if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
NSLog(#"sprite was touched");
[sprite.parent removeChild:sprite cleanup:YES];
[self removeChild:sprite.parent cleanup:YES];
}
} }
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
NSLog(#"touch was _");
return TRUE; }
Ok so I solved the problem. In case anyone was wondering, I set up a -(BOOL)isTapped in my header file and set it to NO in my init method.
When I check for collisions with the touchpoint and the CGRect, I also check to see if isTapped != YES (meaning it hasnt been tapped yet). In that if statement, I do all the actions as normally would but then set isTapped = YES. Now it will skip over when I tap again. Below is my code with the added bits in between *'s
.h file:
BOOL isTapped;
and in the .m file:
.m:
-(id)init
{
isTapped = NO;
// Rest of init method.
}
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView:[touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL:touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
dinosaur1 = CGRectMake(dinosaur1_c.position.x - (dinosaur1_c.contentSize.width / 2), dinosaur1_c.position.y - (dinosaur1_c.contentSize.height / 2), dinosaur1_c.contentSize.width, dinosaur1_c.contentSize.height);
if( CGRectContainsPoint(dinosaur1, touchLocation) **&& isTapped != YES**)
{
CCLOG(#"Tapped Dinosaur1_c!");
PLAYSOUNDEFFECT(PUZZLE_SKULL);
**isTapped = YES;**
}
else
{
CCLog(#"Already Tapped!");
}
}
Thanks for looking!

Resources