SKSpriteNode uses up memory when physicsBody is created - swift

I have a SKSpriteNode that uses no memory when sitting there. Once I specify the physicsBody, the memory goes up at about 0.5MB per second. I have other physics bodies but when they are added, they work normally. There is a sprite called "player" which i use to display the player. I made this function to make sure the problem was isolated. (IOS 10.3.3)
func createPlayerCollisionBox() {
let playerDummy = SKSpriteNode()
playerDummy.position = player.position
playerDummy.name = "dummy"
playerDummy.physicsBody = SKPhysicsBody.init(circleOfRadius: 40)
playerDummy.physicsBody?.isDynamic = false
playerDummy.physicsBody?.usesPreciseCollisionDetection = false
playerDummy.physicsBody?.categoryBitMask = playerCategory
playerDummy.physicsBody?.collisionBitMask = UInt32(8)
playerDummy.physicsBody?.contactTestBitMask = playerCategory | enemyCategory
self.addChild(playerDummy)
}
Note: The memory does not go back down when you leave the scene and come back.
The memory:

Related

SKSpriteNode physics body created from texture results in nil value

I'm currently working on a small iOS game. In its current iteration, 20 targets spawn and move across the screen space-invaders style, and you control a little ship to shoot and destroy them. The code for my targets, the player ship's bullets, and a simple collision detection function I've written in the interim are as follows:
class Red_Target: SKSpriteNode{
var game_scene: GameScene!
private var ship_texture: SKTexture!
convenience init(scale: CGFloat, game_world: GameScene){
self.init(texture: SKTexture(imageNamed: "Proto Target"))
self.ship_texture = SKTexture(imageNamed: "Proto Target")
self.setScale(scale)
game_scene = game_world
game_scene.addChild(self)
self.position = CGPoint(x: game_scene.view!.bounds.width/10, y: 9 * game_scene.view!.bounds.height/10)
//self.physicsBody = SKPhysicsBody(texture: ship_texture, size: self.size)
self.physicsBody = SKPhysicsBody(circleOfRadius: 13)
self.physicsBody!.affectedByGravity = false
self.physicsBody!.collisionBitMask = 0x0
self.physicsBody!.categoryBitMask = CollisionType.Enemy.rawValue
self.physicsBody!.contactTestBitMask = CollisionType.Player_Bullet.rawValue
}
func move() {
self.run(space_invaders(scene: game_scene))
}
}
class PC_Bullet: SKSpriteNode{
convenience init(scale: CGFloat){
self.init(imageNamed: "Goodbullet")
self.setScale(scale)
self.physicsBody = SKPhysicsBody(circleOfRadius: 3)
self.physicsBody!.affectedByGravity = false
self.physicsBody!.categoryBitMask = CollisionType.Player_Bullet.rawValue
self.physicsBody!.collisionBitMask = 0x0
self.physicsBody!.contactTestBitMask = CollisionType.Enemy.rawValue
}
}
func didBegin(_ contact: SKPhysicsContact) {
contact.bodyA.node!.removeFromParent()
contact.bodyB.node!.removeFromParent()
}
}
This code, in its current iteration, works just fine. However, if the line defining the target's physicsbody as its texture is uncommented and the line defining physicsbody as circleOfRadius is removed, the game will consistently crash after the 5th target is destroyed, claiming that didBegin unwraps a nil value. Why does this only happen from physics bodies with textures? Is there any way I could change the code for this to work? I would love to be able to use the physics body from texture function later on, when working with more irregular shapes.
You are pulling a classic nooby mistake. You are removing your bodies too early. If you browse Stackoverflow you will find a plethera of ways to solve it.
The basic idea is do not remove your sprites until the end of the physics phase because your 1 sprite could have multiple contact points to handle. So come up with a way to flag sprites that need to be deleted, and remove them during the didSimulatePhysics function.

Stop sprite from leaving screen using physics bodies

I have a sprite that the user is able to move side to side by pressing on the left or right side of the screen. If you hold down on either side, the player sprite will leave the screen. I want to stop that from happening using physic bodies, but I can't seem to make it work.
To start, here are my categories.
//Categories for physics bodies
let sceneCategory:UInt32 = 0x1 << 0 // Equal to 1
let playerCategory:UInt32 = 0x1 << 1 // Equal to 2
Here is where I set the physics body of the scene itself.
self.physicsWorld.contactDelegate = self
self.physicsBody = SKPhysicsBody(edgeLoopFrom: self.frame)
self.physicsBody?.categoryBitMask = sceneCategory
self.physicsBody?.contactTestBitMask = playerCategory
self.physicsBody?.collisionBitMask = 0
self.physicsBody?.isDynamic = false
This is how I have the physics set for the player itself. I'm trying to set the physics body around the car itself. Is the way I have it setup the same as setting an alpha mask physics body?
let texture = SKTexture(imageNamed: "PorscheBlue")
player.physicsBody = SKPhysicsBody(texture: texture, size: player.size)
player.physicsBody?.isDynamic = false
player.physicsBody?.categoryBitMask = playerCategory
player.physicsBody?.contactTestBitMask = sceneCategory
player.physicsBody?.collisionBitMask = 0
Then, in the didBegin method, I just wanted it to print showing that the method was called. This isn't happening though for some reason.
func didBegin(_ contact: SKPhysicsContact)
{
print("called")
}
Why isn't the didBegin method being called? Do I have the physics set up properly? How can I make it so the player isn't allowed to leave the screen when moving?
Thank you
EDIT: So when having the physics boundaries visible, it looks like the boundaries for the scene are only being drawn on the top and bottom. I can't see any lines being drawn on the sides. That may be the issue, but I can't get it to show on the sides.
I resolved my issue by changing the dynamic value. I had both set to false, which causes the didBegin function to not be called. At least one of the nodes needs to be dynamic.

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.

Reset an SkSpriteNode physics simulation in Swift 3

So I'd like to move a square from the bottom left of the screen to the top right. However every time this action happens, the square has a little push towards the right side of the screen and also remains with its previous tilt... Can I just reset its physics simulation?
object.removeAllActions()
object.position.y = frame.maxY + 50
object.position.x = frame.miX + 50
Here's how to reset completely a node's physics:
object.physicsBody = SKPhysicsBody()
I do this all the time. I find it easier to make a function to do this for me, so that way you don't have to reconfigure the pb each time you want it to be reset:
class Player: SKSpriteNode {
func resetPB() {
self.physicsBody = SKPhysicsBody(rectangleOf: self.size)
self.physicsBody?.categoryBitMask = UInt(4)
self.physicsBody?.mass = 35
// And so on...
}
}
Or if you aren't using a subclass:
func resetPlayerPB(_ player: SKSpriteNode) {
player.physicsBody = SKPhysicsBody(rectangleOf: player.size)
// And so on...
}

Swift/SpriteKit Collision Detection Between Many Objects

So I have two objects that should lose health points at an collision.
func addPlayer(xPos: CGFloat, yPos: CGFloat){
playerNode = SKSpriteNode(imageNamed: "player")
playerNode.physicsBody = SKPhysicsBody(circleOfRadius: width/2)
playerNode.physicsBody!.affectedByGravity = false
playerNode.physicsBody!.categoryBitMask = PhysicsCategory.Player
playerNode.physicsBody!.contactTestBitMask = PhysicsCategory.Wall | PhysicsCategory.Zombie
playerNode.physicsBody!.collisionBitMask = PhysicsCategory.Wall | PhysicsCategory.Zombie
playerNode.name = "Player"
player = Player(node: playerNode, healthPoints: 100, attack: 10)
playerNode.position.x = xPos
playerNode.position.y = yPos
playerNode.size = CGSize(width: width, height: width)
addChild(playerNode)
}
func addZombie(xPos: CGFloat, yPos: CGFloat){
zombieNode = SKSpriteNode(imageNamed: "zombie")
zombieNode.physicsBody = SKPhysicsBody(circleOfRadius: width/2)
zombieNode.physicsBody!.affectedByGravity = false
zombieNode.physicsBody!.categoryBitMask = PhysicsCategory.Zombie
zombieNode.physicsBody!.contactTestBitMask = PhysicsCategory.Zombie | PhysicsCategory.Player | PhysicsCategory.Wall
zombieNode.physicsBody!.collisionBitMask = PhysicsCategory.Zombie | PhysicsCategory.Player | PhysicsCategory.Wall
zombieNode.name = "Zombie"
zombie = Zombie(node: zombieNode, healthPoints: 50, attack: 5)
Zombies.append(zombie!)
zombieNode.position.x = xPos
zombieNode.position.y = yPos
zombieNode.size = CGSize(width: width, height: width)
addChild(zombieNode)
}
When a collision appears this function get activated:
func didBeginContact(contact: SKPhysicsContact) {
let firstBody = contact.bodyA.node as! SKSpriteNode
let secondBody = contact.bodyB.node as! SKSpriteNode
if(firstBody.name == "Player" && secondBody.name == "Zombie"){
changeHealthPointsForZombieWithNode(secondBody, points: player!.attack)
} else if(firstBody.name == "Zombie" && secondBody.name == "Player"){
changeHealthPointsForPlayer(secondBody, points: zombie!.attack)
print(player!.healthPoints)
}
}
func changeHealthPointsForZombieWithNode(node: SKSpriteNode, points: Int) {
for zombie in Zombies {
if zombie.node == node {
zombie.healthPoints -= points
print(zombie.healthPoints)
if(zombie.healthPoints <= 0){
zombieNode.removeFromParent()
}
return
}
}
}
func changeHealthPointsForPlayer(node: SKSpriteNode, points: Int) {
player!.healthPoints -= points
if(player!.healthPoints <= 0){
playerNode.removeFromParent()
gameOver = true
}
}
I want to subtract the health points of the zombie depending on the attack of the player and other way around. When the player hits the zombie the zombie should lose life points. When the zombie hits the player the player should lose life points. Every player/zombie got health points and an attack value. The Problem is that some zombies are killable and lose health and other (normally 1-3) are not able to lose health. These zombies who aren't able to lose health are the only one able to kill the player. Zombies that lose health can't deal damage(why?)? So there only able to do one thing(attack or lose health) although they should be able to do two things(attack and lose health).
Keep in mind that the two physics bodies described in the contact parameter are not passed in a guaranteed order. Inside the delegate function there is not such a thing as "A is the one colliding with B, not the other way around". There is only "A and B are colliding". That said, you have two options at least, depending on your game mechanics:
Make player AND zombie deal damage to each other every time they collide.
Make for player and zombie a subclass of SKSpriteNode with a property isAttacking, a boolean to determine when the entity should deal damage when detecting a contact with it. For the player this boolean might get "activated" every button/tap press. For the zombie, every once in a while, if close enough to the player.
Additionally, the way you implemented you will be getting doubled contact notifications. SpriteKit will detect that the player is in contact with the zombie, by the player contact mask and zombie category mask, and that the zombie is in contact with the player, by the zombie contact mask and the player category mask. You usually don't want this to happen. You should make only one of them detect it. I would suggest the player. This way there is no need to set the zombie contact mask. As long as you set zombie in the player's contact mask and the zombie's category mask, you will already have the detection (and once).