I have a few images that can detect when they get touched right now I am using
plane.physicsBody = SKPhysicsBody(circleOfRadius: plane.frame.height / 2)
but I need it so it is the boarder of the image not a circle or rectangle.
We have a plane and you want a physical body.
Your first approach is circleOfRadius (the physical representation is a circle with a given radius). You can use another shape, such a rectangle:
plane.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(100, height: 100))
As explained in the comments you can also use the shape of a sprite (texture) to create the shape of the physics body:
plane.physicsBody = SKPhysicsBody(texure: plane.texture!, size: plane.size)
But a good way to choose it's this: if you want your game to run smootly on many different devices, don't make sure the physics calculations become too complicated. In other words if you have many object around your plane with physical behavior moving around and colliding with each other, you may choose to use simplified shapes to deal with the physics more efficiently, especially if a shape already looks a lot like a rectangle or a circle.
Related
I have a SKSpriteNode which has a PNG texture, partly transparent.
I want to make a logical split line on this sprite and then measure the volume or opaque pixel count in each part. The object of the exercise is to tell what percentage total sprite volume exists in each part.
I have done some research and came empty. Seems there are no built in methods to do so. Can you guide me in the right direction here?
you could create a "pixel perfect" SKPhysicsBody like this:
let sprite = SKSpriteNode(imageNamed: "Spaceship")
sprite.physicsBody = SKPhysicsBody(texture: sprite.texture!,
size: sprite.texture!.size())
And then access the property sprite.physicsBody.area which gives you the area of the body in square meters (you can convert it to points, here is the documentation)
Using SpriteKit for an iOS 9.0 app/game (and no physics body).
I have a few SpriteNodes on a scene. SpriteNodes are with oddly shaped images and runs through multiple frames for animation.
What is the best way to detect touches on a SpriteNode's image content, but not on transparent area of the entire image rectangle.
I see a lot of posts on using SKCropNode / MaskImage. In my scenario, I have multiple images/frames for each SpriteNode for the animation.
Please advice on an approach or point me in the right direction. Thanks.
Based on additional info from comments above:
With only 16 shapes, from the 16 frames, one of the more efficient ways would be to draw primitive shapes that roughly approximate the outlines of your character at each frame, and convert these to CGPaths. These can then be swapped in and out for each frame or (better) plucked out appropriately when there's a touch for testing based on the frame currently being shown at time of touch.
CGPaths are very lightweight structs, so there shouldn't be any performance problems with either approach.
CGPathContainsPoint is the old name of this test, which is now a modernised API for Swift 3 and onwards:
Troubles using CGPathContainsPoint SWIFT
There is an app called PaintCode, that's about $100 USD, which translates vectors into CGPaths, amongst other things, so you could use this to import your shapes. You can copy/paste vectors into it, which I suggest, because you might want to draw in a friendly drawing program. PaintCode doesn't have the world's best drawing experience.
Additionally, here's a free, online tool for doing the creation of a polygon path from textures. Probably more painful than using a drawing app and PaintCode, but it's FREE!
Alternative: Physics Bodies from Texture, and Contact with Touch
You can automagically create physics body shapes from a texture, like so, from the docs on this page:
let texturedSpaceShip = SKSpriteNode(texture: spaceShipTexture)
texturedSpaceShip.physicsBody = SKPhysicsBody(texture: spaceShipTexture,
size: CGSize(width: circularSpaceShip.size.width,
height: circularSpaceShip.size.height))
There have been reports of problems with determining if a point is within a given physics body, so it might be better to create a small physics object, place it where the touch is, and then determine if its in contact with the physics body shape appropriate for the current frame of the current game entity.
Caveat:
There's no telling (nor way to control, that I know of) how complex the resultant CGPaths are for these automagically created physics shapes.
I personally would just check the pixel of the current texture to see if is transparent or not. You can do this to get the pixel data:
(Note this code is hypothetical to get you started, I will try to work out a real example later on for you if you can't figure it out.)
(Also note, if you scale sprites, you need to handle that with converting touch location to texture)
let image = sprite.texture.cgImage()
if let dataProvider = image.dataProvider, let data = dataProvider.data
{
let screenScale = UIScreen.main.scale //let's handle retina graphics (may need work)
//touchedLocation is the location on the sprite, if the anchor was bottom left (it may be top left, will have to verify)
//So if I touch the bottom left corner of the sprite, I should get (0,0)
let x = touchedLocation.x * screenScale
let y = touchedLocation.y * screenScale
let width = sprite.texture.size().width * screenScale
let bpp = 4 // 4 bytes per pixel
let alphaChannel = 3 //Alpha channel is usually the highest byte
let index = bpp * (y * width + x) + alphaChannel
let byte = data.withUnsafeBytes({[UInt8](UnsafeBufferPointer(start:$0 + index,count:1))})
print(Alpha: \(byte))
}
else
{
//we have no data
}
I am learning Swift, and as a project I am working on a tile based 2D game similar to super mario where my character will walk and jump on tiles.
The latest version of Xcode and Sprite Kit give the ability to create a Tile Map directly in Xcode.
In the presentation of the new Xcode and Sprite kit, the guy demonstrates a game similar to what i am working on.
https://developer.apple.com/videos/play/wwdc2016/610/ (around the 20th minute).
He mentions giving Tiles user data properties which i did, and in code we search through all the tiles which have that user data and give them some physics properties so that the character can collide or interact with them (in my case, my character not falling or walking through the tiles).
so basically, the idea is giving those tiles a physics Body, but this can't be done using SKphysicsBody. So there must be another way, and since i am new to Swift i am missing it.
if anyone knows this, i would very much appreciate the help.
If the question is unclear let me know because i am also new to stack overflow.
Apple staff member Bobjt says here that "the right approach" is to add user data to your SKTileDefinition objects and use that to identify and add physics bodies to your tiles.
So you would add a user data value to a tile definition either programmatically or in the editor, like so:
Then in code you would check each tile definition to see if it has the user data value. If so, then you need to calculate the tile's position and add a physics body on a new node, parenting it to your tile map. Here is the code for this which Bobjt referred to as "the correct approach":
self.tileMap = self.childNode(withName: "Tile Map") as? SKTileMapNode
guard let tileMap = self.tileMap else { fatalError("Missing tile map for the level") }
let tileSize = tileMap.tileSize
let halfWidth = CGFloat(tileMap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tileMap.numberOfRows) / 2.0 * tileSize.height
for col in 0..<tileMap.numberOfColumns {
for row in 0..<tileMap.numberOfRows {
let tileDefinition = tileMap.tileDefinition(atColumn: col, row: row)
let isEdgeTile = tileDefinition?.userData?["edgeTile"] as? Bool
if (isEdgeTile ?? false) {
let x = CGFloat(col) * tileSize.width - halfWidth
let y = CGFloat(row) * tileSize.height - halfHeight
let rect = CGRect(x: 0, y: 0, width: tileSize.width, height: tileSize.height)
let tileNode = SKShapeNode(rect: rect)
tileNode.position = CGPoint(x: x, y: y)
tileNode.physicsBody = SKPhysicsBody.init(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.isDynamic = false
tileNode.physicsBody?.collisionBitMask = playerCollisionMask | wallCollisionMask
tileNode.physicsBody?.categoryBitMask = wallCollisionMask
tileMap.addChild(tileNode)
}
}
}
Personally, I think this approach is too fussy. I'm going to try a different approach in my game and if it works I'll post it here. What I'd really like is for Apple to enhance the tile map API so that we can add physics bodies directly to individual tiles. Maybe in the process they could optimize the engine so that physics bodies on individual tiles would be automatically merged to form larger, more optimal shapes in order to improve system performance.
Update: I filed a request with Apple about this issue, for whatever good might come of it.
I'm not sure there's a surefire way to do this yet... but here's two ways to think about how to try apply physics to a tilemap.
Option 1: Apply SKNodes to each positions of each tile on your map, and apply an appropriate physicsbody to that SKNode based on the content and state of that tile.
Option 2: Use the position information of each tile to add an array of physicsbodies to the SKTileMapNode, and position each of them accordingly.
I'm imagining a gravity down, Mario style platformer, with this kind of terrain in need of physics bodies for the ground:
Lifted transcript from Apple's WWDC about tiles and physics:
And you'll note that I'm colliding with the tiles here.
And I achieve this by leveraging custom user data that we can put on
each of our tiles.
Here, I'll show you in our tile set.
Select one of the variants here.
And you can see that we have some user data over here.
And I just have a value called edgeTile which is a Boolean, and I set
to 1.
So, in code, I'm going through the tile map in our platform demo here,
and I'm looking for all of these edge tiles.
And whenever I find one, I create some physics data to allow the
player to collide with it.
And since it is just in our tile map here, say I wanted to get over
this large wall here.
When I run the game, you'll see that my guy can't actually jump high
enough to get over it.
And he really wants to because that red button over there looks really
tempting.
I really want to push that thing.
So, since we're just generating our physics data from our tiles and
our user data, all we can do is just going here and erase these tiles
and build and run our game again.
The tiles are now gone and we can now move through there.
And we didn't have to change code or anything.
We just used the data that we pulled from the tile map to set up our
tile.
It's that simple.
Makes it sound very simple, but takes a far greater mind than mine to figure out what is being done, and how it's being done.
Alternatively, you can apply a line sweep algorithm to the tiles you want to give a SKPhysicsBody.
You can do this in four steps;
Iterate through the position of the tiles in your SKTileMap.
Find the tiles that are adjacent to one another.
For each group of adjacent tiles, collect:
a down-left corner coordinate and
an up-right corner coordinate.
Draw a square, and move on to the next group of tiles until you run out of tile coordinates.
Screenshot without visuals for comparison
Screenshot without visuals showing the physicsbodies
See my answer in a similar post on how to implement this.
I spent two days trying different ideas but could find nothing better than what I posted in my other answer. As mentioned there, it is the way recommended by an Apple staff member and as far as I know it's the most efficient way to have SpriteKit automatically add physics bodies to all your tiles. I've tested it and it works. (Although I'm still holding my breath for Apple to add a straightfoward way of putting physics bodies on tiles).
But there are other considerations. If you are having performance issues because there are too many physics bodies in your scene, you might want to try one of these other approaches. However, they are both more time-consuming than the approach described above. The only reason that may justify using one of these more labor-intensive approaches is if you need to reduce the number of physics bodies in your scene due to performance issues. Otherwise, I think the "automatic" approach mentioned above is the best option we have.
So I won't go into detail here because I think the automatic option is the best. These are just ideas for alternate approaches if your game needs to be extra stingy with system resources.
Alternate Approach #1 - Use Fewer Physics Bodies
Create your tile map in the editor. Then keep working in the editor to drag Color Sprites (SKSpriteNodes) over parts of your map that need a physics body. Shape the nodes to make the largest rectangle possible for areas that need physics bodies. This works best for for large, flat surfaces like walls, floors, ceilings, platforms, crates, etc. It's tedious but you end up with far fewer physics bodies in your simulation than if you had used the automatic approach mentioned above.
Alternate Approach #2 - Use No Physics Bodies
This idea would probably require even more work, but you could potentially avoid using physics bodies altogether. First, create your tile map in the editor. Analyze your map to identify which tiles mark a barrier, beyond which the player should not cross. Assign a user data identifier to that type of tile. You would need different categories of identifiers for different types of barriers, and you may also need to design your artwork to fit this approach.
Once your barrier tiles are sufficiently identified, write code which checks the user data value for the tile currently occupied by the player sprite and restrict the sprite's movement accordingly. For example, if the player enters a title that marks an upper boundary, your movement code would not allow the player sprite to move up. Likewise, if the player enters a tile that marks the leftmost boundary, your movement code will not let the player travel left.
I am trying to make a tube and its physics body like so in SceneKit.
let BoxGeometry = SCNTube(innerRadius: 5, outerRadius: 12.5, height: 4)
Box = SCNNode(geometry: BoxGeometry)
Box.pivot = SCNMatrix4MakeRotation(Float(M_PI_2/8), 0, 1, 0)
Box.physicsBody = SCNPhysicsBody(type: SCNPhysicsBodyType.Static, shape: nil)
Box.physicsBody?.mass = 5
Box.categoryBitMask = colorCategory
scene.rootNode.addChildNode(Box)
However, when another object falls onto this object it does not pass through the center. Instead it sits appearing to be levitating in air. It is acting like the physics body is a complete cylinder, not like the tube with the hole in the middle. How can I fix this so that objects can pass through the center? The tubes appearance looks as expected.
Thanks!
Dynamic bodies in SceneKit must be convex. (If you look into the general theory behind collision detection in games, i.e. not just SceneKit, you'll find that there are massive differences in speed and efficiency between collision detection on convex versus concave shapes.) A tube is a concave shape — it has a hole in it.
Luckily, your tube is being used as a static body. For static bodies only, there's an option to make the physics shape (an approximation of) a concave geometry:
let shape = SCNPhysicsShape(geometry: tube,
options: [SCNPhysicsShapeTypeKey: SCNPhysicsShapeTypeConcavePolyhedron])
box.physicsBody = SCNPhysicsBody(type: .Static, shape: shape)
There is a performance cost to this. If you find your framerate limited by CPU usage after this change, or if you want to have a dynamic body be (effectively) concave, you might get better performance by making a physics shape that's a compound of several other shapes — e.g. build a dummy node hierarchy (not one that's actually in your scene) containing a bunch of cylinders or boxes arranged into a ring, then make a physics shape from them with SCNPhycsicsShape(node:options:).
Change the shape of your physicsBody to match the geometry of your tube.
I have this situation: http://mokainteractive.com/example.png
I'd like to move the white ball inside the red track and detect wherever the balls touch the limit of the red track.
Which is the best solution? I have to create multiple transparent shape along the borders? Do you have other ideas?
thanks so much
In iOS8 you can create a single physics body for that kind of shape.
Make a texture of your shape with p.e. Adobe Illustrator and use this method:
init(texture texture: SKTexture!,alphaThreshold alphaThreshold: CFloat,size size: CGSize) -> SKPhysicsBody
The SKTexture is your shaped image. The body is defined by the colored pixels.
The alphaThresHold: The minimum alpha value for texels that should be part of the new physics body.
The Size is clear I think.
The texture is analyzed and all invisible pixels around the egg are ignored and only the color pixels are interpreted as the body of the SKPhysicsNode. You should use too many of these because they are very expensive to calculate for the Physics Engine.
Variations of this method are found in the SpriteKit Class Reference.
To your problem. Make an inverse texture of your area which should be transparent and pass it as texture to the physics body. It will be analyzed and a body around the free zone is created.
You cannot create a single physics body for that kind of shape.
Using bodyWithPolygonFromPath: will only allow you to create a convex polygonal path which obviously does not work for your shape.
I think you have 3 options here:
Use a number of bodyWithPolygonFromPath: bodies (probably the hardest to do and time consuming).
Use a number of various size bodyWithRectangleOfSize: bodies (not so hard but time consuming).
Use only straight lines in your image and use bodyWithRectangleOfSize: (the easiest and fastest). If you choose this option remember you are still free to rotate your straight lines to various angles.