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.
Related
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.
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.
I am currently trying to build a loading scene into my SpriteKit game with my developer logo on it and need to transition to the MainMenuScene after say 5 seconds. How would I go about that.
My code right now looks like this, which is basically just the background/logo image.
import SpriteKit
class LoadingScene: SKScene {
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "fatscoprion")
background.position = CGPoint (x: self.size.width / 2, y: self.size.height / 2)
background.zPosition = -1
self.addChild(background)
}
}
You can create a Scheduled Timer and configure a function to call your method that's creates and present the new scene.
Example:
class LoadingScene: SKScene {
var timer = Timer()
override func didMove(to view: SKView) {
let background = SKSpriteNode(imageNamed: "")
background.position = CGPoint (x: self.size.width / 2, y: self.size.height / 2)
background.zPosition = -1
self.addChild(background)
//Create a Scheduled timer thats will fire a function after the timeInterval
timer = Timer.scheduledTimer(timeInterval: 5.0,
target: self,
selector: #selector(presentNewScene),
userInfo: nil, repeats: false)
}
#objc func presentNewScene() {
//Configure the new scene to be presented and then present.
let newScene = SKScene(size: .zero)
view?.presentScene(newScene)
}
deinit {
//Stops the timer.
timer.invalidate()
}
}
I have created a Test Scene to practice some basic Swift 3 and SpriteKit. I'm trying to learn by understanding the basics before moving on to more complex goals.
Here I have a SKLabelNode that is created and then moves to the left. I have created a sequence to repeat the action but it does not work. Please could you help me understand where it fails. NodeCount notes that there is only 1 node.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
var testShape = SKLabelNode()
override func didMove(to view: SKView) {
func createShape() {
testShape = SKLabelNode(text: "TEST")
testShape.position = CGPoint(x: 0.5, y: 0.5)
testShape.zPosition = 1
addChild(testShape)
}
let moveTestShape = SKAction.moveBy(x: -500, y: 0, duration: 5)
func repeater() {
createShape()
testShape.run(moveTestShape)
}
let delay = SKAction.wait(forDuration: 2)
let repeatingAction = SKAction( repeater() )
let sequence = SKAction.sequence([ delay, repeatingAction ] )
run(SKAction.repeatForever(sequence))
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}
Are you not getting compiler errors?
Why are you creating methods in didMoveToView?
Your code should look more like this
class GameScene: SKScene {
var testShape = SKLabelNode()
override func didMove(to view: SKView) {
let delay = SKAction.wait(forDuration: 2)
let repeatingAction = SKAction.run(repeater)
let sequence = SKAction.sequence([ delay, repeatingAction ] )
run(SKAction.repeatForever(sequence))
}
func createShape() {
testShape = SKLabelNode(text: "TEST")
testShape.position = CGPoint(x: 0.5, y: 0.5)
testShape.zPosition = 1
addChild(testShape)
}
func repeater() {
createShape()
let moveTestShape = SKAction.moveBy(x: -500, y: 0, duration: 5)
testShape.run(moveTestShape)
}
}
This is how you call functions/code blocks in SKActions.
let repeatingAction = SKAction.run(repeater)
or
let repeatingAction = SKAction.run {
repeater()
}
Also remember our are only repeating the spawn action for new labels. The actual action to move the labels is non repeating. So what you should see is 1 label created and moved once, than 2 seconds later a new label gets created and moved once etc
Hope this helps
Im trying to make it so that the program will print something to the console when an instance of the sprite is in the center of the screen from top to bottom
var sprite = SKSpriteNode()
override func didMoveToView(view: SKView) {
//spawns an instance of the sprite
NSTimer.scheduledTimerWithTimeInterval(0.3, target: self, selector: Selector("spawnObject"), userInfo: nil, repeats: true)
//call the check middle function
checkMiddle()
}
//spawns a sprite
func spawnObject() {
let size = CGSizeMake(self.frame.size.width/3, self.frame.size.width3)
self.physicsWorld.gravity = CGVectorMake(0, -CGFloat(objectSpeed))
sprite = SKSpriteNode(imageNamed: "spriteImage")
sprite.position = CGPointMake(CGFloat(100), self.frame.height)
sprite.size = size
sprite.physicsBody = SKPhysicsBody(circleOfRadius: self.frame.size.width)
sprite.physicsBody?.affectedByGravity = true
addChild(self.sprite)
}
//PRINT WHEN SPRITE IS IN THE CENTER OF SCREEN FROM TOP TO BOTTOM
func checkMiddle() {
if sprite.position.y == self.frame.size.height/2 {
print("center")
}
}
if this is for debugging purposes you can use :
override func update(currentTime: NSTimeInterval)
{
checkMiddle()
}
The update() function is called before every frame so you need to be careful what you put in there
If you don't need it to be checked as frequently as that (60 x per second), you could also use an action:
let checkCenterAction = SKAction.runBlock(){ self.checkMiddle() }
let waitAction = SKAction.waitForDuration( 0.1 ) // every 1/10th of a second
let checkAndWaitAction = SKAction.sequence([checkCenterAction, waitAction])
sprite.runAction(SKAction.repeatForever(checkAndWaitAction))
And yet another option is to use the SKPhysicsBody to detect collisions