How can I rotate sprite around his center in cocos2d in ccTouchesMoved. I found some code, but this not what I need. I need to rotate sprite like photo in Photo gallery app.
- (void)ccTouchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
NSArray *allTouch = [touches allObjects];
if([allTouch count] > 1){
UITouch *touch = [allTouch objectAtIndex:0];
CGPoint point = [touch locationInView: [touch view]];
point = [[CCDirector sharedDirector] convertToGL:point];
float angleRadians = atanf((float)point.y / (float)point.x);
float angleDegrees = CC_RADIANS_TO_DEGREES(angleRadians);
float cocosAngle = -1 * angleDegrees;
imageFromPicker.rotation = imageFromPicker.rotation + cocosAngle;
}
}
probably is not the best way but im using this:
When I touch the sprite with two fingers, i use the arctan of the two points to rotate the sprite:
first i look for the direction with this function:
static inline int ccw(CGPoint p0, CGPoint p1, CGPoint p2)
{
int dx1, dx2, dy1, dy2;
dx1 = p1.x - p0.x; dy1 = p1.y - p0.y;
dx2 = p2.x - p0.x; dy2 = p2.y - p0.y;
int v1 = dx1 * dy2;
int v2 = dy1 * dx2;
if (v1 > v2)
return 1;
if (v1 < v2)
return -1;
if ((dx1*dx2 < 0) || (dy1*dy2 < 0))
return -1;
if ((dx1*dx1 + dy1*dy1) < (dx2*dx2 + dy2*dy2))
return 1;
return 0;
}
and after on the ccTouchesMoved method I do this:
if ([allTouches count] == 2) {
rotating=TRUE;
NSArray *twoTouches = [allTouches allObjects];
UITouch *first = [twoTouches objectAtIndex:0];
UITouch *second = [twoTouches objectAtIndex:1];
CGPoint a = [first previousLocationInView:[touch view]];
CGPoint b = [second previousLocationInView:[touch view]];
CGPoint c = [first locationInView:[touch view]];
int direction =ccw(b, a, c);
float pX= fabs(c.x-b.x);
float pY= fabs(c.y-b.y);
float rotation = atan2(pY, pX);
// rotTotal= (rotTotal+rotacion)/10;
if(direction<0)
YourSprite.rotation=(YourSprite.rotation-rotation);
else
YourSprite.rotation=(YourSprite.rotation+rotation);
}
it works for me, hope it works for you too
Related
I am working with the SpriteKit's Spaceship Demo and I want it to rotate to the location of where I click on the screen and then move toward it.
The current code I have only makes it move up the screen starting at position 50,50:
sprite.position = CGPointMake(50,50);
SKAction *fly = [SKAction moveByX:0.0F y:50.0F duration:1];
[sprite runAction:[SKAction repeatActionForever:fly];
How would I make it work?
So long answer, this will help you.(I benefited from raywenderlich's book)
//Math Utilities
static inline CGPoint CGPointSubtract(const CGPoint a,
const CGPoint b)
{
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint CGPointMultiplyScalar(const CGPoint a,
const CGFloat b)
{
return CGPointMake(a.x * b, a.y * b);
}
static inline CGFloat CGPointLength(const CGPoint a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline CGPoint CGPointNormalize(const CGPoint a)
{
CGFloat length = CGPointLength(a);
return CGPointMake(a.x / length, a.y / length);
}
static inline CGPoint CGPointAdd(const CGPoint a,
const CGPoint b)
{
return CGPointMake(a.x + b.x, a.y + b.y);
}
static const float SHIP_SPEED = 60.0;
#implementation yourScene
{
SKSpriteNode *ship;
NSTimeInterval _lastUpdateTime;
NSTimeInterval _dt;
CGPoint _velocity;
CGPoint _lastTouchLocation;
}
-(void)didMoveToView:(SKView *)view
{
ship = [SKSpriteNode spriteNodeWithImageNamed:#"Spaceship"];
ship.position = CGPointMake(50, 50);
[self addChild:ship];
}
- (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
[self moveShipToward:touchLocation];//Move Toward
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
{
if (_lastUpdateTime) {
_dt = currentTime - _lastUpdateTime;
} else {
_dt = 0;
}
_lastUpdateTime = currentTime;
CGPoint offset = CGPointSubtract(_lastTouchLocation, ship.position);
float distance = CGPointLength(offset);
if (distance < SHIP_SPEED * _dt) {
ship.position = _lastTouchLocation;
_velocity = CGPointZero;
} else {
[self moveSprite:ship velocity:_velocity];
[self rotateSprite:ship toFace:_velocity];
}
}
}
- (void)moveShipToward:(CGPoint)location
{
_lastTouchLocation = location;
CGPoint offset = CGPointSubtract(location, ship.position);
CGPoint direction = CGPointNormalize(offset);
_velocity = CGPointMultiplyScalar(direction, SHIP_SPEED);
}
- (void)moveSprite:(SKSpriteNode *)sprite
velocity:(CGPoint)velocity
{
CGPoint amountToMove = CGPointMultiplyScalar(velocity, _dt);
sprite.position = CGPointAdd(sprite.position, amountToMove);
}
- (void)rotateSprite:(SKSpriteNode *)sprite
toFace:(CGPoint)direction
{
sprite.zRotation = atan2f(direction.y, direction.x);
}
hi guys I have this Sprite Kit cannon game that it works perfectly but with one problem. if I point the cannon to a very top or very bottom as you can see in the image, the bullet since it is spawning from the center of the cannon's sprite, it shows clearly that the bullet is being fired not from the cannon's tunnel but from the center of the image, how can I fix this bullet position so that it would always spawn from the mouth of the tunnel.
here is my code so far.
- (void) touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event {
CGPoint location = [_Player position];
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
bullet = [SKSpriteNode spriteNodeWithImageNamed:#"cannonbullet"];
bullet.xScale = 0.06;
bullet.yScale = 0.06;
bullet.position =
CGPointMake(location.x+_Player.zRotation,location.y+_Player.zRotation);
bullet.zPosition = 0;
CGPoint offset = rwSub(touchLocation, bullet.position);
if (offset.x <= 0) return;
[self addChild:bullet];
CGPoint direction = rwNormalize(offset);
CGPoint shootAmount = rwMult(direction, 400);
CGPoint realDest = rwAdd(shootAmount, bullet.position);
float velocity = 480.0/1.0;
float realMoveDuration = self.size.width / velocity;
SKAction * actionMove = [SKAction moveTo:realDest duration:realMoveDuration];
SKAction * actionMoveDone = [SKAction removeFromParent];
[bullet runAction:[SKAction sequence:#[actionMove, actionMoveDone]]];
[self animStarter];
}
as you can see in the image the bullet is spawning from a very off location if the cannon is not shooting straight.
Make sure that your cannon's barrel sprite image is lined up with the center X axis of the image (see the picture). You can drag out or copy the image if you want to duplicate the project.
Below is the code to rotate the cannon to the touch location and fire a cannonball once it has reached the the desired rotation angle. I think you are still using the zombie conga code so I used its functions for your convenience.
#import "MyScene.h"
static inline CGPoint CGPointSubtract(const CGPoint a,
const CGPoint b)
{
return CGPointMake(a.x - b.x, a.y - b.y);
}
static inline CGPoint CGPointMultiplyScalar(const CGPoint a,const CGFloat b)
{
return CGPointMake(a.x * b, a.y * b);
}
static inline CGFloat CGPointLength(const CGPoint a)
{
return sqrtf(a.x * a.x + a.y * a.y);
}
static inline CGPoint CGPointNormalize(const CGPoint a)
{
CGFloat length = CGPointLength(a);
return CGPointMake(a.x / length, a.y / length);
}
static inline CGFloat CGPointToAngle(const CGPoint a)
{
return atan2f(a.y, a.x);
}
static inline CGFloat ScalarSign(CGFloat a)
{
return a >= 0 ? 1 : -1;
}
static inline CGFloat ScalarShortestAngleBetween(const CGFloat a, const CGFloat b)
{
CGFloat difference = b - a;
CGFloat angle = fmodf(difference, M_PI * 2);
if (angle >= M_PI) {
angle -= M_PI * 2;
}
return angle;
}
static const float ROTATE_RADIANS_PER_SEC = 4 * M_PI;
static const float MOVE_POINTS_PER_SEC = 120.0;
#implementation MyScene
{
SKSpriteNode *cannon;
NSTimeInterval _lastUpdateTime;
NSTimeInterval _dt;
CGPoint _velocity;
CGPoint _lastTouchLocation;
BOOL fireCannon;
CGPoint destination;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
[self createCannon];
fireCannon = false;
}
return self;
}
-(void)createCannon
{
cannon = [SKSpriteNode spriteNodeWithImageNamed:#"cannon"];
cannon.position = CGPointMake(self.size.height/2, self.size.width/2);
[self addChild:cannon];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint touchLocation = [touch locationInNode:self.scene];
fireCannon = true;
destination = touchLocation;
_lastTouchLocation = touchLocation;
CGPoint offset = CGPointSubtract(touchLocation, cannon.position);
CGPoint direction = CGPointNormalize(offset);
_velocity = CGPointMultiplyScalar(direction, MOVE_POINTS_PER_SEC);
}
-(void)update:(CFTimeInterval)currentTime
{
if (_lastUpdateTime) {
_dt = currentTime - _lastUpdateTime;
} else {
_dt = 0;
}
_lastUpdateTime = currentTime;
[self rotateSprite:cannon toFace:_velocity rotateRadiansPerSec:ROTATE_RADIANS_PER_SEC];
}
- (void)rotateSprite:(SKSpriteNode *)sprite
toFace:(CGPoint)velocity
rotateRadiansPerSec:(CGFloat)rotateRadiansPerSec
{
float targetAngle = CGPointToAngle(velocity);
float shortest = ScalarShortestAngleBetween(cannon.zRotation, targetAngle);
float amtToRotate = rotateRadiansPerSec * _dt;
if (ABS(shortest) < amtToRotate)
{
amtToRotate = ABS(shortest);
}
sprite.zRotation += ScalarSign(shortest) * amtToRotate;
if ((ABS(shortest) == amtToRotate) && (fireCannon == true))
{
fireCannon = false;
[self fire:targetAngle];
}
}
-(void)fire:(float)targetAngle
{
SKSpriteNode *cannonBall = [SKSpriteNode spriteNodeWithColor:[SKColor redColor] size:CGSizeMake(5, 5)];
cannonBall.position = cannon.position;
[self addChild:cannonBall];
int x = cannon.position.x + 1000 * cos(targetAngle);
int y = cannon.position.y + 1000 * sin(targetAngle);
[cannonBall runAction:[SKAction moveTo:CGPointMake(x, y) duration:2]];
}
#end
In my app, i used SMRotatory wheel for rotation. It is working fine as rotation regarding.
But i want to detect that rotation is clockwise or anti-clockwise??
//My code is as follow:
- (BOOL)continueTrackingWithTouch:(UITouch*)touch withEvent:(UIEvent*)event
{
CGFloat radians = atan2f(container.transform.b, container.transform.a);
// NSLog(#"rad is %f", radians);
CGPoint pt = [touch locationInView:self];
float dx = pt.x - container.center.x;
float dy = pt.y - container.center.y;
float ang = atan2(dy,dx);
float angleDifference = deltaAngle - ang;
// NSLog(#"%f",angleDifference);
if (angleDifference>=0) {
//NSLog(#"positive");
[[NSUserDefaults standardUserDefaults]setValue:#"positive" forKey:#"CheckValue"];
}
else
{
// NSLog(#"negative");
[[NSUserDefaults standardUserDefaults]setValue:#"negative" forKey:#"CheckValue"];
}
container.transform = CGAffineTransformRotate(startTransform, -angleDifference);
bg.transform=CGAffineTransformRotate(startTransform, -angleDifference);
return YES;
}
Try something like this:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
UITouch *touch = [touches anyObject];
CGPoint currentTouch1=[touch locationInView:self.superview];
CGPoint currentTouch2=[touch previousLocationInView:self.superview];
CGFloat x = currentTouch2.x - currentTouch1.x;
CGFloat y = currentTouch2.y - currentTouch1.y;
CGFloat dist = sqrtf(x*x + y*y);
CGFloat cx = currentTouch1.x - self.superview.bounds.size.width / 2;
CGFloat cy = currentTouch2.y - self.superview.bounds.size.height / 2;
CGFloat cdist = sqrtf(cx*cx + cy*cy);
CGFloat angle = atan2(dist, cdist);
//NSLog(#"%f",angle);
CGFloat radians = atan2f(super.transform.b, super.transform.a);
mDegrees = radians * (180 / M_PI);
if (mDegrees < 0.0)
{
mDegrees+=360;
}
NSLog(#"Angle is = %f",angle); //positive is clockwise, negative is counterclockwise
CGAffineTransform transform = CGAffineTransformRotate(self.transform, angle);
self.transform = transform;
touch1 = currentTouch1;
touch2 = currentTouch2;
}
I seem to be having an issue with calculating the angle between my sprite and a touch point. I'm trying to get my sprite to directly face the direction of the touch point whenever the user touches the screen. Here's my code:
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
CGPoint tapPosition;
for (UITouch *touch in touches){
CGPoint location = [touch locationInView:[touch view]];
tapPosition = [self convertToNodeSpace:[[CCDirector sharedDirector] convertToGL:location]];
}
float angle = CC_RADIANS_TO_DEGREES(ccpAngle(fish.position, tapPosition));
[fish runAction:[CCRotateTo actionWithDuration:0.5 angle:angle]];
}
Any ideas? Thanks
Add this to the end of Nikhil's answer to avoid getting a negative angle when the touch location is to the bottom right of the sprite.
if (calculatedAngle < 0)
{
calculatedAngle+=360;
}
Try this out, first of all you don't need that for loop because you are just going to end up with the last touch location anyway. You may as well use [touches anyObject] as below.
Second, I am not sure what ccpAngle does off the top of my head but when macros like that don't work sometimes its easier to just do the maths yourself.
-(void)ccTouchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
CGPoint tapPosition;
UITouch *touch = [touches anyObject];
CGPoint location = [touch locationInView:[touch view]];
tapPosition = [self convertToNodeSpace:[[CCDirector sharedDirector] convertToGL:location]];
float dY = fish.position.y - tapPosition.y;
float dX = fish.position.x - tapPosition.x;
float offset = dX<0 ? 90.0f : -90.0f;
float angle = CC_RADIANS_TO_DEGREES(atan2f(dY, dX)) + offset;
[fish runAction:[CCRotateTo actionWithDuration:0.5 angle:angle]];
}
You probably could replace dX and dY with a point from ccpDiff as well but it doesn't really matter. Also depending on where the head of your fish is you may need to adjust the angle offset but I will leave that up to you.
Let me know if this helps.
CCPoint pos1 = [fish position];
CCPoint pos2 = touchlocation;
float theta = atan((pos1.y-pos2.y)/(pos1.x-pos2.x)) * 180 * 7 /22;
float calculatedAngle;
if(pos1.y - pos2.y > 0)
{
if(pos1.x - pos2.x < 0)
{
calculatedAngle = (-90-theta);
}
else if(pos1.x - pos2.x > 0)
{
calculatedAngle = (90-theta);
}
}
else if(pos1.y - pos2.y < 0)
{
if(pos1.x - pos2.x < 0)
{
calculatedAngle = (270-theta);
}
else if(pos1.x - pos2.x > 0)
{
calculatedAngle = (90-theta);
}
}
Use this calculatedAngle in your Run Action.. hope this helps... :)
I'm creating a subclass of UIImageView that detects touches, and will move, rotate and scale the image based on the touches. However, I really feel like I'm reinventing the wheel here, and it's driving me nuts. Shouldn't this already exist somewhere?
Does anyone have any examples, or links to a class that is already doing this? Or if you have a class you've written, that'd be helpful too.
Thanks a lot in advance.
I figured it out... I answered my own question.
Hopefully this is useful to someone.
For anyone who is interested, here is the implementation for a UIImageView subclass, that you can use to move, scale, and rotate an image. It works pretty well for me.
- (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event {
if( [touches count] == 1 ) {
float difx = [[touches anyObject] locationInView:self].x - [[touches anyObject] previousLocationInView:self].x;
float dify = [[touches anyObject] locationInView:self].y - [[touches anyObject] previousLocationInView:self].y;
CGAffineTransform newTransform1 = CGAffineTransformTranslate(self.transform, difx, dify);
self.transform = newTransform1;
} else if( [touches count] == 2 ) {
int prevmidx = ([[[touches allObjects] objectAtIndex:0] previousLocationInView:self].x + [[[touches allObjects] objectAtIndex:1] previousLocationInView:self].x) / 2;
int prevmidy = ([[[touches allObjects] objectAtIndex:0] previousLocationInView:self].y + [[[touches allObjects] objectAtIndex:1] previousLocationInView:self].y) / 2;
int curmidx = ([[[touches allObjects] objectAtIndex:0] locationInView:self].x + [[[touches allObjects] objectAtIndex:1] locationInView:self].x) / 2;
int curmidy = ([[[touches allObjects] objectAtIndex:0] locationInView:self].y + [[[touches allObjects] objectAtIndex:1] locationInView:self].y) / 2;
int difx = curmidx - prevmidx;
int dify = curmidy - prevmidy;
CGPoint prevPoint1 = [[[touches allObjects] objectAtIndex:0] previousLocationInView:self];
CGPoint prevPoint2 = [[[touches allObjects] objectAtIndex:1] previousLocationInView:self];
CGPoint curPoint1 = [[[touches allObjects] objectAtIndex:0] locationInView:self];
CGPoint curPoint2 = [[[touches allObjects] objectAtIndex:1] locationInView:self];
float prevDistance = [self distanceBetweenPoint1:prevPoint1 andPoint2:prevPoint2];
float newDistance = [self distanceBetweenPoint1:curPoint1 andPoint2:curPoint2];
float sizeDifference = (newDistance / prevDistance);
CGAffineTransform newTransform1 = CGAffineTransformTranslate(self.transform, difx, dify);
self.transform = newTransform1;
CGAffineTransform newTransform2 = CGAffineTransformScale(self.transform, sizeDifference, sizeDifference);
self.transform = newTransform2;
float prevAngle = [self angleBetweenPoint1:prevPoint1 andPoint2:prevPoint2];
float curAngle = [self angleBetweenPoint1:curPoint1 andPoint2:curPoint2];
float angleDifference = curAngle - prevAngle;
CGAffineTransform newTransform3 = CGAffineTransformRotate(self.transform, angleDifference);
self.transform = newTransform3;
}
}
- (NSInteger)distanceBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2 {
CGFloat deltaX = fabsf(point1.x - point2.x);
CGFloat deltaY = fabsf(point1.y - point2.y);
CGFloat distance = sqrt((deltaY*deltaY)+(deltaX*deltaX));
return distance;
}
- (CGFloat)angleBetweenPoint1:(CGPoint)point1 andPoint2:(CGPoint)point2
{
CGFloat deltaY = point1.y - point2.y;
CGFloat deltaX = point1.x - point2.x;
CGFloat angle = atan2(deltaY, deltaX);
return angle;
}
You might also look at Erica Sadun's example from the iphone dev cookbook (oreilly). It's in chapter 8 on gestures and touches.
http://github.com/erica/iphone-3.0-cookbook-/tree/master/C08-Gestures/14-Resize%20And%20Rotate/