bounding a sprite in cocos2d - iphone

am making a canon to fire objects. back of the canon the plunger is attached. plunger acts for set speed and angle. canon rotates 0-90 degree and plunger moves front and back for adjust speed. when am rotates the canon by touches moved its working fine. when plunger is pull back by touches moved and it rotates means the plunger is bounds outside of the canon.
how to control this:-
my code for plunger and canon rotation on touches moved. ( para3 is the canon , para6 is my plunger):-
CGPoint touchLocation = [self convertTouchToNodeSpace:touch];
CGPoint oldTouchLocation = [touch previousLocationInView:touch.view];
oldTouchLocation = [[CCDirector sharedDirector] convertToGL:oldTouchLocation];
oldTouchLocation = [self convertToNodeSpace:oldTouchLocation];
if (CGRectContainsPoint(CGRectMake(para6.position.x-para6.contentSize.width/2, para6.position.y-para6.contentSize.height/2, para6.contentSize.width, para6.contentSize.height), touchLocation) && (touchLocation.y-oldTouchLocation.y == 0))
{
CGPoint diff = ccpSub(touchLocation, oldTouchLocation);
CGPoint currentpos = [para6 position];
NSLog(#"%d",currentpos);
CGPoint destination = ccpAdd(currentpos, diff);
if (destination.x < 90 && destination.x >70)
{
[para6 setPosition:destination];
speed = (70 + (90-destination.x))*3.5 ;
}
}
if(CGRectIntersectsRect((CGRectMake(para6.position.x-para6.contentSize.width/8, (para6.position.y+30)-para6.contentSize.height/10, para6.contentSize.width, para6.contentSize.height/10)),(CGRectMake(para3.position.x-para3.contentSize.width/2, para3.position.y-para3.contentSize.height/2, para3.contentSize.width, para3.contentSize.height))))
{
[para3 runAction:[CCSequence actions:
[CCRotateTo actionWithDuration:rotateDuration angle:rotateDiff],
nil]];
CGFloat plungrot = (rotateDiff);
CCRotateTo *rot = [CCRotateTo actionWithDuration:rotateDuration angle:plungrot];
[para6 runAction:rot];
}
}

how about u do this that you use the [CCMoveTo actionWithDuration: position:] method??
Through this method you can easily control the speed by the "actionWithDuration" argument which takes integer values of time in seconds, while direction can be adjusted through "position" argument which takes ccp(x,y) as the values to the point you want your plunger to move to.
You can use it like this....
CCSprite *plunger = [[CCSprite alloc] initWithFile:#"plunger.png"];
plunger.position = ccp(240,240);
[self addChild:plunger z:10];
[plunger release];
id = [CCMoveTo actionWithDuration:3 position:ccp(300,240)];
The values given are of my choice. You may use them to your accordance.
Hope it helps you....

I hope i understood the question correctly:
if the problem is, that the cannon and plunger both rotate around their own center points, but you want them to rotate together, then the solution should be to make the plunger a child sprite of the cannon (this also makes the plugers position relative to the cannon) i.e.
[para3 addChild:para6]
then you only need to rotate the cannon and the plunger will be rotatet with it.
if i got your question totally wrong, maybe you could post a screenshot :-)

Related

Creating a trail with SKEmitterNode and particles in SpriteKit

I am trying to make it so a particle I made follows the player whenever the player is moved. The effect I am trying to replicate is like when you are on a website and they have some sort of set of objects following your mouse. I tried to do this by making the particle move by the amount the player does, but it is not reproducing the intended effect. Any suggestions? My code:
Declaring particle
NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:#"trail" ofType:#"sks"];
self.trailParticle = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
self.trailParticle.position = CGPointMake(0,0);
[self.player addChild:self.trailParticle];
Move method
-(void)dragPlayer: (UIPanGestureRecognizer *)gesture {
if (gesture.state == UIGestureRecognizerStateChanged) {
//Get the (x,y) translation coordinate
CGPoint translation = [gesture translationInView:self.view];
//Move by -y because moving positive is right and down, we want right and up
//so that we can match the user's drag location (SKView rectangle y is opp UIView)
CGPoint newLocation = CGPointMake(self.player.position.x + translation.x, self.player.position.y - translation.y);
CGPoint newLocPart = CGPointMake(self.trailParticle.position.x + translation.x, self.trailParticle.position.y - translation.y);
//Check if location is in bounds of screen
self.player.position = [self checkBounds:newLocation];
self.trailParticle.position = [self checkBounds:newLocPart];
self.trailParticle.particleAction = [SKAction moveByX:translation.x y:-translation.y duration:0];
//Reset the translation point to the origin so that translation does not accumulate
[gesture setTranslation:CGPointZero inView:self.view];
}
}
Try this:
1) If your emitter is in the Scene, use this emitter's property targetNode and set is as Scene. That means that particles will not be a child of emitter but your scene which should leave a trail..
Not sure if this is correct (I do it in C#):
self.trailParticle.targetNode = self; // self as Scene
And some extra:
2) I think you could rather attach your emitter to self.player as child so it would move together and automatically with your player and then there is no need of this:
self.trailParticle.position = [self checkBounds:newLocPart];
self.trailParticle.particleAction = [SKAction moveByX:translation.x y:-translation.y duration:0];
In your update loop Check if your player is moving along the X axis.
Then create a sprite node each time this is happening. In this example name your player "player1"
The key here is your particle must have a max set in the particles column near birthrate.
The blow code works for me.
-(void)update:(CFTimeInterval)currentTime {
// Find your player
SKNode* Mynode = (SKSpriteNode *)[self childNodeWithName:#"player1"];
// Check if our player is moving. Lower the number if you are not getting a trail.
if (Mynode.physicsBody.velocity.dx>10|
Mynode.physicsBody.velocity.dy>10|
Mynode.physicsBody.velocity.dx<-10|
Mynode.physicsBody.velocity.dy<-10){
// Unpack your particle
NSString *myParticlePath = [[NSBundle mainBundle] pathForResource:#"trail" ofType:#"sks"];
//Create your emitter
SKEmitterNode *myTrail = [NSKeyedUnarchiver unarchiveObjectWithFile:myParticlePath];
// This ensures your trail doesn't stay forever . Adjust this number for different effects
myTrail.numParticlesToEmit=10;
// The length of your trail - higher number, longer trail.
myTrail.particleLifetime = 2.0;
//Set the trail position to the player position
myTrail.position=Mynode.position;
//Add the trail to the scene
[self addChild:myTrail];
}
}

Collision of Sprites in CCSpriteBatchNode and CCParallaxNode

I have two sprites one is added as the child of CCSpriteBatchNode and the other as the child of CCParallaxNode. Is there any method to detect their collision? I have used the following code.
-(void)CheckCollition:(CCSprite *)Opp_Obs Opponent:(CCSprite *) H_man
{
// NSLog(#"inside check collision");
CGRect b_rect=[Opp_Obs boundingBox];
CGPoint p_position=[H_man position];
if (CGRectContainsPoint(b_rect,p_position))
{
NSLog(#"collision with opponent");
// Zoom Animation with Points
CCScaleBy *zzomscal=[CCScaleTo actionWithDuration:.2 scale:.12];
CCRotateTo * rotLeft = [CCRotateBy actionWithDuration:0.2 angle:360];
CCCallFunc *ccfun=[CCCallFunc actionWithTarget:self selector:#selector(zoomComplete)];
CCSequence * zzomseq = [CCSequence actions:zzomscal,rotLeft,ccfun, nil];
[H_man runAction:zzomseq];
}
else
{
NSLog(#"no collision");
}
}
But here the control never enters into the loop. Is there any other solution? Anyone please help me.
Set a breakpoint and compare the values of rect and position. One of them may be zero, or way off.
In the latter case you may need to convert the bbox origin and position to world coordinates first in order to compare them. This is the case when the sprites' parents are moving too (parent position != 0,0).

How to give effect like throwing a ball in a Bucket?

I am new to Cocos2d and hence not aware of most of the classes and also the functions. In my application, I want to implement a module like touching and dragging the ball in a direction of a barrel will pass the ball and will put the ball in the barrel. I am able to pass the ball in the direction but then it just would go out of the screen following the coordinates(x.y).
- (void)ccTouchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
// Choose one of the touches to work with
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
location = [[CCDirector sharedDirector] convertToGL:location];
// Determine offset of location to projectile
int offX = location.x - ball.position.x;
int offY = location.y - ball.position.y;
// Bail out if we are shooting down or backwards
if (offX <= 0)
return;
// Determine where we wish to shoot the projectile to
int realX = winSize.width + (ball.contentSize.height/2);
float ratio = (float) offY / (float) offX;
int realY = (realX * ratio) + ball.position.y;
CGPoint realDest = ccp(realX, realY);
if(realX>=320)
realX = 320;
if(realY>=480)
realY = 480;
CGPoint realDest = ccp(realX, realY);
int good = goodBarrel.position.x;
int bad = badBarrel.position.x;
int destY = realDest.x;
if(destY<=good)
destY = good;
if(destY>=bad)
destY = bad;
realDest.x = destY+10;
// Determine the length of how far we're shooting
int offRealX = realX - ball.position.x;
int offRealY = realY - ball.position.y;
float length = sqrtf((offRealX*offRealX)+(offRealY*offRealY));
float velocity = 480/1; // 480pixels/1sec
float realMoveDuration = length/velocity;
// Move projectile to actual endpoint
[ball runAction:[CCSequence actions:
[CCMoveTo actionWithDuration:realMoveDuration position:realDest],
[CCCallFuncN actionWithTarget:self selector:#selector(spriteMoveFinished:)],
nil]];
}
I need to do something like it will get the idea of barrels position and finding the top of the barrel and will put the ball in the center of the barrel and also while moving the ball,the size of the ball gets reduced to give effect of throwing the ball to a distant place. GoodBarrel and BadBarrel are the two barrels I have put in the opposite of ball to put the ball in the same. Is there any function or method I can work on?
Immediately after the [ball runAction: .... ]; line, you can run another action on the same object, simultaneously. Add a
[ball runAction:[CCScaleTo actionWithDuration:realMoveDuration scale:0.4f];
both actions will complete at the same time, providing the 'illusion' of distance you are seeking to create.
You can use collision detection, or in other words check to see if the CGRect of ball and barrel intersect
- (void)collisionDetection:(ccTime)dt{
CGRect ballRect = CGRectMake(
ball.position.x,
ball.position.y,
10,
10);
CGRect barrelRect = CGRectMake(
barrel.position.x,
barrel.position.y,
50,
10);
if (CGRectIntersectsRect(ballRect, barrelRect))
{
// Run Animation of Ball Dropping in the Barrel
[self removeChild:ball cleanup: YES];
}
}
hope this is what you need.

Cocos2d - how to make individual particles follow the layer, not the emitter?

I have a CCSprite and a CCParticleSystemQuad that are both children of the CCLayer. In my update method, I set the emitter's position to that of the sprite, so it tracks the sprite around. The smoke puff fall out the bottom of the sprite like you'd expect and even though you move the sprite around, the smoke appears to be part of the background layer.
The problem come if I match up their rotations. Now, for example if my sprite is rocking back and forth, the puffs of smoke swing in an arc and appear attached to the sprite.
How can I make the puffs of smoke continue along the parent layer in a straight line and not rotate with the sprite? They don't translate with the sprite when I move it, so why do they rotate with it?
EDIT: adding code...
- (id)init
{
if (!(self = [super init])) return nil;
self.isTouchEnabled = YES;
CGSize screenSize = [[CCDirector sharedDirector] winSize];
sprite = [CCSprite spriteWithFile:#"Icon.png"]; // declared in the header
[sprite setPosition:ccp(screenSize.width/2, screenSize.height/2)];
[self addChild:sprite];
id repeatAction = [CCRepeatForever actionWithAction:
[CCSequence actions:
[CCRotateTo actionWithDuration:0.3f angle:-45.0f],
[CCRotateTo actionWithDuration:0.6f angle:45.0f],
[CCRotateTo actionWithDuration:0.3f angle:0.0f],
nil]];
[sprite runAction:repeatAction];
emitter = [[CCParticleSystemQuad particleWithFile:#"jetpack_smoke.plist"] retain]; // declared in the header - the particle was made in Particle Designer
[emitter setPosition:sprite.position];
[emitter setPositionType:kCCPositionTypeFree]; // ...Free and ...Relative seem to behave the same.
[emitter stopSystem];
[self addChild:emitter];
[self scheduleUpdate];
return self;
}
- (void)update:(ccTime)dt
{
[emitter setPosition:ccp(sprite.position.x, sprite.position.y-sprite.contentSize.height/2)];
[emitter setRotation:[sprite rotation]]; // if you comment this out, it works as expected.
}
// there are touches methods to just move the sprite to where the touch is, and to start the emitter when touches began and to stop it when touches end.
I found the answer on a different site - www.raywenderlich.com
I don't know why this is true, but it seems that CCParticleSystems don't like to be rotated while you move them around. They don't mind changing their angle property. Actually, there may be cases where you want that behavior.
Anyway I made a method that adjusts the emitter's angle property and it works fine. It takes your touch location and scales the y component to be the angle.
- (void)updateAngle:(CGPoint)location
{
float width = [[CCDirector sharedDirector] winSize].width;
float angle = location.x / width * 360.0f;
CCLOG(#"angle = %1.1f", angle);
[smoke_emitter setAngle:angle]; // I added both smoke and fire!
[fire_emitter setAngle:angle];
// [emitter setRotation:angle]; // this doesn't work
}
CCSprite's anchorPoint is {0.5f, 0.5f), while the emitter descends directly from CCNode, which has an anchorPoint of {0.0f, 0.0f}. Try setting the emitter's anchorPoint to match the CCSprite's.

Applying Zoom Effect In cocos2D gaming environment?

I'm working on a game with cocos2D game engine and make load all the sprites while it load the level, now as because some of sprites (obstacles) are taller than 320 pixel, thus it seems difficult to check them out. So for the convenience sake I want to apply ZOOM IN and ZOOM out effect, which minimizes entire level's all sprites at once, and in zoom out case these will resided to there old position.
Can I achieve this?
If yes, then how?
Please tell about pinch zoom also.
Zooming, is fairly simple, simply set the scale property of your main game layer... but there are a few catches.
When you scale the layer, it will shift the position of the layer. It won't automatically zoom towards the center of what you're currently looking at. If you have any type of scrolling in your game, you'll need to account for this.
To do this, set the anchorPoint of your layer to ccp(0.0f, 0.0f), and then calculate how much your layer has shifted, and reposition it accordingly.
- (void) scale:(CGFloat) newScale scaleCenter:(CGPoint) scaleCenter {
// scaleCenter is the point to zoom to..
// If you are doing a pinch zoom, this should be the center of your pinch.
// Get the original center point.
CGPoint oldCenterPoint = ccp(scaleCenter.x * yourLayer.scale, scaleCenter.y * yourLayer.scale);
// Set the scale.
yourLayer.scale = newScale;
// Get the new center point.
CGPoint newCenterPoint = ccp(scaleCenter.x * yourLayer.scale, scaleCenter.y * yourLayer.scale);
// Then calculate the delta.
CGPoint centerPointDelta = ccpSub(oldCenterPoint, newCenterPoint);
// Now adjust your layer by the delta.
yourLayer.position = ccpAdd(yourLayer.position, centerPointDelta);
}
Pinch zoom is easier... just detect the touchesMoved, and then call your scaling routine.
- (void) ccTouchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
// Examine allTouches instead of just touches. Touches tracks only the touch that is currently moving...
// But stationary touches still trigger a multi-touch gesture.
NSArray* allTouches = [[event allTouches] allObjects];
if ([allTouches count] == 2) {
// Get two of the touches to handle the zoom
UITouch* touchOne = [allTouches objectAtIndex:0];
UITouch* touchTwo = [allTouches objectAtIndex:1];
// Get the touches and previous touches.
CGPoint touchLocationOne = [touchOne locationInView: [touchOne view]];
CGPoint touchLocationTwo = [touchTwo locationInView: [touchTwo view]];
CGPoint previousLocationOne = [touchOne previousLocationInView: [touchOne view]];
CGPoint previousLocationTwo = [touchTwo previousLocationInView: [touchTwo view]];
// Get the distance for the current and previous touches.
CGFloat currentDistance = sqrt(
pow(touchLocationOne.x - touchLocationTwo.x, 2.0f) +
pow(touchLocationOne.y - touchLocationTwo.y, 2.0f));
CGFloat previousDistance = sqrt(
pow(previousLocationOne.x - previousLocationTwo.x, 2.0f) +
pow(previousLocationOne.y - previousLocationTwo.y, 2.0f));
// Get the delta of the distances.
CGFloat distanceDelta = currentDistance - previousDistance;
// Next, position the camera to the middle of the pinch.
// Get the middle position of the pinch.
CGPoint pinchCenter = ccpMidpoint(touchLocationOne, touchLocationTwo);
// Then, convert the screen position to node space... use your game layer to do this.
pinchCenter = [yourLayer convertToNodeSpace:pinchCenter];
// Finally, call the scale method to scale by the distanceDelta, pass in the pinch center as well.
// Also, multiply the delta by PINCH_ZOOM_MULTIPLIER to slow down the scale speed.
// A PINCH_ZOOM_MULTIPLIER of 0.005f works for me, but experiment to find one that you like.
[self scale:yourlayer.scale - (distanceDelta * PINCH_ZOOM_MULTIPLIER)
scaleCenter:pinchCenter];
}
}
If all the sprites have the same parent you can just scale their parent and they will be scaled with it, keeping their coordinates relative to the parent.
this code scale my Layer by 2 to specific location
[layer setScale:2];
layer.position=ccp(240/2+40,160*1.5);
double dx=(touchLocation.x*2-240);
double dy=(touchLocation.y*2-160);
layer.position=ccp(inGamePlay.position.x-dx,inGamePlay.position.y-dy);
My code and it works better than other ones:
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray* allTouches = [[event allTouches] allObjects];
CCLayer *gameField = (CCLayer *)[self getChildByTag:TAG_GAMEFIELD];
if (allTouches.count == 2) {
UIView *v = [[CCDirector sharedDirector] view];
UITouch *tOne = [allTouches objectAtIndex:0];
UITouch *tTwo = [allTouches objectAtIndex:1];
CGPoint firstTouch = [tOne locationInView:v];
CGPoint secondTouch = [tTwo locationInView:v];
CGPoint oldFirstTouch = [tOne previousLocationInView:v];
CGPoint oldSecondTouch = [tTwo previousLocationInView:v];
float oldPinchDistance = ccpDistance(oldFirstTouch, oldSecondTouch);
float newPinchDistance = ccpDistance(firstTouch, secondTouch);
float distanceDelta = newPinchDistance - oldPinchDistance;
NSLog(#"%f", distanceDelta);
CGPoint pinchCenter = ccpMidpoint(firstTouch, secondTouch);
pinchCenter = [gameField convertToNodeSpace:pinchCenter];
gameField.scale = gameField.scale - distanceDelta / 100;
if (gameField.scale < 0) {
gameField.scale = 0;
}
}
}