How to add a sprite from another class on the gameScene - swift

can anyone explain how to add a node to the gameScene please.
I subclassed my Boss class but i dont know how i can display my Boss1 on the GameScene
class Boss: GameScene {
var gameScene : GameScene!
var Boss1 = SKSpriteNode(imageNamed: "boss1")
override func didMove(to view: SKView) {
Boss1.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
Boss1.zPosition = 2
self.gameScene.addChild(Boss1)
}
}
im using swift 4 and xcode 9

You don't generally subclass the scene for instances such as this. More likely you are meaning to make boss a subclass of SKSpriteNode and adding it to your scene. While there is probably dozens of ways that you could subclass this, this is just 1 way.
Also worth noting that it is not generally acceptable practice to make your variable names capitalized, classes yes, variables no.
class Boss: SKSpriteNode {
init() {
let texture = SKTetxure(imageNamed: "boss1")
super.init(texture: texture , color: .clear, size: texture.size())
zPosition = 2
//any other setup such as zRotation coloring, additional layers, health etc.
}
}
...meanwhile back in GameScene
class GameScene: SKScene {
let boss1: Boss!
override func didMove(to view: SKView) {
boss1 = Boss()
boss1.position = CGPoint(x: size.width * 0.1, y: size.height * 0.5)
self.gameScene.addChild(boss1)
}
}
in GameScene you create an instance of the new class and position it and add it in the GameScene.
edit
the init function has a short cut in swift.
Boss()
is the same as
Boss.init()
you can also add custom parameters to your init in order to further clarify or add more features to your class. for example...
class Boss: SKSpriteNode {
private var health: Int = 0
init(type: Int, health: Int, scale: CGFloat) {
let texture: SKTetxure!
if type == 1 {
texture = SKTetxure(imageNamed: "boss1")
}
else {
texture = SKTetxure(imageNamed: "boss2")
}
super.init(texture: texture , color: .clear, size: texture.size())
self.health = health
self.setScale(scale)
zPosition = 2
//any other setup such as zRotation coloring, additional layers, health etc.
}
}

Related

How Can I increase radius of SKShapeNode by 1 pixel every second?

I am trying to write a function where I can increase SKShapeNode radius every second, but don't know how:(
var eRadius: CGFloat = 20
var eCircle = SKShapeNode()
override func didMove(to view: SKView) {
super.didMove(to: view)
eCircle = SKShapeNode(circleOfRadius: eRadius)
eCircle.strokeColor = .black
eCircle.glowWidth = 1.0
eCircle.fillColor = .white
eCircle.fillTexture = SKTexture(imageNamed: "e")
addChild(eCircle)
gameTimer = Timer.scheduledTimer(timeInterval: 1, target: self, selector: #selector(addRadius), userInfo: nil, repeats: true)
}
#objc func addRadius(){
eRadius += 1
???? :(
}
Thank you!
Here's a working solution:
import SpriteKit
class GameScene: SKScene {
private var eRadius: CGFloat = 20
private var eCircle = SKShapeNode()
override func didMove(to view: SKView) {
eCircle.strokeColor = .black
eCircle.glowWidth = 1.0
eCircle.fillColor = .white
eCircle.fillTexture = SKTexture(imageNamed: "e")
addChild(eCircle)
run(SKAction.repeatForever(SKAction.sequence([SKAction.run {
self.updateCirclePath()
self.eRadius += 1
}, SKAction.wait(forDuration: 1.0)])))
}
private func updateCirclePath() {
eCircle.path = UIBezierPath(ovalIn: CGRect(x: -eRadius * 0.5, y: -eRadius * 0.5, width: eRadius, height: eRadius)).cgPath
}
}
Basically, you create the circle using a UIBezierPath and then update it as your radius changes. For the updating you should use a forever-repeating SKAction sequence that both sets the path, then changes the radius. I chose to do the path first, then update the radius for the next upcoming turn, for clarity.
You can also do it the opposite way, so run the update function first to set the initial path, then wait one second, after which you change the radius, then update the path based on that radius.
In SpriteKit, you should use SKActions for timers instead of scheduling or dispatch queues. With SKActions, everything conforms to the same game time, which you can then control by pausing and whatever.
Also, there are multiple ways to make a circle using UIBezierPath, including ovals, rounded rectangles and arcs: https://developer.apple.com/documentation/uikit/uibezierpath.

Creating a background sprite node

I'm trying to create an endless scroller game and I'm trying to create two backgrounds.
I have a background class like so,
class BackgroundMG1: SKNode {
let background: SKSpriteNode
let size: CGSize
init(size: CGSize) {
self.size = size
let hue = CGFloat((arc4random() % 1000) / 1000)
let randomColor = UIColor(hue: hue, saturation: 1, brightness: 1, alpha: 1)
self.background = SKSpriteNode(color: randomColor, size: size)
super.init()
setup()
}
func setup() {
background.position.x = 0
background.position.y = 0
addChild(background)
}
}
I'm going to be populating my background with other objects so I needed it to be a class and not just a static image.
I have my game scene like so,
class AntarcticaMiniGame1: SKScene {
var player: SKSpriteNode!
var backgrounds: [BackgroundMG1]!
override func didMove(to view: SKView) {
setup()
}
func setup(){
// Initializers
player = Player()
// Positions
player.position.x = 0
player.position.y = 0
player.zPosition = 1
// Set up backgrounds
backgrounds = [BackgroundMG1]()
for i in 0..<2 {
let background = BackgroundMG1(size: size)
background.position.x = CGFloat(i) * size.width
background.position.y = 0
backgrounds.append(background)
addChild(background)
}
// Add to scene
addChild(player)
}
override func update(_ currentTime: TimeInterval) {
player.physicsBody!.applyForce(CGVector(dx: 25, dy:0))
}
}
You can also assume that I have a ground and camera object set up. I had to omit that code because it's not relevant to this question. The player stays on the ground and the camera follows the player.
Right now, when I load the game, the player object is moving to the right as expected. I see one background, but the second one doesn't show up.
I feel like I'm making some mistakes with the positioning. I'm not a 100% how the coordinate system works in Swift.
Edit: My scene is connected to a .sks file which is totally empty right now. The game is in landscape orientation.

SpriteKit calling function from class

This is from a simple game in SpriteKit with a Ball() class that has a function shieldOn() which, for the moment, simply replaces the texture of a single ball to that of a ball surrounded by a shield.
The ball is created like this in GameScene:
func getBall() {
let ball = Ball()
ball.createBall(parentNode: self)
}
Here is the Ball class
class Ball: SKSpriteNode {
func createBall(parentNode: SKNode) {
let ball = SKSpriteNode(texture: SKTexture(imageNamed: "ball2"))
ball.physicsBody = SKPhysicsBody(circleOfRadius: 25)
ball.name = "ball"
parentNode.addChild(ball)
ball.size = CGSize(width: 50, height: 50)
ball.position = CGPoint(x: 20, y: 200)
launch(spriteNode: ball, parentNode: parentNode)
}
private func launch(spriteNode: SKSpriteNode, parentNode: SKNode) {
spriteNode.physicsBody?.applyImpulse(CGVector(dx: 5, dy: 0))
}
func shieldOn() {
self.texture = SKTexture(imageNamed: "ballShield")
}
func shieldOff() {
self.texture = SKTexture(imageNamed: "ball2")
}
}
In the main section of my code (GameScene.swift) I don't have a reference to the ball. So I cycle through all of the nodes on the screen and try to cast the matching one as shown below. I crash with an error saying that it could not cast value of type SKSpriteNode to Ball.
for node in self.children {
if node.name == "ball" {
let ball = node as! Ball
ball.shieldOn()
}
}
I've tried a few variations with no luck. Am I at least working in the right direction? Thanks!
With the new information I think you want something like this:
Ball Class:
class Ball: SKSpriteNode{
init() {
let texture = SKTexture(imageNamed: "ball2")
let size = CGSize(width: 50, height: 50)
super.init(texture: texture, color: UIColor.clear, size: size)
self.name = "ball"
self.physicsBody = SKPhysicsBody(circleOfRadius: size.height/2)
self.physicsBody?.applyImpulse(CGVector(dx: 5, dy: 0))
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func shieldOn() {
self.texture = SKTexture(imageNamed: "ballShield")
}
func shieldOff() {
self.texture = SKTexture(imageNamed: "ball2")
}
}
Then use this to create the ball:
func getBall() {
let ball = Ball()
ball.position = CGPoint(x: 20, y: 200)
scene?.addChild(ball)
}
Perhaps a better way to do this would be to keep an array of all Balls created and added to the scene. Then you could just iterate through your array and update their texture. You would not need to enumerate them on the screen, which can decrease performance if there are many moving sprites.
As far as your code goes, it looks like you might be affected by this bug:
https://forums.developer.apple.com/thread/26362

Creating a sub-class from a node in the GameScene class

I'm trying to make a class from this node so I can make various objects from it, but Xcode keeps saying "Expected Declaration". How can I make a class from my node? Here is the code that's giving me an error:
P.S. I am relatively new to StackOverflow, so if my question needs more details please let me know instead of putting it on hold. Thanks!
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
class nodeClass{
let platform = SKSpriteNode(color: UIColor.yellow, size: CGSize(width: 400, height: 60))
platform.xScale = 0.8
platform.yScale = 0.8
platform.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: platform.size.width, height: platform.size.width / 6))
platform.position = CGPoint(x: 150, y: -100)
platform.physicsBody?.categoryBitMask = 1
platform.physicsBody?.collisionBitMask = 0
platform.physicsBody?.isDynamic = false
platform.physicsBody?.affectedByGravity = false
self.addChild(platform)
}
}
}
You should (ideally) declare and define your class outside of a function.
For lack of a better term, at the 'root' level of your file containing code.
For personal clarity, most folks, most of the time, create a new file and use that to hold their declaration and definition of all their classes can do. This isn't strictly necessary in Swift, but it's a helpful clarity technique for when you're starting out. As I am.
Next problem: nodeClass isn't, the way you've written it, subclassing anything. It's a 'standalone' type. By virtue of it not having anything after a colon stating what it's subclassing, it's an entirely new type.
Right above, you can see GameScene is a subclass of SKScene, because it's doing this. Any type names after the one being subclassed are Protocols GameScene is agreeing to conform to.
Ad #Confused just said, you should avoid declaring a class inside a method.
Here's a possible solution
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
let platform = Platform(size: CGSize(width: 400, height: 60))
platform.xScale = 0.8
platform.yScale = 0.8
platform.position = CGPoint(x: 150, y: -100)
self.addChild(platform)
}
}
class Platform: SKSpriteNode {
init(size: CGSize) {
super.init(texture: nil, color: .yellow, size: size)
let physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: size.width, height: size.width / 6)) // Are you sure about this??
physicsBody.categoryBitMask = 1
physicsBody.collisionBitMask = 0
physicsBody.isDynamic = false
physicsBody.affectedByGravity = false
self.physicsBody = physicsBody
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}

Attemped to add a SKNode which already has a parent - Swift

Ok. this code is driving me crazy. It just don't work. The only message I received is "Attemped to add a SKNode which already has a parent". Yes I know that there has been some discussions here, but none of them give the solution I need.
This is the code. I really appreciate any help.
import SpriteKit
class MyScene: SKScene {
let intervalShapeCreation:NSTimeInterval = 2.0 // Interval for creating the next Shape
let gravitationalAcceleration:CGFloat = -0.5 // The gravitational Y acceleration
let shapeSequenceAction = SKAction.sequence([
SKAction.scaleTo(1.0, duration: 0.5),
SKAction.waitForDuration(2.0),
SKAction.scaleTo(0, duration: 0.5),
SKAction.removeFromParent()
])
override init(size: CGSize) {
super.init(size: size)
}
required init(coder aDecoder: NSCoder) {
super.init(coder: aDecoder)
}
override func didMoveToView(view: SKView) {
super.didMoveToView(view)
addBackground()
initializeScene()
}
// MARK: Level Building
func initializeScene() {
self.physicsWorld.gravity = CGVectorMake(0.0, gravitationalAcceleration)
runAction(SKAction.repeatActionForever(
SKAction.sequence([SKAction.runBlock(self.createShape),
SKAction.waitForDuration(intervalShapeCreation)])))
}
func addBackground() {
let backgroundAtlas = SKTextureAtlas(named: "background")
let background = SKSpriteNode(texture: backgroundAtlas.textureNamed("background"))
background.position = CGPoint(x: size.width/2, y: size.height/2)
background.anchorPoint = CGPointMake(0.5, 0.5)
background.zPosition = -1
background.name = "background"
self.addChild(background)
}
func createShape() {
let newShape = sSharedAllPossibleShapes[0]
print("\n shape creada: \(newShape.name)")
newShape.position = CGPointMake(size.width / 2, CGFloat( Int.random(fromZeroToMax: 500)))
self.addChild(newShape)
newShape.runAction(shapeSequenceAction)
}
}
createShape doesn't actually create a SKShapeNode. It gets the first shape from the sSharedAllPossibleShapes array, then adds it as child to self. The second time you call this method that shape already has a parent and can't be added again.
You have to create a new instance of SKShapeNode. The way I see it your array here really needs to contain the CGPath objects that define the shape, not the nodes themselves because you can't reuse nodes the way you intended to.