I have been trying to have a sprite with more than one texture so that it looks as if it was moving. I was looking at other questions and nothing they have done has fixed my issue, this is my code:
var textures = [SKTexture]()
for x in 0...2 {
let texture = SKTexture(imageNamed: "peng_" + String(x))
textures.append(texture)
print("pen_" + String(x))
}
let pen = SKSpriteNode(imageNamed: "peng_0")
self.addChild(pen)
let action = SKAction.animate(withNormalTextures: textures, timePerFrame: 0.1, resize: false, restore: true)
pen.run(SKAction.repeatForever(action))
When I run the simulation, the sprite stays still with the "peng_0" texture, but I want it to iterate the other textures every 0.1s
Does anyone know what am I doing wrong, thanks.
Here you go set your code like this
var array = ["R1", "R2", "R3", "R4", "R5", "R6", "R7", "R8"]
var textures:[SKTexture] = []
for i in 0 ..< array.count{
let texture: SKTexture = SKTexture(imageNamed: array[i])
textures.insert(texture, at:i)
}
let pen = SKSpriteNode(imageNamed: "R1")
self.addChild(pen)
let animation = SKAction.animate(with: textures, timePerFrame: 8/60, resize: true , restore:false )
pen.run = SKAction.repeatForever(animation)
Related
The problem I have is when I initialize my sprite, I have another sprite defined the same way and that sprite shows that it has a physics body because I see the green outline on that, but this sprite below has no green outline on it, I think that there is a problem when I create the SKPhysicsBody or maybe because I create 13 duplicates of the same sprite at random positions as shown in the second snippet of code.
#objc func CreateNewAsteroid() {
var asteroid : SKSpriteNode?
let moveAsteroidDown = SKAction.repeatForever(SKAction.moveBy(x: 0, y: -1, duration: 0.01))
let rotateAsteroid = SKAction.repeatForever(SKAction.rotate(byAngle: 25, duration: 5))
let asteroidXpos = randomNum(high: self.frame.size.width/2, low: -1 * self.frame.size.width/2)
let asteroidYpos = randomNum(high: 2.5*self.frame.size.height, low: self.frame.size.height/2)
let asteroidOrigin : CGPoint = CGPoint(x: asteroidXpos, y: asteroidYpos)
asteroid = SKSpriteNode(imageNamed: possibleAsteroidImage[Int(arc4random_uniform(4))])
asteroid?.scale(to: CGSize(width: player.size.height, height: player.size.height))
asteroid?.position = asteroidOrigin
asteroid?.run(moveAsteroidDown)
asteroid?.run(rotateAsteroid)
let asteroidRadius : CGFloat = (asteroid?.size.width)!/2
asteroid?.physicsBody? = SKPhysicsBody(rectangleOf: (asteroid?.size)!)
asteroid?.physicsBody?.categoryBitMask = asteroidCategory
asteroid?.physicsBody?.affectedByGravity = false
asteroid?.physicsBody?.isDynamic = false
asteroid?.physicsBody?.allowsRotation = false
asteroid?.physicsBody?.categoryBitMask = asteroidCategory
asteroid?.physicsBody?.collisionBitMask = 0
asteroid?.physicsBody?.contactTestBitMask = shipCategory
asteroidArray.append(asteroid!)
self.addChild(asteroid!)
}
I create 13 of the sprites like this
for _ in 0...13 {
CreateNewAsteroid()
}
Instead of making the SKSpriteNode asteroid an optional, make it a non-optional so it does not become a null value by deleting the ? in var asteroid : SKSpriteNode?
So you want to put var asteroid : SKSpriteNode
After making this change, some errors will show up telling you to delete some !'s and ?'s (Make sure to delete these of course).
then go to your GameViewController.swift file and add the line view.showsPhysics = true under view.showsNodeCount = true
When you are done you should see a green outline around the sprite.
I just tried to animate some coins in my scene. In my other projects the animation works. But recently not here.
func addCoins() {
for coins in map.coinSpawns {
var coin = SKSpriteNode(imageNamed: "coin1")
coin.position = coins
coin.size = CGSize(width:map.tileSize - 10,height: map.tileSize - 10)
let action = SKAction.repeatForever(SKAction.animate(withNormalTextures: [SKTexture(imageNamed: "coin1.png"),SKTexture(imageNamed: "coin2.png"),SKTexture(imageNamed: "coin3.png"),SKTexture(imageNamed: "coin4.png")], timePerFrame: 0.5, resize: false, restore: true))
self.addChild(coin)
coin.run(action)
self.coins.append(coin)
}
}
Try to refactor a bit your code, removing .png from the file name (should be the fix) and extracting the textures array outside the coin loop (optimization), so your code might be:
func addCoins() {
let textures = ["coin1", "coin2", "coin3", "coin4"].flatMap { SKTexture(imageNamed: $0) }
for coins in map.coinSpawns {
var coin = SKSpriteNode(imageNamed: "coin1")
coin.position = coins
coin.size = CGSize(width:map.tileSize - 10,height: map.tileSize - 10)
self.addChild(coin)
self.coins.append(coin)
let action = SKAction.repeatForever(SKAction.animate(with: textures, timePerFrame: 0.5, resize: true, restore: false))
coin.run(action)
}
}
Is there a way to assign the filteringMode attribute to SKTextureFilteringMode.nearest for ALL SKTextures? Other than assigning to each texture individually. The following works fine, but I'd rather that I didn't have to iterate over the textures, but just set a default for the filtering mode. Is this possible?
func walk () -> SKAction {
let walkTexture1 = SKTexture(imageNamed: "walk1.png")
let walkTexture2 = SKTexture(imageNamed: "walk2.png")
let walkTexture3 = SKTexture(imageNamed: "walk3.png")
let walkTexture4 = SKTexture(imageNamed: "walk4.png")
let walkTexture5 = SKTexture(imageNamed: "walk5.png")
let animationTextures: [SKTexture] = [walkTexture1, walkTexture2, walkTexture3, walkTexture4, walkTexture5]
for texture in animationTextures {
texture.filteringMode = SKTextureFilteringMode.nearest
}
let walkAnimation = SKAction.animate(with: animationTextures, timePerFrame: 0.3/5)
return walkAnimation
Create textures and set properties in the same loop
let textures = (1...5).map {
let texture = SKTexture(imageNamed: "walk\($0).png")
texture.filteringMode = SKTextureFilteringMode.nearest
return texture
}
or just set properties using forEach
textures.forEach {
$0.filteringMode = .nearest
}
You could perhaps make an extension to SKTexture, something like this:
extension SKTexture {
class func pixeled(imageNamed imageName: String) -> SKTexture {
let texture = SKTexture(imageNamed: imageName)
texture.filteringMode = .nearest
return texture
}
}
Then you'll simply have
let walkTexture1 = SKTexture.pixeled(imageNamed: "walk1.png")
etc.
Hey so I have a problem detecting collisions. I have this project I am making based on Ray Wenderlich's tutorial on How to Make a game like Mega Jump. So everything is works great at the beginning, where platforms and stars are static in one place. But as you go further in the game, stars and platforms start moving (I am using SKActions for this), tried to do this to make it a little harder. But when I get there, collision is NOT being detected at all, player just passes by through the objects like they weren't there. I've been reading everywhere online, I am using precise collision detection but still I see no difference. Any ideas on what can be wrong or what else can I do? Here is a bit of code on how I am doing this:
player = SKSpriteNode(texture: firstFrame)
player.position = (CGPoint(x: self.size.width / 2, y: 50.0))
player.physicsBody = SKPhysicsBody(circleOfRadius: player.size.width / 2)
player.physicsBody?.dynamic = true
player.physicsBody?.allowsRotation = false
player.physicsBody?.restitution = 1.0
player.physicsBody?.friction = 0.0
player.physicsBody?.angularDamping = 0.0
player.physicsBody?.linearDamping = 0.0
player.physicsBody?.usesPreciseCollisionDetection = true
player.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Player
player.physicsBody?.collisionBitMask = 0
player.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform | CollisionCategoryBitmask.Monster
foregroundNode.addChild(player)
func createPlatformAtPosition(position: CGPoint, ofType type: PlatformType) -> PlatformNode {
let node = PlatformNode()
let thePosition = CGPoint(x: position.x * scaleFactor, y: position.y)
node.position = thePosition
node.name = "NODE_PLATFORM"
node.platformType = type
var sprite: SKSpriteNode
var spriteFrames : [SKTexture]!
if type == .Break {
let spriteAnimatedAtlas = SKTextureAtlas(named: "CloudBreak")
var cloudFrames = [SKTexture]()
spriteFrames = cloudFrames
let firstFrame = SKTexture(imageNamed: "Cloud02")
sprite = SKSpriteNode(texture: firstFrame)
let move = SKAction.moveToX(self.position.x - 160.0, duration:2.0)
let back = SKAction.moveToX(self.position.x, duration:2.0)
let sequence = SKAction.sequence([move, back, move, back])
sprite.runAction(SKAction.repeatActionForever(sequence))
} else {
let spriteAnimatedAtlas = SKTextureAtlas(named: "Cloud")
var cloudFrames = [SKTexture]()
spriteFrames = cloudFrames
let firstFrame = SKTexture(imageNamed: "Cloud")
sprite = SKSpriteNode(texture: firstFrame)
}
node.addChild(sprite)
node.physicsBody = SKPhysicsBody(rectangleOfSize: sprite.size)
node.physicsBody?.dynamic = false
node.physicsBody?.categoryBitMask = CollisionCategoryBitmask.Platform
node.physicsBody?.contactTestBitMask = CollisionCategoryBitmask.Player
node.physicsBody?.collisionBitMask = 0
return node
}
func didBeginContact(contact: SKPhysicsContact) {
var updateHUD = false
let whichNode = (contact.bodyA.node != player) ? contact.bodyA.node : contact.bodyB.node
let other = whichNode as GameObjectNode
updateHUD = other.collisionWithPlayer(player)
}
struct CollisionCategoryBitmask {
static let Player: UInt32 = 0x00
static let Star: UInt32 = 0x01
static let Platform: UInt32 = 0x02
static let Monster: UInt32 = 0x03
}
Slow the objects down and see if they collide. I had an issue where I was moving my nodes too fast and they were past the collision points when the new frame was rendered. If they move so fast they pass each other's physics body in the frame cycle they will not register a hit.
To detect a collision in that case, you can compare where they are in each frame, and if they have passed each other or their x/y plane values overlap, you can execute your collision code.
The collision is not detected because you didn't set the collisionBitMask.
player.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Star | CollisionCategoryBitmask.Platform | CollisionCategoryBitmask.Monster
node.physicsBody?.collisionBitMask = CollisionCategoryBitmask.Player
Contact and Collision aren't the same thing. You can find more information online.
If it still doesn't work, please show us your CollisionCategoryBitmask to make sure you created it properly.
Edit :
struct CollisionCategoryBitmask {
static let Player: UInt32 = 0
static let Star: UInt32 = 0b1
static let Platform: UInt32 = 0b10
static let Monster: UInt32 = 0b100
}
Here is a table I've made for another question, that might help you too :
http://goo.gl/7D8EGY
Check out my collision physics engine, it's very simple but has support for continuous/predicting/bullet collisions. You might learn something from the code, and it's written in javascript so it should be easy to read.https://github.com/Murplyx/AAE---Axis-Aligned-Engine
I'm creating a simple game. I'm trying to set the frame as an physicsBody so the plane, never falls below the screen. The function below stops it from falling all the way off, but the SKSpriteNode falls just out of view before stopping.
func setBoundry(view: SKView) {
// Create ground
var boundry = SKNode()
boundry.physicsBody = SKPhysicsBody(edgeLoopFromRect: view.frame)
boundry.physicsBody?.dynamic = false
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.Plane
boundry.physicsBody?.categoryBitMask = PhysicsCategory.Boundry | PhysicsCategory.Collidable
self.addChild(boundry)
}
Plane is being created like this...
func createPlane(sceneView: SKView) {
let planeTexture = SKTexture(imageNamed: "planeRed1")
let planeTexture1 = SKTexture(imageNamed: "planeRed2")
let planeTexture2 = SKTexture(imageNamed: "planeRed3")
// Animate plans propeller
let animation = SKAction.animateWithTextures([planeTexture, planeTexture1, planeTexture2], timePerFrame: 0.05)
let makePropellerSpin = SKAction.repeatActionForever(animation)
// Set planes position
plane = SKSpriteNode(texture: planeTexture)
plane.position = CGPointMake(size.width/4, size.height/2)
plane.runAction(makePropellerSpin)
plane.physicsBody = SKPhysicsBody(rectangleOfSize: CGSizeMake(plane.size.width, plane.size.height))
plane.physicsBody?.dynamic = true
plane.physicsBody?.allowsRotation = false
plane.physicsBody?.categoryBitMask = PhysicsCategory.Plane
plane.physicsBody?.collisionBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
plane.physicsBody?.contactTestBitMask = PhysicsCategory.Collidable | PhysicsCategory.Boundry
// Set elevation
plane.zPosition = 5
self.addChild(plane)
}
You need to set the contactTestBitMask for your boundry node and also set the physics for your plane-node:
boundry.physicsBody?.contactTestBitMask = PhysicsCategory.YourPlayerEnum
Also check out the collisionBitMask property of the physicsbody.