Using Sprite Kit, how can I let an object jump? - sprite-kit

I am making a game with sprite kit and now I am wondering what the best way is to let the object 'jump'. So it will be launched up vertical with a few pixels.
This is the code of the object I want to jump:
SKSpriteNode *sprite = [SKSpriteNode spriteNodeWithImageNamed:#"bal.png"];
sprite.position = CGPointMake(self.frame.size.width/4 + arc4random() % ((int)self.frame.size.width/2), (self.frame.size.height/2 + arc4random() % ((int)self.frame.size.height/2)));
sprite.color = [self randomColor];
sprite.colorBlendFactor = 1.0;
sprite.xScale = 0.2;
sprite.yScale = 0.2;
[self addChild:sprite];
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
self.physicsWorld.gravity = CGVectorMake(0.0f, -4.0f);

I would try to avoid setting object's velocity directly. Instead I'd apply forces and impulses to the objects. For most cases this works better as it doesn't break the physics simulation. For example this is how I'd make my object jump:
- (void) jump:(SKSpriteNode*)obj
{
if (obj.isTouchingGround)
{
CGFloat impulseX = 0.0f;
CGFloat impulseY = 25.0f;
[object.physicsBody applyImpulse:CGVectorMake(impulseX, impulseY) atPoint:obj.position];
}
}

Give a velocity to the body by changing the velocity property of the physicsBody associated with the sprite.
Specifically, you need to add
sprite.physicsBody.velocity = CGVectorMake(vx,vy);
EDIT : If you want to to do this through actions, make use of one of these two class methods of SKAction
+ (SKAction *)followPath:(CGPathRef)path duration:(NSTimeInterval)sec;
+ (SKAction *)moveByX:(CGFloat)deltaX y:(CGFloat)deltaY duration:(NSTimeInterval)sec;
However, using any of these requires you to calculate stuff based on your velocity vector and take care of gravity yourself(for giving suitable deltaX and deltaY or the correct path) which is quite unnecessary. Why not just mention velocity and let SpriteKit work it out for you?

Related

Move SKSpriteNode with Physics

I am trying to move an SKSpriteNode with physics by setting it's velocity. I am doing this on top of another SKNode (the map) that is part of the SKScene. Positions of "cities" are stored in custom Location objects.
I know the location of the initial SKSpriteNode (the ship) and I know the desired location. From what I've read, I can move the sprite by setting it's velocity. I do so like this:
float dy = _player.desiredLocation.yCoordinate - _mapNode.pin.position.y;
float dx = _player.desiredLocation.xCoordinate - _mapNode.pin.position.x;
_mapNode.pin.physicsBody.velocity = CGVectorMake(dx, dy);
This is all inside the didSimulatePhysics function inside the SKScene. Once the user taps a Location, I find the position and set the velocity. This seems to work well the FIRST time, but the sprite moves all over the place in subsequent times. Any idea what could be going wrong here?
PS: Setting skView.showsPhysics = YES; puts the circle of the physics body way off the position of the sprite.
In the map node:
self.pin = [SKSpriteNode spriteNodeWithImageNamed:[IPGameManager sharedGameData].world.player.ship.type];
self.pin.userInteractionEnabled = NO;
self.pin.position = CGPointMake([IPGameManager sharedGameData].world.player.location.xCoordinate, [IPGameManager sharedGameData].world.player.location.yCoordinate);
self.pin.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:10.0];
self.pin.physicsBody.dynamic = YES;
self.pin.physicsBody.allowsRotation = NO;
self.pin.physicsBody.friction = 0.0;
self.pin.physicsBody.linearDamping = 0.0;
[self addChild:self.pin];

Using SpriteKit how can you move sprite nodes to a point with a random but consistent speed

I'm currently making my first sprite game and one of the things i have implemented into my game is objects coming in from the side, move all the way across the screen, and then be removed from the parent. Here's what that looks like.
MyScene.m
-(void)createObstacle0 {
CGPoint startPoint = CGPointMake(-20, 100);
SKSpriteNode *obstacle = [SKSpriteNode spriteNodeWithImageNamed:#"obstacle"];
obstacle.position = CGPointMake(startPoint.x, startPoint.y);
obstacle.name = #"obstacle0";
[self addChild:obstacle];
float randomNum = arc4random_uniform(2) + 1.92;
[self performSelector:#selector(createObstacle0) withObject:nil afterDelay:randomNum];
}
excerpt from update method
[self enumerateChildNodesWithName:#"obstacle0" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.x > 360) {
[node removeFromParent];
} else {
node.position = CGPointMake(node.position.x + 5.2, node.position.y);
}
}];
At the moment, my objects only move across the screen at one speed. I tried using arc4random inside the currentTime method code, but because thats evaluated every frame, my object would not stay at one speed. It would speed up, slow down, and look very erratic. How can i change my code in CurrentTime to allow my objects crossing the screen to be set a random speed as soon as its made and to keep it until it is destroyed?
Second question:
int yMin = 90;
int yMax = 110;
CGPoint startPoint = CGPointMake(-20, yMin + arc4random_uniform(yMax - yMin));
I would suggest that instead of manually updating the child nodes position to move it from one side to another you create your obstacle and then call runAction: on it using an SKAction created with the moveTo:duration: method. You specify the final point you want your obstacle to end up at as well as how long you want this move to take. Pass in a random number with in the minimum and maximum time you want it to take to duration.
This should allow you to create consistent but randomised object movement.
int MAX_TIME = 20;
int MIN_TIME = 10;
[obstacle runAction:[SKAction moveTo:CGPointMake( 10, 100 ) duration:MIN_TIME + arc4random_uniform( MAX_TIME - MIN_TIME )]];

SpriteKit 1 Dimensional Movement

I'm using apple's Sprite Kit and I need to move a SKSprite Node in horizontal movement only. I want the rest of the physics to apply but only in the horizontal component.
Context: This is for an object supposedly on a slider that can bounce back and forth. I have everything done but if it is hit the end the wrong way it simply floats off vertically, how can I simply make it ignore all forces in the vertical direction.
By putting the node's position back at the desired Y coordinate every frame after physics has been simulated:
-(void) didSimulatePhysics
{
CGPoint pos = horizontalMoveNode.position;
pos.y = fixedVerticalPosY;
horizontalMoveNode.position = pos;
}
Add this method to your scene class and apply it to whichever node(s) you want to lock in at a given Y coordinate.
You could use constraints for this purpose. I made a short sample with a node that only moves in a fixed X range and never leaves a specified Y position:
SKSpriteNode* node = [SKSpriteNode node];
node.color = [SKColor greenColor];
node.size = CGSizeMake(20, 20);
SKRange* rangeX = [[SKRange alloc] initWithLowerLimit: 100 upperLimit: 400];
SKRange* rangeY = [SKRange rangeWithConstantValue: 100];
SKConstraint* positionConstraint = [SKConstraint positionX: rangeX Y: rangeY];
NSArray* constraintArray = [NSArray arrayWithObject: positionConstraint];
node.constraints = constraintArray;
[self addChild: node];
This method is from SKAction class to move objects only on Horizontal or X-axis:-
[mySpriteNode runAction:[SKAction moveToX:260 duration:0.5]];
I hope this work's for you.

CCMoveTo doesn't get the updated player postition.....(iphone,cocos2d)

I'm adding an enemy like this:
-(void)addEnemy {
if ([spawnedEnemies count] < 25) {
CCSprite* sprite = [CCSprite spriteWithFile:#"white.png"];
float randomX = arc4random() % 480;
float randomY = arc4random() % 320;
sprite.position = ccp(randomX,randomY);
[spawnedEnemies addObject:sprite];
[self addChild:sprite];
[sprite runAction:[CCMoveTo actionWithDuration:5 position:player.position]];
} }
but if my player moves the sprite still moves to the last player position...
because of that i tried this in my tick:
-(void)tick:(ccTime)delta {
for (CCSprite *sp in spawnedEnemies) {
[sp stopAllActions];
[sp runAction:[CCMoveTo actionWithDuration:5
position:player.position]];
} }
but the enemies will only move if i stop moving (there just moving reallllllllyyyyyyyyy slow because every time i move it calls [sp stopAllActions]
What should I do now?
EDIT:*EDIT:*EDIT:*EDIT:*EDIT:*EDIT:*EDIT:*EDIT:*EDIT:
Now I'm doing this and the enemies are moving to the player even if the player is moving
but there's a problem: the nearer the enemy (is to the player) the slower they are moving...
How to solve this?
//function to apply a velocity to a position with delta
static CGPoint applyVelocity(CGPoint velocity, CGPoint position, float delta){
return CGPointMake(position.x + velocity.x * delta, position.y + velocity.y * delta);
}
-(void)tick:(ccTime)delta {
for (CCSprite *sp in spawnedEnemies) {
CGPoint m = ccpSub(player.position, sp.position);
sp.position = applyVelocity(m, sp.position, 1.0/60.0f);
}
}
I have four ideas:
Schedule a new update selector with a lower frequency and move the enemies in that method: [self schedule:#selector(moveEnemies) interval:0.1];
Only move the enemies when the player.position changes, maybe in your ccTouchesMoved-method.
Instead of using CCActions, set the position of the sprite directly, you need to do some vector-computations.
Use a physics-engine like Box2D (included in Cocos2D SDK). Then you can simply apply a force to each enemy to the direction of the player in each step.
EDIT:
In order to move the enemies with constant velocity, normalize the velocity vector m:
m = ccpMult(ccpNormalize(m), kSpeed);
kSpeed is a constant float value to adjust the speed.
Hope that helps...

Change Sprite Anchorpoint without moving it?

I am trying to change my Sprite anchor point so that I can rotate over a 0.0f,0.0f anchorpoint. At first my object is rotation at the default anchor point (0.5f,0.5f). However later on I need it to rotate over a 0.0,0.0 AnchorPoint.
The problem is I cannot change the anchor point and change the position accordingly, so it stays on the same position, without the object appearing to quickly move and reposition to its original point.
Is there a way I can set the anchor point and the position of my Sprite at once, without it moving at all?. Thank you.
-Oscar
I found a solution to this with a UIView elsewhere, and rewrote it for cocos2d:
- (void)setAnchorPoint:(CGPoint)anchorPoint forSprite:(CCSprite *)sprite
{
CGPoint newPoint = CGPointMake(sprite.contentSize.width * anchorPoint.x, sprite.contentSize.height * anchorPoint.y);
CGPoint oldPoint = CGPointMake(sprite.contentSize.width * sprite.anchorPoint.x, sprite.contentSize.height * sprite.anchorPoint.y);
newPoint = CGPointApplyAffineTransform(newPoint, [sprite nodeToWorldTransform]);
oldPoint = CGPointApplyAffineTransform(oldPoint, [sprite nodeToWorldTransform]);
CGPoint position = sprite.position;
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
sprite.position = position;
sprite.anchorPoint = anchorPoint;
}
This is a good question, and I don't know the full answer yet.
As you may have noticed, the anchorPoint cannot be changed without affecting scale and rotation.
For scaled sprites:
You have to simultaneously change the anchorPoint and position of your sprite. See this question for a hint
For rotated sprites:
Intuition says you would need to simultaneously change anchorPoint, rotation, and position. (I have no idea how to compute this.)
NOTE: I'm still learning graphics programming, so I'm not 100% able to compute this stuff yet.
I've needed this a couple of times and decided to make a extension for CCNode, tested it abit and seems to work fine. Can be really useful to some :)
It's tested with 1.x but It should work fine in 2.x too. Supports transformed nodes and HD.
Just add this to your project and import whenever you need it - It will be added to all classes deriving from CCNode. (CCSprite, CCLayer)
Interface
#import "cocos2d.h"
#interface CCNode (Extensions)
// Returns the parent coordinate for an anchorpoint. Useful for aligning nodes with different anchorpoints for instance
-(CGPoint)positionOfAnchorPoint:(CGPoint)anchor;
// As above but using anchorpoint in points rather than percentage
-(CGPoint)positionOfAnchorPointInPoints:(CGPoint)anchor;
//Sets the anchorpoint, to not move the node set lockPosition to `YES`. Setting it to `NO` is equal to setAnchorPoint, I thought this would be good for readability so you always know what you do when you move the anchorpoint
-(void)setAnchorPoint:(CGPoint)a lockPosition:(BOOL)lockPosition;
#end
Implementation
#import "CCNode+AnchorPos.h"
#implementation CCNode (Extensions)
-(CGPoint)positionOfAnchorPoint:(CGPoint)anchor
{
float x = anchor.x * self.contentSizeInPixels.width;
float y = anchor.y * self.contentSizeInPixels.height;
CGPoint pos = ccp(x,y);
pos = CGPointApplyAffineTransform(pos, [self nodeToParentTransform]);
return ccpMult(pos, 1/CC_CONTENT_SCALE_FACTOR());
}
-(CGPoint)positionOfAnchorPointInPoints:(CGPoint)anchor;
{
CGPoint anchorPointInPercent = ccp(anchor.x/self.contentSize.width, anchor.y/self.contentSize.height);
return [self positionOfAnchorPoint:anchorPointInPercent];
}
-(void)setAnchorPoint:(CGPoint)a lockPosition:(BOOL)lockPosition
{
CGPoint tempPos = [self positionOfAnchorPoint:a];
self.anchorPoint = a;
if(lockPosition)
{
self.position = tempPos;
}
}
#end
Cocos2d-x + Fixed scale
YourClass.h
virtual cocos2d::Vec2 positionFromSprite(cocos2d::Vec2 newAnchorPoint, cocos2d::Sprite *sprite);
YourClass.m
Vec2 YourClass::positionFromSprite(Vec2 newAnchorPoint, cocos2d::Sprite *sprite) {
Rect rect = sprite->getSpriteFrame()->getRect();
Vec2 oldAnchorPoint = sprite->getAnchorPoint();
float scaleX = sprite->getScaleX();
float scaleY = sprite->getScaleY();
Vec2 newPoint = Vec2(rect.size.width * newAnchorPoint.x * scaleX, rect.size.height * newAnchorPoint.y * scaleY);
Vec2 oldPoint = Vec2(rect.size.width * oldAnchorPoint.x * scaleX, rect.size.height * oldAnchorPoint.y * scaleY);
Vec2 position = sprite->getPosition();
position.x -= oldPoint.x;
position.x += newPoint.x;
position.y -= oldPoint.y;
position.y += newPoint.y;
return position;
}