Getting SpriteKit texture from SKView returning nil after 104 calls - sprite-kit

I am having some odd behavior in SpriteKit when creating a texture. The function below shows you what I am doing. In short, I'm in SceneKit and making a SCNNode out of an Array of colors (think pixel/voxels). It works like a charm. However, after exactly 104 calls the texture returned is nil. Afterwards, it is hit or miss whether the texture will be nil or not. I am also providing the exact color information. Thoughts?
func create2dModelSK(with colors: [String]) -> SCNNode? {
let geo = SCNBox(width: 1.0, height: 1.0, length: 0.1, chamferRadius: 0.0)
let base = SCNNode(geometry: geo)
let view = SKSpriteNode(color: .white, size: CGSize(width: 160, height: 160))
view.anchorPoint = CGPoint(x: 0, y: 1)
var xOffset = 0
var yOffset = 0
var count = 0
for _ in 0...15 {
for _ in 0...15 {
guard let newColor = UIColor(hexString: "#" + colors[count] + "ff") else { return base }
let n = SKSpriteNode(color: newColor, size: CGSize(width: 10, height: 10))
n.anchorPoint = CGPoint(x: 0, y: 1)
n.position = CGPoint(x: xOffset, y: yOffset)
view.addChild(n)
xOffset += 10
count += 1
}
xOffset = 0
yOffset -= 10
}
let skView = SKView(frame: CGRect(x: 0, y: 0, width: 160, height: 160))
let texture = skView.texture(from: view)
//AFTER being called 104 times, texture is nil.
let faceMaterial = SCNMaterial()
faceMaterial.diffuse.contents = texture
let sideMaterial = SCNMaterial()
sideMaterial.diffuse.contents = UIColor.white
let materialsForBox = [faceMaterial,sideMaterial,faceMaterial,sideMaterial,sideMaterial,sideMaterial]
base.geometry?.materials = materialsForBox
let scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
base.scale = scale
return base
}

This is where autoreleasepool comes in handy, it allows you to release the memory when the autoreleasepool is finished so that you do not run out of space before using it again.
Of course this is not going to solve your main problem, where you are creating too many textures and running out of memory space, but it will allow you to at least make some more because it will release the temporary memory that view.texture(from:node) is holding on to.
func create2dModelSK(with colors: [String]) -> SCNNode? {
let geo = SCNBox(width: 1.0, height: 1.0, length: 0.1, chamferRadius: 0.0)
let base = SCNNode(geometry: geo)
let view = SKSpriteNode(color: .white, size: CGSize(width: 160, height: 160))
view.anchorPoint = CGPoint(x: 0, y: 1)
var xOffset = 0
var yOffset = 0
var count = 0
for _ in 0...15 {
for _ in 0...15 {
guard let newColor = UIColor(hexString: "#" + colors[count] + "ff") else { return base }
let n = SKSpriteNode(color: newColor, size: CGSize(width: 10, height: 10))
n.anchorPoint = CGPoint(x: 0, y: 1)
n.position = CGPoint(x: xOffset, y: yOffset)
view.addChild(n)
xOffset += 10
count += 1
}
xOffset = 0
yOffset -= 10
}
autoreleasepool{
let skView = SKView(frame: CGRect(x: 0, y: 0, width: 160, height: 160))
let texture = skView.texture(from: view)
//AFTER being called 104 times, texture is nil.
let faceMaterial = SCNMaterial()
faceMaterial.diffuse.contents = texture
let sideMaterial = SCNMaterial()
sideMaterial.diffuse.contents = UIColor.white
let materialsForBox = [faceMaterial,sideMaterial,faceMaterial,sideMaterial,sideMaterial,sideMaterial]
base.geometry?.materials = materialsForBox
}
let scale = SCNVector3(x: 0.1, y: 0.1, z: 0.1)
base.scale = scale
return base
}

Related

Creating a scnplain that is the same size as half of the screen using SceneKit

I am trying to figure out how to create a plainNode in SceneKit that takes up exactly half of the screen.
So I found this routine to projectValues that seems correct.
extension CGPoint {
func scnVector3Value(view: SCNView, depth: Float) -> SCNVector3 {
let projectedOrigin = view.projectPoint(SCNVector3(0, 0, depth))
return view.unprojectPoint(SCNVector3(Float(x), Float(y), projectedOrigin.z))
}
}
And I fed these values into it...
let native = UIScreen.main.bounds
let maxMax = CGPoint(x: native.width, y: native.height * 0.5)
let newPosition1 = maxMax.scnVector3Value(view: view, depth: Float(0))
print("newPosition \(newPosition1)")
let minMin = CGPoint(x: 0, y: 0)
let newPosition2 = minMin.scnVector3Value(view: view, depth: Float(0))
print("newPosition \(newPosition2)")
let minMax = CGPoint(x: 0, y: native.height * 0.5)
let newPosition3 = minMax.scnVector3Value(view: view, depth: Float(0))
print("newPosition \(newPosition3)")
let maxMin = CGPoint(x: native.width, y: 0)
let newPosition4 = maxMin.scnVector3Value(view: view, depth: Float(0))
print("newPosition \(newPosition4)")
// approximations that look almost correct, but they are not...
let width = (maxMax.x - minMin.x) / 100 * 2
let height = (maxMax.y - minMin.y) / 100 * 2
let plainGeo = SCNPlane(width: width, height: height)
let planeNode = SCNNode(geometry: plainGeo)
planeNode.geometry?.firstMaterial?.diffuse.contents = UIColor.red
view.scene.rootNode?.addChildNode(planeNode)
But it isn't right? What am I doing wrong here?

Why does the app lag a lot when calling functions

I am building a game that requires a function (out of several) to be called depending if a condition is true or not. The condition checks the variable every 0.0001 seconds for game purposes, but the functions are called every 4-6 seconds. I just needed the
Whenever I was testing the game on a device, I noticed that the app lags at certain times. I believe its when a function is called but I'm not sure.
This is an SKAction that calls the function that checks the condition:
let triangle = SKAction.sequence([SKAction.run(obstaclesFunc),SKAction.wait(forDuration: 0.0001)])
run(SKAction.repeatForever(triangle))
This is the condition checker:
func obstaclesFunc() {
GameScene.nextObstacle = Int(arc4random_uniform(6) + 1 )
if(GameScene.first == true){
GameScene.randomNum = 1
}
if (GameScene.pass == true){
number = GameScene.nextObstacle
print(number)
//Rectangle Call Function
if(GameScene.randomNum == 1){
sideRectangles()
//More code at bottom
}
I noticed that the biggest lag occurs when this function is called:
func sideRectangles() {
let rectangle1 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle2 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle3 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangleTexture = SKTexture(imageNamed: "rectangleWalls")
//Physics World
rectangle1.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle1.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_1
rectangle1.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle1.physicsBody?.affectedByGravity = false
rectangle1.physicsBody?.isDynamic = true
rectangle1.physicsBody?.collisionBitMask = 0
//Physics World
rectangle2.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle2.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_2
rectangle2.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle2.physicsBody?.affectedByGravity = false
rectangle2.physicsBody?.isDynamic = true
rectangle2.physicsBody?.collisionBitMask = 0
//Physics World
rectangle3.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle3.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_3
rectangle3.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle3.physicsBody?.affectedByGravity = false
rectangle3.physicsBody?.isDynamic = true
rectangle3.physicsBody?.collisionBitMask = 0
let moveDown = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 2)
let moveDown2 = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 3)
let moveDown3 = SKAction.moveTo(y: self.frame.minY - rectangle3.size.height / 2, duration: 4)
let removeNode = SKAction.removeFromParent()
rectangle1.position = CGPoint(x: self.frame.minX - rectangle3.size.height * 0.1, y: self.frame.maxY + rectangle3.size.width) //use 4.5 for the close one
rectangle2.position = CGPoint(x: self.frame.minX - rectangle3.size.width / 40 , y: self.frame.maxY + rectangle3.size.height) //use 4.5 for the close one
rectangle3.position = CGPoint(x: self.frame.midX - rectangle3.size.width / 1.55, y: self.frame.maxY + rectangle3.size.height * 1.8)
rectangle1.run(SKAction.sequence([moveDown,removeNode]))
rectangle2.run(SKAction.sequence([moveDown2,removeNode]))
rectangle3.run(SKAction.sequence([moveDown3,removeNode]))
//second set
let rectangle5 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle6 = SKSpriteNode(imageNamed: "rectangleWalls")
let rectangle7 = SKSpriteNode(imageNamed: "rectangleWalls")
//Physics World
rectangle5.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle5.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_5
rectangle5.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle5.physicsBody?.affectedByGravity = false
rectangle5.physicsBody?.isDynamic = true
rectangle5.physicsBody?.collisionBitMask = 0
//Physics World
rectangle6.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle6.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_6
rectangle6.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle6.physicsBody?.affectedByGravity = false
rectangle6.physicsBody?.isDynamic = true
rectangle6.physicsBody?.collisionBitMask = 0
//Physics World
rectangle7.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle7.physicsBody?.categoryBitMask = PhysicsNumbering.rectangle_7
rectangle7.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle7.physicsBody?.affectedByGravity = false
rectangle7.physicsBody?.isDynamic = true
rectangle7.physicsBody?.collisionBitMask = 0
rectangle5.zPosition = 0
rectangle6.zPosition = 0
rectangle7.zPosition = 0
rectangle5.position = CGPoint(x: self.frame.maxX + rectangle3.size.height * 0.1, y: self.frame.maxY + rectangle3.size.width) //use 4.5 for the close one
rectangle6.position = CGPoint(x: self.frame.maxX + rectangle3.size.width / 40 , y: self.frame.maxY + rectangle3.size.height) //use 4.5 for the close one
rectangle7.position = CGPoint(x: self.frame.midX + rectangle3.size.width / 1.55, y: self.frame.maxY + rectangle3.size.height * 1.8)
rectangle5.run(SKAction.sequence([moveDown,removeNode]))
rectangle6.run(SKAction.sequence([moveDown2,removeNode]))
rectangle7.run(SKAction.sequence([moveDown3,removeNode]))
addChild(rectangle1)
addChild(rectangle2)
addChild(rectangle3)
addChild(rectangle5)
addChild(rectangle6)
addChild(rectangle7)
}
Can someone please help me find out what is causing this lag? I cannot seem to figure it out. Thank You in advance!
Generating physicsBodies on the fly is very expensive and that is why your game will lag everytime it tries to do that.
I suggest you create the objects and put them in an array of the obstacle and then copy the array items whenever you need to recreate them.
You have soooooo much code that is being repeated for every object, I've tried to reduce the amount of code by reusing a lot of it for every obstacle.
FYI SKAction.wait(forDuration: 0.0001) is the same as 0 so it is pointless
The code in your obstaclesFunc contains irrelevant or code not contained in your questions so I have omitted that functionality, if it is dire to your game you'll have to figure it out and put it in the generateObstacles func.
//variables needed in your scene
private var rectangles = [SKSpriteNode]()
private var moveDown: SKAction!
private var moveDown2: SKAction!
private var moveDown3: SKAction!
private var removeNode: SKAction!
//in your didMove func
//preload the obstacles into an array
createRectangles()
//in your startGame func
//this func call will start the generation of the obstacles
generateObstacles()
func createRectangles() {
let rectangle1 = createRectangle(category: PhysicsNumbering.rectangle_1)
rectangles.append(rectangle1)
let rectangle2 = createRectangle(category: PhysicsNumbering.rectangle_2)
rectangles.append(rectangle2)
let rectangle3 = createRectangle(category: PhysicsNumbering.rectangle_3)
rectangles.append(rectangle3)
let rectangle5 = createRectangle(category: PhysicsNumbering.rectangle_5)
rectangles.append(rectangle5)
let rectangle6 = createRectangle(category: PhysicsNumbering.rectangle_6)
rectangles.append(rectangle6)
let rectangle7 = createRectangle(category: PhysicsNumbering.rectangle_7)
rectangles.append(rectangle7)
let width: CGFloat = rectangle3.size.width
let height: CGFloat = rectangle3.size.height
//position the rectangles afterward creating them since they appear to be relevant to each other
rectangle1.position = CGPoint(x: frame.minX - height * 0.1, y: frame.maxY + width) //use 4.5 for the close one
rectangle2.position = CGPoint(x: frame.minX - width / 40 , y: frame.maxY + height) //use 4.5 for the close one
rectangle3.position = CGPoint(x: frame.midX - width / 1.55, y: frame.maxY + height * 1.8)
rectangle5.position = CGPoint(x: frame.maxX + height * 0.1, y: frame.maxY + width) //use 4.5 for the close one
rectangle6.position = CGPoint(x: frame.maxX + width / 40 , y: frame.maxY + height) //use 4.5 for the close one
rectangle7.position = CGPoint(x: frame.midX + width / 1.55, y: frame.maxY + height * 1.8)
//now create the actions so that they can be used again and again
createActions(height: height / 2)
}
func createActions(height: CGFloat) {
moveDown = SKAction.moveTo(y: frame.minY - height, duration: 2)
moveDown2 = SKAction.moveTo(y: frame.minY - height, duration: 3)
moveDown3 = SKAction.moveTo(y: frame.minY - height, duration: 4)
removeNode = SKAction.removeFromParent()
}
func generateObstacles() {
let randomWaitTime = Int(arc4random_uniform(6) + 1 )
self.run(.wait(forDuration: randomWaitTime) {
loadAndMoveRectangles()
//now call the generate func again to get a diff random time to wait
generateObstacles()
}
}
func loadAndMoveRectangles() {
let rectangle1 = rectangles[0].copy() as? SKSpriteNode
addChild(rectangle1)
let rectangle2 = rectangles[1].copy() as? SKSpriteNode
addChild(rectangle2)
let rectangle3 = rectangles[2].copy() as? SKSpriteNode
addChild(rectangle3)
let rectangle5 = rectangles[3].copy() as? SKSpriteNode
addChild(rectangle5)
let rectangle6 = rectangles[4].copy() as? SKSpriteNode
addChild(rectangle6)
let rectangle7 = rectangles[5].copy() as? SKSpriteNode
addChild(rectangle7)
rectangle1.run(.sequence([moveDown, removeNode]))
rectangle2.run(.sequence([moveDown2, removeNode]))
rectangle3.run(.sequence([moveDown3, removeNode]))
rectangle5.run(.sequence([moveDown, removeNode]))
rectangle6.run(.sequence([moveDown2, removeNode]))
rectangle7.run(.sequence([moveDown3, removeNode]))
}
func createRectangle(category: UInt32) -> SKSpriteNode {
let rectangle = SKSpriteNode(imageNamed: "rectangleWalls")
rectangle.zPosition = 0
rectangle.physicsBody = SKPhysicsBody(texture: rectangleTexture, size: CGSize(width: rectangle1.size.width - 20, height: rectangle1.size.height - 20))
rectangle.physicsBody?.categoryBitMask = category
rectangle.physicsBody?.contactTestBitMask = PhysicsNumbering.player
rectangle.physicsBody?.affectedByGravity = false
rectangle.physicsBody?.isDynamic = true
rectangle.physicsBody?.collisionBitMask = 0
return rectangle
}

Display a fraction in one UILabel

I am trying to display a fraction as something like 1/2 but I want to line to be horizontal so the fraction looks more fraction like, some thing like this:
1
_
2
However with no massive space and in a way that I can use it inside one UILabel (which can have multiple lines). I also need to be able to put a fraction as the numerator/denominator of a fraction and so on. Any suggestions? (Fonts, characters...)
Thank you,
Gleb Koval
Here's a UIView subclass that draws a fraction:
class FractionView: UIView {
var font: UIFont = UIFont.systemFont(ofSize: 16) {
didSet { self.setNeedsDisplay() }
}
var numerator: Int = 1 {
didSet { self.setNeedsDisplay() }
}
var denominator: Int = 2{
didSet { self.setNeedsDisplay() }
}
var spacing: CGFloat = 5{
didSet { self.setNeedsDisplay() }
}
override func draw(_ rect: CGRect) {
let numString = "\(numerator)" as NSString
let numWidth = numString.size(withAttributes: [.font: font]).width
let denomString = "\(denominator)" as NSString
let denomWidth = denomString.size(withAttributes: [.font: font]).width
let numX: CGFloat
let denomX: CGFloat
if numWidth <= denomWidth {
denomX = 0
numX = (denomWidth - numWidth) / 2
} else {
denomX = (numWidth - denomWidth) / 2
numX = 0
}
numString.draw(at: CGPoint(x: numX, y: 0), withAttributes: [.font : font])
let path = UIBezierPath()
path.move(to: CGPoint(x: 0, y: font.lineHeight + spacing))
path.addLine(to: CGPoint(x: self.frame.maxX, y: font.lineHeight + spacing))
UIColor.black.setStroke()
path.lineWidth = 1
path.stroke()
denomString.draw(at: CGPoint(x: denomX, y: font.lineHeight + spacing * 2), withAttributes: [.font: font])
}
}
// usage:
let width = ("12" as NSString).size(withAttributes: [.font: UIFont.systemFont(ofSize: 16)]).width
let view = FractionView(frame: CGRect(x: 0, y: 0, width: width, height: 48))
view.backgroundColor = .white
view.denominator = 12
As Sulthan in comments has suggested, using an NSAttributedString would be an easier approach:
let attrString = NSMutableAttributedString(string: "1", attributes: [NSAttributedStringKey.underlineStyle : 1])
attrString.append(NSAttributedString(string: "\n2"))
attrString
let label = UILabel(frame: CGRect(x: 0, y: 0, width: 100, height: 100))
label.attributedText = attrString
label.numberOfLines = 2

Does a parent have its children's physicsBodies?

I was wondering what the physicsBody of a parent would be if all I do is this:
let combinedBodies = SKSpriteNode()
addChild(combinedBodies)
//Create its children like this as an example:
let child = SKSpriteNode(color: UIColor.red, size: CGSize(width: 10, height: 20))
child.physicsBody = SKPhysicsBody(rectangleOf: child.size)
child.physicsBody?.categoryBitMask = collisionType.child.rawValue
combinedBodies.addChild(child)
allBodies.append(child.physicsBody?)
Would the physicsBody of combinedBodies be the combination of all its children's physicsBodies, or would it have none and I would have to manually add it? Also, what would the categoryBitMask of the parent be? Would it be collisionType.child.rawValue if all of its children had that category, or would I manually have to give it its own category?
Edit: I have used this:
combinedBodies.physicsBody = SKPhysicsBody(bodies: )
I have a list where I store all the children's physicsBodies and then put that list into this command (I keep updating the combinedBodies.physicsBody when new and old children get deleted). However, now I have the problem that the physicsbodies of the parent aren't where the original physicsbodies of the children were (they are at different positions on the screen), they are in the middle of the screen. Is there a way to fix this?
I have also changed the physicsBody of the combinedBodies like this:
combinedBodies.physicsBody = SKPhysicsBody(bodies: allBodies)
combinedBodies.physicsBody?.isDynamic = true
combinedBodies.physicsBody?.categoryBitMask = collisionType.combinedBodies.rawValue
combinedBodies.physicsBody?.collisionBitMask = 0
combinedBodies.physicsBody?.contactTestBitMask = 0
combinedBodies.physicsBody?.affectedByGravity = false
combinedBodies.physicsBody?.friction = 0.0
Please note that all the code in this question is basically my code, just that the new children are created through an iteration with different positions, rotations, and colors.
EDIT WITH EXAMPLE CODE: I have been able to recreate my issue in a test program which I have added here (the allLine is the parent of ScreenLine and line in this code).
class GameScene: SKScene, SKPhysicsContactDelegate {
enum type: UInt32 {
case line = 1
case screenLine = 2
case allLine = 4
}
override func didMove(to view: SKView) {
let allLine = SKSpriteNode()
addChild(allLine)
var points = [CGPoint]()
var bodies = [SKPhysicsBody]()
points = [CGPoint(x: 0, y: 300), CGPoint(x: -200, y: 100)]
let width = 5.0
let height = {() -> Double in
let distX = abs(points[1].x - points[0].x)
let distY = abs(points[1].y - points[0].y)
let dist = sqrt((distX * distX) + (distY * distY))
return Double(dist)
}()
let ScreenLine = SKSpriteNode(color: UIColor.init(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: width, height: height))
ScreenLine.position = CGPoint(x: ((points[1].x + points[0].x)/2), y: ((points[1].y + points[0].y)/2))
let rotation = {() -> CGFloat in
let m = (points[1].y - points[0].y)/(points[1].x - points[0].x)
let angle = CGFloat(Double.pi/2) - atan(m)
return -angle
}()
ScreenLine.zRotation = rotation
ScreenLine.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: CGPoint(x: 0, y: 0))
ScreenLine.physicsBody?.contactTestBitMask = type.screenLine.rawValue
ScreenLine.physicsBody?.collisionBitMask = 0
ScreenLine.physicsBody?.contactTestBitMask = 0
ScreenLine.physicsBody?.isDynamic = true
ScreenLine.physicsBody?.affectedByGravity = false
allLine.addChild(ScreenLine)
bodies.append(ScreenLine.physicsBody!)
points = [CGPoint(x: -100, y: 300), CGPoint(x: -300, y: 100)]
let line = SKSpriteNode(color: UIColor.init(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0), size: CGSize(width: width, height: height))
line.position = CGPoint(x: ((points[1].x + points[0].x)/2), y: ((points[1].y + points[0].y)/2))
line.zRotation = rotation
line.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: CGPoint(x: 0, y: 0))
line.physicsBody?.contactTestBitMask = type.screenLine.rawValue
line.physicsBody?.collisionBitMask = 0
line.physicsBody?.contactTestBitMask = 0
line.physicsBody?.isDynamic = true
line.physicsBody?.affectedByGravity = false
allLine.addChild(line)
bodies.append(line.physicsBody!)
allLine.physicsBody = SKPhysicsBody(bodies: bodies)
allLine.physicsBody?.isDynamic = true
allLine.physicsBody?.categoryBitMask = type.allLine.rawValue
allLine.physicsBody?.collisionBitMask = 0
allLine.physicsBody?.contactTestBitMask = 0
allLine.physicsBody?.affectedByGravity = false
allLine.physicsBody?.friction = 0.0
}
}
UPDATE: Now by centering the physicsBody of the children to where the node is (not CGPoint.zero), but where I place the child on the screen, the physicsBody is in the right position when I add it to the list.
I set the line and screen line physicsBody positions like this:
ScreenLine.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: ScreenLine.position)
line.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: width, height: height), center: line.position)
PROBLEM THAT I'M CURRENTLY TRYING TO FIGURE OUT:
This makes the physicsBodies of allLine centred. However, they aren't rotated like they should be. They don't preserve their rotations. How do I fix this??
Thanks for any answers and help :D

CollisionBehavior not being called

I have been trying to get this CollisionBehavior func to run.
lazy var collisionDelegate: UICollisionBehaviorDelegate = self
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item1: UIDynamicItem, with item2: UIDynamicItem, at p: CGPoint) {
print("colliding")
}
func collisionBehavior(_ behavior: UICollisionBehavior, beganContactFor item: UIDynamicItem, withBoundaryIdentifier identifier: NSCopying?, at p: CGPoint) {
print("colliding")
}
As you can see I tried both collisionBehavior methods. The ball and blocks are all dynamic objects and I have the screen ends being a boundary. So every time a block or "ball" hits another block, paddle, ball, or the end of the screen, it should print "colliding", but nothing gets printed to the terminal. Below is the code for the block, ball, paddle, and boundaries.
Paddle:
func paddle () {
lastPaddle.removeFromSuperview()
collider.removeItem(lastPaddle)
let yPos = CGFloat(bounds.size.height / 6 * 5)
let width = bounds.size.width / 4
let height = bounds.size.width / 20
if !first {xPos = bounds.midX; first = true}
let paddle = CGRect(x: xPos + width/2, y: yPos + height/2, width: width, height: height)
let frame = UIView(frame: paddle)
frame.backgroundColor = UIColor.red()
addSubview(frame)
let item: UIDynamicItem = frame
let dib = UIDynamicItemBehavior()
animator.addBehavior(dib)
dib.allowsRotation = false
dib.isAnchored = true
dib.elasticity = 0
dib.addItem(item)
collider.addItem(item)
lastPaddle = frame
//collider.removeBoundary(withIdentifier: "paddle")
//update()
}
Ball:
func createBall () {
xBall = bounds.midX
yBall = bounds.midY
let smallRect = CGRect(x: xBall, y: yBall, width: bounds.size.width/12, height: bounds.size.width/12)
//let lBall = CGPath(ellipseIn: smallRect, transform: nil)
ball = smallRect
let frame = UIView(frame: smallRect)
frame.backgroundColor = UIColor.green()
addSubview(frame)
let item: UIDynamicItem = frame
//collider.elasticity = 100
gravity.magnitude = 0.5
gravity.addItem(item)
collider.addItem(item)
//let arr = [item]
animator.addBehavior(ballBehaviour)
ballBehaviour.elasticity = 1.5
ballBehaviour.addItem(item)
}
Blocks and Screen End (boundary):
func createBlocks () {
for a in 0..<numberOfRows {
for b in 0..<numberOfColumns {
//let view = UIView()
let x = CGFloat(b) * (bounds.size.width/CGFloat(numberOfColumns))
let y = CGFloat(a) * (bounds.size.height/CGFloat(numberOfRows))
let width = bounds.size.width/CGFloat(numberOfColumns)/2
let height = bounds.size.height/CGFloat(numberOfRows)/8
let rect = CGRect(x: x + width/2, y: y/3 + height*3, width: width, height: height)
//print(rect)
let frame = UIView(frame: rect)
blocks.append(frame)
frame.backgroundColor = UIColor.blue()
addSubview(frame)
let item: UIDynamicItem = frame
//gravity.addItem(item)
collider.addItem(item)
blockBehaviour.addItem(item)
animator.addBehavior(blockBehaviour)
}
}
let rectangle = CGRect(x: 0, y: 0, width: bounds.size.width, height: 3/2 * bounds.size.height)
//print(rectangle)
let boundary = UIBezierPath(rect: rectangle)
collider.addBoundary(withIdentifier: "screen", for: boundary)
}
Thanks for any help
Your lazy var is not going to work. An object (self) does not somehow magically raise its hand and say "Look at me, I am the collision delegate!" That isn't how delegation operates.
The UICollisionBehavior object (which I don't actually see anywhere in your code — where is it?) has a collisionDelegate property, and it is this that must be set to self.