I am working on a spritekit game and it all is working correctly, yet my ground is in the middle of the simulators screen. I have tried everything to make it be at the bottom of the screen. one function.
let groundTexture = SKTexture(imageNamed: "Ground")
groundTexture.filteringMode = .nearest
for i in stride(from: 0, to: 2 + self.frame.size.width / groundTexture.size().width, by: 1) {
let ground = SKSpriteNode(texture: groundTexture)
ground.zPosition = 1.0 //-10
//ground.position = CGPoint(x: (groundTexture.size().width / 2.0 + (groundTexture.size().width * CGFloat(i))), y: groundTexture.size().height / 4.0) //original position
ground.position = CGPoint(x: 0, y: groundTexture.size().height / +0) //tried this from a tutorial
ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width * 2.0, height: groundTexture.size().height / 4.0)) //erase * 4 test
ground.setScale(1.2)
ground.physicsBody?.isDynamic = false
ground.physicsBody?.allowsRotation = false
ground.physicsBody?.affectedByGravity = false
ground.physicsBody?.categoryBitMask = groundCategory
//contact and collision bitmask
ground.physicsBody?.contactTestBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
ground.physicsBody?.collisionBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
ground.physicsBody?.restitution = 0.0
self.addChild(ground)
let moveLeft = SKAction.moveBy(x: -groundTexture.size().width, y: 0, duration: 5)
let moveReset = SKAction.moveBy(x: groundTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
ground.run(moveForever)
}
By default a scene's anchorPoint coordinates are 0, 0 unless otherwise specified. anchorPoint(x: 0, y: 0) is the center of the screen. You are not specifying a position for your ground so it gets automatically added to the scenes anchorPoint (which is the middle of the screen).
You need to either change the scenes anchorPoints to the bottom of the screen or adjust the ground position accordingly such as...
ground.position = CGPoint(x: 0 - self.size.width / 2 + ground.size.width / 2, y: 0 - self.size.height / 2 + ground.size.height / 2)
(the above example assumes you are adding the ground in the scene and self = scene)
for your reference...
Related
I found this may have to do with the setScale, I tried it with a few different sizes and the ground don't disappear after few seconds when on 3.0-4.0, but those scale sizes are way too big for the game. I need to use setScale(1.0). I've tested it many times with different sizes,
however I setScale(1.0) is the size I need for the ground sprite.
Here is the code:
func createGround() {
let groundTexture = SKTexture(imageNamed: "Ground")
groundTexture.filteringMode = .nearest
for _ in stride(from: 0, to: 2 + self.frame.size.width / groundTexture.size().width, by: 1) { //i
let ground = SKSpriteNode(texture: groundTexture)
ground.zPosition = -10
ground.anchorPoint = CGPoint(x:0.5,y:0.5)
ground.position = CGPoint(x: 0 - self.size.width / 2 + ground.size.width / 2, y: 0 - self.size.height / 2 + ground.size.height / 2)
ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width * 2.0, height: groundTexture.size().height / 1.0))
ground.setScale(1.0) //1.0 is the number I need however it disappears after a few seconds and the character falls and doesn't come back.
ground.physicsBody?.isDynamic = false
ground.physicsBody?.allowsRotation = false
ground.physicsBody?.affectedByGravity = false
ground.physicsBody?.usesPreciseCollisionDetection = true
ground.physicsBody!.categoryBitMask = groundCategory
ground.physicsBody!.contactTestBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
// ground.physicsBody!.collisionBitMask = playerCategory | enemy1Category | enemy2Category | enemy3Category | enemy4Category | obstacleCategory | coinCatergory
ground.physicsBody!.collisionBitMask = 0 //tried with 0 and with catergories
ground.physicsBody?.restitution = 0.0
//ground.zPosition = 4.0 //see if zPosition makes the robot stay
// self.addChild(ground)
let moveLeft = SKAction.moveBy(x: -groundTexture.size().width, y: 0, duration: 7)
let moveReset = SKAction.moveBy(x: groundTexture.size().width, y: 0, duration: 0)
let moveLoop = SKAction.sequence([moveLeft, moveReset])
let moveForever = SKAction.repeatForever(moveLoop)
ground.run(moveForever)
self.addChild(ground)
}
}
I'm having a weird Spritekit issue where my moving SKSpriteNode is passing through a fixed SKSpriteNode depending on the position of the fixed SKSpriteNode.
UPDATE: Code works in simulator but not on real device.
Example:
Placing my bin SKSpriteNode at position x: -500, y: 100 works fine and my moving SKSpriteNode collides as expected.
Placing my bin SKSpriteNode at position x: -600, y: 100 DOES NOT work and my moving SKSpriteNode DOES NOT collide with the bin.
Using view.showsPhysics = true shows that there is physics bodies in both cases.
x values between -500 and -508 work as expected. All other values I have tried did not work.
Collisions with my other fixed SKSpriteNodes work as expected.
enum CollisionTypes: UInt32 {
case Plane = 1
case FloorAndRoof = 2
case OtherObject = 4
case FinishPoint = 8
}
Code to create levels
func createLevel(level: Int) {
switch level {
case 1:
createFloor()
createRoof()
createTable(position: CGPoint(x: 750, y: 150))
createCeilingFan(position: CGPoint(x: 750, y: 560))
createCeilingFan(position: CGPoint(x: 2000, y: 560))
createWaterDispenser(position: CGPoint(x: 1500, y: 212))
createBin(position: CGPoint(x: -500, y: 100)) // THIS IS THE PROBLEM LOCATION
createFinishPoint(position: CGPoint(x: -500, y: 100))
break
case 2:
createFloor()
createRoof()
createTable(position: CGPoint(x: 250, y: 150))
createCeilingFan(position: CGPoint(x: 750, y: 560))
createCeilingFan(position: CGPoint(x: 2000, y: 560))
createWaterDispenser(position: CGPoint(x: 1500, y: 212))
createBin(position: CGPoint(x: -600, y: 100))
createFinishPoint(position: CGPoint(x: -300, y: 200))
break
default:
break
}
}
Moving SKSpriteNode
func createPlane() {
plane = SKSpriteNode(imageNamed: "plane1")
plane.name = "plane1"
//plane.position = CGPoint(x: -UIScreen.main.bounds.width + plane.size.width , y: 0)
plane.position = CGPoint(x: 0, y: 300)
plane.zPosition = 1
//plane.physicsBody = SKPhysicsBody(texture: plane.texture!, size: plane.texture!.size())
plane.physicsBody = SKPhysicsBody(texture: planeTexture, size: planeTexture.size())
plane.physicsBody?.allowsRotation = true
plane.physicsBody?.categoryBitMask = CollisionTypes.Plane.rawValue
plane.physicsBody?.collisionBitMask = CollisionTypes.FloorAndRoof.rawValue | CollisionTypes.OtherObject.rawValue // dont collide with finish point
plane.physicsBody?.contactTestBitMask = CollisionTypes.FloorAndRoof.rawValue | CollisionTypes.OtherObject.rawValue | CollisionTypes.FinishPoint.rawValue
plane.physicsBody?.usesPreciseCollisionDetection = true
plane.physicsBody?.isDynamic = true
plane.physicsBody?.friction = 1
plane.physicsBody?.restitution = 0
plane.physicsBody?.mass = 0.1 // customise for different planes
plane.physicsBody?.angularDamping = 1
plane.physicsBody?.linearDamping = 0.2 // customise for different planes
liftFactor = 0.1 // customise for different planes
addChild(plane)
flightMode = 4 // dead, should drop to floor and change to mode 0 when at rest
//print(flightMode)
}
Bin SKSpriteNode that moving Plane should collide with.
func createBin(position: CGPoint) {
binFront = SKSpriteNode(imageNamed: "binFront")
binFront.name = "binFront"
binFront.setScale(0.15)
binFront.position = position
binFront.zPosition = 2 // in front of plane
addChild(binFront)
binBack = SKSpriteNode(imageNamed: "binBack")
binBack.name = "binBack"
binBack.setScale(0.15)
binBack.position = position
binBack.zPosition = 0 // behind plane
addChild(binBack)
binPhysicsBody = SKSpriteNode(imageNamed: "binPhysicsBody")
binPhysicsBody.name = "binPhysicsBody"
binPhysicsBody.physicsBody = SKPhysicsBody(texture: binPhysicsBody.texture!, size: binPhysicsBody.texture!.size())
binPhysicsBody.setScale(0.15)
binPhysicsBody.physicsBody?.allowsRotation = false
binPhysicsBody.physicsBody?.categoryBitMask = CollisionTypes.OtherObject.rawValue
binPhysicsBody.physicsBody?.collisionBitMask = CollisionTypes.Plane.rawValue
binPhysicsBody.physicsBody?.contactTestBitMask = CollisionTypes.Plane.rawValue
binPhysicsBody.physicsBody?.usesPreciseCollisionDetection = false
binPhysicsBody.physicsBody?.isDynamic = false
binPhysicsBody.physicsBody?.friction = 1
binPhysicsBody.physicsBody?.restitution = 0
binPhysicsBody.physicsBody?.mass = 50
binPhysicsBody.position = position
binPhysicsBody.zPosition = 0
addChild(binPhysicsBody)
}
I still don't know the cause of my issue but I got around it by changing the image used to create my physics body. I used a slightly thicker 'wall thickness' on the bin.
i've got a problem with the collisions.
adding physicbody depending on tilemap:
guard let tilemap = childNode(withName: "LevelGround") as? SKTileMapNode else { return }
let tileSize = tilemap.tileSize
let halfWidth = CGFloat(tilemap.numberOfColumns) / 2.0 * tileSize.width
let halfHeight = CGFloat(tilemap.numberOfRows) / 2.0 * tileSize.height
for row in 0..<tilemap.numberOfRows {
for col in 0..<tilemap.numberOfColumns {
if tilemap.tileDefinition(atColumn: col, row: row) != nil {
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(rectangleOf: tileSize, center: CGPoint(x: tileSize.width / 2.0, y: tileSize.height / 2.0))
tileNode.physicsBody?.isDynamic = false
tileNode.physicsBody?.affectedByGravity = false
tileNode.physicsBody?.collisionBitMask = Physics().player | Physics().bullet
tileNode.physicsBody?.categoryBitMask = Physics().ground
tileNode.physicsBody?.contactTestBitMask = Physics().ground | Physics().player
tileNode.physicsBody?.friction = 0.1
tileNode.physicsBody?.restitution = 0
tileNode.strokeColor = .white
tileNode.name = "Ground"
self.world.addChild(tileNode)
}
}
}
Let the bullet spawn on tapping the action2 button:
if self.ctrlAction2.contains(touch.location(in: cam)) {
let bullet = SKShapeNode(ellipseOf: CGSize(width: 5, height: 4))
bullet.fillColor = .orange
bullet.physicsBody = SKPhysicsBody(edgeLoopFrom: bullet.path!)
bullet.physicsBody?.affectedByGravity = true
bullet.physicsBody?.categoryBitMask = Physics().bullet
bullet.physicsBody?.collisionBitMask = Physics().enemy | Physics().ground
bullet.physicsBody?.contactTestBitMask = Physics().bullet | Physics().enemy | Physics().ground
bullet.position.y = charakter.position.y + charakter.size.height / 7
bullet.physicsBody?.isDynamic = true
bullet.name = "bullet"
if facing == "right" {
bullet.position.x = charakter.position.x + charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: 2000, y: 0, duration: 4))
} else if facing == "left" {
bullet.position.x = charakter.position.x - charakter.size.width / 1.9
self.world.addChild(bullet)
bullet.run(SKAction.moveBy(x: -2000, y: 0, duration: 4))
}
}
The bullet collides with enemies, the enemies collides with the player and the player collides with the ground - everything works, except the collision between bullet and ground.
Thank you for your help in advance
Pascal
So I am using 3 SKSpriteNode's in my application. floorSprite, leftWall and rightWall.
This is the code I am currently using:
let floorSprite = SKSpriteNode()//(imageNamed: "floor")
floorSprite.alpha = 0.0
floorSprite.anchorPoint = CGPoint(x: 0.5, y: CGPoint.zero.y)
floorSprite.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: self.frame.size.width, height: floorSprite.size.height + 12))
floorSprite.physicsBody?.isDynamic = false
floorSprite.physicsBody?.restitution = 0.4
floorSprite.position = CGPoint(x: self.frame.minX, y: self.frame.minY + (floorSprite.size.height / 2))
floorSprite.physicsBody?.contactTestBitMask = 0x1 << 1
floorSprite.physicsBody?.collisionBitMask = 0x1 << 1
floorSprite.zPosition = 4
floorSprite.aspectFillToSize(self.frame.size)
self.addChild(floorSprite)
self.floorSprite = floorSprite
let leftwall = SKSpriteNode()
leftwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 0.1, height: self.frame.size.height*2))
leftwall.physicsBody?.collisionBitMask = 0x1 << 1
leftwall.physicsBody?.isDynamic = false
leftwall.position = CGPoint(x: 0 , y: self.frame.size.height / 2.0)
leftwall.zPosition = 4
leftwall.physicsBody?.restitution = 1.0
self.addChild(leftwall)
let rightwall = SKSpriteNode()
rightwall.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 0.1, height: self.frame.size.height*2))
rightwall.physicsBody?.collisionBitMask = 0x1 << 1
rightwall.physicsBody?.isDynamic = false
rightwall.physicsBody?.restitution = 1.0
rightwall.position = CGPoint(x: self.frame.size.width , y: self.frame.size.height / 2.0)
rightwall.zPosition = 4
self.addChild(rightwall)
Here is a screenshot of the issue I have. You can see the light blue lines that shows. If I remove the self.addChild line, the Sprite is not there, and the light blue color gone.
CLICK HERE FOR IMAGE.
Any ideas what might be wrong here? I have tried adding leftWall.isHidden = true and leftWall.alpha = 0.0 but that has no effect...
This looks like you have SKScene.showPhysics on.. turn this off in your appdelegate (or didmovetoview) or wherever you turned it on, and the lines should go away.
These are caused by your physics bodies :)
I awant to add SKSpriteNode's after something repositioned therefore I want to add the nodes through the update method in Swift Sprite Kit.
There are Objects ( platforms ) that move downwards the y axis, once they are out of screen they appear on the top side again ( like an infinite loop ). I visualize this in the update method. Note that the platforms dont really move, rather then I centered a camera on my Player node that moves up.
After the reposition of the platform node, I want to add an Enemy again on top of the platforms, however because I try it through the update method, it adds sometimes more than 1 node on top of it.
I cant reposition the Enemy like I do with the platforms, because it should be a random enemy node.
Any way to call the SpawnEnemy method in the update method, and check if it was called only once?
My Code:
override func didMoveToView(view: SKView) {
/* Setup your scene here */
self.World = SKNode()
self.World.name = "World"
addChild(World)
self.WorldCamera = SKNode()
self.WorldCamera.name = "Camera"
self.World.addChild(WorldCamera)
....
SpawnPlatforms()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform3.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform4.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform5.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform6.position.y + 30))
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform7.position.y + 30))
}
func SpawnPlatforms() {
Platform0 = SKSpriteNode (color: SKColor.greenColor(), size: CGSize(width: self.frame.size.width * 2 , height: 25))
Platform0.position = CGPoint(x: self.frame.size.width / 2, y: -36)
Platform0.zPosition = 1
Platform0.physicsBody = SKPhysicsBody(rectangleOfSize:Platform0.size)
Platform0.physicsBody?.dynamic = false
Platform0.physicsBody?.allowsRotation = false
Platform0.physicsBody?.restitution = 0.0
Platform0.physicsBody?.usesPreciseCollisionDetection = true
Platform0.physicsBody?.velocity = CGVectorMake(0, 0)
Platform0.physicsBody?.categoryBitMask = Platform0Category
Platform0.physicsBody?.collisionBitMask = PlayerCategory | EnemyCategory
Platform0.physicsBody?.contactTestBitMask = PlayerCategory | EnemyCategory
World.addChild(Platform0)
Platform1 = SKSpriteNode (color: SKColor.redColor(), size: CGSize(width: self.frame.size.width * 2 , height: 25))
Platform1.position = CGPoint(x: self.frame.size.width / 2, y: Platform0.position.y + self.frame.size.height / 4.5)
Platform1.zPosition = 1
Platform1.physicsBody = SKPhysicsBody(rectangleOfSize:Platform1.size)
Platform1.physicsBody?.dynamic = false
Platform1.physicsBody?.allowsRotation = false
Platform1.physicsBody?.restitution = 0
Platform1.physicsBody?.usesPreciseCollisionDetection = true
Platform1.physicsBody?.categoryBitMask = Platform1Category
Platform1.physicsBody?.collisionBitMask = PlayerCategory | EnemyCategory
Platform1.physicsBody?.contactTestBitMask = PlayerCategory | EnemyCategory
World.addChild(Platform1)
....a.s.o. for the other 6 platform nodes
}
func SpawnEnemy(position: CGPoint!){
let random = arc4random_uniform(4)
switch (random) {
case 0:
let node1 = SpawnNode1(position)
self.addChild(node1)
break
case 1:
let node2 = SpawnNode2(position)
self.addChild(node2)
break
case 2:
let node3 = SpawnNode3(position)
self.addChild(node3)
break
case 3:
break
default:
break
}
}
override func didSimulatePhysics() {
WorldCamera.position = CGPoint(x: Player.position.x, y: Player.position.y)
self.centerOnNode(WorldCamera!)
}
func centerOnNode(node: SKNode) {
let cameraPositionInScene: CGPoint = WorldCamera.scene!.convertPoint(WorldCamera.position, fromNode: World)
World.runAction(SKAction.moveTo(CGPoint(x:World.position.x , y:World.position.y - cameraPositionInScene.y), duration: 1.0))
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if(Platform1.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform1.position = CGPointMake(self.frame.size.width / 2, Platform7.position.y + self.frame.size.height / 4.5)
Platform1.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform1.position.y + 30))
}
if(Platform2.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform2.position = CGPointMake(self.frame.size.width / 2, Platform1.position.y + self.frame.size.height / 4.5)
Platform2.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform2.position.y + 30))
}
if(Platform3.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform3.position = CGPointMake(self.frame.size.width / 2, Platform2.position.y + self.frame.size.height / 4.5)
Platform3.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform3.position.y + 30))
}
if(Platform4.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform4.position = CGPointMake(self.frame.size.width / 2, Platform3.position.y + self.frame.size.height / 4.5)
Platform4.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform4.position.y + 30))
}
if(Platform5.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform5.position = CGPointMake(self.frame.size.width / 2, Platform4.position.y + self.frame.size.height / 4.5)
Platform5.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform5.position.y + 30))
}
if(Platform6.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform6.position = CGPointMake(self.frame.size.width / 2, Platform5.position.y + self.frame.size.height / 4.5)
Platform6.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform6.position.y + 30))
}
if(Platform7.position.y < (Player.position.y - self.frame.size.height / 2)){
Platform7.position = CGPointMake(self.frame.size.width / 2, Platform6.position.y + self.frame.size.height / 4.5)
Platform7.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: Platform7.position.y + 30))
}
}
Your problem is you do not create any kind of check that moving the platform has already spawned an enemy. Sure, you move the platform, but until your player moves, it will constantly spawn enemies. I do not see where this is happening, but you should spawn enemy when you move the platform, not on your update
Edit:
yikes, I see it now, this is some ugly code lol, In all those ifs that move the platform, you should spawn the enemy. But this needs to be worked to also check if the previous platform is below the current, not above
Edit++:
Upon further review and added details, try something like this instead:
First, give all Platforms a name "Platform"
Then
override func update(currentTime: CFTimeInterval) {
for node in scene.children
{
if (node.name == "Platform" && !node.intersectsNode(scene) && node.position.y < player.y - (self.frame.size.height / 4.5))
{
let platform = node as! SKSpriteNode
platform.position.y += (7 * (self.frame.size.height / 4.5))
platform.color = SKColor.redColor()
SpawnEnemy(CGPoint(x: self.frame.size.width / 2, y: platform.position.y + 30))
}
}
}
Question: How does this even work, I would just check if a platform hits a certain point in the scene, recycle it, instead of having to check every single platform and moving it's previous one
so you're spawning an enemy on every frame.. thats going to be way too many enemies.. I'm still not totally following the exact architecture of your game, but this might help you out.
get delta time to see how much time has passed.. and every 10 seconds (or whatever) call your spawn enemy code instead of calling it on every frame
// time values
var delta = NSTimeInterval(0)
var last_update_time = NSTimeInterval(0)
let spawnTimerMax = NSTimeInterval(10)
var spawnTimer = NSTimeInterval(0)
override func update(currentTime: NSTimeInterval) {
if last_update_time == 0.0 {
delta = 0
} else {
delta = currentTime - last_update_time
}
last_update_time = currentTime
// ten seconds elapsed, spawn enemy and restart.
spawnTimer += delta
if spawnTimer >= spawnTimerMax {
// your spawn code goes here
spawnTimer = 0
}
}