I want to rotate this arrow across a fixed point in clock. The code below doesn't do what i intend to do, it rotates the whole arrow towards a center of a circle. I also tried to animate it across a circular path but it didn't give me the effect i want ...
i want this arrow to rotate around a circle while the arrow head is pointed towards the center of this circle
-(void) addArrow2:(CGSize)size{
_Arrow2 = [SKSpriteNode spriteNodeWithImageNamed:#"arrow2"];
_Arrow2.position = CGPointMake(self.frame.size.width/2 , self.frame.size.height/2 +30);
SKAction *rotate = [SKAction rotateByAngle:M_2_PI duration:1];
SKAction *forever = [SKAction repeatActionForever:rotate];
_Arrow2.yScale = 0.45;
_Arrow2.xScale = 0.45;
[self addChild:_Arrow2];
[_Arrow2 runAction:forever];
}
I suppose your got this kind of arrow →. Change the anchorPoint as (1.0, 0.5) and make the arrow follow a circle path. Make some change to fit your need.
- (void)addArrow2
{
_Arrow2 = [SKSpriteNode spriteNodeWithImageNamed:#"arrow2"];
_Arrow2.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2+30);
_Arrow2.anchorPoint = CGPointMake(1.0, 0.5);
_Arrow2.yScale = 0.45;
_Arrow2.xScale = 0.45;
// Make a circle path
CGMutablePathRef circle = CGPathCreateMutable();
CGFloat centerX = self.frame.size.width/2;
CGFloat centerY = self.frame.size.height/2;
CGFloat radius = 25.0;
CGFloat startAngle = 0.0;
CGFloat endAngle = 2*M_PI;
CGPathAddArc(circle, NULL, centerX, centerY, radius, startAngle, endAngle, true);
CGPathCloseSubpath(circle);
SKAction *followPath = [SKAction followPath:circle asOffset:NO orientToPath:YES duration:2.0];
// Infinite rotation
SKAction *forever = [SKAction repeatActionForever:followPath];
[_Arrow2 runAction:forever];
[self addChild:_Arrow2];
}
Animation preview:
Related
The Child SKSpriteKITnodes in the coordinate system doesn't work well I am trying to move the SKSprite nodes from Top To Bottom But they Get stop in the middle ?
ColorCoin *coin = [[ColorCoin alloc] initWithImageNamed: coinName] ;
SKAction *move = [SKAction moveToY:0 duration: 10];
CGFloat screenWidth = [self dropNode].size.width;
int x = arc4random() % (int) _dropNode.calculateAccumulatedFrame.size.width;
coin.zPosition =0.0;
coin.position = CGPointMake( x, _dropNode.calculateAccumulatedFrame.size.height-30);
coin.name = coinName;
[coin runAction: move];
[[self dropNode] addChild:coin];
The anchorPoint of an SKSpriteNode is (0.5, 0.5) by default. Therefore, when [self dropNode].position.y == 0 it's correct the the child node will be in the middle of its parent SKSpriteNode.
If you want the coin to move to the bottom of dropNode you can either set the anchorPoint of dropNode to (x, 0) (where x is any value in the range 0-1). Or you can change move to:
SKAction *move = [SKAction moveToY:-screenWidth / 2 duration: 10];
I do some simple games using SpriteKit now. There is one problem with stroke color in drawing process.
I have to do my colored shapeNode transparent, but my stroke color stays the same every time. I've tried different techniques to do my node be entirely transparent: 1)setting alpha component to stroke color 2)setting alpha component to whole node. It didn't help. Is there is any way to achieve or get around this?
Creation of my node
CGMutablePathRef arc = CGPathCreateMutable();
cardWidth = cardWidth + cardAlpha;
CGPathAddRoundedRect(arc, NULL, CGRectMake(-cardWidth/2, -cardHeight/2, cardWidth, cardHeight), roundRadius, roundRadius);
roundRect = [SKShapeNode node];
roundRect.name = self.name;
CGFloat lineWidth = 2.0;
CGPathRef strokedArc =
CGPathCreateCopyByStrokingPath(arc, NULL,
lineWidth,
kCGLineCapButt,
kCGLineJoinMiter, // the default
10); // 10 is default miter limit
roundRect.path = strokedArc;
[self addChild:roundRect];
Then i try to change color and opacity
roundRect.fillColor = rightColor;
roundRect.strokeColor = rightColor;
roundRect.strokeColor = rightColor;
termLabel.fontColor = rightColor;
roundRect.alpha = 0.5;
I think the problem is with your path. the fill color is covering your stoke so you don't see any change of the stroke color. But with my test, the node.alpha component should work. It maybe the type of iOS version, Apple may change how some things work together.
Here is some code to play with:
CGMutablePathRef arc = CGPathCreateMutable();
CGFloat cardWidth = 100;
CGFloat cardHeight = 170;
CGFloat roundRadius = 10;
CGFloat r,g,b = 1; // color components
CGRect rect = CGRectMake(-cardWidth/2, -cardHeight/2, cardWidth, cardHeight);
CGPathAddRoundedRect(arc, NULL, rect, roundRadius, roundRadius);
__block SKShapeNode *roundRect = [SKShapeNode node];
roundRect.name = #"card";
CGFloat lineWidth = 20.0;
CGPathRef strokedArc =
CGPathCreateCopyByStrokingPath(arc, NULL,
lineWidth,
kCGLineCapButt,
kCGLineJoinMiter, // the default
10); // 10 is default miter limit
roundRect.path = strokedArc;
// roundRect.alpha = 0.3; // uncomment if want to use.
[scene addChild:roundRect];
// delay
SKAction *waitAction = [SKAction waitForDuration:1/15.0f];
// variables
static CGFloat direction = 1;
static CGFloat speed = .1f;
static CGFloat alpha = 0;
// action to aniamte shape
SKAction *changeColorAction = [SKAction runBlock:^{
CGFloat vel = direction * speed;
CGFloat newValue = alpha + vel;
if(newValue < 0 || newValue > 1){
direction *= -1;
} else {
alpha = newValue;
}
UIColor *newColor = [UIColor colorWithRed:r green:g blue:b alpha:alpha];
// animate shape
[roundRect setStrokeColor:newColor];
// [roundRect setFillColor:newColor]; // uncoment to animate.
}];
// Did SKAction because its under the scene run loop.
SKAction *seq = [SKAction sequence:#[ waitAction, changeColorAction ]];
[scene runAction:[SKAction repeatActionForever:seq]];
I am trying to move a SKSpriteNode in a circular path, pause that animation, and then reverse it from where it left off.
Here is what I have so far.
CGPathRef circle = CGPathCreateWithEllipseInRect(CGRectMake(self.frame.size.width/2-75,self.frame.size.height/2-75,150,150), NULL);
self.circlePathActionClockwise = [SKAction followPath:circle asOffset:NO orientToPath:YES duration:5.0];
self.circlePathActionClockwise = [SKAction repeatActionForever:self.circlePathActionClockwise];
[self.ball runAction:self.circlePathActionClockwise];
self.ball.speed = 1.0;
This animated the ball in a circle. Then when I want to pause and reverse, I try:
[self.ball runAction:[self.circlePathActionClockwise reversedAction]];
self.ball.speed = 1.0;
This reverses the animation, but it does not start it from where it left off. Instead it treats it was a new animation and starts from the beginning.
Is there a way I can start an action from self.ball's current position?
Thanks a lot!
The steps to reverse the direction of a sprite that is following a circular path are
Compute the angle of the sprite at the stopping point (see point a and angle theta in the Figure 1)
Create a new circular path that starts at angle theta
Run an action in the opposite direction
Figure 1. A sprite following a counter-clockwise circular path
Here's an example of how to do that:
Step 1: compute the starting angle of the new path based on the current position of the sprite.
- (CGFloat) angleOnCircleWithPosition:(CGPoint)position andCenter:(CGPoint)center
{
CGFloat dx = position.x - center.x;
CGFloat dy = position.y - center.y;
return atan2f(dy, dx);
}
Step 2: create a circular path given a center point and radius. Create the path starting from the specified angle (in radians).
- (CGPathRef) circlePathRefWithCenter:(CGPoint)center radius:(CGFloat)radius andStartingAngle:(CGFloat)startAngle
{
CGMutablePathRef circlePath = CGPathCreateMutable();
CGPathAddRelativeArc(circlePath, NULL, center.x, center.y, radius, startAngle, M_PI*2);
CGPathCloseSubpath(circlePath);
return circlePath;
}
You will need to declare the following instance variables:
BOOL clockwise;
SKSpriteNode *sprite;
CGFloat circleRadius;
CGPoint circleCenter;
Step 3: Reverse the direction of the sprite when the user taps the screen. It assumes that you've created a sprite and added it to the scene and set the radius and center of the circle path.
- (void) touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[sprite removeActionForKey:#"circle"];
CGFloat angle = [self angleOnCircleWithPosition:sprite.position andCenter:circleCenter];
CGPathRef path = [self circlePathRefWithCenter:circleCenter radius:circleRadius andStartingAngle:angle];
SKAction *followPath = [SKAction followPath:path asOffset:NO orientToPath:YES duration:4];
if (clockwise) {
[sprite runAction:[SKAction repeatActionForever:followPath] withKey:#"circle"];
}
else {
[sprite runAction:[[SKAction repeatActionForever:followPath] reversedAction] withKey:#"circle"];
}
// Toggle the direction
clockwise = !clockwise;
}
I am programming a game and want to move a sprite along a fixed path (straights and curves - think railroad engine) but I want to be able to drive the animation in terms of the sprites velocity - and change that velocity in response to game events.
followPath:duration: in SKAction is close, but there seems no way to adjust the speed "on the fly" - I definitely care about sprite speed - not 'duration to follow path'.
CGPath seems like the right construct for defining the path for the sprite, but there doesn't seem to be enough functionality there to grab points along the path and do my own math.
Can anyone suggest a suitable approach?
You can adjust the execution speed of any SKAction with its speed property. For example, if you set action.speed = 2, the action will run twice as fast.
SKAction *moveAlongPath = [SKAction followPath:path asOffset:NO orientToPath:YES duration:60];
[_character runAction:moveAlongPath withKey:#"moveAlongPath"];
You can then adjust the character's speed by
[self changeActionSpeedTo:2 onNode:_character];
Method to change the speed of an SKAction...
- (void) changeActionSpeedTo:(CGFloat)speed onNode:(SKSpriteNode *)node
{
SKAction *action = [node actionForKey:#"moveAlongPath"];
if (action) {
action.speed = speed;
}
}
Try some think like this :
CGMutablePathRef cgpath = CGPathCreateMutable();
//random values
float xStart = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float xEnd = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
//ControlPoint1
float cp1X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp1Y = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.height ];
//ControlPoint2
float cp2X = [self getRandomNumberBetween:0+enemy.size.width to:screenRect.size.width-enemy.size.width ];
float cp2Y = [self getRandomNumberBetween:0 to:cp1Y];
CGPoint s = CGPointMake(xStart, 1024.0);
CGPoint e = CGPointMake(xEnd, -100.0);
CGPoint cp1 = CGPointMake(cp1X, cp1Y);
CGPoint cp2 = CGPointMake(cp2X, cp2Y);
CGPathMoveToPoint(cgpath,NULL, s.x, s.y);
CGPathAddCurveToPoint(cgpath, NULL, cp1.x, cp1.y, cp2.x, cp2.y, e.x, e.y);
SKAction *planeDestroy = [SKAction followPath:cgpath asOffset:NO orientToPath:YES duration:5];
[self addChild:enemy];
SKAction *remove2 = [SKAction removeFromParent];
[enemy runAction:[SKAction sequence:#[planeDestroy,remove2]]];
CGPathRelease(cgpath);
This is my code for stars that move from left to right, I used it from a tutorial online:
SKTexture* starsTexture = [SKTexture textureWithImageNamed:#"stars"];
starsTexture.filteringMode = SKTextureFilteringNearest;
SKAction* movestarsSprite = [SKAction moveByX:-starsTexture.size.width*2 y:0 duration:0.02 * starsTexture.size.width*2];
SKAction* resetstarsSprite = [SKAction moveByX:starsTexture.size.width*2 y:0 duration:0];
SKAction* movestarsSpritesForever = [SKAction repeatActionForever:[SKAction sequence:#[movestarsSprite, resetstarsSprite]]];
for( int i = 0; i < 2 + self.frame.size.width / ( starsTexture.size.width * 2 ); ++i ) {
SKSpriteNode* starsSprite = [SKSpriteNode spriteNodeWithTexture:starsTexture];
[starsSprite setScale:2.0];
starsSprite.position = CGPointMake(i * starsSprite.size.width, starsSprite.size.height / 2);
[starsSprite runAction:movestarsSpritesForever];
[self addChild:starsSprite];
Im quite a beginner to coding, right now the stars show up repeating at the bottom of the screen and I want them to appear at the top like stars really would. What should I change to achieve that? Please Help!
The coordinate system in SpriteKit has 0,0 bottom left of the screen so you need to increase the y coordinate of the position of the nodes to move them higher.
You currently have them set to:
starsSprite.position = CGPointMake(i * starsSprite.size.width, starsSprite.size.height / 2);
Try:
starsSprite.position = CGPointMake(i * starsSprite.size.width, self.size.height - starsSprite.size.height / 2);