Sprite kit moving sprite - ios

I have made this sprite that i can grab and move around.
My issue is that i want to be able to "throw" the sprite. Meaning, when i release the sprite i want it to continue in the direction that i moved it. Just like throwing a ball.
What should i do?
#implementation NPMyScene
{
SKSpriteNode *sprite;
}
-(id)initWithSize:(CGSize)size
{
if (self = [super initWithSize:size])
{
self.backgroundColor = [SKColor colorWithRed:0.15 green:0.15 blue:0.3 alpha:1.0];
sprite.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:sprite.size.width/2];
sprite.physicsBody.dynamic = YES;
self.scaleMode = SKSceneScaleModeAspectFit;
sprite = [SKSpriteNode spriteNodeWithImageNamed:#"GreenBall"];
sprite.position = CGPointMake(CGRectGetMidX(self.frame),
CGRectGetMidY(self.frame));
sprite.physicsBody.velocity = self.physicsBody.velocity;
sprite.physicsBody.affectedByGravity = false;
sprite.physicsBody.dynamic = true;
sprite.physicsBody.friction = 0;
[sprite.physicsBody isDynamic];
[sprite.physicsBody allowsRotation];
[self addChild:sprite];
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
[self touchesMoved:touches withEvent:event];
}
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
[sprite runAction:[SKAction moveTo:[[touches anyObject] locationInNode:self]duration:0.21]];
}

If you are talking about applying physics to the ball, then you need to think deeper about how Sprite Kit deals with physics.
When you specify the 'moveTo' action, you're not actually using Sprite kit's physics engine at all. You're simply specifying a point to animate that sprite to.
What you should be doing to achieve the effect you're looking for is attaching the sprite's position to your finger as it's moved around on the screen like so:
-(void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event
{
sprite.position = [[touches anyObject] locationInNode:self];
}
Then when the finger is lifted, you need to calculate the direction and speed in which your finger just moved and apply the appropriate amount of force to the sprite's physics body using the applyForce method:
[sprite.physicsBody applyForce:calculatedForce];
You will need to figure out how to calculate the force to be applied, but play around with the applyForce method and look at what information you are able to get back from the touchedMoved: event to help you apply that force. Hope that helps.

Related

How to add Highlighted Button in SpriteKit?

I know how to add Button in SpriteKit.
I have done with SKSpriteNode.
However i don't know how to add highlight button like UIButton.
I mean when i pressing SKSpriteNode(Button), i want to show highlighted image like UIButton from UIKit.
How can i do that?
Here is my codes that for Button with SKSpriteNode.
self.playButton = [SKSpriteNode spriteNodeWithImageNamed:#"playButton.png"];
self.playButton.position = CGPointMake(self.frame.origin.x + 400, 100);
self.playButton.name = #"playButton";
self.playButton.zPosition = 2;
[self addChild:self.playButton];
I Tried and got what you are exactly looking for,
BOOL isMySpriteNodeTouched = NO;
mySprite = [[SKSpriteNode alloc]initWithImageNamed:#"ball"];
mySprite.position = CGPointMake(self.size.width/2.0f, self.size.height/2.0f);
mySprite.name = #"mySprite";
[self addChild:mySprite];
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
for (UITouch *touch in touches)
{
CGPoint touchPoint = [touch locationInNode:self];
SKNode *localNode = [[SKNode alloc]init];
localNode = [self nodeAtPoint:touchPoint];
if ([localNode.name isEqualToString:mySprite.name])
{
mySprite.texture = [SKTexture textureWithImage:[UIImage imageNamed:#"highlightedBall"]];
isMySpriteNodeTouched = YES;
}
}
-(void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event
{
if(isMySpriteNodeTouched)
{
isMySpriteNodeTouched = !isMySpriteNodeTouched;
mysprite.texture = [SKTexture textureWithImage:[UIImage imageNamed:#"ball"]];
}
}
One solution is to create two SKSpriteNodes, one for the active state and one for the default state, and add both of them to your view.
In your touchesMoved and touchesEnded methods, hide and unhide the active and default buttons accordingly.
Here's a tutorial in Swift.
You can create a UIButton in SK using the view property in the method -(void)didMoveToView:(SKView *)view.
All the UIButton features, including highlight will be accessible.

Spritekit: move SKSpritekitNode in arc with SKPhysicsBody

I'm trying to move an SKSpriteNode in an arc with physics in spritekit like so:
But am unsure as to which physic I should apply to it (applyImpulse, applyForce, applyTorque).
Currently using applyTorque, the code does not actually work, and produces no movement on the object:
_boy.physicsBody.velocity = CGVectorMake(1, 1);
CGVector thrustVector = CGVectorMake(0,100);
[_boy.physicsBody applyTorque:(CGFloat)atan2(_boy.physicsBody.velocity.dy, _boy.physicsBody.velocity.dx)];
applyTorque is not the right method for this. Torque is a twisting force that causes your node to rotate around its center point.
There is no one easy command for what you are looking to do. You also fail to mention your method of movement for your node. Apply force, impulse, etc... You are going to have to come up with a hack for this one.
The sample project below does what you are looking for and it will point you in the right direction. You are going to have to modify the code to suit your specific project's needs though.
Tap/click the screen once to start moving the node and tap/click the screen a second time to start to 90 degree movement change.
#import "GameScene.h"
#implementation GameScene {
int touchCounter;
BOOL changeDirection;
SKSpriteNode *node0;
}
-(void)didMoveToView:(SKView *)view {
self.backgroundColor = [SKColor whiteColor];
node0 = [SKSpriteNode spriteNodeWithColor:[SKColor grayColor] size:CGSizeMake(50, 50)];
node0.position = CGPointMake(150, 200);
node0.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:node0.size];
node0.physicsBody.affectedByGravity = NO;
[self addChild:node0];
touchCounter = 0;
changeDirection = NO;
}
-(void)update:(CFTimeInterval)currentTime {
if(changeDirection)
[self changeMovement];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
touchCounter++;
for (UITouch *touch in touches) {
if(touchCounter == 1)
[node0.physicsBody applyImpulse:CGVectorMake(25, 0)];
if(touchCounter == 2)
changeDirection = YES;
}
}
-(void)changeMovement {
if(node0.physicsBody.velocity.dy<200) {
[node0.physicsBody applyImpulse:CGVectorMake(-0.1, 0.1)];
} else {
changeDirection = NO;
node0.physicsBody.velocity = CGVectorMake(0, node0.physicsBody.velocity.dy);
}
}

I'm having issues with touchesBegan updating a CGVector value

I have made a CGVector which makes an upward impulse (simulating a jump) of the player ball. If I run the game with what I currently have (below), it will start fine, and the jump will happen perfectly as soon as the game starts. I am trying to make the ball jump every time a touch is recorded but I'm having issues calling the right method / making it work at all.
This is my code:
#import "MyScene.h"
#interface MyScene ()
#property (nonatomic) SKSpriteNode *paddle;
#end
int jump = 0;
#implementation MyScene
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event{
NSLog(#"Touch Detected!");
// create the vector
// myVector = CGVectorMake(0, 15);
// [ball.physicsBody applyImpulse:myVector];
}
- (void)addBall:(CGSize)size {
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
// create a new sprite node from an image
// create a CGPoint for position
CGPoint myPoint = CGPointMake(size.width/2,0);
ball.position = myPoint;
// add a physics body
ball.physicsBody = [SKPhysicsBody bodyWithCircleOfRadius:ball.frame.size.width/2];
ball.physicsBody.restitution = 0;
// add the sprite node to the scene
[self addChild:ball];
// create the vector
CGVector myVector = CGVectorMake(0, 15);
// apply the vector
[ball.physicsBody applyImpulse:myVector];
}
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor whiteColor];
// add a physics body to the scene
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
// change gravity settings of the physics world
self.physicsWorld.gravity = CGVectorMake(0, -8);
[self addBall:size];
// [self addPlayer:size];
}
return self;
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
You can see I have commented out the myVector = CGVectorMake(0, 15); and the [ball.physicsBody applyImpulse:myVector]; in touchesBegan because It can't find ball and i'm not even sure it's right at all. Could anyone help me implement this jump using the impulse? Thanks!
You are on the right track, but multiple impulses of the vector (0,15), will cause the ball to go very high. You need to reset the ball's velocity to zero before applying any subsequent impulse.
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event
{
NSLog(#"Touch Detected!");
// create the vector
ball.physicsBody.velocity = CGVectorMake(0,0);
myVector = CGVectorMake(0, 15);
[ball.physicsBody applyImpulse:myVector];
}
Also, you need to declare the ball as a global variable like so:
#implementation MyScene
{
SKSpriteNode *ball;
}
And then in the addBall: method, instead of
SKSpriteNode *ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];
just write
ball = [SKSpriteNode spriteNodeWithImageNamed:#"ball"];

Can't move sprites connected by joints to the right location

I have a marionette connected by SKPhysicsJoints. I want it to behave so when I touch a certain point in the scene, the marionette's head moves to that point. Problem is, when I do it, the head moves TOWARDS that point, but does not reach it. If I keep clicking the same point, it will get a bit closer each time.
I have set affectedByGravity=NO on all the SKSpriteNodes that make up the marionette.
I am moving the sprite just by doing
head.position = (the position of the mouse)
and it works fine as long as the head isn't connected to the rest of the marionette by a joint.
So it's kind of like the rest of the sprites are weighing down the head. I wish I knew more about this, theres just so little info on Sprite Kit and not a lot handles this, from what I can find.
edit: Since I was asked, here is a sample project that demonstrates the problem I am having:
-(void) didMoveToView:(SKView *)view
{
self.physicsWorld.speed = 1;
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
_head = [SKSpriteNode spriteNodeWithImageNamed:#"head.png"];
_chest = [SKSpriteNode spriteNodeWithImageNamed:#"chest_neck.png"];
_head.position = CGPointMake(512, 380);
_chest.position = CGPointMake(512, 290);
_head.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_head.size];
_head.physicsBody.mass = 1;
_head.physicsBody.dynamic = YES;
_chest.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:_chest.size];
_chest.physicsBody.mass = 1;
_chest.physicsBody.dynamic = YES;
[self addChild:_head];
[self addChild:_chest];
CGPoint chestJointPinAnchor = CGPointMake(_chest.position.x, _chest.position.y+39);
SKPhysicsJointPin *chestJointPin = [SKPhysicsJointPin jointWithBodyA:_head.physicsBody bodyB:_chest.physicsBody anchor:chestJointPinAnchor];
[self.physicsWorld addJoint:chestJointPin];
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
_head.physicsBody.affectedByGravity = NO;
_chest.physicsBody.affectedByGravity = NO;
UITouch *touch = [touches anyObject];
CGPoint positionInScene = [touch locationInNode:self];
_head.position = positionInScene;
}
Just set your head to: _head.physicsBody.dynamic = NO, while the chest remains dynamic: _chest.physicsBody.dynamic = YES.
You'll find the head now moves exactly to the touch location, dragging the chest along.

SpriteKit: Collisions with 300 nodes, very low FPS

I want to make a simple SpriteKit app where I can add "rocks" and they fall to bottom of the screen. Just like this: http://aamukasa.fi/II-13-347. The implementation is quite easy but I will get huge performance issues when there are more than 100 nodes. The FPS goes under 10 when all the blocks collides together. Is there anyway to achieve this kind of functionality with good FPS and about 300-400 blocks?
#implementation MyScene
-(id)initWithSize:(CGSize)size {
if (self = [super initWithSize:size]) {
/* Setup your scene here */
self.backgroundColor = [SKColor colorWithWhite:1 alpha:1];
self.physicsBody = [SKPhysicsBody bodyWithEdgeLoopFromRect:self.frame];
self.scaleMode = SKSceneScaleModeAspectFit;
}
return self;
}
-(void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event {
/* Called when a touch begins */
for (UITouch *touch in touches) {
CGPoint location = [touch locationInNode:self];
SKSpriteNode *rock = [[SKSpriteNode alloc] initWithColor:[SKColor blackColor] size:CGSizeMake(20,10)];
rock.position = location;
rock.name = #"rock";
rock.physicsBody = [SKPhysicsBody bodyWithRectangleOfSize:rock.size];
[self addChild:rock];
}
}
-(void)update:(CFTimeInterval)currentTime {
/* Called before each frame is rendered */
}
#end
You might want to try to give your rocks a circular physicsbody. I don't think it will solve the issue but might increase framerate a bit! As far as I've understood, circular physicsbodies are better for performance than rectangular ones.
You should test on a device to see actual FPS. In the simulator I get 60 FPS until I reach 200 bricks.
Then it starts dropping but on the device you could get better results.
Not sure if you are still looking, but could try something like this:
-(void)didSimulatePhysics
{
[self enumerateChildNodesWithName:#"rock" usingBlock:^(SKNode *node, BOOL *stop) {
if (node.position.y < 0)
[node removeFromParent];
}];
}
Otherwise the nodes will stay in memory and FPS will worsen overtime.

Resources