Spritekit - could not create physics body - image occasionally returns nil - sprite-kit

I have looked at these questions but their answers did not work for me: 1, 2. I am targeting iOS 10.0. Occasionally when running the code a physicsBody is not created due to a problem with the texture/image and occasionally it does get created but is completely wrong yet I can't find anything wrong with the image, no stray pixels. Sometimes it does get created perfectly fine, about 1/3 times. Setting the alpha threshold as one answer suggests also doesn't change anything.
Code:
let pigeon: SKSpriteNode = SKSpriteNode(imageNamed: "pigeonHitBox")
init(){
pigeon.size = CGSize(width: 100, height: 100)
pigeon.position = CGPoint(x: 400, y: 631)
pigeon.physicsBody = SKPhysicsBody(texture: pigeon.texture ?? SKTexture(imageNamed: "pigeonHitBox"), size: pigeon.size)
pigeon.physicsBody!.isDynamic = true
}
Example of completely wrong outline

Related

Fast-paced SpriteKit game has irregular CPU activity and is jittery/lags despite frame rate staying high - Swift

I'm having the issue on a simple but fast-paced SpriteKit game, but I've reduced my code just to a bouncing ball and still get the issue to a lesser extent:
override func didMove(to view: SKView) {
super.didMove(to: view)
physicsWorld.contactDelegate = self
physicsWorld.speed = 1
physicsWorld.gravity = CGVector(dx: 0.0, dy: 0.0)
let borderBody = SKPhysicsBody(edgeLoopFrom: self.frame)
borderBody.friction = 0
self.physicsBody = borderBody
borderBody.contactTestBitMask = BallCategory
addBall()
}
func addBall() {
let size = CGSize(width: 20, height: 20)
let position = CGPoint(x: frame.width / 2, y: 50)
let texture = SKTexture(image: #imageLiteral(resourceName: "whiteCircle"))
let ball = SKSpriteNode(texture: texture, size: size)
ball.position = position
ball.physicsBody = SKPhysicsBody(circleOfRadius: size.width / 2)
ball.fillColor = .white
ball.lineWidth = 0
addStandardProperties(node: ball, name: "ball", z: 5, contactTest: 0, category: BallCategory)
ball.physicsBody?.isDynamic = true
addChild(ball)
launchBall()
}
func addStandardProperties(node: SKNode, name: String, z: CGFloat, contactTest: UInt32, category: UInt32) {
node.name = name
node.zPosition = z
node.physicsBody?.isDynamic = false
node.physicsBody?.affectedByGravity = false
node.physicsBody?.mass = 0
node.physicsBody?.restitution = 1
node.physicsBody?.friction = 0
node.physicsBody?.linearDamping = 0
node.physicsBody?.angularDamping = 0
node.physicsBody?.angularVelocity = 0
node.physicsBody?.contactTestBitMask = contactTest
node.physicsBody?.categoryBitMask = category
}
func launchBall() {
let ball = childNode(withName: "ball")!
ball.physicsBody?.velocity = CGVector(dx: 0, dy: 500)
}
This code results in a ball (SKSpriteNode) bouncing up and down. When I run this, CPU usage starts at around 10% on my iPhone 6s and then after increases to around 25-30% after maybe 30-60 seconds (no idea why it's increasing). Throughout all of this, the frame rate stays very close to 60 FPS, usually going no lower than 58 FPS (it's the same way when I run the full game).
Almost any time an alert pops up (e.g., text messages, logging into Game Center, etc.), the lag shows up and shows up at random times when I'm running the full game.
I've also tried deleting and re-running the app, cleaning the project, deleting derived data and running in Release mode. None of these worked permanently.
Should I give up on SpriteKit and try another framework? If so, which? Cocos2D?
Any help is appreciated.
This is the result of Apple prioritising system calls over just about everything else.
When the system wants to know something, check something or otherwise do its thing it does so at the mercy of everything else.
No other engine will be able to help with this, there's no way to silence the system's constant activities through code.
You can get a slight improvement by putting on Flight Mode and turning off WIFI and Bluetooth. The system seems to be somewhat aware that it's in a quieter mode and does less because it's got no 4G or other connectivity it can go communicating with.
Further, there's been some pretty big changes to palm rejection in iOS 11 that's played havoc with the first round of iPad Pro models and creative software, creating multi-second rejection of all touch input. When this kind of thing can make it through to a GM you can be pretty sure they're slipping other messiness through.
Here's some complaints about iOS 11 performance: https://www.macrumors.com/2017/09/25/ios-11-app-slowdowns-performance-issues/
Turns out I had 2 SKViews in my view controller. By default, when you start a project as a SpriteKit game, Xcode sets the view controller root/superview of the GameViewController as an SKView. At some point, I had added a second SKView because I didn't intend for the scene to take up the entire screen and I apparently didn't realize that the VC root view was still set as an SKView. So every time GameViewController loaded, it was loading two SKViews, which is why I saw 120 FPS in Xcode.
I fixed the issue by simply removing the SKView class designation from the VC root view.

Why are the positions of my nodes different on every device?

Ok, so I've been trying to position a line on my iPhone 6s+ and my iPad 2. The line was added in an SKCamera (I don't know if that effects it or not). Here is the code for the line:
var leftWall = SKSpriteNode()
leftWall.size = CGSize(width: 1, height: 10000)
leftWall.position = CGPoint(x: 0, y: 0)
leftWall.color = UIColor.red
leftWall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: leftWall.size.width, height: leftWall.size.height))
leftWall.physicsBody?.affectedByGravity = false
leftWall.physicsBody?.isDynamic = false
leftWall.physicsBody?.categoryBitMask = groundGroup
leftWall.physicsBody?.collisionBitMask = ballGroup
leftWall.physicsBody?.contactTestBitMask = 0
theCamera.addChild(leftWall)
Okay, so the x position is 0,0 right? Here is how it looks on my iPhone 6s+:
As you see, the line is in the middle?
Here is how it looks on my iPad 2:
I just don't understand why the position of the line is completely different.. Please help me!
You are not taking into account the cropping that happens on the iPad, or your scaleMode is all wrong
What you need to do is set all nodes anchor points (Including the scene) to (0.5,0.5), make sure your GameScene has a static size and not based on view.frame (750,1334 is a good one) and that your scale mode is set to .aspectFill. This makes (0,0) the center of the screen, and you will notice everything line up on all devices

SpriteKit Nodes with Gravity False tilted in place

I'm building a menu Game Scene where I have a Logo and 4 buttons, all of those have a SKPhysicsBody with the rectangle size of the image.
All of those I only set:
logo.physicsBody = SKPhysicsBody(rectangleOf: logo.size)
logo.physicsBody?.affectedByGravity = false
Full code of creating the objects (all are the same)
logo = SKSpriteNode(imageNamed: "logo")
logo.anchorPoint = CGPoint.zero
logo.name = "logo"
logo.zPosition = 2
logo.size = CGSize(width: 309, height: 272)
logo.setScale(Generic.utilities.getScale())
self.addChild(logo)
logo.position = CGPoint(x: self.frame.width/2 - logo.size.width/2, y: self.frame.height - logo.size.height - 40)
logo.physicsBody = SKPhysicsBody(rectangleOf: logo.size)
logo.physicsBody?.affectedByGravity = false
and during a button press I change all the nodes affectedByGravity to true so they can fall of the screen and new elements can come in.
The problem I'm having is that when I run the app, the elements are all shifted/tilted on the screen and not in their original position
All I wanted is for them to stay in their position until I click one of the buttons and then they can fall off the screen.
Any reason why when I set a physics body and affectedByGravity they all tilt like this?
Well, Thanks to #RonMyschuk and #Knight0fDragon I found out (didn't know before) that I could add to my Scene loading the following:
skView.showsPhysics = true
Which add border lines around the physicsBody of your nodes, that way you can see them interacting. And by doing that I saw that the physicsBody of one of my nodes was completely in a different position then it should
By taking care of this issue, everything went back to normal

SpriteKit. Why .strokeTexture does not work for SKShapeNode?

I can't understand why the "strokeTexture" option isn't working on a SKShapeNode. Or, how can I do border with texture? Thanks in advance!
let shape = SKShapeNode(circleOfRadius: 100)
shape.position = CGPoint(x: 200, y: 400)
shape.fillColor = SKColor.white
shape.fillTexture = SKTexture(imageNamed: "test")
shape.lineWidth = 50
shape.strokeColor = SKColor.white
shape.strokeTexture = SKTexture(imageNamed: "test(5)")
Output:
Test image:
Test(5) image:
It doesn't work in Simulator!
Try it on your device.
I get the feeling this stroke might be being done with metal, whereas the fill is somehow not being done with metal, so is visible in the simulator.
The fill also doesn't rotate with the object on the device, but the stroke/outline and its texture do.
This would tend to indicate that an SKShapeNode without a fill might have reasonable performance.
Set the width and height of the texture, that is used to render the stroke, multiple of 8 pixels. In your case Test(5) has dimensions 100x100. If you change this texture for another one with dimensions, e.g. 96x96 pixels, the stroke will be rendered correctly and displayed. I don't know, why there are no reference to this fact in the official documentation.

CIFilter GaussianBlur seems to be broken on iOS9.x (used with SKEffectNode)

I am trying to create a blur effect using the following snippet:
let glowEffectNode = SKEffectNode()
glowEffectNode.shouldRasterize = true
let glowSize = CGSize(width: barSize.width, height: barSize.height)
let glowEffectSprite = SKSpriteNode(color: barColorData.topColor, size: glowSize)
glowEffectNode.addChild(glowEffectSprite)
let glowFilter = CIFilter(name: "CIGaussianBlur")
glowFilter!.setDefaults()
glowFilter!.setValue(5, forKey: "inputRadius")
glowEffectNode.filter = glowFilter
Of course on iOS 8.x it works perfectly but from iOS 9.x (tried it both both on 9.0 and 9.1) the blur is not working properly. (On the simulator the node seems to be a bit transparent but definitely not blurred and on the device it seems blurred but cropped and also has an offset from its center position:/)
Is there a quick way to fix this using CIFilter ?
I fiddled a bit more with this and found a solution...
First of all, it seems that using odd numbers for the blur radius causes the entire node to be rendered with an offset (???) so using 10 for example fixed the offset issue.
Secondly, it seems that the blur is cropped since the entire node is the rendered sprite and for a blur effect you need an extra space so I use a transparent sprite for the extra space and the following code snippet now works:
let glowEffectNode = SKEffectNode()
glowEffectNode.shouldRasterize = true
let glowBackgroundSize = CGSize(width: barSize.width + 60, height: barSize.height + 60)
let glowSize = CGSize(width: barSize.width + 10, height: barSize.height + 10)
let glowEffectSprite = SKSpriteNode(color: barColorData.topColor, size: glowSize)
glowEffectNode.addChild(SKSpriteNode(color: SKColor.clearColor(), size: glowBackgroundSize))
glowEffectNode.addChild(glowEffectSprite)
let glowFilter = CIFilter(name: "CIGaussianBlur")
glowFilter!.setDefaults()
glowFilter!.setValue(10, forKey: "inputRadius")
glowEffectNode.filter = glowFilter
I should have mentioned that I am creating a texture from this node using view.textureFromNode(glowEffectNode) for efficiency purposes but I tried using the node itself and the problem was still there so the above should work regardless