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

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")
}
}

Related

How to add a sprite from another class on the gameScene

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.
}
}

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

Nodes don't appear in iOS Simulator (GameKit)

Update: Even Apple's template code won't display nodes in the simulator.
The simplest of code, such as creating a red square node will not work (this project worked until today, and I haven't changed it). For example:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let myNode = SKSpriteNode(color: UIColor.red, size: CGSize(width: 300, height: 300))
myNode.position = CGPoint(x: 0, y: 0)
self.addChild(myNode)
}
}
Or more complicated code such as:
import SpriteKit
import GameplayKit
class GameScene: SKScene {
let myNode = SKSpriteNode(imageNamed: "Spaceship.png")
override func didMove(to view: SKView) {
myNode.position = CGPoint(x: 0, y: 0)
myNode.xScale = 0.5
myNode.yScale = 0.5
let action = SKAction.rotate(byAngle: 2, duration: TimeInterval(2))
myNode.run(SKAction.repeatForever(action))
self.addChild(myNode)
}
}
For some reason either changing the project name in general, or changing it to not include a dash fixed it. I thought I would post this in case anyone else has this problem.

Swift SpriteKit Mario style rotating platform that stays horizontal

I am trying to create a mario style rotating platform that stays horizontal.
What I have done so far is create a simple class for this for testing purposes.
class PlatformRound: SKNode {
let platform: Platform
// MARK: - Init
init(barSize: CGSize, color: SKColor, pos: CGPoint) {
/// Base
let base = SKShapeNode(circleOfRadius: 6)
//base.name = Platform.Name.normal
base.fillColor = SKColor.darkGrayColor()
base.strokeColor = base.fillColor
base.position = pos
let rotatingAction = SKAction.rotateByAngle(CGFloat(-M_PI), duration: 8)
base.runAction(SKAction.repeatActionForever(rotatingAction))
/// Bar
let bar = Platform(size: barSize, color: color, pos: CGPointMake(0, 0 - (barSize.height / 2)), ofType: .Normal)
bar.zPosition = -200
/// Platform that supposed to stay horizontal
let platformSize = CGSizeMake(40, GameplayConfig.World.platformHeight)
let platformPos = CGPointMake(0, 0 - (bar.size.height / 2))
platform = Platform(size: platformSize, color: color, pos: platformPos, ofType: .Normal)
super.init()
addChild(base)
base.addChild(bar)
bar.addChild(platform)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I am creating a roundBase that I can rotate. I than create a bar that goes down from the base, that is added to the base node. Finally I create the platform that is supposed to stay horizontal at all times.
I am using another Platform subclass to create the bar and platform, but they are not relevant to this question.
How can I make the platform stay horizontal. I tried the following which didnt work.
1) In update in my gameScene I constantly update the platform position or zRotation
platformRound.platform.zRotation = ...
2) Create a zRotation property that gets set once the platform is added and than use that property to constantly update the zRotation.
3) Tried playing around with physicsJoints
Im sure there is a easy way that I am missing. I would appreciate any help.
This should work:
class GameScene: SKScene{
override func didMoveToView(view: SKView) {
backgroundColor = .blackColor()
let centerSprite = SKSpriteNode(color: .whiteColor(), size: CGSize(width: 10, height: 10))
centerSprite.zPosition = 3
let platform = SKSpriteNode(color: .orangeColor(), size: CGSize(width: 70, height: 20))
platform.zPosition = 2
platform.name = "platform"
let container = SKNode()
container.position = CGPoint(x: frame.midX, y: frame.midY)
container.addChild(centerSprite) //Just for debugging
container.addChild(platform)
let radius = 120
let chain = SKSpriteNode(color: .grayColor(), size: CGSize(width: 3, height: radius))
chain.position = CGPoint(x: 0, y: radius/2)
container.addChild(chain)
platform.position = CGPoint(x: 0, y: radius)
let rotatingAction = SKAction.rotateByAngle(CGFloat(-M_PI), duration: 8)
container.runAction(SKAction.repeatActionForever(rotatingAction), withKey: "rotating")
addChild(container)
}
override func didEvaluateActions() {
self.enumerateChildNodesWithName("//platform") { node,stop in
if let parent = node.parent{
node.zRotation = -parent.zRotation
}
}
}
}
What I did, is that I have added platform node into container node and applied rotation to that container. Later on, in didEvaluateActions I've adjusted the rotation of platform (to have a negative value of its parent's zRotation). And that's it.
Here is the result of what I am seeing:
The adjusting is needed, because otherwise the platform will end-up rotating along with its parent (notice how white, center sprite is being rotated along with container node).

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.