using selector in xcode with swift - xcode

ran into some trouble with my coding today. Trying to make an "automatic" weapon but can't get the selector to function correctly.
Here is the code
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ){
let location = touch.location(in: self)func spawnBullets(){
let Bullet = SKSpriteNode(imageNamed: "circle")
Bullet.zPosition = -1
Bullet.position = CGPoint(x: ship.position.x,y: ship.position.y)
Bullet.size = CGSize(width: 30, height: 30)
Bullet.physicsBody = SKPhysicsBody(circleOfRadius: 15)
Bullet.physicsBody?.categoryBitMask = PhysicsCategory.Bullet
Bullet.name = "Bullet"
Bullet.physicsBody?.isDynamic = true
Bullet.physicsBody?.affectedByGravity = false
self.addChild(Bullet)
var dx = CGFloat(location.x - base2.position.x)
var dy = CGFloat(location.y - base2.position.y)
let magnitude = sqrt(dx * dx + dy * dy)
dx /= magnitude
dy /= magnitude
let vector = CGVector(dx: 30.0 * dx, dy: 30.0 * dy)
Bullet.physicsBody?.applyImpulse(vector)
}
spawnBullets()
Timer.scheduledTimer(timeInterval: 0.2, target: self, selector:#selector("spawnBullets"),userInfo: nil, repeats: true)
}
'
Yet when I run this, I get an error that the selector doesn't reference anything. can anyone help me please?
thanks

You didn't add the swift version you are using. There are some minor changes in swift 3.x.
In swift 3.x you don't use selectors with quotes in this way, so you have to remove the quotes & do something like this:
selector:#selector(spawnBullets)
This will also give you some type safety when changing your code. So instead of runtime errors you will get compile time errors when you do something wrong.
What I also would do in your case is to move the function spawnBullets outside of touchBegan like this:
func spawnBullets(_ location: CGPoint) {
...
}
You also need another seperate func to deal with timer parameters (more info on that here: https://stackoverflow.com/a/41987413/404659):
func spawnBullets(sender: Timer) {
if let location = sender.userInfo as! CGPoint? {
spawnBullets(location)
}
}
Your touchBegan would then end up something like this:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches ) {
let location = touch.location(in: self)
spawnBullets(location)
Timer.scheduledTimer(
timeInterval: 0.2,
target: self,
selector:#selector(spawnBullets(sender:)),
userInfo: location,
repeats: true
)
}
}

Related

AnimatableData for Line Chart in SwiftUI

I'd like to create animation of the line chart like drawing from start point to end point.
I've created InsettableShape (code below) in order to use with strokeBorder(), but can't figure out how to define animatableData.
Thank you!
(I know I can do it with Shape and trim(), but I need inset.)
import SwiftUI
struct LineChartInsettableShape: InsettableShape {
let series: [CGFloat]
var insetAmount: CGFloat = 0
func inset(by amount: CGFloat) -> some InsettableShape {
var chart = self
chart.insetAmount += amount
return chart
}
func path(in rect: CGRect) -> Path {
guard !series.isEmpty else { return Path() }
let minY = series.min()!
let maxY = series.max()!
let xStep = (rect.width - insetAmount * 2) / CGFloat(series.count - 1)
func point(index: Int) -> CGPoint {
let yValue: CGFloat = CGFloat(series[index])
return CGPoint(
x: xStep * CGFloat(index) + insetAmount,
y: (rect.height - insetAmount * 2) * (1 - (yValue - minY) / (maxY - minY)) + insetAmount
)
}
return Path { path in
path.move(to: point(index: 0))
for index in 1..<series.count {
path.addLine(to: point(index: index))
}
}
}
}
It's not hard once you figure it out, but initially how this works can be a bit confusing. You need to have a property named animatableData which tells SwiftUI the name of the variable you'll be using for animation, and how to get it and set its value. For example, I have a small piece of animation code that uses a variable named inset, which is very similar to your insetAmount. Here's all the code my app needs to use this variable for animation:
var inset: CGFloat
var animatableData: CGFloat {
get { inset }
set { self.inset = newValue}
}
Not all data types can be used for animation. CGFloat, used here, certainly can be. Paul Hudson has an excellent short video on this topic: https://www.hackingwithswift.com/books/ios-swiftui/animating-simple-shapes-with-animatabledata.
There is a bar chart library, you can check it out as well https://github.com/dawigr/BarChart

SKSpriteNode not able to have multiple children

I'm trying to have multiple sprite nodes of the same type/parent, but when I try to spawn another node I get an error. 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent'
Here's my code:
import SpriteKit
class GameScene: SKScene {
//global declarations
let player = SKSpriteNode(imageNamed: "mage")
let fireball = SKSpriteNode(imageNamed: "fireball")
override func didMoveToView(view: SKView) {
/* Setup your scene here */
createScene()
}
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)
spawnFireball(location)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
self.enumerateChildNodesWithName("fireball", usingBlock: ({
(node,error) in
if (self.fireball.position.x < -self.fireball.size.width/2.0 || self.fireball.position.x > self.size.width+self.fireball.size.width/2.0
|| self.fireball.position.y < -self.fireball.size.height/2.0 || self.fireball.position.y > self.size.height+self.fireball.size.height/2.0) {
self.fireball.removeFromParent()
self.fireball.removeAllChildren()
self.fireball.removeAllActions()
}
}))
}
func createScene() {
//player
player.size = CGSizeMake(100, 100)
player.position = CGPointMake(self.frame.size.width/2, self.frame.size.height/2 + 50)
player.zPosition = 2.0
self.addChild(player)
}
func spawnFireball(point: CGPoint) {
//setup
fireball.name = "fireball"
fireball.size = CGSizeMake(100, 50)
let fireballCenter = CGPointMake(fireball.size.width / 4 * 3, fireball.size.height / 2)
fireball.position = player.position
fireball.physicsBody = SKPhysicsBody(circleOfRadius: fireball.size.height/2, center: fireballCenter)
fireball.physicsBody?.affectedByGravity = false
//action
var dx = CGFloat(point.x - player.position.x)
var dy = CGFloat(point.y - player.position.y)
let magnitude = sqrt(dx * dx + dy * dy)
dx /= magnitude
dy /= magnitude
let vector = CGVector(dx: 32.0 * dx, dy: 32.0 * dy)
var rad = atan2(dy,dx)
fireball.runAction(SKAction.rotateToAngle(rad, duration: 0.0))
self.addChild(fireball)
fireball.physicsBody?.applyImpulse(vector)
}
}
You need to instantiate another SKSpriteNode. In your existing code, you create a single fireball and add it to the scene; your program crashes when you try to add the same fireball again.
First, remove your let fireball = SKSpriteNode... line. Move it to inside the spawnFireball() method, like so:
func spawnFireball(point: CGPoint) {
let fireball = SKSpriteNode(imageNamed: "fireball")
//Insert all customization here (your existing code should mostly work)
self.addChild(fireball)
}
Because the fireball variable is a local variable, you can now instantiate a new one every time you call the function. Now, just change your update() method to properly use enumerateChildrenWithName() by getting changing every self.fireball to just node.
This way, the code will loop through every existing fireball that is currently on the scene, rather than your current code, which only allows you to create one fireball.

Moving a sprite with a joystick in swift Xcode

hello :D I have this code below.
import SpriteKit
class GameScene: SKScene {
let base = SKSpriteNode(imageNamed: "yellowArt/Base")
let ball = SKSpriteNode(imageNamed: "yellowArt/Ball")
let ship = SKSpriteNode(imageNamed: "yellowArt/Ship")
var stickActive:Bool = false
override func didMoveToView(view: SKView) {
self.backgroundColor = SKColor.blackColor()
self.anchorPoint = CGPointMake(0.5, 0.5)
self.addChild(base)
base.position = CGPointMake(0, -200)
self.addChild(ball)
ball.position = base.position
self.addChild(ship)
ship.position = CGPointMake(0, 200)
ball.alpha = 0.4
base.alpha = 0.4
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if (CGRectContainsPoint(ball.frame, location)) {
stickActive = true
} else {
stickActive = false
}
}
}
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
if (stickActive == true) {
let v = CGVector(dx: location.x - base.position.x, dy: location.y - base.position.y)
let angle = atan2(v.dy, v.dx)
let deg = angle * CGFloat( 180 / M_PI)
print( deg + 180 )
let length: CGFloat = base.frame.size.height / 2
let xDist: CGFloat = sin(angle - 1.57079633) * length
let yDist: CGFloat = cos(angle - 1.57079633) * length
if (CGRectContainsPoint(base.frame, location)) {
ball.position = location
} else {
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
ship.zRotation = angle - 1.57079633
ship.position = CGPointMake(angle, angle)
}
} // ends stick active test
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
if (stickActive == true) {
let move: SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
override func update(currentTime: CFTimeInterval) {
/* Called before each frame is rendered */
}
}
The code above creates a joystick and a ship. By moving the joystick I am able to rotate the "ship" with the joystick. However I want to move the ship in the direction that the joystick is holding. How do I tackle this problem? Thanks.
Your ship isn't going to go anywhere since you have ship.position = CGPointMake(angle, angle). As long as you're moving the joystick, the ship is going to go to that point - remove that line.
I would use the update method to move the ship around. First you need to find how much you want to move it in the x and y direction.
Create class variables for the joystick movements below your var stickActive:Bool = false statement:
var xJoystickDelta = CGFloat()
var yJoystickDelta = CGFloat()
Put the following code in your touchesMoved method:
xJoystickDelta = location.x - base.position.x
yJoystickDelta = location.y - base.position.y
Put the following code in your update method:
let xScale = 1.0 //adjust to your preference
let yScale = 1.0 //adjust to your preference
let xAdd = xScale * self.xJoytickDelta
let yAdd = yScale * self.yJoystickDelta
self.ship.position.x += xAdd
self.ship.position.y += yAdd
I hope this helps.

Why do i get this error in swift xcode? "Attemped to add a SKNode which already has a parent"

I dont know why I keep getting this error message. Can you take a look at my code and tell me what Im doing wrong? Im getting an error on the arrowLeft for some reason.
Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: 'Attemped to add a SKNode which already has a parent: name:'(null)' texture:[
class GameScene: SKScene, SKPhysicsContactDelegate {
let arrowLeft = SKSpriteNode(imageNamed: "leftarrow")
let arrowRight = SKSpriteNode(imageNamed: "rightarrow")
func moveArrowInstructions() {
arrowLeft.position = CGPointMake(self.size.width / 4.0, self.size.height / 2)
arrowLeft.zPosition = 20
arrowRight.position = CGPointMake(self.size.width / 1.3, self.size.height / 2)
arrowRight.zPosition = 20
arrowLeft.setScale(0.4)
arrowRight.setScale(0.4)
arrowRight.alpha = 0
arrowLeft.alpha = 0
let moveArrowToRight = SKAction.moveByX(150 + arrowRight.size.width, y: 0, duration: 2.5)
let moveArrowToLeft = SKAction.moveByX(-150 - arrowLeft.size.width, y: 0, duration: 2.5)
let fadeInArrows = SKAction.fadeInWithDuration(1.0)
let fadeOutArrows = SKAction.fadeOutWithDuration(1.0)
let sequenceLeftArrow = SKAction.sequence([fadeInArrows, moveArrowToLeft, fadeOutArrows])
let moveLeftArrowRepeat = SKAction.repeatActionForever(sequenceLeftArrow)
let sequenceRightArrow = SKAction.sequence([fadeInArrows, moveArrowToRight, fadeOutArrows])
let moveRightArrowRepeat = SKAction.repeatActionForever(sequenceRightArrow)
arrowLeft.runAction(moveLeftArrowRepeat)
arrowRight.runAction(moveRightArrowRepeat)
addChild(arrowLeft)
addChild(arrowRight)
}
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
func moveArrows() {
let generateArrows = SKAction.sequence([
SKAction.runBlock(self.moveArrowInstructions),
SKAction.waitForDuration(3.5)])
let endlessAction = SKAction.repeatActionForever(generateArrows)
runAction(endlessAction)
}
var touch: UITouch = touches.first as! UITouch
var location = touch.locationInNode(self)
var node = self.nodeAtPoint(location)
if(node.name == "startgame") {
arrowRight.removeFromParent()
arrowLeft.removeFromParent()
}
You are attempting to add the same node to the scene multiple times.
A quick fix could be to check if the node has already been added to the scene. like so:
if arrowLeft.parent == nil && arrowRight.parent == nil{
addChild(arrowLeft)
addChild(arrowRight)
}
you are constantly adding the same child to the parent every touch
addChild(arrowLeft)
addChild(arrowRight)
needs to be in an area when setting up your scene

Change scroll speed

var speed: CGFloat = 5
override func didMoveToView(view: SKView) {
/* Setup your scene here */
var move = SKAction.moveTo(CGPoint(x: node.position.x, y: -(self.frame.height)), duration: NSTimeInterval(speed))
var reset = SKAction.moveTo(CGPoint(x: node.position.x, y: self.frame.height ), duration: 0)
var scroll = SKAction.sequence([move, reset])
node.runAction(SKAction.repeatActionForever(scroll))
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
speed++
println(speed)
}
I have a node that i want to scroll, and i want it's speed changes depending on the player touch, my problem is when the player touch the value indeed increases but the node itself keeps the same initial value. I also tried changing in the update method and that didn't work either.
How to solve this ?

Resources