1
votes

I'm writing a 2D game with SpriteKit physics engine in which discs slide on a board (similar to an air hockey game, from a top-down perspective). Most of the physics can be handled with the default properties of SpriteKit but there's one specific case that I think I have to code by myself.

There is a circular hole in the center of the board in which the discs might fall. The hole's size is only slightly larger than the discs' so if the center of a disc passes over the hole slowly enough, the disc should be "attracted" to the center (as it falls off balance into the hole). Also if the whole disc passes over the hole, it should bounce against the border of the hole since it falls entirely. I'm not yet completely sure how to write the physics for that but I think I can come up with something. My question is about where I should write the code for that and what information I can use.

The About SpriteKit page gives the different steps that are performed by SpriteKit on each frame. Using contact collisions with the center of the hole, I can detect when a disc is passing over the hole and so when I need to add forces or correct the movement of the disc. However, I don't know when exactly this contact detection happens in the SpriteKit cycle. It would be nice if it were after the physics simulation and before the rendering of the scene.

Is there a way to write some code to be executed after SpriteKit has computed the new positions of all physics bodies but before it is displayed, while having access to both the new and the old position for each body, similar to oldValue in a didSet property observer? If there is no easy way to get the new and old position of a body, I guess I'll have to store the old position myself as a property at each update but I'd rather avoid doing that if possible.

If there is no simple way to "correct" the physics simulation before it is rendered, where should I write the code that simulates the physics of discs interacting with the center hole?

1
Maybe something with a radial gravity field (really small radius, and not strong enough to pull in really fast moving pucks, simulating them going over it instead of into it)? That will limit it to be iOS 8+. You mention in the third paragraph about "after the physics simulation and before the rendering of the scene," you are looking for didSimulatePhysics, which is in your SKScene (just like update).Gliderman
Ok, I didn't know about SKField and I could patch a solution with that and some other hacking (not perfect but good enough for now). A little bit of testing answered one of my questions : didBeginContact takes place after update but before didSimulatePhysics in a frame cycle. I still don't know if there's a way to write code that would be executed right before or right after didSimulatePhysics that gives access to the old and the new position of the node so that some correction can be done before the change is actually updated on the scene...Zanapher
Before didSimulatePhysics is didEvaluateActions. After it is didApplyConstraints. I think it is this last one that you want as it is called after the SpriteKit game engine has moved everything but before it renders the scene.Steve Ives
I don't quite follow the setup of this hole, but what you could do is, once you've decided that the hole has 'captured' the disk, give the hole a physics body of a circular due loop. You should. Then be able to have the disk bounce around inside the edge of the hole. If you also have the disk decrease in size, you might be able to come up with a reasonable simulation of the disk falling into the hole and falling down, away from the player.Steve Ives
@Steve Ives Thanks for the answer. I solved the problem (in a way that was satisfactory to me) some time ago doing mostly what you said. I simply tested in the disc's update method whether it was close enough to fall in the hole and if so, captured it in the hole by adding collisions with the hole's circle border (there is no need for special animation when the disc falls because the hole is not supposed to be deep, it merely captures the disc, but the disc doesn't disappear deep inside).Zanapher

1 Answers

0
votes

There shouldn't be a "last position" or anything like that.

You could pull the disc.physicsBody?.velocity, and compute where it will go from there though. You can do this either in update or in didSimulatePhysics.

Because you have a center hole, you may be able to set a small circle the radius of the hole to have a physics body, you may be able to set the hole.physicsBody?.collisionBitMask to 0x0 (0, or no collisions) and have the hole.physicsBody?.contactBitMask = 0x1 (default is 0x0 for all nodes), along with the disc.physicsBody?.contactBitMask = 0x1. This should allow you to detect the contact (not collision, as there won't be any), and check if disc.physicsBody?.velocity is less than a certain value.