Detect long touch - cocos2d-iphone

I need to detect long touch in the game the game I am trying to make.How can I do so?
Other problem I am facing is to limit simultaneous touches. i.e the sprite won't jump if user touches more than two times immedeatly one after other.
Also is their a way by which I can add touch duration factor to the height of jump the sprite makes?
Thanks

how do you grab your touches? I always use the following methods:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
- (void)ccTouchMoved:(UITouch *)touchx withEvent:(UIEvent *)event
- (void)ccTouchEnded:(UITouch *)touchx withEvent:(UIEvent *)event
And in these methods you may take total control over all touches. For example remember the time the touch started:
self.startTime = [NSDate date];
for an instance variable startTime, or to check if a touch belongs to a certain object:
CGRectContainsPoint(self.rect, [self convertTouchToNodeSpaceAR:touch]);
This way you can easily implement your touch logic the way you like it...
A good way to do this is - I find - to define all the required variables within the object of the game, like...
#interface Enemy : CCSprite <CCTargetedTouchDelegate> {
EnemyState state;
NSInteger enemyID;
NSDate *startTime;
NSDate *endTime;
UITouch *lastTouch;
ADDED TO SHOW CONCRETE EXAMPLE CODE FOR ccTouchBegan:
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
D_DBG (#"ENEMY ID %i",self.enemyID);
//implement your logic if a touch for this enemy is allowed or not
if (![self.delegate touchIsAllowed: enemyID touch: touch]) return NO;
//if the enemy is untouched, then may be touched
if ((state == kEnemyStateUngrabbed) && (![self containsTouchLocation:touch] )) return NO;
state = kEnemyStateGrabbed;
self.startTime = [NSDate date];
[self.delegate informAboutEnemyStarted: self.enemyID startTime: self.startTime atPoint: self.position];
return YES;
}

Related

ccPhysicsCollisionPostSolve: method isn't called

I just started using the SpriteBuilder with Cocos2d-v3
For the exercise of the SpriteBuilder, I practice in the tutorial of the following WebSite
for the time being.
https://www.makegameswith.us/tutorials/getting-started-with-spritebuilder/collision-detection/
As a flow of the games of the tutorials,at first the catapult and the catapultArm are located on stage,the object(Penguin) which was attached to the arm flies forward when I pull the arm to the rear with a finger and separate a finger from a screen.
When the object(Penguin) hits other objects(is called Seal),output log,"Something collided with a seal!"
In a "Implementing a delegate method" of tutorial, I was going to try to display a letter,"Something collided with a seal!" in Consoll using CCLOG method.
In the Gameplay.m, I implemented ccPhysicsCollisionPostSolve:seal:wildcard method that is called to dispaly a letter "Something collide with a seal!",but this method isn't called when Penguin object hits other objects(seal).
I certainly made Penguin.ccbi and Seal.ccbi in SpriteBuilder,and thier ccbi file is Physics body.
Why ccPhysicsCollisionPostSolve:seal:wildcard method isn't called ?
It's my code that I really implemented as follows
This is Gameplay.m file
#import "Gameplay.h"
#implementation Gameplay{
CCPhysicsNode *_physicsNode;
//To joint betweent catapult and catapultArm
CCNode *_catapultArm;
CCNode *_catapult;
CCPhysicsJoint *_catapultJoint;
//Invisible Physics force
CCNode *_pullbackNode;
CCPhysicsJoint *_pullbackJoint;
//to move catapultArm
CCNode *_mouseJointNode;
CCPhysicsJoint *_mouseJoint;
//to fly penguin
CCNode *_currentPenguin;
CCPhysicsJoint *_penguinCatapultJoint;
//Object
CCNode *_levelNode;
//To Prevent a 'retry' button from moving with a fly penguin
CCNode *_contentNode;
}
//is called when CCB file has completed loading
-(void)didLoadFromCCB{
_physicsNode.collisionDelegate = self;
//tell this scene to accept touches
self.userInteractionEnabled = TRUE;
//loads the Levels/Leve1.ccb we have set up in SpriteBuilder
CCScene *level = [CCBReader loadAsScene:#"Levels/Level1"];
[_levelNode addChild:level];
//visualize physics bodies & joints
_physicsNode.debugDraw = TRUE;
//catapultArm and catapult shall not collide
[_catapultArm.physicsBody setCollisionGroup:_catapult];
[_catapult.physicsBody setCollisionGroup:_catapult];
//create a joint to connect the catapult arm with the catapult
_catapultJoint = [CCPhysicsJoint connectedPivoJointWithBodyA:_catapultArm.physicsBody
bodyB:_catapult.physicsBody
anchorA:_catapultArm.anchorPointInPoints];
//nothing shall collide with our invisible nodes
_pullbackNode.physicsBody.collisionMask = #[];
//nothing shall collide with our invisible nodes
_mouseJointNode.physicsBody.collisionMask = #[];
_pullbackJoint = [CCPhysicsJointconnectedSpringJointWithBodyA:_pullbackNode.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:ccp(0,0)
anchorB:ccp(34,138)
restLength:60.f
stiffness:500.f
damping:40.f
];
}
//called on every touch in this scene (called every touch)
-(void)touchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
CCLOG(#"touch Began");
CGPoint touchLcation = [touch locationInNode:_contentNode];
//start catapult dragging when a touch inside of the catapult arm occurs
if(CGRectContainsPoint([_catapultArm boundingBox], touchLcation))
{
//move the mouseJointNode to the touch position
_mouseJointNode.position = touchLcation;
_mouseJoint = [CCPhysicsJoint connectedSpringJointWithBodyA:_mouseJointNode.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:ccp(0,0)
anchorB:ccp(34,138)
restLength:0.f
stiffness:3000.f
damping:150.f
];
//create a penguin from the ccbFile
_currentPenguin = [CCBReader load:#"Penguin"];
CGPoint penguinPosition = [_catapultArm convertToWorldSpace:ccp(34, 138)];
_currentPenguin.position = [_physicsNode convertToNodeSpace:penguinPosition];
//add it to the physics world
[_physicsNode addChild:_currentPenguin];
//we don't want the penguin to rotate in the scoop
_currentPenguin.physicsBody.allowsRotation = FALSE;
//create a joint to keep the penguin fixed to the scoop until the catapult is released
_penguinCatapultJoint =
[CCPhysicsJoint connectedPivoJointWithBodyA:_currentPenguin.physicsBody
bodyB:_catapultArm.physicsBody
anchorA:_currentPenguin.anchorPointInPoints
];
}
}
-(void)touchMoved:(UITouch *)touch withEvent:(UIEvent *)event{
CCLOG(#"MOVING!!!!!!!!!!");
//whenever touches move,update the position of the mousejointNode to touch position
CGPoint touchLocation = [touch locationInNode:_contentNode];
_mouseJointNode.position = touchLocation;
}
-(void)touchEnded:(UITouch *)touch withEvent:(UIEvent *)event{
//when touches end,meaning the user releases their finger,release the catapult
[self releaseCatapult];
}
-(void)touchCancelled:(UITouch *)touch withEvent:(UIEvent *)event{
[self releaseCatapult];
}
//destroy our joint and let the catapult snap when a touch ends
-(void)releaseCatapult{
CCLOG(#"Release");
if(_mouseJoint != nil)
{
//releases the joint and lets the catapult snap back
[_mouseJoint invalidate];
_mouseJoint = nil;
//release the joint and lets the penguin fly
[_penguinCatapultJoint invalidate];
_penguinCatapultJoint = nil;
//after snapping rotation is fine
_currentPenguin.physicsBody.allowsRotation = TRUE;
//follow the flying penguin
CCActionFollow *follow =
}
}
-(void)retry
{
[[CCDirector sharedDirector]replaceScene:[CCBReader loadAsScene:#"Gameplay"]];
}
-(void)ccPhysicsCollisionPostSolve:(CCPhysicsCollisionPair *)pair seal:(CCNode *)nodeA
wildcard:(CCNode *)nodeB
{
CCLOG(#"Something collided with a seal!");
}
#end
Most likely you did not set a collision type for the Sealclass.
Double check that you have:
Set the custom class for the Seal in SpriteBuilder to Seal
Have set the collisionType within the didLoadFromCCB method inside the Seal class to "seal"
That should fix you issue.
Also, in Spritebuilder, make sure the seals already loaded in the level (Level1.ccb) have Collision type set to seal.

how to properly handle touch events for multiple layers?

I currently have a "Console" CClayer, which is handling touch detection for sprites that have been added to it. However, I also have some sprites that I want to do touch detection on that are not part of the Console layer... They are currently children of a class that inherits from CCNode.
My understanding is, the more cocos objects have the "isTouchEnabled" property set to true, the more performance will be affected, so I am curious how I should approach this?
Should I:
A) Have the console's touchesBegan method perform detection of the sprites belonging to the CCNode?
B) Just implement isTouchEnabled on the CCNode object
C) Some other approach?
well, for starters, you should only concern yourself about performance if you are concerned i.e. you are seeing or measuring (on DEVICES not a simulator) some inappropriate response times.
I would avoid detecting touches that concern another node - it could get messy, software wise. I tend to return YES (from a ccTouchBegan) strictly when the touch is at a location of an object of concern to the detecting node. When you return NO, the dispatcher will pass on the touch to other handlers ('under' the console), until one such CCNode takes the bite. Kind of as follows:
- (void) onEnter{
[[[CCDirector sharedDirector] touchDispatcher] addTargetedDelegate:self priority:0 swallowsTouches:YES];
[super onEnter];
}
- (void) onExit{
[[[CCDirector sharedDirector] touchDispatcher] removeDelegate:self];
[super onExit];
}
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event{
if (!_visible || !_enabled) {
return NO;
}
CGPoint loc = [touch locationInView:touch.view];
loc = [[CCDirector sharedDirector] convertToGL:loc];
if ([self containsPoint:loc]) {
// do your thing here !
return YES;
}
return NO;
}
-(BOOL) containsPoint:(CGPoint) location {
// determine here whether this node should be handling
// this touch.
}

How to make certain sprite disappear after touched - Cocos2d

I want to know how to disappear certain sprite(star) once the MainPlayer touches the Star.
Thanks. By the way, I'm new to Cocos2d and I'm doing it just for fun and educational purpose.
Thanks.
If you want to be able to detect touches in cocos2d, you need to set the isTouchEnabled property to YES in your init method. You can now take advantage of touch events.
Next, create the new method:
- (void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
//This method gets triggered every time a tap is detected
NSSet *allTouches = [event allTouches]; //Get all the current touches in the event
UITouch *aTouch = [allTouches anyObject]; //Get one of the touches, multitouch is disabled, so there is only always going to be one.
CGPoint pos = [aTouch locationInView:touch.view]; //Get the location of the touch
CGPoint ccPos = [[CCDirector sharedDirector] convertToGL:pos]; //Convert that location to something cocos2d can use
if (CGRectContainsPoint(yourSprite.boundingBox, ccPos)) //Method to check if a rectangle contains a point
{
yourSprite.visible = NO; //Make your sprite invisible
}
}
Other methods that you may want to take advantage of eventually are the following:
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event;
- (void)ccTouchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event;
Hope this helped.

Animations are not working properly(Cocos2d)

My problem is that animation are not working properly during movements of sprite.
Below is the code which i'm using
- (BOOL)ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event {
[selSprite resumeSchedulerAndActions];
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
[self selectSpriteForTouch:touchLocation];
return TRUE;
}
- (void)selectSpriteForTouch:(CGPoint)touchLocation
{
CCSprite * newSprite = nil;
for (CCSprite *sprite in movableSprite) {
if (CGRectContainsPoint(sprite.boundingBox, touchLocation)) {
newSprite = sprite;
break;
}
}
if (newSprite != selSprite) {
[selSprite stopAllActions];
selSprite = newSprite;
_MoveableSpritetouch = TRUE;
}
if(_MoveableSpritetouch==TRUE)
{
movement=0;
CGRect selRect=CGRectMake((SpriteX)-20.0,(SpriteY)-20.0,40.0,40.0);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
}
}
- (void)ccTouchMoved:(UITouch *)touch withEvent:(UIEvent *)event {
if(gameState == TRUE){
CGPoint point = [touch locationInView:[touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
if (moveDifference.x>0)
{
selSprite.flipX = YES;
}
else {
selSprite.flipX = NO;
}
[selSprite setPosition:point];
}
}
-(void)ccTouchEnded:(UITouch *)touch withEvent:(UIEvent *)event
{
movement=0;
if(selSprite==MarshallCar)
{
[selSprite setDisplayFrame:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:#"mcar_idle.png"]];
}
[selSprite pauseSchedulerAndActions];
}
The animation frames for movement are not playing every time during movements sometimes it plays or sometimes not. It plays properly when you touch and move your sprite for the first time but if touch another sprite and then again move the previous sprite the animations for movement won't play.
Is anyone having any idea why this is happening?
Please tell me the proper code for removing this bug.
Thanks!!!
I believe your problem is the if/else if construct:
if (_MoveableSpritetouch==TRUE)
{
CGRect selRect = CGRectMake(SpriteX - 20, SpriteY - 20, 40, 40);
if(CGRectContainsPoint(selRect, touchLocation))
{
[selSprite stopAllActions];
}
else if(...)
{
...
[selSprite runAction:walkMarshallCar];
}
}
If you don't see it right away: if the touch location is inside the selRect, you call stopAllActions on the selected (new) sprite and do nothing else. Only if the touch location is not within that rectangle you'll run the animation action.
I think the "in rectangle" check is superfluous since you've already called stopAllActions anyway a few lines above.
Allow me a few general remarks about your code:
The method "selectSpriteForTouch" tells me that you're selecting a new sprite. The function does that. But it does not advertise playing an animation. You might want to refactor this out to a seperate "playAnimationOnSelectedSprite" method.
You wrote 20.0 and 40.0 several times. This means you're actually passing a double (8 bytes floating point data type) to a CGPoint which takes floats (4 bytes floating point). Strictly speaking use either 20.0f with the suffixed "f" to denote it as a floating point data type, or use integers since you don't use the floating point part.
Why you put (SpriteX) in brackets is not clear to me, if you want to enhance readability you'll achieve more by adding spaces after commas and operands.
In Objective-C, use YES and NO macros instead of TRUE and FALSE.
The bool variable _MoveableSpritetouch seems superfluous, unless needed someplace else. In any case you should move the following if(_MoveableSpritetouch==TRUE) block to where you set the _MoveableSpritetouch variable to TRUE because it just makes your code harder to read by setting a variable, leaving the code block you were in ( if(selSprite != newSprite) ) just to jump into another block of code ( if(_MoveableSpritetouch==TRUE) ) that you already know you're going to run anyway.
if((selSprite==MarshallCar)&& (!(CGRectContainsPoint(selRect, touchLocation))))
{
movement=1;
[self reorderChild:selSprite z:5];
NSMutableArray *MarshallCarWalkAnimFrames = [NSMutableArray array];
for(int i = MarshallCarTouchStartFrameIndex; i <= MarshallCarTouchEndFrameIndex; ++i) {
[MarshallCarWalkAnimFrames addObject:[[CCSpriteFrameCache sharedSpriteFrameCache] spriteFrameByName:[NSString stringWithFormat:#"mcar_move_%d.png", i]]];
}
MarshallCarWalkAnim = [CCAnimation animationWithFrames:MarshallCarWalkAnimFrames delay:MarshallCarTouchFrameDelay];
walkMarshallCar = [CCRepeatForever actionWithAction:[CCAnimate actionWithAnimation:MarshallCarWalkAnim restoreOriginalFrame:NO]];
[selSprite runAction:walkMarshallCar];
}
I have added [selSprite stopAllActions];
and it started working correctly because in the touch ended method i was pausing the actions
but not resuming them so when i touch the sprite for the second time it was not playing animation because the action was paused.

Is it possible to to re-initialize an object in cocos2d

I have a class named insect child of sprite.I have created a
instance of that class in GameLayer and then initialize with
it using,
insect *bgg = [insect spriteWithFile:#"bird2a.gif"];
then i set a timer(10 second) to change the image using
*bgg = [insect spriteWithFile:#"2.gif"];
but my program crashes.Now my question is it possible to
re-initialize an object or it is immutable??
I have another question, when i used
- (BOOL) ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *) event
{
UITouch *touch = [touches anyObject];
CGPoint point2 = [touch locationInView:[touch view]];
CGPoint cpoint=[[Director sharedDirector] convertCoordinate:point2];
NSLog(#"In touch began");
}
in my insect class it cannot detect touch on 'bgg' object declared
in GameLayer.But when I used this function in GameLayer it can
detect touch.
whats wrong with my approach??Plz someone explain.
Advanced thanx for your reply.
Your program crashes because you have a typo. Remove the '*' from '*bgg'. That means you are dereferencing the pointer, then trying to apply the creation of a new object to the dereferenced pointer. You just want
bgg = [insect spriteWithFile:#"2.gif"];
However, it's overkill to create an entirely new sprite just to change the image. A Sprite is a subclass of TextureNode, so just use TextureNode's texture property to give a Sprite a different image.
Texture2D *newImage = [[TextureMgr sharedTextureMgr] addImage:#"2.gif"];
bgg.texture = newImage;