SpriteNodes stop moving in the middle of the screen - ios

I'm building a game using spriteKit.
I created a spriteNode called 'planet' and set a velocity. It drops down from the top of the screen with constant speed to the bottom of the screen. However, it slows down towards the middle of the screen and stops moving entirely. I added the method 'moveTowardPosition' as an attempt to fix this issue by forcing the objects to move offscreen. Does anyone have an idea of what the issue might be?
Thanks!
Here is the setup of the object:
-(void)physicsBodySetup
{
//initializes the physicsBody
self.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:40];
self.physicsBody.affectedByGravity = NO;
//sets its category mask
self.physicsBody.categoryBitMask = TKCollisionCategoryPlanet;
//planet doesn't collide with anything
self.physicsBody.collisionBitMask = 0;
//the planet can get into contact with an astranaut
self.physicsBody.contactTestBitMask = TKCollisionCategoryAstronaut |
}
//adds 'planet' to the screen at random position
//#param frame: the frame of the main view
-(void)addPosition:(CGRect)frame
{
//specifies a random position to add the planet on the screen
float randomPositionY = frame.size.height + self.size.height;
float randomPositionX = [TKUtil randomWithMin:10+self.size.width max:frame.size.width-10 - self.frame.size.width];
self.position = CGPointMake(randomPositionX, randomPositionY);
[self velocitySetup];
}
-(void)velocitySetup
{
//initializes the speed to be a constant
self.planetSpeed = TKPlanetSpeed;
self.physicsBody.velocity = CGVectorMake(0, self.planetSpeed);
}
//#param position: the point of planet on the screen
-(void)moveTowardsPosition:(CGPoint)position
{
float offScreenX = position.x;
float offScreenY = position.y - 2000;
CGPoint pointOffScreen = CGPointMake(offScreenX, offScreenY);
float Distance = pointOffScreen.y - position.y;
float time = Distance / self.planetSpeed;
SKAction *movePlanet = [SKAction moveTo:pointOffScreen duration:time];
NSArray *sequence = #[movePlanet, [SKAction removeFromParent]];
[self runAction:[SKAction sequence:sequence]];
}

The default value of friction is 0.2. So you can try:
self.physicsBody.friction = 0.0
now it won't be affected by surface anymore.

Related

How to make SKSpriteNode rotate in the direction of touching?

Could you please help me with the problem I have?
define CC_RADIANS_TO_DEGREES(ANGLE) ((ANGLE) * 57.29577951f) // PI
-(void)didMoveToView:(SKView )view {
/ Setup your scene here */
_skyColor = [SKColor colorWithRed:113.0/255.0 green:197.0/255.0 blue:207.0/255.0 alpha:1.0];
[self setBackgroundColor:_skyColor];
//Setup the array to hold the walking frames
NSMutableArray *padlingFrames = [NSMutableArray array];
//Load the TextureAtlas for the bear
SKTextureAtlas *kajakAnimatedAtlas = [SKTextureAtlas atlasNamed:#"KajakImages"];
//Load the animation frames from the TextureAtlas
long numImages = kajakAnimatedAtlas.textureNames.count;
for (int i=1; i <= numImages; i++) {
NSString *textureName = [NSString stringWithFormat:#"kajak_0%d", i];
SKTexture *temp = [kajakAnimatedAtlas textureNamed:textureName];
[padlingFrames addObject:temp];
}
_kajakPadlingFrames = padlingFrames;
//Create kajak sprite, setup position in middle of the screen, and add to Scene
SKTexture *temp = _kajakPadlingFrames[0];
_kajak = [SKSpriteNode spriteNodeWithTexture:temp];
_kajak.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame));
[_kajak setScale:0.2];
[self addChild:_kajak];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent )event {
/ Called when a touch begins */
CGPoint touchLocation = [[touches anyObject] locationInNode:self];
[self updateRotate:touchLocation];
}
(void)updateRotate:(CGPoint)touchLocation
{
float deltaX = touchLocation.x - _kajak.position.x;
float deltaY = touchLocation.y - _kajak.position.y;
float angle = atan2f(deltaY, deltaX);
SKAction *action = [SKAction rotateByAngle: CC_RADIANS_TO_DEGREES(angle) duration: 5.0];
[_kajak runAction:action];
}
The kayak is not rotate in the direction the I touch. It is some thing I missing? Please help me. Tank you in advance
The zero angle for SKSpriteNodes is pointing up (as opposed to standard trig functions that treat zero angle as pointing right). I had to create a special angle calculation function to take that into account in my game (see below, in Swift).
You may also need to make sure that the coordinate system you are using for the positions is the same for the sprite and the touch location. From your code (i'm not very comfortable with Obj-C) I believe they are both base on the scene's bounds but I'm not certain.
// angle between two points
// ------------------------
// SpriteKit's zero angle is pointing up
// atan2 returns an angle pointing right
// we're usung sprite kit's conventions so removing 90° from the result
//
func spriteAngleFrom(start:CGPoint, to finish:CGPoint) -> CGFloat
{
let deltaX = finish.x - start.x
let deltaY = finish.y - start.y
return atan2(deltaY,deltaX) - CGFloat.pi/2
}
You also need to use rotateToAngle, not rotateByAngle. And I would suggest you also specify shortestUnitArc : true.

Spritekit PhysicsBody applyTorque

I'm working on a universal app.
I want to applyTorque to a Paddle
But the problem is the paddle sizes are bigger on a ipad than the one one iphone.
How can I calculate the torque to apply the same affect to the Paddle's physicalBody?
SKSpriteNode *bar = [SKSpriteNode spriteNodeWithImageNamed:nameImage];
bar.name = #"bar";
bar.size = CGSizeMake(PaddleWidth, PaddleHeight);
bar.physicsBody =[SKPhysicsBody bodyWithTexture:bar.texture size:bar.size];
bar.physicsBody.restitution = 0.2;
bar.physicsBody.angularDamping = 0;
bar.physicsBody.friction = 0.02;
bar.physicsBody.mass=.2;
[paddle addChild:bar];
SKSpriteNode *anchor = [SKSpriteNode spriteNodeWithColor:[SKColor clearColor] size:CGSizeMake(PaddleWidth, PaddleHeight)];
anchor.name = #"anchor";
anchor.size = CGSizeMake(PaddleHeight, PaddleHeight);
anchor.position = CGPointMake(bar.position.x + bar.size.width/2, 0);
[paddle addChild:anchor];
CGFloat anchorRadius = anchor.size.width/20;
anchor.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:anchorRadius];
anchor.physicsBody.dynamic = NO;
CGPoint positionInScene = [self convertPoint:anchor.position toNode:self.scene];
pin = [SKPhysicsJointPin jointWithBodyA:bar.physicsBody
bodyB:anchor.physicsBody
anchor:positionInScene];
pin.shouldEnableLimits = YES;
pin.lowerAngleLimit = -0.5;
pin.upperAngleLimit = 0.5;
[self.scene.physicsWorld addJoint:pin];
-(void)flip{
SKNode *bar=[self childNodeWithName:#"bar"];
CGFloat torque;
torque=10;
[bar.physicsBody applyTorque:torque];
}
You can use the size of the paddle as a factor in the torque value:
CGSize paddleSize = <Initialize with paddle size>
CGFloat torque=paddleSize.width * factor; // factor will be the value you need to multiply in order to reach your desired value
So if for example paddle width is 100 use a constant factor of 0.05
On the iPad the different paddle width will make the torque value to be calculated accordingly using the same factor value as above.
You can set a constant mass of the physicsBody so that the torque applied affects the paddle the same way regardless of the node's size.
//While instantiating the physicsBody for the "bar" node
bar.physicsBody.mass = 0.2; //This will need to be adjusted according to your needs.
From the documentation:
The mass of the body affects its momentum as well as how forces are
applied to the object.

figuring out the conversions from UIKit coordinates to SKScene coordinates

I am new to SpriteKit but not to iOS. I'm having a great deal of difficulty figuring out the conversions from UIKit coordinates to SKScene coordinates. In addition, their relations to the units used for the SpriteKit physics engine evades me.
Here's what I'm trying to do: I am launching a sprite with an initial velocity in both x and y directions. The sprite should start near the screen's bottom left corner (portrait mode) and leave near the bottom right corner. Another sprite should appear somewhere on the previous sprite's trajectory. This sprite is static.
The blue frame is the screen boundary, the black curve is the first sprite's trajectory and the red circle is the static sprite.
Now using the screen as 640 x 960. In my SKScene's didMoveToView: method I set the gravity as such:
self.physicsWorld.gravity = CGVectorMake(0.0f, -2.0f);
Then I make the necessary calculations for velocity and position:
"t" is time from left corner to right;
"y" is the maximum height of the trajectory;
"a" is the acceleration;
"vx" is velocity in the x direction;
"vy" is velocity in the y direction;
and
"Tstatic" is the time at which the dynamic sprite will be at the static sprite's position;
"Xstatic" is the static sprite's x-position;
"Ystatic" is the static sprite's y-position;
-(void)addSpritePair {
float vx, vy, t, Tstatic, a, Xstatic, Ystatic, y;
a = -2.0;
t = 5.0;
y = 800.0;
vx = 640.0/t;
vy = (y/t) - (1/8)*a*t;
Tstatic = 1.0;
Xstatic = vx*Tstatic;
Ystatic = vy*Tmark + 0.5*a*Tstatic*Tstatic;
[self spriteWithVel:CGVectorMake(vx, vy) andPoint:CGPointMake(xmark, ymark)];
}
-(void)spriteWithVel:(CGVector)velocity andPoint:(CGPoint)point{
SKSpriteNode *staticSprite = [SKSpriteNode spriteNodeWithImageNamed:#"ball1"];
staticSprite.anchorPoint = CGPointMake(.5, .5);
staticSprite.position = point;
staticSprite.name = #"markerNode";
staticSprite.zPosition = 1.0;
SKSpriteNode *dynamicSprite = [SKSpriteNode spriteNodeWithImageNamed:#"ball2"];
dynamicSprite.anchorPoint = CGPointMake(.5, .5);
dynamicSprite.position = CGPointMake(1 ,1);
dynamicSprite.zPosition = 2.0;
dynamicSprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:hoop.size.width/2];
dynamicSprite.physicsBody.allowsRotation = NO;
dynamicSprite.physicsBody.velocity = velocity;
dynamicSprite.physicsBody.affectedByGravity = YES;
dynamicSprite.physicsBody.dynamic = YES;
dynamicSprite.physicsBody.mass = 1.0f;
dynamicSprite.markerPoint = point;
dynamicSprite.physicsBody.restitution = 1.0f;
dynamicSprite.physicsBody.friction = 0.0f;
dynamicSprite.physicsBody.angularDamping = 0.0f;
dynamicSprite.physicsBody.linearDamping = 0.0f;
[self addChild:hoop];
[self addChild:marker];
}
When I execute the code, the following happens:
Can someone please help me out?

Add SKSpriteNode children back to the parent after removing

I'm working on a game that has 3 clouds in the background which are each SKSpriteNodes. In order to create a parallax effect I'm moving them down the screen as the character is moving up (the screen is moving up as well).
I removing them in the update method after they reach the bottom of the screen. I would like to add them back at a Y position 20px above the viewable scene so that would be 500x on an iPhone 4 and 588px on a iPhone 5. Then just repeat over and over.
Then only way I can think of to do this is to chain SKActions and run a sequence, but I don't think that is the best way. I'm trying to find a way to add them back after they have been removed in the update method.
-(id)initWithSize:(CGSize)size {
...
//Moving Clouds
[self performSelector:#selector(createClouds) withObject:nil afterDelay:.2];
}
-(void)createClouds {
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
[cloud1 runAction:[SKAction repeatActionForever:moveCloud1]];
[self addChild:cloud1];
SKSpriteNode *cloud2 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud2"];
cloud2.position = CGPointMake(300, 320);
cloud2.xScale = .5;
cloud2.yScale = .5;
cloud2.name = #"Cloud2";
cloud2.zPosition = 0;
SKAction *moveCloud2 = [SKAction moveToY:-40 duration:15];
[cloud2 runAction:[SKAction repeatActionForever:moveCloud2]];
[self addChild:cloud2];
SKSpriteNode *cloud3 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud3"];
cloud3.position = CGPointMake(200, 450);
cloud3.xScale = .5;
cloud3.yScale = .5;
cloud3.name = #"Cloud3";
cloud3.zPosition = 0;
SKAction *moveCloud3 = [SKAction moveToY:-200 duration:20];
[cloud3 runAction:[SKAction repeatActionForever:moveCloud3]];
[self addChild:cloud3];
}
-(void)update:(CFTimeInterval)currentTime {
....
// Remove Cloud from parent
[self enumerateChildNodesWithName:#"Cloud1" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x < 0 || node.position.y < 0) {
[node removeFromParent];
//Add node back to the scene at a Y position above the screen then move
// the cloud downwards again. Repeat Forever.
// Exp: Move Cloud 1 to X position 40, Y position 500 (20 pixels above the top of the screen for an iPhone 4s)
}
else {
// Do something?
}
}];
You can add an SKAction to move your cloud back to the top:
SKSpriteNode *cloud1 = [SKSpriteNode spriteNodeWithImageNamed:#"cloud1"];
cloud1.position = CGPointMake(40, 200);
cloud1.xScale = .5;
cloud1.yScale = .5;
cloud1.name = #"Cloud1";
cloud1.zPosition = 0;
SKAction *moveCloud1 = [SKAction moveToY:-20 duration:15];
// SKAction to move cloud back to top of screen
SKAction *moveToTop = [SKAction moveToY:CGRectGetHeight(self.frame)+20 duration:0];
SKAction *cloud1Action = [SKAction sequence:#[moveCloud1, moveToTop]];
[cloud1 runAction:[SKAction repeatActionForever:cloud1Action]];
You won't need the code in the update method.
I think you can simply change the position
Also, use CGRectGetMinY to use relative positioning, easier to manage different screen sizes.
And check only the Y position too, because X won't change if they're moving vertically.
This is my idea:
-(void)update:(NSTimeInterval)currentTime {
for(int i = 1; i<=3; i++){
NSString* cloud = [NSString stringWithFormat:#"Cloud%d",i];
[self enumerateChildNodesWithName:cloud usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0) {
node.position = CGPointMake(node.position.x,CGRectGetMaxY(self.frame)+20);
}
}];
}
}

How can I keep my sprites velocity constant?

I'm making a simple space shooter in Sprite Kit. I have asteroid rock sprites that are randomly added to my scene. I give them a random velocity within a certain range. However, once added to the scene their velocity decreases every second and eventually they stop. as they get closer to the bottom of the screen their velocity decreases at a slower rate. I have gravity of my physics world set to (0,0).
below is the method that adds the rocks to the scene
- (void)addRock {
SPRockNode *rock = [SPRockNode rock];
float dy = [SPUtil randomWithMin:SPRockMinSpeed max:SPRockMaxSpeed];
rock.physicsBody.velocity = CGVectorMake(0, -dy);
float y = self.frame.size.height + rock.size.height;
float x = [SPUtil randomWithMin:10 + rock.size.width max:self.frame.size.width - 10 - rock.size.width];
rock.position = CGPointMake(x, y);
[self addChild:rock];
}
I set up the physics world in my scenes initWIthSize method
self.physicsWorld.contactDelegate = self;
self.physicsWorld.gravity = CGVectorMake(0, 0);
below is the code for my rock node class
+ (instancetype)rock {
SPRockNode *rock = [self spriteNodeWithImageNamed:#"rock"];
rock.name = #"Rock";
float scale = [SPUtil randomWithMin:50 max:100] / 100.0f;
rock.xScale = scale;
rock.yScale = scale;
NSInteger randomInt = [SPUtil randomWithMin:1 max:3];
if (randomInt == 1) {
SKAction *oneRevolution = [SKAction rotateByAngle:-M_PI*2 duration: 1.0];
SKAction *repeat = [SKAction repeatActionForever:oneRevolution];
[rock runAction:repeat];
} else {
SKAction *oneRevolution = [SKAction rotateByAngle:M_PI*2 duration: 1.0];
SKAction *repeat = [SKAction repeatActionForever:oneRevolution];
[rock runAction:repeat];
}
[rock setupPhysicsBody];
return rock;
}
- (void)setupPhysicsBody {
self.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:self.frame.size];
self.physicsBody.affectedByGravity = NO;
self.physicsBody.categoryBitMask = SPCollisionCategoryRock;
self.physicsBody.collisionBitMask = 0;
self.physicsBody.contactTestBitMask = SPCollisionCategoryProjectile | SPCollisionCategoryShip;
}
any ideas on what causing them to slow down? I dont modify their velocity anywhere else but the addRock method. is there air resistance or something like that on by default? I just dont understand why their velocity is decreasing.
It sounds like you are having an issue with linear damping.
From SKPhysicsBody reference :
This property is used to simulate fluid or air friction forces on the body. The property must be a value between 0.0 and 1.0. The default value is 0.1. If the value is 0.0, no linear damping is applied to the object.
Try adding this :
rock.physicsBody.linearDamping = 0.0f;
It's worthwhile to give the SKPhysicsBody class reference a reading at least once. It only takes a short while, but can save you a ton of time if you have become acquainted with it's properties and methods.

Resources