Im getting 2 errors in line #objc func addAlien() (#objc can only be used with members of classes) and gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true) (use of local variable 'addAlian' before its declaration)
I'm sorry, i'm new in Swift developming, any ideas how i can fix it?
Thanks.
import SpriteKit
import GameplayKit
class GameScene: SKScene, SKPhysicsContactDelegate {
var starfield: SKEmitterNode!
var player: SKSpriteNode!
var scoreLabel: SKLabelNode!
var score: Int = 0{
didSet {
scoreLabel.text = "Счет \(score)"
}
}
var gameTimer: Timer!
var aliens = ["alien" , "alien2" , "alien3 "]
override func didMove(to view: SKView) {
starfield = SKEmitterNode (fileNamed: "Starfield") // Connect animation to starfield
starfield.position = CGPoint(x: 0, y: 1472) // Position starfield on screen
starfield.advanceSimulationTime(10)
self.addChild(starfield) // Add starfield on screen
starfield.zPosition = -1
player = SKSpriteNode(imageNamed: "damir2") // Подключаем картинку игрока
player.position = CGPoint (x: 0, y: -300) // Позиция игрока
self.addChild(player) // add player
self.physicsWorld.gravity = CGVector(dx: 0, dy: 0) // Delete gravitation from the game
self.physicsWorld.contactDelegate = self
scoreLabel = SKLabelNode(text: "Score: 0") // Score table
scoreLabel.fontName = "AmericanTypewriter-Bold" // Шрифт для таблички
scoreLabel.fontSize = 56
scoreLabel.fontColor = UIColor.white
scoreLabel.position = CGPoint(x: -200, y: 500)
score = 0
self.addChild(scoreLabel)
gameTimer = Timer.scheduledTimer(timeInterval: 0.75, target: self, selector: #selector(addAlien), userInfo: nil, repeats: true)
#objc func addAlien(){
aliens = GKRandomSource.sharedRandom().arrayByShufflingObjects(in: aliens) as! [String]
let alien = SKSpriteNode(imageNamed: aliens[0])
let randomPos = GKRandomDistribution(lowestValue: 20, highestValue: 350)
let pos = CGFloat(randomPos.nextInt())
alien.position = CGPoint(x: pos, y: -800)
}
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
According to code you shared - you declared addAlien inside of the didMove function. You need to move addAlien out of the didMove function and put it same level with didMove - in a class scope.
Related
I am trying to create a program where the longer you hold down the screen the higher an object will move. I want to do it where the longer you hold down the longer the function repeats increasing the distance. However, for some reason it is not working.
import SpriteKit
import GameplayKit
var calculateTimer = Timer()
var distance = 10
var choice = 1
class GameScene: SKScene, SKPhysicsContactDelegate {
var planet = SKNode()
override func didMove(to view: SKView) { // viewdidload
let planetTexture = SKTexture(imageNamed: "mercury.jpg")
planet = SKSpriteNode(texture: planetTexture)
planet.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
addChild(planet)
}
func calculateDistance() {
distance += 10
print(distance)
print(choice)
}
func touchDown(atPoint pos : CGPoint) {
calculateTimer = Timer.scheduledTimer(timeInterval: 0.1, target: self, selector: #selector(self.calculateDistance), userInfo: nil, repeats: true)
print("test")
}
func touchUp(atPoint pos : CGPoint) {
planet.physicsBody!.isDynamic = true
//planet.physicsBody?.velocity = (CGVector(dx: 0, dy: 0))
planet.physicsBody!.applyImpulse(CGVector(dx: 0, dy: distance))
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
}
This is my code. If anyone has any suggestions they would be much appreciated!
In touchDown, set a flag to indicate a touch has started.
In touchUp, clear/remove the flag.
Whilst the flag is set, keep performing your movement code.
So I've got a class named FlashyPaddleEffect. I also mention it in another class, GameScene, but it doesn't apply any effect that it should. The paddle should be flashing blue and white colors, but it simply stays white. The paddle also should lose its physics body when it is blue.
If you need any other information, don't hesitate to ask.
NOTE: There might be problems with the code I gave (because of indents, it's quite tricky to make code by indenting 4 spaces every line, sorry).
import SpriteKit
import GameplayKit
class FlashyPaddleEffect {
var node = SKSpriteNode()
var ballNode = SKSpriteNode()
var updateTimer: Timer? = nil
var timer: Timer? = nil
#objc func changeNodeColor() {
switch node.color {
case SKColor.blue: node.color = SKColor.white
case SKColor.white: node.color = SKColor.blue
default: _ = 1 + 2
}
}
#objc func update() //I used the objc prefix to silence the warning the selectors of the timers produced. {
let previousPhysicsBody = node.physicsBody
if node.color == SKColor.blue {
node.physicsBody = nil
}
node.physicsBody = previousPhysicsBody
}
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
node = applyEffectTo
ballNode = ball
timer = Timer.scheduledTimer(timeInterval: 0.6, target: self, selector: #selector(changeNodeColor), userInfo: nil, repeats: true)
updateTimer = Timer.scheduledTimer(timeInterval: 0.3, target: self, selector: #selector(update), userInfo: nil, repeats: true)
_ = node
_ = timer
}
}
class GameScene: SKScene {
var ball = SKSpriteNode()
var player = SKSpriteNode()
var enemy = SKSpriteNode()
var scores = [0, 0]
var initScores = [0, 0]
var scoreLabels: [SKLabelNode]? = nil
let playLabel = SKLabelNode()
let timeLabel = SKLabelNode()
let timeUpLabel = SKLabelNode()
var secondsLeft: Int = 180
var initialTime: Int? = nil
var timer = Timer()
var amountOfPauseMenuCloses = 0
var resultsLabel = SKLabelNode()
let flashEffect = FlashyPaddleEffect() //I define a variable to mention the class easier later on.
override func didMove(to view: SKView) {
//Initialize nodes of the scene editor.
ball = self.childNode(withName: "Ball") as! SKSpriteNode
player = self.childNode(withName: "PPaddle") as! SKSpriteNode
enemy = self.childNode(withName: "EPaddle") as! SKSpriteNode
//Set styles for the Play Notification Label (referred to as PNL later)
playLabel.position = CGPoint(x: 0, y: 0)
playLabel.text = "Tap anywhere to play."
playLabel.name = "playLabel"
//Set styles for the Timer Label (referred to as Timer later)
timeLabel.position = CGPoint(x: 90, y: 0)
timeLabel.text = String(secondsLeft)
//Doing manipulations connected with scores here.
scores = [0, 0]
initScores = scores
scoreLabels = [self.childNode(withName: "PScoreLabel") as! SKLabelNode, self.childNode(withName: "EScoreLabel") as! SKLabelNode]
//Create a border for our ball to bounce off.
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
border.restitution = 1
border.linearDamping = 0
border.angularDamping = 0
self.physicsBody = border
//To avoid the ball's damping.
ball.physicsBody?.linearDamping = 0
ball.physicsBody?.angularDamping = 0
showPause() //Show the (pause) menu at the beginning
//Set a variable to refer to as a time standard later.
initialTime = secondsLeft
//The game timer.
timer = Timer.scheduledTimer(timeInterval: 1.0, target: self, selector: #selector(reduceSecondFromTimer), userInfo: nil, repeats: true)
flashEffect.make(applyEffectTo: player, ball: ball) //This is where I reference the class in another class.
}
//A function to show the (pause) menu.
func showPause(hideNodes: Bool = true) {
self.addChild(playLabel)
if hideNodes == true {
ball.removeFromParent()
player.removeFromParent()
enemy.removeFromParent()
scoreLabels?[0].removeFromParent()
scoreLabels?[1].removeFromParent()
}
}
override func update(_ currentTime: TimeInterval) {
var alreadyChangedTextLabel = false
for i in scoreLabels! {
if i.name == "PScoreLabel" {i.text = String(scores[0])}
if i.name == "EScoreLabel" {i.text = String(scores[1])}
}
if ball.position.y <= player.position.y {scores[1] += 1}
if ball.position.y >= enemy.position.y {scores[0] += 1}
if secondsLeft == 0 {
showPause(hideNodes: false)
if alreadyChangedTextLabel == false {
timeUpLabel.text = "TIME UP! \(whoIsWinning(scores: scores)) won!"
alreadyChangedTextLabel = true
}
timeUpLabel.name = "timeUpLabel"
timeUpLabel.position = CGPoint(x: 0, y: 180)
if !(self.children.contains(timeUpLabel)) {
self.addChild(timeUpLabel)
}
timeLabel.removeFromParent()
}
if self.children.contains(playLabel) {
secondsLeft = initialTime!
}
timeLabel.text = String(secondsLeft)
let alignWithBall = SKAction.move(to: CGPoint(x: ball.position.x, y: enemy.position.y), duration: 0.8)
enemy.run(alignWithBall)
}
func initGame() {
//Initializing process for every game.
scores = initScores
ball.position = CGPoint(x: 0, y: 0)
if amountOfPauseMenuCloses == 1 {ball.physicsBody?.applyImpulse(CGVector(dx: 15, dy: 15))}
secondsLeft = initialTime!
timeLabel.text = String(secondsLeft)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for childNode in self.children {
if childNode.name == "playLabel" {
if self.children.contains(playLabel) {
playLabel.removeFromParent()
}
if !(self.children.contains(player)) {
self.addChild(player)
}
if !(self.children.contains(enemy)) {
self.addChild(enemy)
}
if !(self.children.contains(ball)) {
self.addChild(ball)
}
if !(self.children.contains((scoreLabels?[0])!)) {
self.addChild((scoreLabels?[0])!)
}
if !(self.children.contains((scoreLabels?[1])!)) {
self.addChild((scoreLabels?[1])!)
}
if !(self.children.contains(timeLabel)) {
self.addChild(timeLabel)
}
if self.children.contains(timeUpLabel) {
timeUpLabel.removeFromParent()
}
amountOfPauseMenuCloses += 1
initGame()
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches {
if self.nodes(at: t.location(in: self)).contains(player) {player.position.x = t.location(in: self).x}
}
}
func reduceSecondFromTimer() {
secondsLeft -= 1
}
func whoIsWinning(scores: Array<Int>) -> String {
var r: String? = nil
if scores[0] >= scores[1] {
r = "You"
}
if scores[1] >= scores[0] {
r = "Enemy"
}
return r!
}
}
Thanks a lot for answers.
P.S It's my first question ever so don't judge me strictly.
1) Do not use NSTimer use SKAction. I can see the way you are doing it you stack timer after timer, this is bad.
2) Do not have your temp variable global (node in this case), it makes code hard to read
3) Do not remove your physics body, simply remove the category.
func make(applyEffectTo: SKSpriteNode, ball: SKSpriteNode) {
ballNode = ball
let blue = SKAction.colorize(with SKColor.blue, colorBlendFactor: 1.0, duration sec: 0)
let white = SKAction.colorize(with SKColor.white, colorBlendFactor: 1.0, duration sec: 0)
let wait = SKAction.wait(for:0.6)
let turnOnPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = #######})
let turnOffPhysics = SKAction.run({applyEffectTo.physicsBody?.categoryBitmask = 0})
let seq = [blue, turnOffPhysics,wait,white,turnOnPhysics,wait]
let repeat = SKAction.repeatForever(seq)
applyEffectTo.run(repeat, withKey:"flashing")
}
Note: I have no idea what your categoryBitmask is, you need to fill it in
I started working with SKS files to create games in swift, and in this game im trying to get enemies to spawn randomly within the width of the phone screen as opposed to all over the sks file scene
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
func spawnEnemy(){
//supposed to pick random point within the screen width
let xPos = Int.random(self.frame.width)
enemy = SKSpriteNode(imageNamed: "enemy")
enemy.position = CGPointMake(CGFloat(xpos), self.frame.size.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 7)
enemy.physicsBody?.affectedByGravity = true
enemy.physicsBody?.categoryBitMask = 0
enemy.physicsBody?.contactTestBitMask = 1
addChild(self.enemy)
}
Anyways, I wrote an example for you :)
So, here is how your example should look in order to work:
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
NSTimer.scheduledTimerWithTimeInterval(0.2, target: self, selector: Selector("spawnEnemy"), userInfo: nil, repeats: true)
}
func randomBetweenNumbers(firstNum: CGFloat, secondNum: CGFloat) -> CGFloat{
return CGFloat(arc4random()) / CGFloat(UINT32_MAX) * abs(firstNum - secondNum) + min(firstNum, secondNum)
}
func spawnEnemy(){
//supposed to pick random point within the screen width
let xPos = randomBetweenNumbers(0, secondNum: frame.width )
let enemy = SKSpriteNode(imageNamed: "enemy") //create a new enemy each time
enemy.position = CGPointMake(CGFloat(xPos), self.frame.size.height/2)
enemy.physicsBody = SKPhysicsBody(circleOfRadius: 7)
enemy.physicsBody?.affectedByGravity = true
enemy.physicsBody?.categoryBitMask = 0
enemy.physicsBody?.contactTestBitMask = 1
addChild(enemy)
}
Method randomBetweenNumbers is borrowed from here.
And, this is another way of how you can spawn enemies by using SKAction:
override func didMoveToView(view: SKView) {
self.physicsWorld.contactDelegate = self
let wait = SKAction .waitForDuration(1, withRange: 0.5)
let spawn = SKAction.runBlock({
self.spawnEnemy()
})
let spawning = SKAction.sequence([wait,spawn])
self.runAction(SKAction.repeatActionForever(spawning), withKey:"spawning")
}
Method spawnEnemy remains the same in both cases. To stop spawning you can remove an action for certain key ("spawning" in this case). You can do it like this:
if((self.actionForKey("spawning")) != nil){
self.removeActionForKey("spawning")
}
How do you make a sprite rock back and forth constantly like a boat?
Heres the code i wrote:
import UIKit
import SpriteKit
let boat = SKSpriteNode(imageNamed: "boat_floor")
var rotationVal = CGFloat(-1)
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.whiteColor()
boat.size = CGSize(width: self.frame.size.width, height: self.frame.size.width)
boat.position = CGPoint(x: self.frame.size.width/2, y: 0)
boat.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "boat_floor"), size: boat.size)
boat.physicsBody?.dynamic = false
boat.alpha = 1
self.addChild(boat)
ChangeAngle()
NSTimer.scheduledTimerWithTimeInterval(3, target: self, selector: Selector("ChangeAngle"), userInfo: nil, repeats: true)
}
func ChangeAngle(){
let action = SKAction.rotateByAngle(rotationVal, duration:30)
if rotationVal < 0 {
rotationVal = CGFloat(M_PI)
boat.runAction(action)
}
else if rotationVal > 0 {
rotationVal = -CGFloat(M_PI)
boat.runAction(action)
}
print(rotationVal)
}
}
If I understand you correctly, you can do it like this (just copy&paste the code to see how it works):
import UIKit
import SpriteKit
let boat = SKSpriteNode(color: SKColor.greenColor(), size:CGSize(width: 200, height: 80))
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
backgroundColor = UIColor.whiteColor()
boat.position = CGPoint(x: self.frame.size.width/2, y: 100)
boat.alpha = 1
self.addChild(boat)
let rotate = SKAction.rotateByAngle(-0.6, duration:4)
let sequence = SKAction.repeatActionForever(SKAction.sequence([rotate, rotate.reversedAction()]))
let complete = SKAction.sequence([SKAction.rotateByAngle(0.3, duration:2), sequence])
boat.runAction(complete, withKey:"someKey")
}
}
Using NSTimer is generally bad idea in SpriteKit because it don't respect node's, scene's or view's paused state.
I am developing a game on sprite kit, and when the player loses they are taken to the EndScene and giving the option to play again. The problem I am having is when the player presses the button to play again they are taken to the GameScene, but instead the GameScene its appearing all blue. Can anyone tell me what I am doing wrong? I posted the EndScene below, if you need me to post the GameScene class just ask.
class EndScene: SKScene {
var RestartBtn : UIButton!
override func didMoveToView(view: SKView) {
scene?.backgroundColor = UIColor.whiteColor()
RestartBtn = UIButton(frame: CGRect(x: 0, y: 0, width: view.frame.size.width, height: 30))
RestartBtn.setTitle("Restart", forState: UIControlState.Normal)
RestartBtn.setTitleColor(UIColor.darkGrayColor(), forState: UIControlState.Normal)
RestartBtn.addTarget(self, action: Selector("Restart"), forControlEvents: UIControlEvents.TouchUpInside)
self.view?.addSubview(RestartBtn)
}
func Restart() {
// close EndScene and start the game again
self.view?.presentScene(GameScene())
RestartBtn.removeFromSuperview()
}
}
GameScene
import SpriteKit
struct PhysicsCatagory {
static let Enemy : UInt32 = 1 //000000000000000000000000000001
static let Bullet : UInt32 = 2 //00000000000000000000000000010
static let Player : UInt32 = 3 //00000000000000000000000000100
}
class GameScene: SKScene, SKPhysicsContactDelegate {
var Score = Int()
var Player = SKSpriteNode(imageNamed: "PlayerGalaga.png")
var ScoreLbl = UILabel()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
physicsWorld.contactDelegate = self
self.scene?.backgroundColor = UIColor.blackColor()
Player.position = CGPointMake(self.size.width / 2, self.size.height / 5)
Player.physicsBody = SKPhysicsBody(rectangleOfSize: Player.size)
Player.physicsBody?.affectedByGravity = false
Player.physicsBody?.categoryBitMask = PhysicsCatagory.Player
Player.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
Player.physicsBody?.dynamic = false
var Timer = NSTimer.scheduledTimerWithTimeInterval(0.2, target: self,
selector: Selector("SpawnBullets"), userInfo: nil, repeats: true)
var EmenyTimer = NSTimer.scheduledTimerWithTimeInterval(1.0, target:
self, selector: ("SpawnEnemies"), userInfo: nil, repeats: true)
self.addChild(Player)
ScoreLbl.text = "\(Score)"
ScoreLbl = UILabel(frame: CGRect(x: 0,y: 0, width: 100, height: 20))
ScoreLbl.backgroundColor = UIColor(red: 0.1, green: 0.1, blue: 0.1,
alpha:0.3 )
ScoreLbl.textColor = UIColor.whiteColor()
self.view?.addSubview(ScoreLbl)
}
func didBeginContact(contact: SKPhysicsContact) {
var firstBody : SKPhysicsBody = contact.bodyA
var secondBody : SKPhysicsBody = contact.bodyB
if ((firstBody.categoryBitMask == PhysicsCatagory.Enemy) &&
(secondBody.categoryBitMask == PhysicsCatagory.Bullet) ||
(firstBody.categoryBitMask == PhysicsCatagory.Bullet) &&
(secondBody.categoryBitMask == PhysicsCatagory.Enemy)){
CollisionWithBullet(firstBody.node as! SKSpriteNode, Bullet:
secondBody.node as! SKSpriteNode)
}
else if ((firstBody.categoryBitMask == PhysicsCatagory.Enemy) &&
(secondBody.categoryBitMask == PhysicsCatagory.Player) ||
(firstBody.categoryBitMask == PhysicsCatagory.Player) &&
(secondBody.categoryBitMask == PhysicsCatagory.Enemy)){
CollisionWithPerson(firstBody.node as! SKSpriteNode, Person:
secondBody.node as! SKSpriteNode)
}
}
func CollisionWithBullet(Enemy: SKSpriteNode, Bullet:SKSpriteNode){
Enemy.removeFromParent()
Bullet.removeFromParent()
Score++
ScoreLbl.text = "\(Score)"
}
func CollisionWithPerson(Enemy:SKSpriteNode, Person:SKSpriteNode){
Enemy.removeFromParent()
Player.removeFromParent()
self.view?.presentScene(EndScene())
ScoreLbl.removeFromSuperview()
}
func SpawnBullets(){
var Bullet = SKSpriteNode(imageNamed: "BulletGalaga.png")
Bullet.zPosition = -5
Bullet.position = CGPointMake(Player.position.x, Player.position.y)
let action = SKAction.moveToY(self.size.height + 30, duration: 0.5)
let actionDone = SKAction.removeFromParent()
Bullet.runAction(SKAction.sequence([action, actionDone]))
Bullet.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet.size)
Bullet.physicsBody?.categoryBitMask = PhysicsCatagory.Bullet
Bullet.physicsBody?.contactTestBitMask = PhysicsCatagory.Enemy
Bullet.physicsBody?.affectedByGravity = false
Bullet.physicsBody?.dynamic = false
self.addChild(Bullet)
}
func SpawnEnemies(){
var Enemy = SKSpriteNode(imageNamed: "EnemyGalaga.png")
var MinValue = self.size.width / 8
var MaxValue = self.size.width - 20
var SpawnPoint = UInt32(MaxValue - MinValue)
Enemy.position = CGPointMake(CGFloat(arc4random_uniform(SpawnPoint)),
self.size.height)
Enemy.physicsBody = SKPhysicsBody(rectangleOfSize: Enemy.size)
Enemy.physicsBody?.categoryBitMask = PhysicsCatagory.Enemy
Enemy.physicsBody?.contactTestBitMask = PhysicsCatagory.Bullet
Enemy.physicsBody?.affectedByGravity = false
Enemy.physicsBody?.dynamic = true
let action = SKAction.moveToY(-70, duration: 2.0)
let actionDone = SKAction.removeFromParent()
Enemy.runAction(SKAction.sequence([action, actionDone]))
self.addChild(Enemy)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event:
UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
Player.position.x = location.x
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event:
UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
Player.position.x = location.x
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
From what I can see, when there's a collision with person, you're removing nodes from the view, so they're still going to be gone when you recall GameScene from the restart() function in the EndScene.
What you can do is create a func initialScene() in GameScene where you create everything in that function and call it in the didMoveToView function (instead of doing it all in the didMoveToView function). That way, whenever you present the GameScene from another class, it will call that function and recreate everything you need as if you actually restarted. Hope that's a helpful start, if you need anything else just comment.