I'm trying to move a sprite around a CCTMXMap in a smooth fashion. I've figured out how (using CCActions) to move from tile to tile, but I get gaps in my animation (it pauses for a frame while it reevaluates which direction to walk). I've tried moving the character in a scheduled update: method, but that gets messy when you try and restrict the sprite to only moving from tile to tile. Any suggestions on how to get the clean, consistent animation without messy manual animation using update?
Yes, don't use actions. You'll always have the 1-frame delay problem when using CCActions.
Moving the sprite in update is really pretty simple. Especially if you restrict movement to a speed (points per frame) that is clearly divisible by the tile size. For example if your tiles are 40x30, then horizontal speeds of 1,2,4,8,10 would work fine. Vertically 1,2,3,5,6,10 would work.
Update the position by this number, cast it down to int, compare it with the destination location:
if ((int)currentPos.x == (int)targetPos.x && (int)currentPos.y == (int)targetPos.y)
{
NSLog(#"I'm there!");
}
The reason for casting to int is to avoid rounding errors in floating point values.
Another solution would be - especially if your character can only move in one direction at a time - to figure out the number of frames it will take him to get there. If the character has to move 40 points to the right, and he moves at 4 points per frame, it'll take him 10 frames. Then just count the number of frames (how many times the update method ran) and if it reaches 10 (or 0 if you count down) then you know that the character has arrived without needing to check his position.
Related
Note: I have tried this answer: Gap between SKSpriteNodes in SpriteKit collision detection
I am getting gaps in between my SKSpriteNodes, after 5 minutes of letting my game run. Here is my code to make the node:
let tileNode = SKSpriteNode(imageNamed: "world1Tile\(tileNumber).png")
tileNode.position.x = x
tileNode.position.y = y
tileNode.size.width = 128
tileNode.size.height = 128
tileNode.zPosition = 10
tileNode.physicsBody = SKPhysicsBody(rectangleOf: tileNode.size)
tileNode.physicsBody!.isDynamic = false
tileNode.physicsBody!.restitution = 0
tileNode.physicsBody!.allowsRotation = false
tileNode.name = "Tile"
row.append(tileNode)
When I remove the physics body, it is running fine. Here is are some images to show you what I mean:
This image has a physics body, and was taken after immediately after running the app.
This image was taken 5 minutes after running the app.
Why is this happening? I assume it has something to do with the physics body, because my app looks exactly like the first picture, even an hour after running the app if there is no physics body. What physics body property should I change to stop this from happening? Any help would be appreciated.
I had a similar issue not too long ago, where gaps were appearing between nodes that were tiled (although I didn't use physics). Based on this answer, I found that if you want perfect alignment between nodes, it is best to ensure that the positions of nodes as well as the nodes' width and height are whole numbers.
I would suggest to round-off the x and y values of the position of tileNode and see if it will make any difference .
I'm guessing there is no gap. you probably have 'showPhysics' to true in your gameviewcontroller, and the line appears as a gap to me.
compare position with and without the pb to verify.
I had similar problem where gaps between sprites started appearing after around 5 minutes of scrolling with constant speed (game with infinite scroll). I did not use physics and I even had all positions, widths, heights rounded to integer value. I was scrolling the camera and adding new sprites one right after another and everything was working fine except after around 5 minutes of that infinite scrolling gaps begin to appear just as in your case. I spent some time in looking for a solution and it turned out that the problem was that when positions of my objects were becoming big that is in my case X position in the scene was around 150000 then those gaps started to appear and also I noticed that this problem occurred only on devices which had to scale the scene. I was using aspect fill with default scene size for iPhone 6 resolution and those gaps only appeared on iPhone 5 but on iPhone 6 I did not notice them. I was able to fix that issue by subtracting some constant value from X position of all objects (including camera position) from time to time so that everything on the scene relatively did not change position to the camera and look the same but actually absolute positions were changed to keep them low. I guess that larger position values like 150000 and scene scaling cause some floating point rounding issue in SpriteKit and that is why gaps are then becoming visible.
So based on my experience if you have similar gaps I recommend using integer values for all positions, widths, heights and additionally keep values of objects positions of all objects low.
For future reference in case someone is still searching for this, here are my experiences:
If tiles have PhysicsBodies, they are prone to making gaps. A solution for me was making a blank SKNode as a child of the tile, and assigning the PhysicsBody to that.
If possible, make sure bit masks are set in a way that tiles can't collide with each other.
As stated in a previous answer, make sure all measurements are integers and rounded in a way that doesn't leave a one unit gap between them.
A related problem is also SpriteKit's PhysicsBody drifting. There are some threads about this (e.g. https://forums.developer.apple.com/thread/27057 ), and it seems to be abug. In my case, the problem was a combination of PhysicsBodies causing random small gaps, and the drifting making some of them larger. The steps above removed the small gaps. Unfortunately the only workaround for the drifting problem in my case was to only generate PhysicsBodies for nodes that are within a certain distance from the player and destroying them after they are left behind.
For future reference for anyone who needs, I found an different answer specific to my problem. As JohnV said, I may need to round of values when setting the position, but when running the code, I found out that I also need to do that when running SKActions.
I have 20 different sprites. I essentially want a "flow" of them to be constantly falling from the top and disappearing when they reach the bottom. Essentially, imagine rain where each drop is a random sprite. I want each sprite to fall with a random rotation and each "drop" to be a random sprite from my selection of 20.
Can someone please point me in the right direction? I've never made a game before and this is my first time working with SpriteKit. I'm using Swift.
Make use of arc4random to get a random sprite from your selection of 20 sprites by adding those sprites to a group. arc4random will also be able to get a random rotation for you.
For them to fall down from the top to the bottom, you can apply -gravity to them or apply a -impulse.
To check whether they have reached the bottom, get the y value when they disappear off the bottom of the screen and use that as a comparision value and then use removeFromParent().
Alternatively the third method could be to use the movement of SKAction and simply adjust their Y coordinate. At the end of the completion block just use removeFromParent().
I have an object that is moving very fast (max velocity 900). When it reaches max speed it starts to create trailing objects or motion blur.
But I just want it to be the object moving fast. I am running on 60 fps.
I like the speed of the object but I don't like how its getting rendered (motion blur). How do I handle this?
This object bounces all around the screen with a restitution of 1.02, because I want to make it pick up speed as it keeps bouncing. I want to make it go faster thats why I did the 1.02 restitution.
The motion blur may simply be due to the LCD display having an "afterglow". So the position the object was in the previous frame is still a little brighter in the next frame because it takes some time for the crystals inside the LCD to return to the unlit state.
This causes "motion blur" on any moving object on the screen, and is of course more noticeable the faster the object moves. You may even be able to make out multiple versions of the same objects at different light levels trailing behind the object's position.
This effect may also depend somewhat on the device and model, and is often called 'ghosting'.
Regardless, there's nothing you can do about the "motion blur" caused by the LCD screen's afterglow effect. Here's a good article explaining the effects and their causes.
Hmm... you'll have trouble getting it to render smoothly.
At that speed (900 points per second) it will move 15 points every FRAME if running at 60 fps. That's a significant amount to move in such a short amount of time. In about 1/3 of a second it will travel entirely across the screen.
I'm guessing it will be getting to the limit of the ability of the hardware. Both the processor, the screen and your actual eyes. I imagine you'll also hit physics errors too with it possibly escaping through walls etc...
Can you show a video of how it is currently behaving?
Background
I'm making an iOS app for kids where you can use your finger to drag balls around on a screen.
I'm using Chipmunk 7.0.0 for the physics simulation.
I've adapted the Chipmunk demo code to implement the dragging functionality.
I'm using 2 ms fixed time step. (It's not like my app has anything better to do...)
Issue
I've recently added code to play a sound whenever the balls collide with each other or with a wall, which I'm doing inside a postSolve callback:
static void postSolve(cpArbiter *arb, cpSpace *space, cpDataPointer userData)
{
GameLayer *layer = (__bridge GameLayer *)userData;
[layer collisionHandler:arb];
}
-(void) collisionHandler:(cpArbiter*)arb
{
if(cpArbiterIsFirstContact(arb)) {
[[SimpleAudioEngine sharedEngine] playEffect:kCollisionEffectFilename];
}
}
Here's the problem... When I drag a ball into a wall, it generates a very large number of collisions, even when filtering on cpArbiterIsFirstContact, and it sounds terrible. It appears that the ball is bouncing off the wall, being driven back into wall by the constraint, rinse, and repeat. I'd like to play the collision sound only once or twice in this scenario.
Things I've tried that don't seem to work...
Filtering using cpArbiterTotalKE, cpArbiterTotalImpulse, or relative velocity: Impulse, kinetic energy, and relative velocity are all in the range of typical collisions.
Using a separate callback: The ball really is bouncing off the wall multiple times.
Reducing the step size: The physics engine actually takes longer to converge.
Rate limiting the sound effects: Better, but the ball still makes noise even after it looks like it's stationary.
Question
Is there a way to filter collisions for trapped bodies?
A simple and effective solution is to avoid playing the sound in quick succession.
When the ball contacts with the wall, check if the ball's "last contact" timer is lower than the current time. If so, play the sound and set the ball's "last contact" time to the current time plus x, where x is the timeout duration the sound shouldn't play again (ie 0.5 seconds).
In cocos2d-iphone you can add up an update method's deltaTime to keep track of time. There are also a number of ways to get system time, for example [NSDate date].timeIntervalSince1970 gives you the current number of seconds since 1970. I know, seems ridiculous, but if you get that number again sometime later, and subtract the previous number of seconds you get the difference in seconds, that's all that counts.
So you are using a constraint to drag the ball around, and the ball is elastic correct? Have you tried tweaking the constraint parameters at all? If the constraint has a lower maxForce, the chance for oscillations would drop significantly.
Another thing that can help is to increase the space's collision slop (how much shapes are allowed to overlap). The default value is 0.1 (in whatever scale you are using), but increasing it to a pixel or so can help this sort of thing significantly without being very visible.
I discovered a lot of new info since I posted this question so I have completely rewritten it:
I have run into some problems while implementing gravity and jumping in my tile-based game.
Sometimes when my character lands between tiles after a jump, the characters falls through them.
The problem certainly is not that my speed is too high since my max speed is the same as my tile-size. Also, the problem does not occur after every time the character jumps. I can often make a jump, get to max velocity, and still land well. But sometimes the character just falls through.
My level exists of layers of tiles. Each layer has it's own collision-type. So every tile in the same layer, follows the same rules for collision.
In my current set-up all layers use per-pixel collision.
This is how I update the player coordinates
http://pastebin.com/qVc6gv6T
This is the class where I calculate my collissions.
http://pastebin.com/7GqrFih6
In the update I just do:
controls.Update(player);
physicsEngine.HandleCollissions(levelManager, player);
I imagine that the problem could be because the player gets moved to another tile after collission. Collides with the other tile. But that doesn't count since the other tile already has been checked for collision?
Or could it be because I use foreach instead of for-loops?
You say the error occurs after jumping. So, your character falls toward a tile (has some positive velocity.Y) and hits it. The collision code executes the // the player comes FROM ABOVE and collides with the tile block. You run a loop that sets player velocity to zero and moves the player out of collision. This loops calls a function using a term tile as an argument. I assume you loop through a set of tiles in order to collide with more than a single square. Am I right?
If I am, then the problem occurs after that first collision and correction. Now your player has a velocity.Y of 0, but they are colliding with a new tile. So in spite of your comment about the meaning of your else block, you may have more position changes happening:
if (player.Velocity.Y > 0)
else // if (moveVector.Y < 0) // what you commented
else // if (moveVector.Y <= 0) // what your else really means
Now sometimes (only when you have slightly mis-alligned per-pixel collisions) you have course corrections happening the the wrong direction. That would produce a fall-through effect. You could try setting a break point in the // the player comes FROM UNDER and collides with the tile block, and then running the program in a scenario that shouldn't cause player.WorldPositionY += 1; to happen. If it happens when it wasn't supposed to, then you have your culprit.
I am doing a lot of speculation here. I think you should post more code so that we can know for sure. But perhaps a paste bin would be appropriate place for it.