We have two custom subclasses of SKSpriteNode.
One is called circleNode and the other triangleNode. There are three sub types to the circle option, which are created with a custom init method passing in an enum.
For example:
enum CircleType: UInt {
case Circle1 = 1
case Circle2 = 2
case Circle3 = 3
}
class TapCircleIcon : SKSpriteNode
{
convenience init(typeOfCircle: CircleType) {
Within the Game Scene
Within the game scene we control spawning the sprite nodes. There are 4 different options in total, circle1, circle2, circle3 and triangle.
Question
What is the best way in swift, to randomly select one of these sprites to spawn? Depending on the result we could initialise the correct sprite such as TapCircleIcon(typeOfCircle: CircleType.Circle2)
To randomly select something arc4random and its friends are nice. My take for the CircleType:
let ct = CircleType(rawValue: UInt(arc4random_uniform(3) + 1))
As described in http://nshipster.com/random/ arc4random_uniform(N) delivers a random int between 0 and N-1.
Related
In Blender, you can see and access each face of a 3D model like this one: https://poly.google.com/view/6mRHqTCZHxw
Is it possible in SceneKit to do the same, that is access each face of the model?
This question is similar and implies it is impossible, but does not confirm if SceneKit lets you programmatically access all faces of a model. (It focuses on identifying the face touched.)
Two questions:
1) Can you programmatically access each face?
2) Can you filter and only access faces that are visible (i.e., ignore faces that "inside" or occluded by other faces)?
An implementation of #Xartec's answer for your first question #1, based also on the Apple documentation, in Swift 5.3:
extension SCNGeometryElement {
var faces: [[Int]] {
func arrayFromData<Integer: BinaryInteger>(_ type: Integer.Type, startIndex: Int = 0, size: Int) -> [Int] {
assert(self.bytesPerIndex == MemoryLayout<Integer>.size)
return [Integer](unsafeUninitializedCapacity: size) { arrayBuffer, capacity in
self.data.copyBytes(to: arrayBuffer, from: startIndex..<startIndex + size * MemoryLayout<Integer>.size)
capacity = size
}
.map { Int($0) }
}
func integersFromData(startIndex: Int = 0, size: Int = self.primitiveCount) -> [Int] {
switch self.bytesPerIndex {
case 1:
return arrayFromData(UInt8.self, startIndex: startIndex, size: size)
case 2:
return arrayFromData(UInt16.self, startIndex: startIndex, size: size)
case 4:
return arrayFromData(UInt32.self, startIndex: startIndex, size: size)
case 8:
return arrayFromData(UInt64.self, startIndex: startIndex, size: size)
default:
return []
}
}
func vertices(primitiveSize: Int) -> [[Int]] {
integersFromData(size: self.primitiveCount * primitiveSize)
.chunked(into: primitiveSize)
}
switch self.primitiveType {
case .point:
return vertices(primitiveSize: 1)
case .line:
return vertices(primitiveSize: 2)
case .triangles:
return vertices(primitiveSize: 3)
case .triangleStrip:
let vertices = integersFromData(size: self.primitiveCount + 2)
return (0..<vertices.count - 2).map { index in
Array(vertices[(index..<(index + 3))])
}
case .polygon:
let polygonSizes = integersFromData()
let allPolygonsVertices = integersFromData(startIndex: polygonSizes.count * self.bytesPerIndex, size: polygonSizes.reduce(into: 0, +=))
var current = 0
return polygonSizes.map { count in
defer {
current += count
}
return Array(allPolygonsVertices[current..<current + count])
}
#unknown default:
return []
}
}
}
The resulting arrays is an array of faces, each faces containing a list of vertex index.
An answering for how to extract the vertices from SCNGeometrySource can be found there https://stackoverflow.com/a/66748865/3605958, and can be updated to get colors instead.
You will need this extension that implements the chunked(into:) method used above:
extension Collection {
func chunked(into size: Index.Stride) -> [[Element]] where Index: Strideable {
precondition(size > 0, "Chunk size should be atleast 1")
return stride(from: self.startIndex, to: self.endIndex, by: size).map {
Array(self[$0..<Swift.min($0.advanced(by: size), self.endIndex)])
}
}
}
For #2, I don't believe there's a way.
You can, but there is no convenient way built into SceneKit that lets you do it so you would have to built that yourself.
Yes, if you define what a face is and map that to the vertices in the model. For example, you could read the SCNGeometry’s SCNGeometrySources into your own arrays of face objects, in the same order. Using the faceIndex you can than get the index to your array of faces. To update them, you would have to construct a SCNGeometry based on SCNGeometrySources programmatically, based on your own data from the faces array.
Note, the faceIndex returns the triangle rendered and not the quad/polygon so you have to convert it (very doable if all quads).
I’m working on a SceneKit based app that is basically a mini Blender for ipad pros. It uses a halfedge datastructure with objects for vertices and edges and faces. This allows access to those elements but in reality it allows access to the half edge data structure mapped to the model, which forms the basis for the geometry that replaces the one rendered.
Not directly. If you have the geometry mapped to a data model it is of course possible to calculate it before rendering but unfortunately Scenekit doesn’t provide a convenient way to know which faces weren’t rendered.
That all said, a face is merely a collection of vertices and indices, which are stored in the SCNGeometrySources. It may be easier to provide a better answer if you add why you want to add the faces and what you want to do with its vertices.
EDIT: based on your comment "if they tap on face, for instance, the face should turn blue."
As I mentioned above, a face is merely a collection of vertices and indices, a face itself does not have a color, it is the vertices that can have a color. A SCNNode has a SCNGeometry that has several SCNGeometrySources that hold the information about the vertices and how they are used to render faces. So what you want to do is go from faceIndex to corresponding vertex indices in the SCNGeometrySource. You then need to read the latter into an array of vectors, update them as desired, and then create a SCNGeometrySource based on your own array of vectors.
As I mentioned the faceIndex merely provides an index of what was rendered an not necessarily what you fed it (the SCNGeometrySource) so this requires mapping the model to a data structure.
If your model would consists of all triangles and has unique verts opposed to shared, does not interleave the vertex data, then faceIndex 0 would correspond to vertex 0, 1, and 2, and faceIndex 1 would correspond to vertex 3, 4, and 5 in the SCNGeometrySource. In case of quads and other polygons and interleaved vertex data it becomes significantly more complicated.
In short, there is no direct access to face entities in SceneKit but it is possible to modify the SCNGeometrySources (with vertex positions, colors, normals uv coords) programmatically.
EDIT 2: based on further comments:
The primitiveType tells Scenekit how the model is constructed, it does not actually convert it. So it would still require the model to be triangulated already. But then if all triangles, AND if the model uses unique vertices (opposed to sharing verts with adjacent faces, model io provides a function to split vertices to unique from shared if necessary) AND if all the verts in the SCNGeometrySource are actually rendered (which is usually the case if the model is properly constructed), then yes. It is possible to do the same with polygons, see https://developer.apple.com/documentation/scenekit/scngeometryprimitivetype/scngeometryprimitivetypepolygon
Polygon 5, 3, 4, 3 would correspond to face index 0, 1, 2, 3 only if they were all triangles which they are obviously not. Based on the number of vertices per polygon however you can determine how many triangles will be rendered for the polygon. Based on that it is possible to get the index of the corresponding verts.
For example, the first polygon corresponds to face index 0, 1 and 2 (takes 3 triangles to create that polygon with 5 verts), the second polygon is face index 3, the third polygon is faceIndex 4 and 5.
In practice that means looping through the polygons in the element and adding to a faceCounter var (increment with 1 for each vert more than 2) till you reached the same value as faceIndex. Though on my own datastructure, I actually do this same basic conversion myself and works quite well.
EDIT3: in practical steps:
Convert the SCNGeometryElement to an array of ints.
Convert the SCNGeometrySource with the color semantic to an array of vectors. It is possible there is no SCNGeometrySource with the color semantic in which case you will have to create it.
If the polygon primitive is used, loop through the first portion (up to the number of primitives, in this case polygons) of the array you created from the SCNGeometryElement and keep a counter to which you add 1 for every vert more than 2. So if the polygon has 3 verts, increment the counter with 1, if the polygon has 4 verts, increment with 2. Everytime you increment the counter, thus for every polygon, check if faceIndex has been reached. Once you get to the polygon that contains the tapped face, you can get the corresponding vertex indices from the second part of the SCNGeometryElement using the mapping depicted in the image above. If you add a second variable and increment that with the vertex count of each polygon while looping through them you already know the indices of the vertex indices stored in the element.
If all the polygons are quads the conversion is easier and faceindex 0 and 1 correspond to polygon 0, face index 2 and 3 to polygon 1.
Once you got the vertex indices from the SCNGeometryElement, you can modify the vertices at those indices in the array you created from and for the SCNGeometrySource. Then recreate and update the SCNGeometrySource of the SCNGeometry.
Last but not least, unless you use a custom shader, the vertex colors you provide through the SCNGeometrySource will only show up correctly if the material assigned has a white color as diffuse (so you may have to make the base texture white too).
I have tried and tried to get collision detection to work the way I need but to no avail. What I want to accomplish is to get notified when the character touches an object but not stop it from moving on to the object. In GameScene, I have the physics of the object set like this:
Category Mask: 8
Collision Mask: 0
Field Mask: 0
Contact Mask: 0
I add the character to the scene in my code. I have a category structure like this:
struct PhysicsCategory {
static let None: UInt32 = 0
static let Player: UInt32 = 0b1 // 1
static let Pillar:UInt32 = 0b10// 2
static let Chest:UInt32 = 0b100// 4
static let Ladder:UInt32 = 0b1000
}
And the character physics is like this:
playerWalk.physicsBody?.categoryBitMask=PhysicsCategory.Player
playerWalk.physicsBody?.collisionBitMask=PhysicsCategory.Pillar
playerWalk.physicsBody?.contactTestBitMask=PhysicsCategory.Chest | PhysicsCategory.Ladder
playerWalk.physicsBody = SKPhysicsBody(rectangleOf:
playerWalk.frame.size)
For some reason the character stops at the ladder instead of moving on top of it. I can't figure out what I'm doing wrong.
Edit - You've set the properties of the playerWalk physics body before you've actually created the playerWalk.physicsBody. So the values for the categoryBitMask, collisionBitMask and contactTestBitMask don't get set because you've used optional chaining and so playerWalk.physicsBody? resolved to nil and nothing gets set.
Then, when you create the playerWalk physic body it just has the defaults properties i.e. no category, no contacts and collides with everything, so it is colliding with your ladder and thus cannot move over it.
Move the line:
playerWalk.physicsBody = SKPhysicsBody(rectangleOf: playerWalk.frame.size)
to before the lines where you set the physics body's properties.
I'm implementing a mass-spring system (many small physics bodies joined together with SKPhysicsJointSpring instances) with SpriteKit. Some of the particles would get snagged while traversing the center of the scene.
There seems to be a small, static body in the middle of the scene and I don't know why it's there.
Here's an easy way to see what I'm talking about:
In XCode 8, create a brand new project with the "Game" template.
In GameViewController.viewDidLoad(), add view.showsPhysics = true
If you run the project, you should see a little dot in the middle, which is the errant body:
Anyone know how to get rid of it?
Edit: I tried to manually create the scene object:
In GameViewController.viewDidLoad(), I replaced this:
// Load the SKScene from 'GameScene.sks'
if let scene = SKScene(fileNamed: "GameScene") {
view.presentScene(scene)
}
with this:
let scene = GameScene(size: view.frame.size)
scene.anchorPoint = CGPoint(x: 0.5, y: 0.5)
view.presentScene(scene)
but that didn't fix it.
Anyways, I decided to make an answer because comments are not suitable due to lot of info I want to share. Also my answer, sadly, doesn't answer the question but it gives some useful info about this unidentified, obviously capable of flying (physics body) object :)
So this is the code how to grab it (and modify it???):
//you can use self.frame here...I just copied Alessandro's code
self.physicsWorld.enumerateBodies(in:(label?.frame)!) { body, stop in
if let node = body.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}else{
print("This body's node property is nil")
body.affectedByGravity = true
body.isDynamic = true
body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
}
print("Area covered by this node physicsBody: \(body.area)")
}
So if you put a break point inside of that else statement, you can scan this physics body completely and get all the info about it, like that its node property is set to nil or that its isDynamic property is set to false. But you can change that, and like in my code, set for example isDynamics to true. This makes it moveable. So if you apply some forces to it, it will move.
Still, like I said in comments, I don't have an idea why it is there and what it represents or what is its purpose.
Also, for those who are wondering how it is possible that one physics body doesn't have a node associated with it ( body.node equals nil) but is still visible on screen when showsPhysics is set to true, there is a reasonable explanation. Physics world is separated from the node tree. So we can remove a sprite from a node tree, but that doesn't mean that its physics body will be removed instantly. It may happen that physics engine haven't finished simulation... So you probably wonder, how this might happen?
Let say you have three SKSpriteNode objects intersecting at the same time (say A contacts B and A contacts C at the same time). SpriteKit can process only one contact at time. And say that you are removing A from a parent when it is contacting with B. Then, there is a contact between A and C also, so didBegin:(_ contact) will be called twice. And if you remove A from its parent in first didBegin(_ contact) call, in the next didBegin(_ contact) call, bodyA.node will be nil (bodyA is a physics body of sprite A), but its physics body will remain visible until engine finishes what needed. This is because node tree and a physics world are separated.
About the "hello world" xCode game template , it seems a little physicsBody associated to the GameScene node.
With some code I've found this:
class GameScene: SKScene {
private var label : SKLabelNode?
private var spinnyNode : SKShapeNode?
override func didMove(to view: SKView) {
...
// End part of this function:
if let b = physicsWorld.body(in: (label?.frame)!) {
if let node = b.node {
print("Type of this node: \(type(of:node))")
print("Frame of this node: \(node.frame))")
}
print("Area covered by this node physicsBody: \(b.area)")
}
}
}
With a breakpoint to the last brace, you can see two bodies (maybe an array of bodies), one of them is the physicsBody to the left debugArea (array with index 1) with the same hex address as my body b in my code : 0x79794f90, a little rectangle body with area 4.444
Printing description of ((PKPhysicsBody *)0x79794f90):
<SKPhysicsBody> type:<Rectangle> representedObject:[<SKScene> name:'(null)' frame:{{-375, -667}, {750, 1334}} anchor:{0.5, 0.5}]
(lldb)
I had a similar problem. I have a game with two sprite nodes joined together (SKPhysicsJointFixed.joint) moving around an SKEditor created scene.
As per my design, this node-pair would impact a third sprite node and be propelled smoothly away from the third sprite node, EXCEPT when the impact was in the center of the scene. For the center of the scene impact, the node-pair would compress together while be propelled away from the third sprite node, presenting a poor graphical image.
After significant time debugging my code, I found this post. Kudos for the explanations and code. I can’t answer the “why” question but for the “particles would get snagged while traversing the center of the scene” question my suggested solution is to clear the collisionBitMask instead of moving the body.
BTW categoryBitMask is 0 when loaded.
//moves the problem offscreen unless it hits another node
//body.affectedByGravity = true
//body.isDynamic = true
//body.applyImpulse(CGVector(dx: 0.003, dy: 0.0003))
//
// collisionBitMask loads with 4294967295 = default value to collide with all categories
//
body.collisionBitMask = 0
I know scene coordinates are (0,0) at center so how can i distribute 4 SKNodes along the width using a for loop
for index 1...4 {
let node = SKNode()
node.postion = evenly distribute along the scene width. who can i do it?
}
You could use the width of the scene divided by the number of nodes minus 1. This would then be multiplied by the index minus 1, then added to the minimum x value of your scene.
let widthRange = scene.frame.maxX - scene.frame.minX
for index 1...4 {
let node = SKNode()
node.postion = scene.frame.minX + widthRange/(4-1) * (index-1)
}
This should place each node an equal distance away from each other along the width of the scene, starting from each edge.
If your index for the for loop doesn't start at 1, the code would need to be modified to something like this:
let widthRange = scene.frame.maxX - scene.frame.minX
var counter = 0
for index 352...356 {
let node = SKNode()
node.postion = scene.frame.minX + widthRange/(4-1) * counter
counter+=1
}
If you're unsure of which version to use, use the second version, as it's less reliant on the format of the for loop (as long as you reset counter to 0 before every time you use this).
If you need any clarifications as to what does what, feel free to ask.
Edit
This should clarify what the code actually does.
let widthRange = scene.frame.maxX - scene.frame.minX is used to get the size of the scene, then set it to a variable that is used later. Making this a variable is sort of optional as you could just use the scene size, however I put it this way so it's less messy.
var counter = 0 simply makes a counter variable, that's used in the for-loop, to place the nodes. Make sure to set it to 0 before every time you run the loop. counter+=1 is then used later so each node is multiplied by an increasing value, which creates the different x-positions. (I'll get into that later)
node.postion = scene.frame.minX + widthRange/(4-1) * counter is the complicated line. First, I use scene.frame.minX to start placement from the lowest width/x-position. widthRange/(4-1) is a little harder to understand. It uses the scenes width (from earlier), then divides by one less than the amount of nodes. The best way to understand this, is that to cut a rectangle (only in a single direction) so it has 2 cuts through it (2 of the nodes), and 2 edges (the other 2 nodes), you would need 3 sections. (The rectangle would be cut into 3). This gets the distance between each node. The pattern of #ofNodes-1 follows for every amount of nodes. The * counter part is the part that makes the node's x-position actually change. Since every time the loop is gone through, counter increases by 1 (starting at 0) the amount of "sections" from before (widthRange/(4-1)) is increased by 1.
I am new to box2d. I want to create instance of sprites(Coins like tiny wings) and make it to collide with player.Is there a single sprite body definition is enough or i have to create a sprite body separately for all instance sprite
I need exact collision effect like tiny wings coins collection.Any help plz intimate me.Thanks
If I had to solve that problem I would create a class (for example Coin) then make multiple instances of it and storing them by using an array. Here is the code for creating a new coin and storing it in the array:
//in the constructor function of the playing screen:
coins = new Array();
//the constructor function of the Coin should contain the startX and startY coordinate
var newCoin= new Coin(100, 50);
coins.push( newCoin );
addChild( newCoin );
You can remove an element of an array using either exampleArray.pop ---> this will remove the last element
or exampleArray.splice(x,z) x is the element you remove, z is the number of elements you remove. exampleArray.splice(5,2) will remove 2 elements starting from the 5th. The first element of an array is numbered 0, the second is numbered 1 and so on...
You can check for collisions like this:
for each (var coin in coins)
{
if (coin.hitTestObject(player))
{
//do something
}
}