Moving sprites in cocos2d - ios

I have 6 sprites that I want to move around:
-(id) init
{
if( (self=[super init]) )
{
CGSize winSize = [[CCDirector sharedDirector] winSize];
CCSprite * backGround = [CCSprite spriteWithFile:#"background_ipad.png"];
backGround.position = ccp(winSize.width/2, winSize.height/2);
[self addChild:backGround z:0];
cloth1 = [CCSprite spriteWithFile:#"clothe_1.png"];
cloth1.position = ccp(-200, -15);
[self addChild:cloth1 z:1];
cloth2 = [CCSprite spriteWithFile:#"clothe_2.png"];
cloth2.position = ccp(130, 225);
[self addChild:cloth2 z:2];
cloth3 = [CCSprite spriteWithFile:#"clothe_3.png"];
cloth3.position = ccp(365, 225);
[self addChild:cloth3 z:3];
cloth4 = [CCSprite spriteWithFile:#"clothe_4.png"];
cloth4.position = ccp(-110, -15);
[self addChild:cloth4 z:4];
cloth5 = [CCSprite spriteWithFile:#"clothe_5.png"];
cloth5.position = ccp(130, -20);
[self addChild:cloth5 z:5];
cloth6 = [CCSprite spriteWithFile:#"clothe_6.png"];
cloth6.position = ccp(365, -15);
[self addChild:cloth6 z:6];
}
return self;
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
}
and this method to move:
-(void) ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL: location];
//Sprite follows the finger movement.
[cloth1 setPosition:location];
}
and the thing is that, I want to add more sprites there to move the sprite. I triend adding more sprites in the //follow the finger movement but then all sprites follow the finger movement. I want to move one single sprite. For example: when touching cloth1, move cloth 1; when touching cloth2, move cloth 2; but not both at the same time.
Can someone tell me how to do this?

#interface YourClass : NSObject
{
NSMutableArray *mSpriteArray;
CCSprite *mSpriteOnHand;
}
//In implementation:
-(id) init
{
.. //your old code
..
[mSpriteArray addObject: cloth1];
[mSpriteArray addObject: cloth2];
[mSpriteArray addObject: cloth3];
[mSpriteArray addObject: cloth4];
[mSpriteArray addObject: cloth5];
[mSpriteArray addObject: cloth6];
}
-(void)onEnter
{
[super onEnter];
self.touchEnabled = YES; // self.isTouchEnabled = YES;
}
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
mSpriteOnHand = nil;
for(CCSprite *cloth in mSpriteArray)
{
if(CGRectContainsPoint([cloth boundingBox], location))
{
mSpriteOnHand = cloth;
break;
}
}
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView: [touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
if(mSpriteOnHand)
mSpriteOnHand.position = location;
}
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
mSpriteOnHand = nil;
}
-(void)onExit
{
[mSpriteArray release];
mSpriteArray = nil;
[super onExit];
}

return statement always terminates function and returns control to the calling function. You wrote:
return self;
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[[CCTouchDispatcher sharedDispatcher]... line will never get executed.
Is your class a subclass of CCLayer? If you set layer's isTouchEnabled property to YES, it will add this layer as standard (non-targeted) touch delegate.
If you must use targeted touch in your layer, you should return YES in -(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event if you are claiming this touch, and NO if you aren't. Updates of a claimed touch are sent only to the delegate which claimed it.
To determine which sprite is touched: make an instance variable to store "selected" sprite, in touchBegan method check which sprite's bounding box contains touch location and store this sprite in instance variable (also, looks like you want to claim a touch only if it's touching a sprite).
[cloth1 setPosition:location];
-- your sprite will "jump" to touch position. Usually it doesn't look nice. I would get touch's locationInView: and previousLocationInView:, convert them to GL, get the difference and change sprite's position by that difference.

Related

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
}

Move 2 CCSprite with UITouch

I try make a simple game in cocos2d at now i have something like that
#import "GameplayLayer.h"
#implementation GameplayLayer
-(id)init {
self = [super init];
if (self != nil) {
CGSize screenSize = [CCDirector sharedDirector].winSize;
// właczenie obsługi dotyku
self.isTouchEnabled = YES;
if (UI_USER_INTERFACE_IDIOM()==UIUserInterfaceIdiomPad) {
}
else{
paddle1 = [CCSprite spriteWithFile:#"bijakiPhone.png"];
paddle2 = [CCSprite spriteWithFile:#"bijakiPhone.png"];
puck = [CCSprite spriteWithFile:#"krazekiPhone.png"];
}
//Polozenie i inicjalizacja paletki nr 1
[paddle1 setPosition:
CGPointMake(screenSize.width/2,
screenSize.height*0.17f)];
[self addChild:paddle1];
//Polozenie i inicjalizacja paletki nr 2
[paddle2 setPosition:
CGPointMake(screenSize.width/2,
screenSize.height*0.83f)];
[self addChild:paddle2];
//Polozenie i inicjalizacja krązka
[puck setPosition:CGPointMake(screenSize.width/2, screenSize.height/2)];
[self addChild:puck];
}
return self;
}
//onEnter
- (void)onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
//onExit
- (void)onExit
{
[[CCTouchDispatcher sharedDispatcher] removeDelegate:self];
[super onExit];
}
-(BOOL)containsTouch:(UITouch *)touch {
CGRect r=[paddle1 textureRect];
CGPoint p=[paddle1 convertTouchToNodeSpace:touch];
return CGRectContainsPoint(r, p );
}
-(BOOL)containsTouch2:(UITouch *)touch {
CGRect r=[paddle2 textureRect];
CGPoint p=[paddle2 convertTouchToNodeSpace:touch];
return CGRectContainsPoint(r, p );
}
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
if ([self containsTouch:touch]){
CCLOG(#"krarzek 1 tapniety");
isTouched1 = YES;
}
if ([self containsTouch2:touch]){
CCLOG(#"krarzek 2 tapniety");
isTouched2 = YES;
}
return YES;
}
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
if (isTouched1){
CGPoint newTouchLocation = [touch locationInView:touch.view];
newTouchLocation = [[CCDirector sharedDirector] convertToGL:newTouchLocation];
[paddle1 setPosition:newTouchLocation];
}
if (isTouched2){
CGPoint newTouchLocation = [touch locationInView:touch.view];
newTouchLocation = [[CCDirector sharedDirector] convertToGL:newTouchLocation];
[paddle2 setPosition:newTouchLocation];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
if (isTouched1){
isTouched1=NO;
CCLOG(#"krarzek 1 zwolniony");
}
if (isTouched2){
isTouched2=NO;
CCLOG(#"krarzek 2 zwolniony");
}
}
#end
Works CCSprite move, but when i touch 2 CCSprite at same time, They overlap itself!
How i can move them separately?
Sorry for my English and Thanks for help!
The reason of your problem that you don't store somewhere what touch is connected to your paddle. So in case of both touches are inside your paddle sprites, both isTouched1 and isTouched2 variables have value YES. So in your ccTouchMoved:withEvent: method both sprites will be placed to the same position in this case. Store your touches in some variable or I suggest to use dictionary for this with touch as a key and sprite that you need to move as value. In this case your ccTouchMoved:withEvent: method will be look like this
-(void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint newTouchLocation = [touch locationInView:touch.view];
newTouchLocation = [[CCDirector sharedDirector] convertToGL:newTouchLocation];
CCNode paddle = [_yourDict objectForKey: touch];
[paddle setPosition:newTouchLocation];
}
And names of your methods that determine if sprite contains given touch are not good enough. I could not say what they do without looking through the code.

CGRect and touch

In this cocos2d app the nslog is not firing when I press the ccsprite. Could someone help me?
-(BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
NSMutableArray *targetsToDelete = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets) {
CGRect targetRect = CGRectMake(target.position.x - (target.contentSize.width/2),
target.position.y - (target.contentSize.height/2),
27,
40);
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
if (CGRectContainsPoint(targetRect, touchLocation)) {
NSLog(#"Moo cheese!");
}
}
return YES;
}
First of all be sure that you register the sprite for touches into the onEnter method for example:
- (void)onEnter
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self priority:defaultTouchPriority_ swallowsTouches:YES];
[super onEnter];
}
This will make your sprite touchable and so fire the event to the sprite when a user will press it.
Then refactor your code to make it more readable and test something like that:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
NSArray *targetsToDelete = [self touchedSpritesAtLocation:touchLocation];
// Put your code here
// ...
return YES;
}
- (NSArray *)touchedSpritesAtLocation:(CGPoint)location
{
NSMutableArray *touchedSprites = [[NSMutableArray alloc] init];
for (CCSprite *target in _targets)
if (CGRectContainsPoint(target.boundingBox, location))
[touchedSprites addObject:target];
return [touchedSprites autorelease];
}
It should return the targets that have been touched.
In layer init method add this
self.isTouchEnabled = true;
Use this code for touch detection
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
CGRect rect = [self getSpriteRect:yourSprite];
if (CGRectContainsPoint(rect, location))
{
NSLog(#"Sprite touched\n");
}
}
To get sprite rect:
-(CGRect)getSpriteRect:(CCNode *)inSprite
{
CGRect sprRect = CGRectMake(
inSprite.position.x - inSprite.contentSize.width*inSprite.anchorPoint.x,
inSprite.position.y - inSprite.contentSize.height*inSprite.anchorPoint.y,
inSprite.contentSize.width,
inSprite.contentSize.height
);
return sprRect;
}

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!

Cocos 2d draw line doesn't work

I want to draw line on finger touch in cocos 2d.
-(void) ccTouchesMoved:(NSSet *)inappropriateTouches withEvent:(UIEvent *)event
{
UITouch *touch = [inappropriateTouches anyObject];
CGPoint currentTouchArea = [touch locationInView:[touch view] ];
CGPoint lastTouchArea = [touch previousLocationInView:[touch view]];
currentTouchArea = [[CCDirector sharedDirector] convertToGL:currentTouchArea];
lastTouchArea = [[CCDirector sharedDirector] convertToGL:lastTouchArea];
// throw to console my inappropriate touches
NSLog(#"current x=%2f,y=%2f",currentTouchArea.x, currentTouchArea.y);
NSLog(#"last x=%2f,y=%2f",lastTouchArea.x, lastTouchArea.y);
glColor4f(0.8, 1.0, 0.76, 1.0);
glLineWidth(6.0f);
ccDrawLine(currentTouchArea, lastTouchArea);
}
I use this code but nothing is drawn on the screen. What is wrong in my code?
All OpenGL drawing you want to do in the draw method. Like this:
-(void)draw
{
if(lastTouchArea != 0)
{
glColor4f(0.8, 1.0, 0.76, 1.0);
glLineWidth(6.0f);
ccDrawLine(currentTouchArea, lastTouchArea);
lastTouchArea = 0;
}
}
Try this : to save a line in a NSMutableArray
-(void) ccTouchesMoved:(NSSet *)inappropriateTouches withEvent:(UIEvent *)event
{
UITouch *touchMyMinge = [inappropriateTouches anyObject];
CGPoint currentTouchArea = [touchMyMinge locationInView:[touchMyminge view] ];
CGPoint lastTouchArea = [touchMyMinge previousLocationInView:[touchMyMinge view]];
// flip belly up. no one likes being entered from behind.
currentTouchArea = [[CCDirector sharedDirector] convertToGL:currentTouchArea];
lastTouchArea = [[CCDirector sharedDirector] convertToGL:lastTouchArea];
// throw to console my inappropriate touches
NSLog(#"current x=%2f,y=%2f",currentTouchArea.x, currentTouchArea.y);
NSLog(#"last x=%2f,y=%2f",lastTouchArea.x, lastTouchArea.y);
// add my touches to the naughty touch array
naughtyTouchArray addObject:NSStringFromCGPoint(currentTouchArea)];
naughtyTouchArray addObject:NSStringFromCGPoint(lastTouchArea)];
}
#implementation DrawMyTouch
-(id) init
{
if( (self=[super init]))
{ }
return self;
}
-(void)draw
{
glEnable(GL_LINE_SMOOTH);
for(int i = 0; i < [naughtyTouchArray count]; i+=2)
{
start = CGPointFromString([naughtyTouchArray objectAtIndex:i]);
end = CGPointFromString([naughtyTouchArray objectAtIndex:i+1]);
ccDrawLine(start, end);
}
}
-(void) ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
DrawMyTouch *line = [DrawMyTouch node];
[self addChild: line];
}
Hope this help

Resources