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.
Related
At the moment, I have a puzzle game, with some objects that are movable using the ccTouchesBegan && ccTouchesMoved method
Called in ccTouchesBegan:
if (CGRectContainsPoint(newBoundingBox, touchLocation))
{
//Set the selectedSprite to the sprite with newBoundingBox
}
Then in ccTouchesMoved i do:
CGPoint translation = ccpSub(touchLocation, oldTouchLocation);
newPos = ccpAdd(selSprite.position, translation);
selectedSprite.position = newPos;
Now this all works fine and dandy, HOWEVER, some of the objects that I am moving are about 140x40px (on the 320x480 iPhone), and sometimes the ccTouchesBegan method won't acknowledge the boundingBox containing the touchLocation.
My question is, Can I increase the size of the boundingBox of the object so that if the user "misses" with there touch a little bit, it will help them out? It's a little frustrating requiring you to be so precise on a touch screen to move an obstacle.
EDIT: for iPhonic:
How I get touchLocation, this is in ccTouchesBegan:
NSSet *touchSet = [event allTouches];
int numberOfTouches = [touchSet count];
CGPoint touchLocation = [self convertTouchToNodeSpace:[touches anyObject]];
allTouches is passed automatically when the a touch begins.
-(BOOL) ccTouchBegan:(UITouch *)touch withEvent:(UIEvent *)event
{
CGPoint location = [self convertTouchToNodeSpace: touch];
for (CCSprite *tempSprite in sprites )
{
//Increase the size of bounding box..
CGRect rect=tempSprite.boundingBox;
rect.size.height=rect.size.height + 20;
rect.size.width=rect.size.width + 20;
if (CGRectContainsPoint(rect, location))
{
//Your stuffs
}
}
}
Currently i am following this Link to perform Single finger rotation on an image and its working fine.But i am wondering after performing rotation, how to perform translation operation if i want to move the image from one postion of screen to any other position.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
// We can look at any touch object since we know we
// have only 1. If there were more than 1 then
// touchesBegan:withEvent: would have failed the recognizer.
UITouch *touch = [touches anyObject];
// A tap can have slight movement, but we're not interested
// in a tap. We want more movement. So if a tap is detected
// fail the recognizer.
if ([self state] == UIGestureRecognizerStatePossible) {
[self setState:UIGestureRecognizerStateBegan];
} else {
[self setState:UIGestureRecognizerStateChanged];
}
// To rotate with one finger, we simulate a second finger.
// The second figure is on the opposite side of the virtual
// circle that represents the rotation gesture.
UIView *view = [self view];
CGPoint center = CGPointMake(CGRectGetMidX([view bounds]), CGRectGetMidY([view bounds]));
CGPoint currentTouchPoint = [touch locationInView:view];
CGPoint previousTouchPoint = [touch previousLocationInView:view];
// use the movement of the touch to decide
// how much to rotate the carousel
CGPoint line2Start = currentTouchPoint;
CGPoint line1Start = previousTouchPoint;
CGPoint line2End = CGPointMake(center.x + (center.x - line2Start.x), center.y + (center.y - line2Start.y));
CGPoint line1End = CGPointMake(center.x + (center.x - line1Start.x), center.y + (center.y - line1Start.y));
//////
// Calculate the angle in radians.
// (From: http://iphonedevelopment.blogspot.com/2009/12/better-two-finger-rotate-gesture.html )
CGFloat a = line1End.x - line1Start.x;
CGFloat b = line1End.y - line1Start.y;
CGFloat c = line2End.x - line2Start.x;
CGFloat d = line2End.y - line2Start.y;
CGFloat line1Slope = (line1End.y - line1Start.y) / (line1End.x - line1Start.x);
CGFloat line2Slope = (line2End.y - line2Start.y) / (line2End.x - line2Start.x);
CGFloat degs = acosf(((a*c) + (b*d)) / ((sqrt(a*a + b*b)) * (sqrt(c*c + d*d))));
CGFloat angleInRadians = (line2Slope > line1Slope) ? degs : -degs;
//////
[self setRotation:angleInRadians];
}
I am not sure that my answer will help you, but I fear that you cannot do "one-touch" rotation and "one-touch" translation at the same time. Indeed, your code always interprets a single touch as for rotation, by "simulating" the presence of a second finger touching the display.
What you could do is trying and differentiate two single-finger gestures so to associate one with rotation and the other with translation. For example, the gesture for translation could be a "long touch" (i.e., you touch and keep touching for some time, then you move the object).
Hope this helps, anyway...
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;
}
}
}
I have a need to tap and drag a UIView on the screen with deceleration. I have written the code very nicely for moving the view with touches, but need the object to keep moving with a certain degree of inertia (once a certain acceleration threshold is met), decelerating until it stops, or meets the boundary of the screen. This is not for a game, but using some standard UIView controls. The biggest part I am grappling with is the acceleration.
Any good algorithms that you have written to accomplish the same?
Edit:
I am using an animation block on the touchesEnded: method, but there is a noticeable delay between the time that a person lets go of their finger and the animation kicks in:
[UIView transitionWithView:self
duration:UI_USER_INTERFACE_IDIOM() ==
UIUserInterfaceIdiomPhone ? 0.33f : 0.33f * 2.0f
options:UIViewAnimationCurveEaseOut
animations:^(void){
if (dir == 1) // Flicked left
{
self.center = CGPointMake(self.frame.size.width * 0.5f,
self.center.y);
}
else { // Flicked right
self.center = CGPointMake(
self.superview.bounds.size.width -
(self.frame.size.width * 0.5f), self.center.y);
}
}
completion:^(BOOL finished){
// Do nothing
}];
The problem is in the timing function used for the animation. The animation should be as fast as the user's dragging in the first, and quickly decelerates. The following code shows a very simple example of implementing this behavior.
First, in my touchesBegan:withEvent: method, I recorded the first touch location to my point buffer. I'm buffering two touch locations to get the movement vector of the view, and there could be different ways of getting the vector.
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
ivar_lastPoint[0] = [[touches anyObject] locationInView:self];
ivar_lastPoint[1] = ivar_lastPoint[0];
ivar_touchOffset.x = ivar_lastPoint[0].x - self.sprite.position.x;
ivar_touchOffset.y = ivar_lastPoint[0].y - self.sprite.position.y;
self.lastTime = [NSDate date];
}
Then, in touchesMoved:withEvent: method, I updated the location of the view. Actually, I used a CALayer rather than a view, as I want to use a custom timing function for its animation. So, I update the location of the layer according to the user, and for a given interval, I update the location buffers.
#define kSampleInterval 0.02f
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
[CATransaction begin];
[CATransaction setDisableActions:YES];
/* First of all, move the object */
CGPoint currentPoint = [[touches anyObject] locationInView:self];
CGPoint center = self.sprite.position;
center.x = currentPoint.x - ivar_touchOffset.x;
center.y = currentPoint.y - ivar_touchOffset.y;
self.sprite.position = center;
/* Sample locations */
NSDate *currentTime = [NSDate date];
NSTimeInterval interval = [currentTime timeIntervalSinceDate:self.lastTime];
if (interval > kSampleInterval) {
ivar_lastPoint[0] = ivar_lastPoint[1];
ivar_lastPoint[1] = currentPoint;
self.lastTime = currentTime;
self.lastInterval = interval;
}
[CATransaction commit];
}
self.sprite is a reference to the CALayer object on my view. I don't need animation for dragging so I disabled it by using CATransaction class object.
Finally, I calculate the vector and apply the animation in touchesEnded:withEvent: method. Here, I created a custom CAMediaTimingFunction, so it's really "fast-in, ease-out".
#define kDecelerationDuration 1.0f
#define kDamping 5.0f
- (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint targetPoint;
NSDate *currentTime = [NSDate date];
NSTimeInterval interval = self.lastInterval + [currentTime timeIntervalSinceDate:self.lastTime];
targetPoint.x = self.sprite.position.x + (ivar_lastPoint[1].x - ivar_lastPoint[0].x)/interval*kDecelerationDuration/kDamping;
targetPoint.y = self.sprite.position.y + (ivar_lastPoint[1].y - ivar_lastPoint[0].y)/interval*kDecelerationDuration/kDamping;
if (targetPoint.x < 0) {
targetPoint.x = 0;
} else if (targetPoint.x > self.bounds.size.width) {
targetPoint.x = self.bounds.size.width;
}
if (targetPoint.y < 0) {
targetPoint.y = 0;
} else if (targetPoint.y > self.bounds.size.height) {
targetPoint.y = self.bounds.size.height;
}
CAMediaTimingFunction *timingFunction = [CAMediaTimingFunction functionWithControlPoints:
0.1f : 0.9f :0.2f :1.0f];
[CATransaction begin];
[CATransaction setValue:[NSNumber numberWithFloat:kDecelerationDuration] forKey:kCATransactionAnimationDuration];
[CATransaction setAnimationTimingFunction:timingFunction];
self.sprite.position = targetPoint;
[CATransaction commit];
}
This is a very simple example. You may want a better vector-getting mechanism. Also, this only move a visual component (CALayer). You would probably need an UIView object to handle events from the object. In this case, you might want to animate through CALayer, and move the actual UIView object separately. There could be multiple ways of handling the CALayer animation and UIView relocation together.
Use Core Animation. It's pretty straightforward if you look at the docs for UIView -- create an animation that sets the final position of the view, and specify UIViewAnimationOptionCurveEaseOut as the timing curve. The whole thing will take you just a handful of lines to implement.
You can use a constant acceleration for the object to stop moving at a constant rate of slow down. Or you can make the acceleration increase or decrease depending on if you want the deceleration to be quicker/slower towards the point of reaching zero velocity.
struct Velocity {
float x;
float y; }
Vector acceleration = your acceleration equation. // can be constant
Vector newVelocity = oldVelocity + acceleration * timeDelta;
Vector newPosition = newVelocity * timeDelta;
assuming you have a normalized vector for your direction of travel, and you are just countering that direction you can use float instead of Vector.
You have to bound the newPosition on the border. And you have to stop iterating per time delta when velocity becomes negative.
I want to rotate the image around its center point.The problem i am facing is i need to get the angle to calculate in touch moved event (i dont want to use multi touch).I am current using the below code
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event{
NSArray *allTouches = [touches allObjects];
gestureStartPoint = gestureMovedPoint;//i am getting the gestureStartPoint on touch began event
gestureMovedPoint = [[allTouches objectAtIndex:0] locationInView:[self superview]];
NSLog(#"gestureMovedPoint = %#",NSStringFromCGPoint(gestureMovedPoint));
}
CGFloat previousAngle = [self angleBetweenPoints:gestureStartPoint second11:gestureMovedPoint]; // atan2(gestureMovedPoint.y - gestureStartPoint.y, gestureMovedPoint.x - gestureStartPoint.x) * 180 / M_PI;
CGFloat currentAngle =atan2(self.transform.b, self.transform.a);//atan2(gestureMovedPoint.y - gestureStartPoint.y,gestureMovedPoint.x - gestureStartPoint.x) * 180 / M_PI;
CGFloat angleToRotate = currentAngle - previousAngle;
float xpoint = (((atan2((gestureMovedPoint.x - gestureStartPoint.x) , (gestureMovedPoint.y - gestureStartPoint.y)))*180)/M_PI);
CGAffineTransform transform = CGAffineTransformMakeRotation(angleToRotate-100);
self.transform = transform;
Kindly help me find the solution as i am stuck here and need to complete this application very soon as there is a dead line.
Thanks in advance
Glad I remember triginometry
-(void)degreesToRotateObjectWithPosition:(CGPoint)objPos andTouchPoint:(CGPoint)touchPoint{
float dX = touchPoint.x-objPos.x; // distance along X
float dY = touchPoint.y-objPos.y; // distance along Y
float radians = atan2(dY, dX); // tan = opp / adj
//Now we have to convert radians to degrees:
float degrees = radians*M_PI/360;
return degrees;
}
Once you have your nice method, just do this in the touch event method. (I forgot what it's called...)
CGAffineTransform current = view.transform;
[view setTransform:CGAffineTransformRotate(current, [self degreesTorotateObjectWithPosition:view.frame.origin andTouchPoint:[touch locationInView:parentView]]
//Note: parentView = The view that your object to rotate is sitting in.
This is pretty much all the code that you'll need.The math is right, but I'm not sure about the setTransform stuff. I'm at school writing this in a browser. You should be able to figure it out from here.
Good luck,
Aurum Aquila
Have to think at this. But I will prefer rotating the view with two touches. It will be much simpler.
I did struggle a bit with how to get a touch driven rotation, even more so because I want 100% understanding of the code I am using. So I ended up, after many failed attempts, with this:
- (CGFloat) pointToAngleFromCenter: (CGPoint) point {
// transform point to a self.center'ed origin based coordinate system
point.x = point.x - self.center.x ;
// ditto for y, but compensate for y going downwards to y going upwards
point.y = self.center.y - point.y ;
return ::atan2(point.y, point.x) ;
}
If anyone has a better name for this method, I'm all ears.
What it does is that it takes a point in parent view coordinates, remaps it relative to the center of the view (which is in parent view coordinate), and computes the angle between this remapped point and the axis [0X]. To do so, it normalizes y to the normal mathematical coordinates (y goes up when its value increases, not down), hence self.center.y - point.y and not the opposite.
Finally, in touchesMoved:
- (void) touchesMoved: (NSSet *) touches withEvent: (UIEvent *) event {
UITouch * touch = [touches anyObject] ;
CGFloat currA = [self pointToAngleFromCenter:[touch locationInView:self.superview]] ;
CGFloat prevA = [self pointToAngleFromCenter:[touch previousLocationInView:self.superview]] ;
// the current value of the rotation angle
CGFloat tranA = ::atan2(self.transform.b, self.transform.a) ;
// the angle difference between last touch and the current touch
CGFloat diffA = currA - prevA ;
// the new angle resulting from applying the difference
CGFloat angle = tranA - diffA ;
CGAffineTransform t = ::CGAffineTransformMakeRotation(angle) ;
self.transform = t ;
[self setNeedsDisplay] ;
}