Swift Center on Camera not working when ApplyingImpulse in another class - swift

I'm new to swift programming and have been stuck on this issue for the last week. I am trying to centre on a hero while applying impulse to him. My code works if I create a hero in the scene class and centre him but it doesn't work when I put a hero in another class and try to centre him from scene class. Now centre on works if I just move him by changing his x or y position but not if I apply impulse. It seems that when a hero is in a separate class, when impulse is applied x and y while he is in motion is showing up as a nil, however when he is in the same class than x and y while in the motion are showing up ok. Any help is much appreciated.
Code where Center ON works with ApplyImpulse when everything is in the same class*****
class GameScene: SKScene {
var hero:SKSpriteNode?
var mazeWorld:SKNode?
var player=Player()
var heroLocation:CGPoint = CGPointZero
override func didMoveToView(view: SKView) {
mazeWorld = childNodeWithName("mazeWorld")
heroLocation = mazeWorld!.childNodeWithName("sPoint")!.position
self.anchorPoint = CGPoint(x: 0.5,y: 0.3)
hero?.position = heroLocation
hero = SKSpriteNode(imageNamed:"hero_walk_down_0")
hero!.physicsBody?.dynamic = false
hero!.physicsBody?.affectedByGravity = true
hero!.physicsBody?.pinned = false
hero!.physicsBody = SKPhysicsBody(rectangleOfSize: hero!.size)
hero!.physicsBody?.mass = 1.0;
hero!.setScale(0.8)
mazeWorld!.addChild(hero!)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
/* Called when a touch begins */
for touch: AnyObject in touches {
hero!.physicsBody?.applyImpulse(CGVector(dx: 240, dy: 550))
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func centerOnNode(node:SKNode){
let cameraPositionInScene:CGPoint = self.convertPoint(node.position, fromNode: mazeWorld!)
mazeWorld!.position = CGPoint(x:mazeWorld!.position.x - cameraPositionInScene.x, y: mazeWorld!.position.y - cameraPositionInScene.y)
}
override func didSimulatePhysics() {
self.centerOnNode(hero!)
}
}
**** Here is the code with Center ON that doesn't work with apply impulse for some reason
import SpriteKit
class GameScene: SKScene {
var hero:SKSpriteNode?
var mazeWorld:SKNode?
var player=Player()
var heroLocation:CGPoint = CGPointZero
override func didMoveToView(view: SKView) {
mazeWorld = childNodeWithName("mazeWorld")
heroLocation = mazeWorld!.childNodeWithName("sPoint")!.position
self.anchorPoint = CGPoint(x: 0.5,y: 0.3)
player.position = heroLocation
mazeWorld!.addChild(player)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent)
{
/* Called when a touch begins */
for touch: AnyObject in touches {
player.jumpRight()
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
func centerOnNode(node:SKNode){
let cameraPositionInScene:CGPoint = self.convertPoint(node.position, fromNode: mazeWorld!)
mazeWorld!.position = CGPoint(x:mazeWorld!.position.x - cameraPositionInScene.x, y: mazeWorld!.position.y - cameraPositionInScene.y)
}
override func didSimulatePhysics() {
self.centerOnNode(player)
}
}
import Foundation
import SpriteKit
class Player: SKNode{
var objectSprite:SKSpriteNode?
override init()
{
super.init()
objectSprite = SKSpriteNode(imageNamed:"hero_walk_down_0")
objectSprite!.physicsBody?.dynamic = false
objectSprite!.physicsBody?.affectedByGravity = true
objectSprite!.physicsBody?.pinned = false
objectSprite!.physicsBody = SKPhysicsBody(rectangleOfSize: objectSprite!.size)
objectSprite!.physicsBody?.mass = 1.0
objectSprite!.setScale(0.8)
addChild(objectSprite!)
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
func jumpRight()
{
objectSprite!.physicsBody?.applyImpulse(CGVector(dx: 240, dy: 550))
}
}

Related

SpriteKit creating a button issues

Im attempting to use some of the code from a solution found on this page for creating a button Create Button in SpriteKit: Swift
class GameScene: SKScene {
let button = SKSpriteNode(imageNamed: "yourImgName")
override func didMoveToView(view: SKView) {
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPointMake(CGRectGetMidX(self.frame), CGRectGetMidY(self.frame) + 50)
self.addChild(button)
//Adjust button properties (above) as needed
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.locationInNode(self)
let touchedNode = self.nodeAtPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
let yourNextScene = YourNextScene(fileNamed: "YourNextScene")
self.view?.presentScene(yourNextScene!)
}
}
}
}
and the current code that I have is supposed to make the player jump when the button is pressed, but nothing is currently happening when its pressed
import SwiftUI
import SpriteKit
import UIKit
class GameScene: SKScene {
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
let playerRadius = player.frame.width / 2.0
player.position = CGPoint(x: 200, y: 500)
player.name = "Jimmy"
addChild(player)
player.physicsBody = SKPhysicsBody(circleOfRadius: playerRadius)
player.physicsBody?.allowsRotation = false
player.physicsBody?.friction = 0
player.physicsBody?.restitution = 0
player.zPosition = 100
// Button
button.name = "btn"
button.size.height = 100
button.size.width = 100
button.position = CGPoint(x: 100, y: 100)
self.addChild(button)
// Physics
physicsBody = SKPhysicsBody(edgeLoopFrom: frame.inset(by: UIEdgeInsets(top: 0, left: 0, bottom: 0, right: 0)))
Timer.scheduledTimer(withTimeInterval: 2, repeats: true) { _ in
player.physicsBody?.applyForce(CGVector(dx: 100, dy: 1000))
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
let touch = touches.first
let positionInScene = touch!.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "btn" {
player.physicsBody?.applyForce(CGVector(dx: 0, dy: 10000))
}
}
}
}
override func update(_ currentTime: TimeInterval) { }
}
I'm thinking that maybe this is an issue with the press not being resitered at all but I'm not fully sure
Your main problem is you put all of your code inside the didMove function. You put the touchesBegan function inside the didMove function. When the didMove function finishes, touchesBegan goes away so none of your touches are going to be handled in the game.
You also declared the button and the player as local variables inside the didMove function.
override func didMove(to view: SKView) {
let button = SKSpriteNode(imageNamed: "playerOBJ")
let player = SKSpriteNode(imageNamed: "playerOBJ")
// Rest of function omitted
}
When the didMove function finishes, the button and player go away too. You also have the same image name for the player and button.
The fix is to make the button and player variables properties of the GameScene class. Move touchesBegan out of the didMove function too.
class GameScene: SKScene {
// Declare button and player here.
var button = SKSpriteNode()
var player = SKSpriteNode()
override func didMove(to view: SKView) {
// Initialize button, player, and everything else here.
}
func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
// Code in touchesBegan func goes here.
}
}

Keydown not called for objects inside an SKScene

I was working on a little game when I ran into a problem. I made a little test project in order to isolate the problem.
I have the following class:
class Sprite: SKSpriteNode {
override var acceptsFirstResponder: Bool { get { return true } }
init() {
super.init(texture: nil, color: .blue, size: CGSize(width: 200, height: 100))
isUserInteractionEnabled = true
}
override func keyDown(with event: NSEvent) {
position.x += 100
print("keydown")
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
}
I create an instance of this class in the following scene:
class GameScene: SKScene {
override var acceptsFirstResponder: Bool { get { return true } }
let x = Sprite()
override func didMove(to view: SKView) {
anchorPoint = CGPoint(x: 0.5, y: 0.5)
isUserInteractionEnabled = true
addChild(x)
}
override func keyDown(with event: NSEvent) {
x.position.y += 50
}
}
As you can see, I have a keyDown method in both classes. The one in GameScene gets called as I can clearly see the sprite (x) move upwards. The keyDown method in my Sprite class however, never gets called. I have searched the web and the only answer I've really found is that acceptsFirstResponder must be overridden and return true, but this doesn't help in my case. Any suggestions?
Oh my... I found a simple solution and I certainly don't know how I didn't think of it before.
Since GameScene's keyDown method actually gets called, I just called the sprite's keyDown method from there and passed the argument:
class GameScene: SKScene {
let x = Sprite()
override func didMove(to view: SKView) {
anchorPoint = CGPoint(x: 0.5, y: 0.5)
addChild(x)
}
override func keyDown(with event: NSEvent) {
x.position.y += 50
x.keyDown(with: event)
}
}
And it didn't require me to override any variables (I guess it's set to first responder by default - please correct me if this is false).

Apply impulse depending on rotation - Swift

I am currently creating a 2D game in which I have a cannon staying at the bottom of the screen that fires projectiles to some targets in the upper part of the screen. The user can rotate the cannon in order to aim for the targets. I want the projectile to be fired to the location that the canon faces. I have no idea how to do that and I need your help... Heres my code ->
import SpriteKit
class GameScene: SKScene {
var canon = SKSpriteNode()
var projectile = SKSpriteNode()
var touching = false
var locked = false
var location = CGPoint()
override func didMoveToView(view: SKView) {
/* Setup your scene here */
projectile.position = canon.position
projectile.color = UIColor.whiteColor()
projectile.size = CGSize(width: 50, height: 50)
projectile.physicsBody?.affectedByGravity = false
projectile.physicsBody?.linearDamping = 1
projectile.physicsBody?.dynamic = true
addChild(projectile)
canon.zPosition = 2
canon = SKSpriteNode(color: UIColor.redColor(), size: CGSize(width: 50, height: 200))
canon.position = CGPoint(x: self.size.width/2.0, y: 10)
addChild(canon)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
touching = true
let touch = touches.first as UITouch!
location = touch.locationInView(self.view)
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
rotateCanon(location.x)
projectile.zRotation = canon.zRotation
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
touching = false
}
func rotateCanon(locationx:CGFloat) {
if locationx <= frame.width/5 && touching {
print(1)
canon.runAction(SKAction.rotateByAngle(0.01, duration: 0.1))
}
if locationx > frame.width/5 && touching {
print(2)
canon.runAction(SKAction.rotateByAngle(-0.01, duration: 0.1))
}
}
}`
Thanks a lot if you can help that would be awesome :D

Value of type 'SKSpriteNode' has no member 'setscale'

I'm following this YouTube series where you learn to program games when there popped up an error, so I fixed that error but that led to another error. And I'm pretty sure I've done exactly as the YouTuber has.
so if you know the solution please help me. (:
import SpriteKit
class GameScene: SKScene {
var bird = SKSpriteNode()
override func didMoveToView(view: SKView) {
self.physicsWorld.gravity = CGVectorMake(0.0, -5.0);
var BirdTexture = SKTexture(imageNamed: "Flappy_Bird")
BirdTexture.filteringMode = SKTextureFilteringMode.Nearest
bird = SKSpriteNode(texture: BirdTexture)
bird.setscale(0.5)
bird.position = CGPoint(x: self.frame.size.width * 0.35, y: self.frame.size.height * 0.6)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
for touch: AnyObject in touches {
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
Actually it's just a typo: The method is called setScale()

How to create a slingshot mechanism in Swift, SpriteKit

I am trying to create a Sling shot mechanism where I rotate a node with an object attached at the end. The slingshot starts off with a touch and hold interaction to rotate the object and on release, launches the object attached at the end - A similar effect is seen here
This is my code for the rotation which has worked. Not sure how to start the joining and slinging part.
var touchingScreen = false
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesBegan(touches, withEvent: event)
touchingScreen = true
println("Screen Touched")
}
override func touchesCancelled(touches: Set<NSObject>!, withEvent event: UIEvent!) {
super.touchesCancelled(touches, withEvent: event)
touchingScreen = false
println("Screen Not Touched")
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
super.touchesEnded(touches, withEvent: event)
touchingScreen = false
println("Screen Not Touched")
}
override func update(currentTime: CFTimeInterval) {
if touchingScreen {
var RotatingAngle = CGFloat(M_PI)
var rotationDuration = 20.0
var rotateCanon = SKAction.rotateByAngle(CGFloat(RotatingAngle), duration: rotationDuration)
canon.runAction(rotateCanon)
} else if !touchingScreen {
var RotatingAngleTwo = CGFloat(0)
var rotationDurationTwo = 0.0
var rotateCanon = SKAction.rotateByAngle(CGFloat(RotatingAngleTwo), duration: rotationDurationTwo)
canon.runAction(rotateCanon)
}
}
Thanks for the help.
Some thoughts...
It's fairly straightforward to implement a slingshot as shown in the video if you break it down into smaller steps
If you represent the ball's position on the circle with polar coordinates, you can rotate the ball by simply incrementing the angle over time and then converting from polar to cartesian coordinates
When launched, the ball's trajectory should be tangent to the ball's position on the circle and its speed should be the same as its angular velocity.
and some code...
enum State {
case Stopped
case Rotating
case Launched
}
let two_pi = CGFloat(M_PI*2.0)
let pi = CGFloat(M_PI)
// These are useful vector/point operators
func * (left:CGPoint, right:CGFloat) -> CGPoint {
return CGPointMake(left.x*right, left.y*right)
}
func += (inout left:CGPoint, right:CGPoint) {
left = CGPointMake(left.x+right.x, left.y+right.y)
}
func * (left:CGVector, right:CGFloat) -> CGVector {
return CGVectorMake(left.dx*right, left.dy*right)
}
func / (left:CGVector, right:CGFloat) -> CGVector {
return CGVectorMake(left.dx/right, left.dy/right)
}
class GameScene: SKScene {
let shape = SKShapeNode(circleOfRadius: 7)
let radius:CGFloat = 30
var center = CGPointZero
var currentAngle = -pi/2
let angleIncr = two_pi / 60.0
var state:State = .Stopped
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
// Set the center of the sling
center = CGPointMake (CGRectGetMidX(view.frame),CGRectGetMidY(view.frame))
addBall()
let circle = SKShapeNode(circleOfRadius: radius)
circle.position = CGPointMake (CGRectGetMidX(view.frame),CGRectGetMidY(view.frame))
addChild(circle)
}
// Adds a circle shape node at the bottom of the sling
func addBall() {
currentAngle = -pi/2
shape.fillColor = SKColor.blueColor()
shape.position = CGPointMake (center.x, center.y-radius)
shape.physicsBody = SKPhysicsBody(circleOfRadius: 7)
shape.physicsBody?.affectedByGravity = false
shape.physicsBody?.mass = 0.5
shape.zPosition = 1
addChild(shape)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
if let touch = touches.first as? UITouch {
let location = touch.locationInNode(self)
if (state == .Stopped) {
// Start rotating the ball around the sling
state = .Rotating
}
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
if (state == .Rotating) {
// Launch the ball on a vector tangent to its current position on the circle
state = .Launched
// Normal vector
var normal = CGVectorMake(shape.position.x-center.x, shape.position.y-center.y)
normal = normal / magnitude(normal)
// Tangent vector
let vector = CGVectorMake(normal.dy, -normal.dx)
// Convert angular to linear speed
let speed = angleIncr * 60.0 * radius
shape.physicsBody?.velocity = vector*speed
runAction(SKAction.waitForDuration(1.0)) {
self.shape.removeFromParent()
self.state = .Stopped
self.addBall()
}
}
}
override func update(currentTime: CFTimeInterval) {
switch (state) {
case .Rotating:
var point = angleToPoint(currentAngle) * radius
point += center
shape.position = point
currentAngle -= angleIncr
// Wrap at 2 pi
currentAngle %= two_pi
default:
break
}
}
func angleToPoint(angle:CGFloat) -> CGPoint {
return CGPointMake(cos(angle), sin(angle))
}
func magnitude(v1:CGVector) -> CGFloat {
return sqrt(v1.dx*v1.dx+v1.dy*v1.dy)
}
}
and a video...