I'm working on a new project and I am completely stuck with the main character movements.
The picture at this link explain my situation
http://www.chr.to/stuck.jpg
I have a world (black) a character (red) and an obstacle
The character and the world are children of SELF
The obstacle is a world's child
My goal is to move the character around the world but it must be in the same position all the time, just the world must turn below its feet.
To achieve this, I rotate the world in the opposite direction of the character direction and the effect is perfect (I get direction from a joystick)
-(void)update:(CFTimeInterval)currentTime {
rotation = rotation + input;
wordTest.zRotation = rotation; }
The biggest problem is when I found an object in the world.
The character and all obstacles are physics objects.
When the character hit an obstacle it drags off the world because the rotation continues and It can't keep its position in the middle of the screen .
Probably it's not the best approach, I have to find the way to stop the rotation...
or Do you have a better suggestion?
thank you guys
Set up a BOOL flag called canRotate, and use it to check whether rotation must be applied in the -update method.
-(void)update:(CFTimeInterval)currentTime
{
if (canRotate)
{
rotation = rotation + input;
wordTest.zRotation = rotation;
}
}
Set this flag as YES in the -init method and, when a contact between the character and obstacle is triggered in -didBeginContact, set the flag to NO.
Related
I am building an endless runner game where platforms appear at random intervals and then scroll right to left across the screen.
I have a sprite that jumps up vertically using
- (void)makeCharacterJump {
[self.spriteCaveman.physicsBody applyImpulse:CGVectorMake(0.0f, 80.0f)];
}
The problem is that the effect of gravity on the sprite means that it falls quite quickly and cant make the gap between the platforms.
What I would like to do is slightly slow down the effect of gravity on the falling sprite so it creates the impression of slightly floating down.
Any ideas?
If the character is the only node affected by gravity then you can change the scene’s gravity with:
self.physicsWorld.gravity = CGVectorMake(0, desiredGravity);
If it is not then you’ll have to play with the character’s physics body properties: friction, linearDamping or angularDamping values.
Hope that helps.
Does anyone have any idea how I can make my SKSpriteNode defy gravity? I thought of inverting the default gravity but realise I also need things to fall too!
It seems like it should be easy but reading through the documentation I can’t see how I would do it!
Thanks
Update: In iOS 8 / OS X Yosemite (10.10), physics fields provide an elegant solution to these sorts of problems. Here's a quick take on using them to add buoyancy (for a specific set of nodes) to your scene.
Create an SKFieldNode with the linearGravityFieldWithVector constructor, providing a vector that's the opposite of gravity, and add the field node to your scene.
Set the fieldBitMask on your balloons to something unique.
Set the categoryBitMask on the field to something that overlaps with the balloons' fieldBitMask, but that does not overlap with the fieldBitMask of any other bodies.
Now, the balloons will rise or hold steady, but other objects will fall. Tweaking the field's strength will let you tune whether the balloons' buoyancy is perfectly balancing gravity (so that they float in place, but are disturbed when touched), or slightly more or less than gravity (so that they slowly rise or fall).
By default, a field is infinite, covering the whole scene, but you can change that with the field's region property to limit it to a portion of the scene. (This is useful if you want to simulate buoyancy in water — once an object rises past the top of the field at the water's surface, it falls back in.)
Also, if you want variable buoyancy as per #TheisEgeberg's answer, you can control its variation over distance with the falloff property.
In iOS 7 / OS X Mavericks (10.9), or if you want more precise control over which forces apply where and when, you can use the approach from my original answer below.
If you want an object to really float like a balloon — that is, to be buoyant, affected by gravity but also counteracting it — you'll need to apply a force to it on every frame (i.e. in your update: method).
Beware scaling: gravity is a constant acceleration, but if you're applying a force to counteract gravity, a force is proportional to mass. To make a vector that perfectly balances gravity for use in applyForce:, you'll need to:
scale the gravity vector by {-1,-1,-1} to point in the opposite direction
scale by the mass of the body you're applying the force to (F = ma, where gravity or anti-gravity is a).
scale by 150 — there's a bug where the SKPhysicsWorld.gravity property isn't in the same units as applyForce:. (If you turn SKPhysicsWorld gravity off and use SKFieldNode gravity instead, you don't need to do this.)
Unlike turning off affectedByGravity and applying an action to make the balloon rise, this approach works well with the rest of the physics sim. With a balancing force exactly equal to gravity, the balloon will float in place — after colliding with other things it'll return to equilibrium. If the balancing force is greater than gravity, the balloon will rise, but its rise will be hindered by other bodies in its way.
First off, an SKSpriteNode isn't affected by gravity at all. It is the SKPhysicsBody that belongs to the node that is affected by gravity.
Second...
myNode.physicsBody.afectedByGravity = NO;
:D
If you want it to rise upwards then you can add an action to it...
SKAction *moveAction = [SKAction moveByX:0 y:-10 duration:1];
SKAction *repeatingAction = [SKAction repeatActionForever:moveAction];
[myNode runAction:repeatingAction];
As many have said it's about counterforce.
So yes, you can apply a counterforce to the balloon to make it go upwards.
But to make it look like a balloon you need to understand what makes a balloon go up: Air pressure. Since the helium or whatever light gas you are using is lighter than air it will start to go up, or in other words the heavier air will go under the balloon. It's like a piece of wood in water, the heavier water will go under the wood, till the wood is soaked and gets even heavier than the water.
So what you should do is to make the counterforce adapt to the height of the balloon, the higher it gets the less pressure you apply upwards. This is a way to simulate buoyancy.
Here’s my answer:
- (MyClass *)newRisingObject
{
MyClass *risingObject = [MyClass spriteNodeWithImageNamed:#"image"];
[risingObject setPosition:CGPointMake(CGRectGetMidX(self.frame), CGRectGetMinY(self.frame))];
[risingObject setName:#"name"];
[risingObject setUserInteractionEnabled:YES];
// Physics
//
[risingObject setPhysicsBody:[SKPhysicsBody bodyWithCircleOfRadius:risingObject.size.width / 2.0f]];
[risingObject.physicsBody setRestitution:1.0f];
[risingObject.physicsBody setFriction:0.0f];
[risingObject.physicsBody setLinearDamping:0.0f];
[risingObject.physicsBody setAngularDamping:0.0f];
[risingObject.physicsBody setMass:1.3e-6f];
return risingObject;
}
.
- (void)didMoveToView:(SKView *)view
{
/* Setup your scene here */
MyClass *risingObject = [self newRisingObject];
[self addChild:risingObject];
}
.
- (void)update:(CFTimeInterval)currentTime
{
/* Called before each frame is rendered */
[self enumerateChildNodesWithName:#"name"
usingBlock:^(SKNode *node, BOOL *stop)
{
// Push object up
//
[node.physicsBody applyImpulse:CGVectorMake(0.0f, node.physicsBody.mass * OBJECT_ACCELERATION)];
}];
}
I have my first game application in development. In this game, the only character that the user controls will get to jump from one block to another. It's like Mario (in Mario Brothers) jumping from one moving lift to another. If he fails, he'll die. So how could you tell a free fall from a short fall as a result of a successful jump? One thing I thought I could do is measuring character's vertical velocity. So I have the following lines of code. It's used with didSimulatePhysics
SKNode *player = [self childNodeWithName:#"//player"]; // It's the node characterizing the game character
CGVector v = player.physicsBody.velocity;
if (v.dy < -2000) {
[self endTheScene:kEndReasonLose]; // The character has died from free fall => game is over
}
When the game character jumps, the game application can record a vertical velocity of -2022.466797. So this measure won't work. What else can I do? Set an invisible bar and see if the game character has touched it with intersectsNode? That can also fail. I have never developed a game before. So I don't know how they do it, which kind of makes me realize how impressive Nintendo game developers are. Some 30 years later, I still can't do it.
Thank you for your advice.
Update
The following can tell whether or not the character has died from free fall, I think.
- (void)didSimulatePhysics {
if (self.isMoving) {
// isMoving is an instance variable (BOOL): YES if the game has started
CGVector v = player.physicsBody.velocity;
BOOL hasFallen = self.lastFallenDate ? [self.lastFallenDate timeIntervalSinceNow] < -2.0 : YES; // lastFallenDate is an instance variable recording the time when the character fell last time
if (hasFallen) {
if (v.dy < -1500) {
[self endTheScene:kEndReasonLose];
}
}
}
}
Yet, I think Apple has a bug to fix as far as SKAction is concerned. No matter I do, audio will kick in exactly about 5 seconds after the game started although the character is not falling.
- (void)endTheScene:(EndReason)endReason {
if (endReason == kEndReasonLose) {
SKAction *lossAudio = [SKAction playSoundFileNamed:#"failureAudio.caf" waitForCompletion:NO];
[self runAction:lossAudio];
}
}
Super Mario Bros was a tile-based game: the screen was divided up into a number of square regions each of which being represented by a value. Each tile would have a separate sprite blitted to that region of the screen depending on the tile id, and based on that tile's properties Shigeru et al could determine if mario could stand on it, if it would hurt him or if he was free to fly. There's no magic to this, you just have to check where he is!
If you aren't concerned with the damage your Mario will take (i.e can he land a jump from any height as long as it is onto solid ground) this is easy: If his y position is ever lower than the lowest solid platform in your world, he has died:
Ignore velocity; check y position.
If you don't want him to survive a jump from the top of a building, you need to look at his velocity: Is he travelling fast enough that the force of impact would end his life?:
Ignore y position; check velocity
How do you know he is on a platform? A physic node?
Honestly, you can put a 'floor' plane stretching your world and anchor it at the bottom of your scene to catch dying plumbers and it won't impact performance - infact it will be more efficient than running these checks in your Update method.
Edit to add
Don't bother with intersections, -(void)didBeginContact:(SKPhysicsContact*)contact will alert you to any collision that occurred as long as you are a delegate. Skip the update loop completely: this will trigger for any collisions and won't be missed
Code --> http://pastebin.com/W3DMYjXa
I am tinkering with SpriteKit and I cannot seem to get an emitter node to rotate with my spaceship the way I would like.
I watched the Apple Tech Talks Session where they described making a spaceship and using an emitter for the thrust exhaust, but they lock the ship to only face one direction.
In my experiment I am trying to allow the player sprite (spaceship) to travel in any direction, I have rotation and scrolling working for the player, but the particle emitter doesn't seem to rotate with the player.
my hierarchy looks something like this
Scene
-->World Node
-->Player Node
---->Player Sprite
---->Emitter Node
My theory is that If I rotate (Player Node) it should rotate both of its children, and it does rotate them, but the emitter continues to emit in the same direction.
I can change the emission angle manually, but it seems needlessly complicated.
here is what I am using to rotate
-(void)rotatePlayerToDirection:(TTDirection)direction {
CGFloat radDir;
CGFloat emiDir;
scrollDirection = direction;
switch (direction) {
case TTUp:
radDir = 0;
emiDir = 3.142;
break;
case TTRight:
radDir = 4.712;
emiDir = 1.571;
break;
case TTDown:
radDir = 3.142;
emiDir = 0;
break;
case TTLeft:
radDir = 1.571;
emiDir = 4.712;
break;
default:
break;
}
SKAction *rotatePlayer = [SKAction rotateToAngle:radDir duration:0.1 shortestUnitArc:YES];
[playerNode runAction:rotatePlayer];
}
Am I missing something here?
Here is a video of this in action
http://youtu.be/NGZdlB9-X_o
I'm fairly certain there is a bug in spritekit to do with the targetNode and rotation. The other answer seems to suggest this is expected behavior, but that ignores that the docs explicitly give this situation as a motivation for the existence of targetNode.
Working with Other Node Types
What you really want is for the particles to be spawned, but thereafter be
independent of the emitter node. When the emitter node is
rotated, new particles get the new orientation, and old
particles maintain their old orientation.
It then gives an example using targetEmitter on how to achieve this. However, the example code does not work.
It seems that setting targetNode breaks particle rotation.
UPDATE: I found a workaround.
Spritekit is failing to adjust the SKEmitterNode.emissionAngle property when SKEmitterNode.targetNode is set. You can work around this by setting it manually after actions have been processed.
Assuming that your SKEmitterNode has only one parent, you can do something like this in your scene.
- (void)didEvaluateActions
{
// A spaceship with a thrust emitter as child node.
SKEmitterNode *thrust = (SKEmitterNode *)[self.spaceship childNodeWithName:#"thrust"];
thrust.emissionAngle = self.spaceship.zRotation + STARTING_ANGLE;
}
Note that if there are multiple ancestor nodes which may be rotated, you will need to loop through them and add all of their zRotations together.
I know this is an old question, and the OP has probably found a solution or workaround, but I thought I would add by two pence worth, in case it helps anybody else.
I am just starting out with SpriteKit and Swift, and am developing a game where nodes fly around and collide with each other, changing direction of travel frequently.
I add an emitter to each node, and initially found that as the node rotated and changed direction, the emission 'trail' remained fixed. I then read that the emitter should have its target node set not as the node it is a child of, but the scene they both exist in.
That at least made the particle trail twist and turn realistically rather than just spurt out in one direction.
But the nodes can spin and changes direction, and I wanted the particles to trail away from the node in the opposite direction to its travel - just like a smoke trail from a plane or rocket.
As the node may rotate, setting the emitters angle of emission to the z-rotation of the node does not work. So...
I track all onscreen nodes in a dictionary, removing them from the scene as they travel off the screen.
So in didSimulatePhysics I use the same dictionary to get the node and calculate the angle of travel from the velocity vector, and then set the emitters emission angle accordingly :
import Darwin
// in your scene class...
override func didSimulatePhysics() {
removeOffScreenParticles()
for (key,particle) in particles {
let v = particle.physicsBody!.velocity
particle.trail!.emissionAngle = atan2(v.dy, v.dx) - CGFloat(M_PI)
}
}
I am aware that this calculation and adjustment is being performed for each on screen node, in every frame, so will probably use my frame counter to only do this check every n'th frame if performance becomes an issue.
I hope this helps someone!
You are setting the emitter's targetNode property to the background. Note that this forces the emitter's particles to be rendered by the background node, and they will be treated as if the background was their parent. So, the particles aren't changing the angle, because the rotation of the background does not change.
Remove [emitter setTargetNode:backgroundNode] and the emitter's emission angle should be rotating correctly along with the player.
If you're looking for a 'middle ground', where the angle rotates along with the player, while the already-rendered particles aren't 'stuck' to the emitter, try this:
First, do not set the targetNode on the emitter (it defaults to nil).
Second, just as you are about to run the rotation action, temporarily set the targetNode to another node, and reset it back to nil when it completes (so that the particles can rotate along):
emitter.targetNode = backgroundNode;
[playerNode runAction:rotatePlayer completion:^{
emitter.targetNode = nil;
}];
Note that the results are also 'middle ground' - the emitter 'puffs' as the target node changes (the previous particles are removed, and new ones are rendered by the new target node). Hope that helps.
Other than that, there's no really straightforward way of achieving this - setting the emission angle manually might be the simplest way, after all. Here's one way to do it, right before running the rotation (the emitter target node is still nil, also, there's no need to hardcode emission angles):
float originalAngle = emitter.emissionAngle;
emitter.emissionAngle = emitter.emissionAngle - radDir;
[playerNode runAction:rotatePlayer completion:^{
emitter.emissionAngle = originalAngle;
}];
I have working implementation of drag and drop feature written in Cocos2d + Box2d for iOS. I need to port it to Sprite Kit. The logic is pretty basic:
when user touching the screen, find sprite under the finger
create mouse joint between found sprite and the scene's physics body, set joint's target to position of touch
when touches moved, update joint's target to new position
when touches ended, remove the joint
Everything is working quite well. I like the way physics is simulated when touches ends - the dragged shape has velocity dependent on dragging speed and direction, so when I left my finger from the screen while moving the sprite, it will continue moving in the same direction, but now affected by gravity and damping.
Unfortunately, I'm not able to reproduce the same effect using Sprite Kit. There is no such joint like mouse joint, but I have tried using other joint types. I almost succeeded with SKPhysicsJointFixed - I'm creating new sprite and joint between it and dragged sprite, when touches moving I'm moving the newly created sprite. Unfortunately it doesn't work like mouse joint in Cocos2d + Box2d - while dragging the sprite, its velocity always equals zero. So, every time I left my finger from the screen, the dragged sprite stops immediately and start falling affected by gravity. No matter how fast I move the finger when dragging, after releasing dragged shape, it behaves exactly the same way.
My question is how to implement mouse joint in Sprite Kit, or how to implement drag and drop feature that works like described above?
Update:
This is a box2d mouse joint example that could give you more clear view on what I am trying to implement using Sprite Kit:
http://blog.allanbishop.com/wp-content/uploads/2010/09/Box2DJointTutorial1.swf
I'm pretty new to iOS development so I guess this might not be the best way, but this is how I solved it and it seems to work pretty smooth actually.
I'm using a UIPanGestureRecognizer to handle the touch event. I set this one up in my didMoveToView: with this code:
UIPanGestureRecognizer *gestureRecognizer = [[UIPanGestureRecognizer alloc] initWithTarget:self action:#selector(handlePan:)];
An UIPanGestureRecognizer is passed to handlePan: and I check with recognizer.state == UIGestureRecognizerStateEnded if the touched stopped. This is where I want to "release" the object and "let it fly away". For doing so I needed to calculate a few things, first of all I get the velocity of the finger/touch with this code:
CGPoint velocity = [recognizer velocityInView:recognizer.view];
(I use the same method (handlePan:) with other statements for getting the right object when touch starts, and letting the object be under the finger all the time by setting the position of the object to the location of the touch when moving)
Now I know the velocity, but I still don't know how much force I need to apply to the object. You should be able to calculate the force by this formula: Force = mass * acceleration. We know the mass (object.physicsBody.mass) and we know the velocity. To get the acceleration we need the time as well because acceleration = velocity / time.
In my update: method that is called every time a new frame is to be rendered I calculate the difference between the last time a frame was about to be rendered by doing something like this:
self.delta = currentTime - self.lastTime;
self.lastTime = currentTime;
I now can calculate which force that is needed to get the object moving in the velocity I'm "dragging it in". To do this I do the following:
[self.currentNode.physicsBody applyForce:CGVectorMake(velocity.x/self.delta * self.currentNode.physicsBody.mass, -velocity.y/self.delta * self.currentNode.physicsBody.mass)];
I take velocity divided with the difference in time since last frame (self.delta) to get the acceleration, and then multiply it with the mass of the object to get the force needed to keep (or actually getting) the object moving in the same direction and in the same velocity as it was moved by my finger.
This is working for me at the moment and I hope it helps other people too, and please tell me if I got something wrong or if there is a better solution. So far I have not found any "native solution".
If the only issue is velocity, then that is simple to fix.
Subtract the current touch position from the previous touch position to get the velocity between the last and current touch. Use applyForce: to apply this velocity to the body.
In order to prevent the body from moving on its own while dragging, read out the new velocity from the body after calling applyForce: and keep it in an ivar and set the body's velocity to zero.
When the touch ends, set the body's velocity to the one you keep as an ivar.
If this doesn't work, try using applyImpulse: and/or setting the velocity before calling any of the two methods, ie:
sprite.physicsBody.velocity = bodyVelocity;
[sprite.physicsBody applyForce:touchVelocity];
bodyVelocity = sprite.physicsBody.velocity;
sprite.physicsBody.velocity = CGVectorMake(0, 0);
That way you can keep the body's velocity without losing control over it while dragging.