I'm unable to move the sprite even after I set the class on the Sprite in the GameScene. Below is my code for the GameplayScene class. I did not include my Player class because the code is really basic. (just a movePlayer bool function with a if statement that moves based on the x position)
import SpriteKit
class GameplayScene: SKScene {
var player: Player? // created new varible player for the scene, inherited the player class
var canMove = false // created new varible for letting player move; false by default
var moveLeft = false // created new varible to move player left; false by default
var center: CGFloat? // created new varible to set center of screen
override func didMoveToView(view: SKView) {
center = (self.scene?.size.width)! / (self.scene?.size.height)! //calculates center of screen
player.self?.childNodeWithName("Player") as? Player! // adds node "Player" within Player class(Player!)
}
override func update(currentTime: NSTimeInterval) {
managePlayer() //constantly checks if player can/is moving
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self) // gets location inside scene
if location.x > center { // if statement to check if user to taping screen on the right
moveLeft = false //moves player right
} else {
moveLeft = true
}
}
canMove = true
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
canMove = false //says if player is not touching screen, the player will not move
}
func managePlayer() {
if canMove {
player?.movePlayer(moveLeft) //takes the Player varible(which has the class), then grabs the move player func, then checks argument moveLeft
}
}
}
add player = self.childNode(withName: "Player") as? Player to func didMove in GameplayScene.swift
override func didMove(to view: SKView) {
print("didmove")
center = (self.scene?.size.width)! / (self.scene?.size.height)!
player = self.childNode(withName: "Player") as? Player
}
Related
I am working on a simple runner game, I have just 3 shapenodes a player node which is just a shapenode (redbox, 30 x 30) with left/right buttons basically two shapenodes.
I have managed to set flags and increment player node's position X and move it left and right with buttons in touchesBegan method and stop the movement in touchesEnded method everything works fine.
The problem is that if I touch lets say right button, the player moves to the right as expected. But if I touch and move my finger out of the button boundaries in any direction, the player keeps moving constantly as long as I touch that button again then it stops. Otherwise it keeps moving and the other button does not stop it as well.
I used touchesMoved method to stop the movement of the player when I move my finger but this does not fix the issue since touchesMoved method triggers with the slightest movement of the touch even when my finger slightly shakes.
I want the touchesMoved method called when my finger is moved off the button, not on the button.
How can I stop the movement of a sprite when touched and moved out of the node ( button ) boundaries?
You have 3 touch events:
touchesBegan; check if user tap is on one the move buttons (if yes - move player, if not - return)
touchesMoved; check if the user tap is still inside the boundaries of the move button (if yes - continue moving player, if not - stop movement)
touchesEnded; stop player movement
You can solve your problem by checking if the user finger is over the button area.
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
// nothing here
let touch = touches.first
let position_in_scene = touch!.location(in: self)
check_button(position: position_in_scene)
}
func check_button(position : CGPoint)
{
if right_button.contains(position)
{
// the tap was inside button boundaries
player.move_left()
}
else
{
// the tap was outside button boundaries
player.stop()
}
}
I have this code running :
class GameScene: SKScene {
var player = SKSpriteNode()
var left = SKSpriteNode()
var right = SKSpriteNode()
var jumpbtn = SKSpriteNode()
var leftbtnPressed = false
var rightbtnPressed = false
var jump = false
var hSpeed: CGFloat = 2.0
override func didMove(to view: SKView) {
let border = SKPhysicsBody(edgeLoopFrom: self.frame)
border.friction = 0
self.physicsBody = border
jumpbtn = self.childNode(withName: "jumpbtn") as! SKSpriteNode
player = self.childNode(withName: "r1") as! SKSpriteNode
left = self.childNode(withName: "leftbtn") as! SKSpriteNode
right = childNode(withName: "rightbtn") as! SKSpriteNode
}//didmove to view
func moveRight(){
player.position = CGPoint(x: player.position.x + hSpeed, y: player.position.y)
}
func moveLeft (){
player.position = CGPoint(x: player.position.x - hSpeed, y: player.position.y)
}
func movement (){
if rightbtnPressed == true {
moveRight()
}
if leftbtnPressed == true {
moveLeft()
}
}//movement
func CheckButton(position : CGPoint) {
if right.contains(position){
rightbtnPressed = true
}
if left.contains(position) {
leftbtnPressed = true
}
else {
leftbtnPressed = false
rightbtnPressed = false
}
}// checkbutton
func jumping (){
if jump == true {
player.physicsBody?.applyImpulse(CGVector(dx:0, dy: 5.0))
}
}// jumping
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let positionINScene = touch.location(in: self)
let touchednode = self.atPoint(positionINScene).name
if touchednode == "rightbtn"{
rightbtnPressed = true
}
if touchednode == "leftbtn"{
leftbtnPressed = true
}
if touchednode == "jumpbtn"{
jump = true
jumping()
}
}
}//touchesbegan
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first
let positionInScene = touch?.location(in: self)
CheckButton(position: positionInScene!)
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let positionINScene = touch.location(in: self)
let touchednode = self.atPoint(positionINScene)
if touchednode.name == "rightbtn"{
rightbtnPressed = false
}
if touchednode.name == "leftbtn"{
leftbtnPressed = false
}
if touchednode.name == "jumpbtn" {
jump = false
}
}
}//touchesEnded
override func update(_ currentTime: TimeInterval) {
movement()
}
When i touch either left or right button the player starts moving as expected but the problem is while the player is moving let say to the right if i touch the jump button the player pauses moving where it should be moving and jumping
in simple words i can not move and jump at the same time
I have two classes Player and GameScene.
class Player: SKSpriteNode {
init(name: SKSpriteNode) {
// not important for my problem
super.init(...)
}
}
And this is the class, where class Player is used:
class GameScene: SKScene {
var mySprite = SKSpriteNode()
override didMove(to view: SKView) {
let player = Player(name: mySprite)
addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
// I WANT TO CALL let player HERE BUT IT DOES NOT WORK
}
Now I want to override touchesBegan function and make something happen whenever the player is touched. The problem is, that player is declared inside didMove function and touchesBegan cannot see it.
When I declade player globally, I can not insert the SKSpriteNode mySprite because it says:
"Use of unresolved identifier".
So how can I declare my variable player of type Player globally, so I have access in touchesBegan function??
class GameScene: SKScene {
var mySprite = SKSpriteNode()
var player: Player? //declaring your player Object
override didMove(to view: SKView) {
player = Player(name: mySprite) // initialize your player Object here
addChild(player)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
guard let player = self.player else { return }
//now you can call your player Object here
}
}
I am trying to implement moving to another scene when a wheel stops rotating. The code I have is shown below. I cannot figure out how to detect when the velocity has reached 0.0?
import SpriteKit
import GameplayKit
class GameplayScene: SKScene {
var player: Player?;
override func didMove(to view: SKView) {
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
}
So from here, I would like to execute the following when the wheel has stopped spinning:
let play_scene = Questions(fileNamed: "QuestionsScene")
play_scene?.scaleMode = .aspectFill
self.view?.presentScene(play_scene!, transition: SKTransition.doorsOpenVertical(withDuration: 1))
I have now edited the class and it looks as follows:
import SpriteKit
import GameplayKit
class GameplayScene: SKScene, SKSceneDelegate {
var player: Player?
override func didMove(to view: SKView) {
self.delegate = self
player = self.childNode(withName: "spinner") as! Player?;
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if atPoint(location).name == "play_button" {
spin()
}
}
}
func spin () {
let random = GKRandomDistribution(lowestValue: 20, highestValue: 90)
let r = random.nextInt()
player?.physicsBody = SKPhysicsBody(circleOfRadius: CGFloat(self.frame.width))
player?.physicsBody?.affectedByGravity = false
player?.physicsBody?.isDynamic = true
player?.physicsBody?.allowsRotation = true
player?.physicsBody?.pinned = true
player?.physicsBody?.angularVelocity = CGFloat(r)
player?.physicsBody?.angularDamping = 1.0
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func didSimulatePhysics() {
if ((player?.physicsBody?.angularVelocity)! <= CGFloat(0.01))
{
print("Got it")
}
}
}
Problem is I still receive an error on the if statement within didSimulatePhysics function. The error I receive is "Thread 1: EXC_BAD_INSTRUCTION...."
Your wheel's SKPhysicsBody has a built-in property, angularVelocity, that tells you how fast it's spinning. You're already using it when you set it to r to start the wheel spinning.
To watch angularVelocity you can use didSimulatePhysics(). It gets called once every frame, right after the physics calculations are done. That will look something like this:
func didSimulatePhysics() {
if wheelIsSpinning && angularVelocity != nil && angularVelocity! <= CGFloat(0.001) {
wheelIsSpinning = false
// wheel has stopped
// add your code here
}
}
Due to the vagaries of physics modeling, the angularVelocity might never be exactly zero. So instead we watch for it to be less than some arbitrary threshold, in this case 0.001.
You don't want it to execute every frame when the wheel isn't moving, so I added a property wheelIsSpinning to keep track. You'll need to add it as an instance property to GameplayScene, and set it to true in spin().
I am trying to create a "Space Invaders" game in swift, when the user touches the screen a bullet is shot from the ship, but when I try to touch it again while the bullet is moving across the screen I have an NSException and the game breaks. How do I set up the action so that there can be multiple instances of the action, so that the shooter is semi automatic. Below is my current scene controller.
import SpriteKit
class GameScene: SKScene {
let background = SKSpriteNode(imageNamed: "background")
let heroShip = SKSpriteNode(imageNamed: "heroShip")
let bullet = SKSpriteNode(imageNamed: "bullet")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
background.position = CGPointMake(CGRectGetMidX(self.frame),CGRectGetMidY(self.frame))
heroShip.position = CGPointMake(self.size.width/6.0, self.size.height/2.0)
self.heroShip.zPosition = 1.0
self.addChild(background)
self.addChild(heroShip)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
bullet.position = CGPointMake(heroShip.position.x + bullet.size.width/2, heroShip.position.y)
let action = SKAction.moveToX(self.frame.width + self.bullet.size.width, duration: 0.5)
self.addChild(bullet)
bullet.runAction(action, completion: {
self.bullet.removeAllActions()
self.bullet.removeFromParent()
})
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
If you want to have multiple bullets shot from one ship, you have to create multiple instances of a bullet. What you have now is bullet property of GameScene class which is a mistake.
You probably want to instantiate bullets dynamically in your IBAction
So try something like this:
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let bullet = SKSpriteNode(imageNamed: "bullet")
bullet.position = CGPointMake(heroShip.position.x + bullet.size.width/2, heroShip.position.y)
let action = SKAction.moveToX(self.frame.width + bullet.size.width, duration: 0.5)
self.addChild(bullet)
bullet.runAction(action, completion: {
bullet.removeAllActions()
bullet.removeFromParent()
})
}
and remove
let bullet = SKSpriteNode(imageNamed: "bullet")
from the top of the class
I am making a game for iOS in Swift with SpriteKit, the game is currently just a ball moving around with a sword.
I have anchored the sword to the bottom of the sword, but I need to know how to control the direction of rotation with 2 buttons or a slider of some sort.
Here is my code:
import SpriteKit
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
class GameScene: SKScene {
override func didMoveToView(view: SKView) {
let initialPlayerLocation = CGPoint(x: self.frame.width/2, y: self.frame.height/2)
/* Setup your scene here */
//player
sprite.setScale(1.0)
sprite.position = initialPlayerLocation
sprite.zPosition = 20
self.addChild(sprite)
//weapon
weapon.setScale(1.0)
weapon.position = initialPlayerLocation
weapon.zPosition = -20
weapon.anchorPoint = CGPointMake(0.5,0.0);
self.addChild(weapon)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Okay, I think I understand what you are trying to do. It involves a lot of code, but in theory it is pretty simple. We detect if the touch is inside the left or right buttons (which in this example, I've made them SKSpriteNodes), and then set boolean values for the update method to use and rotate the weapon node (I'm assuming this is the sword you were talking about).
I've also included a variable that lets you set the speed of rotation. As you did not say what speed you were looking for, I left it up to you.
At the top of the program:
let sprite = SKSpriteNode(imageNamed:"Player")
let weapon = SKSpriteNode(imageNamed: "weapon")
let leftButton = SKSpriteNode(imageNamed: "leftButton")
let rightButton = SKSpriteNode(imageNamed: "rightButton")
var leftPressed = false
var rightPressed = false
let weaponRotateSpeed = 0.01 // Change this for rotation speed of weapon
And then modify touchesBegan to look like this:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else if (rightButton.containsPoint(p: location))
rightPressed = true
else {
var move = SKAction.moveTo(location, duration:1.0)
sprite.runAction(move)
weapon.runAction(move)
}
}
In update, add this code:
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
if (leftPressed && rightPressed) {
// Do nothing, as both buttons are pressed
} else if (leftPressed) {
weapon.zRotation -= weaponRotateSpeed
} else if (rightPressed) {
weapon.zRotation += weaponRotateSpeed
}
}
We want to stop the weapon from rotating when we detect when the finger has moved off the button like so:
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch in (touches as! Set<UITouch>) {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = true
else
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = true
else
rightPressed = false
}
}
And at last, we need to detect when your finger has left the screen:
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if (leftButton.containsPoint(p: location))
leftPressed = false
if (rightButton.containsPoint(p: location))
rightPressed = false
}
}