COCOS2D. dragging CCSprite on the screen with ccTouchMoved makes the movement jerky - cocos2d-iphone

I have an issue with my ccTouchMoved method. I have 3 ccSprites what I want to be able to move around on the screen using my finger. The thing is that the movement is jerky and it sometimes even looses "the grip" of the ccSprites when dragging the image, if I move the mouse pointer to fast.
what am I missing?
the three touchmethods that I use have the following tasks:
ccTouchBegan : scale up the ccSprite that is being touched
ccTouchMoved : move the ccSprite that is touched across the screen
ccTouchEnded: scale down the ccSprite to it's original size and stop the movement (is there a better way of scaling down the ccSprite to it's original size?)
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CCLOG(#"ccTouchBeganRuby");
CGPoint location = [self convertTouchToNodeSpace: touch];
if(CGRectContainsPoint([animatingRuby1 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleUp1");
id ScaleAction1 = [CCScaleBy actionWithDuration:1 scale:1.2f ];
[animatingRuby1 runAction:ScaleAction1];
}
if(CGRectContainsPoint([animatingRuby2 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleUp2");
id ScaleAction2 = [CCScaleBy actionWithDuration:1 scale:1.2f ];
[animatingRuby2 runAction:ScaleAction2];
}
if(CGRectContainsPoint([animatingRuby3 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleUp3");
id ScaleAction3 = [CCScaleBy actionWithDuration:1 scale:1.2f ];
[animatingRuby3 runAction:ScaleAction3];
}
return YES;
}
- (void)ccTouchMoved:(UITouch *)touches withEvent:(UIEvent *) event{
CCLOG(#"ccTouchMovedRuby");
if (CGRectContainsPoint([animatingRuby1 boundingBox], location)) {
CCLOG (#"moveRubies1");
if((CGRectContainsPoint([animatingRuby2 boundingBox], location))|| (CGRectContainsPoint([animatingRuby1 boundingBox], location))) {
return;
}
animatingRuby1.position = location;
}
if (CGRectContainsPoint([animatingRuby2 boundingBox], location)) {
CCLOG (#"moveRubies2");
if((CGRectContainsPoint([animatingRuby1 boundingBox], location))|| (CGRectContainsPoint([animatingRuby3 boundingBox], location))) {
return;
}
animatingRuby2.position = location;
}
if (CGRectContainsPoint([animatingRuby3 boundingBox], location)) {
CCLOG (#"moveRubies3");
if((CGRectContainsPoint([animatingRuby1 boundingBox], location))|| (CGRectContainsPoint([animatingRuby2 boundingBox], location))) {
return;
}
animatingRuby3.position = location;
}
[self collidableWithEachOther:location];
}
- (void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *) event{
CGSize screenSize = [[CCDirector sharedDirector] winSize];
CCLOG(#"ccTouchEndedRuby");
CGPoint location = [self convertTouchToNodeSpace: touch];
if(CGRectContainsPoint([animatingRuby1 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleDown1");
[animatingRuby1 setScaleX:screenSize.width/14850.0f];
[animatingRuby1 setScaleY:screenSize.height/9552.0f];
}
if(CGRectContainsPoint([animatingRuby2 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleDown2");
[animatingRuby2 setScaleX:screenSize.width/14850.0f];
[animatingRuby2 setScaleY:screenSize.height/9552.0f];
}
if(CGRectContainsPoint([animatingRuby3 boundingBox], location)){
CCLOG(#"ccTouchEndedRubyScaleDown3");
[animatingRuby3 setScaleX:screenSize.width/14850.0f];
[animatingRuby3 setScaleY:screenSize.height/9552.0f];
}
}

Take the vector from the sprite to the touch location and set it as the direction for the sprite, while limiting its speed. This way changes won't be as quick and drastic and the movement will look smoother. Don't require that the touch location always be over the sprite, just keep track of whether a drag operation is taking place, and which sprite is being dragged.

Related

Handling CCSprites with Multiple Touch

I have one or more than one sprites stored in a array. What i want is to drag these sprites with multiple fingers around the screen. i.e (Multiple Touch)
Can anyone tell me how to do that?
Any help will be appreciated..
Here is my code:
-(void)touchBegan:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
CGPoint location = [touch locationInNode:self];
selectedSprite.paused = YES;
[self selectSpriteFromTouch:location];
//this is where i get sprite selected from i.e selectedSprite from array
}
-(void)touchMoved:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
selectedSprite.position = [touch locationInNode:self];
}
-(void)touchEnded:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
selectedSprite.paused = NO;
selectedSprite.position = [touch locationInNode:self];
selectedSprite = nil;
}
-(void)touchCancelled:(CCTouch *)touch withEvent:(CCTouchEvent *)event
{
selectedSprite.paused = NO;
selectedSprite = nil;
}
If I understand correctly you want say two fingers on the screen, each to move 1 sprite? I guess you have the images in an array so get the location of each touch and if each touch is on a sprite, then you know which touch is on which sprite.
CGPoint fingerOne = [event.allTouches.allValues[0] locationInWorld];
CGPoint fingerTwo = [event.allTouches.allValues[1] locationInWorld];
If sprite at index 0 is touched by fingerOne, then you can move that by the same amount as fingerOne moves and similarly for fingerTwo and sprite at index 1.

Drag a sprite attached to a body

I have a CCSprite that is attached to body in a b2world .
When someone is touch it, i want to move it with the touch location.
With a regular sprite that works fine , but with a sprite that has a body- it does not .
it gets the touch but not move the sprite (the body follow the sprite or the opposite?)
How should i do that ? apply a force relative to the touch is a problem..
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView: [touch view]];
//detect a touch ont the button
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
CGPoint location=[[CCDirector sharedDirector] convertToGL: currentPosition];
CCSprite *tempSprite = (CCSprite *) b->GetUserData();
if( tempSprite.tag==2 )
{
if(CGRectContainsPoint([tempSprite boundingBox], location))
{
tempSprite.position=location;
NSLog(#"touched");
}
}
}
}
Try changing the position of the body using SetTransform function. I think it's looking something like:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentPosition = [touch locationInView: [touch view]];
//detect a touch ont the button
for (b2Body* b = world->GetBodyList(); b; b = b->GetNext())
{
CGPoint location=[[CCDirector sharedDirector] convertToGL: currentPosition];
CCSprite *tempSprite = (CCSprite *) b->GetUserData();
if( tempSprite.tag==2 )
{
if(CGRectContainsPoint([tempSprite boundingBox], location))
{
b->SetTransform( b2Vec2( location.x/PTM_RATIO, location.y/PTM_RATIO ), 0 ); //change position of the body
NSLog(#"touched");
}
}
}
}
Don't forget, if you want to change body position apply force or set linear velocity you must use kinematic or dynamic body type.

How to detect touch outside (empty space) - cocos2d

This is my code for detect that if touch on a specific sprite
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *myTouch = [touches anyObject];
CGPoint location = [myTouch locationInView:[myTouch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
for(CCSprite *sprite in shapeArray)
{
if(CGRectContainsPoint(sprite.boundingBox, location))
{
//There is a sprite that is touched
mSpriteOnHand = sprite;
currentPoint = mSpriteOnHand.position;
break;
}
//This part didn't work
else
{
NSLog(#"Touch outside);
}
}
}
Now I want to detect if touch is outside(not on any sprite or empty space) but I don't know how to do it.
If your touches are working, than I think adding a BOOL and then an if statement outside the for loop, like this will work.
BOOL itemFound = NO;
for(CCSprite *sprite in shapeArray)
{
if(CGRectContainsPoint(sprite.boundingBox, location))
{
//There is a sprite that is touched
mSpriteOnHand = sprite;
currentPoint = mSpriteOnHand.position;
NSLog(#"item TOUCHED");
itemFound = YES;
break;
}
}
if (itemFound == NO)
{
NSLog(#"Touch outside");
}

How do I detect multi-touch in cocos2d?

Edited Post
Ok I tried what rptwsthi said in another project just to test it......
-(id) init
{
if( (self=[super init])) {
self.isTouchEnabled = YES;
}
return self;
}
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *touchArray=[touches allObjects];
if ([touchArray count] == 2) {
NSLog(#"touch 2");
}
else if([touchArray count]==1) {
NSLog(#"touch 1");
}
}
But only the "touch 1" NSLog pops ups when I press the screen with two fingers. Do I need to put what LearnCocos2D said in there somewhere too.
Old Post
I have a test app I'm making and in it I have 3 buttons in the hud, 2 are for moving left and right and the other is for shooting. This is what I currently have.....
-(void) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
UITouch *touch = [touches anyObject];
CGPoint loc = [touch locationInView:[touch view]];
loc = [[CCDirector sharedDirector] convertToGL:loc];
//Move Left
CGRect left = CGRectMake(leftButton.position.x-50/2, leftButton.position.y-50/2, 50, 50);
if (CGRectContainsPoint(left, loc)) {
[self schedule:#selector(moveLeft)];
}
//Move Right
CGRect right = CGRectMake(rightButton.position.x-50/2, rightButton.position.y-50/2, 50, 50);
if (CGRectContainsPoint(right, loc)) {
[self schedule:#selector(moveRight)];
}
//Shoot
CGRect shoot = CGRectMake(shootButton.position.x-50/2, shootButton.position.y-50/2, 50, 50);
if (CGRectContainsPoint(shoot, loc)) {
bullet = [CCSprite spriteWithFile:#"bullet.png"];
bullet.position = ccp(plane.position.x, plane.position.y+20);
[self addChild:bullet];
}
}
-(void) ccTouchesEnded:(UITouch *)touch withEvent:(UIEvent *)event {
[self unschedule:#selector(moveLeft)];
[self unschedule:#selector(moveRight)];
}
But I can only press one button at a time. I want to be able to hold the right or left button and also shoot with the shoot button. Can anyone fix my code or show me a basic example of multi-touch?
Also I'm new to iOS development and any help will be much appreciated. Thanks.
Have you enabled multiple touches on the cocos2d view?
[[CCDirector sharedDirector].openGLView setMultipleTouchEnabled:YES];
You Just use allObject instead of anyObject, and check it like:
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
NSArray *touchArray=[touches allObjects];
if ([touchArray count] == 2)
//DO ONE THING
else if([touchArray count]==1)
//DO ANOTHER THING
}

Cocos2D: SneakyJoystick + Touch Events, problem

im having some trouble using SneakyJoystick and ccTouchEnded stuff.
What is my goal, use the joystick to walk around and the touches to interact with the surrounding area.
I have two layers,ControlLayer (z:2) and GamePlayLayer (z:1)
Im Using TiledMap for my ground and map.
The Joystick by it self works fine, it is attached to the ControlLayer. I can walk, colide and stuff.
When i add the Touch events to the GamePlayLayer, the touch works, i can click on something on the "ground" and interact with it. BUT my joystick dosent work then, if i run using the touch the joystick just sit there not responsive.
Here is some of my code, if u guys need more, just ask.
Touch methods in the GameplayLayer
-(void) registerWithTouchDispatcher
{
[[CCTouchDispatcher sharedDispatcher] addTargetedDelegate:self
priority:0 swallowsTouches:YES];
}
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
return YES;
}
-(void) ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint touchLocation = [touch locationInView: [touch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint tileCoord = [self tileCoordForPosition:touchLocation];
int tileGid = [meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collectable = [properties valueForKey:#"Collectable"];
if (collectable && [collectable compare:#"True"] == NSOrderedSame) {
[meta removeTileAt:tileCoord];
[foreground removeTileAt:tileCoord];
}
}
}
}
And how my scene is arranged:
#implementation SandBoxScene
-(id)init {
self = [super init];
if (self!=nil) {
//Control Layer
controlLayer = [GameControlLayer node];
[self addChild:controlLayer z:2 tag:2];
//GamePlayLayer
GameplayLayer *gameplayLayer = [GameplayLayer node];
[gameplayLayer connectControlsWithJoystick:[controlLayer leftJoyStick]
andJumpButton:[controlLayer jumpButton]
andAttackButton:[controlLayer attackButton]];
[self addChild:gameplayLayer z:1 tag:1];
}
return self;
}
#end
Thanks for the help!
The problem is that your code does not account for multiple touches. Using -(BOOL) ccTouchBegan:(UITouch *)touch.. or -(void) ccTouchEnded:(UITouch *)touch.. will only take one touch... instead you need to use ccTouchesEnded:.. your method should look something like this:
-(void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
//get your touch from the set of UITouches
UITouch *myTouch = [touches anyObject];
//the rest below is the same from your method
CGPoint touchLocation = [myTouch locationInView: [myTouch view]];
touchLocation = [[CCDirector sharedDirector] convertToGL: touchLocation];
touchLocation = [self convertToNodeSpace:touchLocation];
CGPoint tileCoord = [self tileCoordForPosition:touchLocation];
int tileGid = [meta tileGIDAt:tileCoord];
if (tileGid) {
NSDictionary *properties = [tileMap propertiesForGID:tileGid];
if (properties) {
NSString *collectable = [properties valueForKey:#"Collectable"];
if (collectable && [collectable compare:#"True"] == NSOrderedSame) {
[meta removeTileAt:tileCoord];
[foreground removeTileAt:tileCoord];
}
}
}
}
You also need to add [glView setMultipleTouchEnabled:YES]; to your appDelegate. I am not entirely sure that the method above works. This question deals with implementing multitouch with cocos2d. Hope this helps
Sorry thats was not the solution, i do want to handle just one touch, the problem was in the priority, i was giving priority 0 to my gameplayLayer, and swalloing every time, so i didnt give chance to the controlayer that had priority 1 to get the touch and act upon my joystick.
The solution that i found was changing the priority from 0 to 2 then, the (controlLayer) joystick would act and pass the touch to the priority 2, that was my gameplayLayer
but thanks anyway :)