Relation between angle and vectors - Sprite Kit Swift - swift

I'm creating a game, and I have a canon that shoots bullets.
I have already the code to change the orientation of the canon, :
override func touchesMoved(touches: NSSet, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let location = touch.locationInNode(self)
if self.nodeAtPoint(location) == self {
if location.x < self.size.width / 2 {
let x = location.x
let y = location.y
let AB = self.size.width / 2 - x
let angleRadians = atan(AB/y)
let angleDegrees = angleRadians * (180 / CGFloat(M_PI))
canon.zRotation = angleDegrees * (CGFloat(M_PI) / 180)
}
if location.x > self.size.width / 2 {
let x = location.x
let y = location.y
let AB = x - self.size.width / 2
let angleRadians = atan(AB/y)
let angleDegrees = angleRadians * (180 / CGFloat(M_PI))
canon.zRotation = -(angleDegrees * (CGFloat(M_PI) / 180))
}
}
}
and I want to determine the direction of the bullet when the user shoots,
I tried with an action (moveBy) but I didn't find a relation between the orientation of the canon, and the vector of the bullet's direction.
If the location of the touch is CGPoint(x : 100, y : 100), the vector cannot be CGVectorMake(100, 100), because it is too big values !
I tested with a moveTo action, the direction of the bullet was perfect, but the bullet stopped on the touch location, and I want that the bullet continue to move forever !
Thanks for your help !

Related

Air Hockey circle-circle impulse on collision in Swift/SpriteKit?

Background: I'm working on a simple game/application in SpriteKit (Swift). It's similar to Air Hockey but with magnets (like tabletop game called Klask). I have already spent a lot of time adding a store, ads, menu, etc. It's been a fun learning project for me so far.
Current method: I am applying an impulse to a ball when the player/mallet strikes the ball. This impulse (which takes in a CGVector(dx:.., dy:..)) is calculated by the touch movement/position frame to frame. So for instance, if the player moved the mallet in the north east direction and a collision between the ball and the player is detected, the ball will move in that northeast direct (up and to the right).
Issue: This doesn't take into account when the player clips the ball, which causes a warpy effect.The player would expect the ball to move in a direction based on where the ball strikes on the players mallet (a round circle) rather than the direction of the touch (which does usually work pretty well).
Desired implementation (demo from acceleroto's Air Hockey):
Current implementation/code:
// In player class
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?)
{
bottomTouchIsActive = true
var releventTouch:UITouch!
//convert set to known type
let touchSet = touches
//get array of touches so we can loop through them
let orderedTouches = Array(touchSet)
for touch in orderedTouches
{
//if we've not yet found a relevent touch
if releventTouch == nil
{
//look for a touch that is in the activeArea (Avoid touches by opponent)
if activeArea.contains(CGPoint(x: touch.location(in: parent!).x, y: touch.location(in: parent!).y + frame.height * 0.24))
{
isUserInteractionEnabled = true
releventTouch = touch
}
else
{
releventTouch = nil
}
}
}
if (releventTouch != nil) && lastTouchTimeStamp != nil
{
//get touch position and relocate player
let location = CGPoint(x: releventTouch!.location(in: parent!).x, y: releventTouch!.location(in: parent!).y + frame.height * 0.24)
position = location
//find old location and use pythagoras to determine length between both points
let oldLocation = CGPoint(x: releventTouch!.previousLocation(in: parent!).x, y: releventTouch!.previousLocation(in: parent!).y + frame.height * 0.24)
let xOffset = location.x - oldLocation.x
let yOffset = location.y - oldLocation.y
let vectorLength = sqrt(xOffset * xOffset + yOffset * yOffset)
let seconds = releventTouch.timestamp - lastTouchTimeStamp!
let velocity = 0.015 * Double(vectorLength) / seconds
//to calculate the vector, the velcity needs to be converted to a CGFloat
let velocityCGFloat = CGFloat(velocity)
// NSUserDefaults for more direct access in collision detection
let forceSaveDX = UserDefaults.standard
forceSaveDX.set(velocityCGFloat * xOffset / vectorLength, forKey: "BottomForceDX")
forceSaveDX.synchronize()
let forceSaveDY = UserDefaults.standard
forceSaveDY.set(velocityCGFloat * yOffset / vectorLength, forKey: "BottomForceDY")
forceSaveDY.synchronize()
//Only apply an impulse if the touch is active.
delegate?.bottomTouchIsActive(bottomTouchIsActive, fromBottomPlayer: self)
//update latest touch time for next calculation
lastTouchTimeStamp = releventTouch.timestamp
}
else if (releventTouch != nil) && lastTouchTimeStamp == nil
{
//get touch position and relocate player
let location = CGPoint(x: releventTouch!.location(in: parent!).x, y: releventTouch!.location(in: parent!).y + frame.height * 0.24)
position = location
//find old location and use pythagoras to determine length between both points
let oldLocation = CGPoint(x: releventTouch!.previousLocation(in: parent!).x, y: releventTouch!.previousLocation(in: parent!).y + frame.height * 0.24)
let xOffset = location.x - oldLocation.x
let yOffset = location.y - oldLocation.y
let vectorLength = sqrt(xOffset * xOffset + yOffset * yOffset)
//update latest touch time for next calculation
lastTouchTimeStamp = releventTouch.timestamp
}
}
// In main gameScene
func didBegin(_ contact: SKPhysicsContact)
{
if (contact.bodyA.categoryBitMask == BodyType.ball.rawValue && contact.bodyB.categoryBitMask == BodyType.player.rawValue) && (contact.bodyA.contactTestBitMask == 25 || contact.bodyB.contactTestBitMask == 25) && bottomTouchForCollision == true
{
ball!.physicsBody!.applyImpulse(CGVector(dx: CGFloat(UserDefaults.standard.float(forKey: "BottomForceDX")), dy: CGFloat(UserDefaults.standard.float(forKey: "BottomForceDY"))))
}
}
I apologize for the long question, it's hard to explain what the issue is. But essentially I just need to make the impulse be calculated using where the ball hits the player mallet rather than by the direction of the touch. If this can be done directly in the collision detection function, that would be even better as there would be no extra frame dedicated to calculating the touch direction. I've been stuck for a while.
I think it would be best to incorporate the speed of the touch and the position where the ball strikes the mallet to have the most realistic impulse. I just don't know how to calculate the vector this way. I'm open to all ideas!

How to move SpriteNode by its angle?

I am trying to create a small game where SpriteNode (aka Player) moves up vertically on constant speed. I want to use its angle for steering left or right. However, I am not able to move the Player properly using its angle.
Thank you for your time.
Here is the partial code I wrote:
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
if let touch = touches.first {
let location = touch.previousLocation(in: self)
if location.x < self.size.width / 2 && location.y < self.size.height / 2 {
// Turn Left
print("TURNING LEFT")
turn(left: true)
} else if location.x >= self.size.width / 2 && location.y < self.size.height / 2 {
// Turn Right
print("TURNING RIGHT")
turn(left: false)
} else if location.y > self.size.height / 2 {
// Go Up
print("GOING UP!")
move()
}
}
}
func turn(left: Bool) {
if left {
// Turn Left
let turnAction = SKAction.rotate(byAngle: 0.1, duration: 0.05)
let repeatAction = SKAction.repeatForever(turnAction)
player?.run(repeatAction)
} else {
// Turn Right
let turnAction = SKAction.rotate(byAngle: -0.1, duration: 0.05)
let repeatAction = SKAction.repeatForever(turnAction)
player?.run(repeatAction)
}
}
func move() {
// Move Up
let moveAction = SKAction.moveBy(x: 0, y: 15, duration: 0.5)
let repeatAction = SKAction.repeatForever(moveAction)
player?.run(repeatAction)
}
Using Trigonometry you can determine the sprite's x and y speed in either direction create an angle for the sprite to point towards. A great article that sums up how to do this can be found here.
If you simply wish to literally rotate the sprite it can be done by creating an SKAction for the rotation and running the action on the node.
// Create an action, duration can be changed from 0 so the user can see a smooth transition otherwise change will be instant.
SKAction *rotation = [SKAction rotateByAngle: M_PI/4.0 duration:0];
//Simply run the action.
[myNode runAction: rotation];
Thanks to #makertech81 link, I was able to write below code which works:
func move() {
// Move Up
let playerXPos = sin((player?.zRotation)!) * -playerSpeed
let moveAction = SKAction.moveBy(x: playerXPos, y: playerSpeed, duration: 0.5)
let repeatAction = SKAction.repeatForever(moveAction)
player?.run(repeatAction)
}
Basically, because I know the angle through zRotation and also I know how much Player will move toward Y, I was able to calculate its sin (which is X value). Therefore, moveAction will be properly calculated toward its destination.
Hope it helps.

Shooting bullets from SKSpriteNode

I try to build 2D - top down game, and I have player (SKSpriteNode) he can move and rotate, and I want to shoot two bullets from him.
I use this code to shoot:
func setBullet(player: Player, bullet: Int)
{
let weaponPosition = scene!.convertPoint(player.weapon.position, fromNode: player)
var xPos, yPos: CGFloat!
let sinus = sin(player.zRotation)
let cosinus = cos(player.zRotation)
if bullet == 1
{
xPos = converted.x - sinus * player.size.height / 2
yPos = converted.y + cosinus * player.size.height / 2
}
else if bullet == 2
{
xPos = weaponPosition.x - sinus * player.size.height / 2
yPos = weaponPosition.y + cosinus * player.size.height / 2
}
position = CGPoint(x: xPos, y: yPos)
physicsBody!.applyImpulse(CGVector(dx: -sinus * normalSpeed, dy: cosinus * normalSpeed))
}
But, i do not know how to correctly set the position...
I try to make something like this
(Green dots - this is a bullets). Can anyone help me please!
Shooting multiple bullets in the same direction is fairly straightforward. The key is to determine the bullets' initial positions and direction vectors when the character is rotated.
You can calculate a bullet's initial position within the scene by
let point = node.convertPoint(weapon.position, toNode: self)
where node is the character, weapon.position is non-rotated position of a gun, and self is the scene.
Typically, a bullet moves to the right, CGVector(dx:1, dy:0), or up, CGVector (dx:0, dy:1), when the character is not rotated. You can calculate the direction of the impulse to apply to the bullet's physics body by rotating the vector by the character's zRotation with
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
Here's an example of how to shoot two bullets from a rotated character:
struct Weapon {
var position:CGPoint
}
class GameScene: SKScene {
let sprite = SKSpriteNode(imageNamed:"Spaceship")
let dualGuns = [Weapon(position: CGPoint(x: -15, y: 15)), Weapon(position: CGPoint(x: 15, y: 15))]
let singleGun = [Weapon(position: CGPoint(x: 0, y: 15))]
let numGuns = 1
// If your character faces up where zRotation == 0, offset by pi
let rotationOffset = CGFloat(M_PI_2)
override func didMoveToView(view: SKView) {
scaleMode = .ResizeFill
sprite.position = view.center
sprite.size = CGSizeMake(25, 25)
self.addChild(sprite)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
if let _ = touches.first {
let action = SKAction.rotateByAngle(CGFloat(M_PI_4/2), duration:1)
sprite.runAction(action) {
[weak self] in
if let scene = self {
switch (scene.numGuns) {
case 1:
for weapon in scene.singleGun {
scene.shoot(weapon: weapon, from: scene.sprite)
}
case 2:
for weapon in scene.dualGuns {
scene.shoot(weapon: weapon, from: scene.sprite)
}
default:
break
}
}
}
}
}
func shoot(weapon weapon:Weapon, from node:SKNode) {
// Convert the position from the character's coordinate system to scene coodinates
let converted = node.convertPoint(weapon.position, toNode: self)
// Determine the direction of the bullet based on the character's rotation
let vector = rotate(CGVector(dx: 0.25, dy: 0), angle:node.zRotation+rotationOffset)
// Create a bullet with a physics body
let bullet = SKSpriteNode(color: SKColor.blueColor(), size: CGSizeMake(4,4))
bullet.physicsBody = SKPhysicsBody(circleOfRadius: 2)
bullet.physicsBody?.affectedByGravity = false
bullet.position = CGPointMake(converted.x, converted.y)
addChild(bullet)
bullet.physicsBody?.applyImpulse(vector)
}
// Rotates a point (or vector) about the z-axis
func rotate(vector:CGVector, angle:CGFloat) -> CGVector {
let rotatedX = vector.dx * cos(angle) - vector.dy * sin(angle)
let rotatedY = vector.dx * sin(angle) + vector.dy * cos(angle)
return CGVector(dx: rotatedX, dy: rotatedY)
}
}
Suppose your player is a circle maked with SKShapeNode or SKSpriteNode.
Both of them have the frame property:
let f = player.frame
So, the first bullet can be in this position:
let firstBullet.position = CGPointMake(player.position.x-(f.width/2),player.position.y)
let secondBullet.position = CGPointMake(player.position.x+(f.width/2),player.position.y)
To know it during rotation do:
let firstBulletXPos = firstBullet.position.x - sinus * bullet.size.height / 2
let firstBulletYPos = firstBullet.position.y + cosinus * bullet.size.height / 2
let secondBulletXPos = secondBullet.position.x - sinus * bullet.size.height / 2
let secondBulletYPos = secondBullet.position.y + cosinus * bullet.size.height / 2

how do i make a node go from one side of the screen to the other?

I'm just starting in sprite kit and have hit a road block. I'm trying to recreate the old atari game asteroids. I'm currently trying to find out how to move the node "ship" from one side of the screen and come out the opposite side. An example of this would be pacman going from the right side of the screen and out the left side of the screen. Help is greatly appreciated.
Thanks in advance,
Jared
import SpriteKit
class GameScene: SKScene, SKPhysicsContactDelegate{
let base = SKSpriteNode(imageNamed: "Base")
let ball = SKSpriteNode(imageNamed: "Ball")
let ship = SKSpriteNode(imageNamed: "Ship")
let shoot = SKSpriteNode(imageNamed: "shootButton")
override func didMoveToView(view: SKView){
// var DynamicView=UIView(frame: CGRectMake(100, 200, 100, 100))
// DynamicView.backgroundColor=UIColor.greenColor()
// DynamicView.layer.cornerRadius=2
// DynamicView.layer.borderWidth=2
// self.view!.addSubview(DynamicView)
self.anchorPoint = CGPointMake(0.5, 0.5)
self.physicsWorld.contactDelegate = self
self.physicsWorld.gravity = CGVectorMake(0.0, 0.0)
self.addChild(base)
base.position = CGPointMake(-350, -200)
self.addChild(shoot)
shoot.position = CGPointMake(350, -200)
self.addChild(ball)
ball.position = base.position
self.addChild(ship)
ship.position = CGPointMake(20, 47)
ship.xScale = 0.7
ship.yScale = 0.7
ship.physicsBody?.dynamic = true
ship.physicsBody?.allowsRotation = true
ship.physicsBody?.affectedByGravity = true
ship.physicsBody = SKPhysicsBody(texture: SKTexture(imageNamed: "Ship"), size: ship.size)
self.physicsBody = SKPhysicsBody(edgeLoopFromRect: self.frame)
ball.alpha = 0.4
base.alpha = 0.4
}
// 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)
//
//
// }
// }
override func touchesMoved(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch in (touches ) {
let location = touch.locationInNode(self)
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
ball.position = CGPointMake(base.position.x - xDist, base.position.y + yDist)
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.physicsBody?.mass = 2
var shipRotation : CGFloat = ship.zRotation
var calcRotation : Float = Float(angle - 1.57079633) + Float(M_PI_2);
let intensity : CGFloat = 2000.0 // put your value
let xVelocity = intensity * CGFloat(cosf(calcRotation))
let yVelocity = intensity * CGFloat(sinf(calcRotation))
let vector : CGVector = CGVectorMake(xVelocity, yVelocity)
//Apply force to spaceship
ship.physicsBody?.applyForce(vector)
}
}
override func touchesEnded(touches: Set<UITouch>, withEvent event: UIEvent?) {
let move:SKAction = SKAction.moveTo(base.position, duration: 0.2)
move.timingMode = .EaseOut
ball.runAction(move)
}
}
// overridefunc update(currentTime: CFTimeInterval) {
// /* Called before each frame is rendered */
//
//}
In func update(currentTime) check if ship.position.x < 0 or ship.position.x > scene.width. If true, set ship.position.x to the opposite side.
It looks like you're using physics to move the ship (as opposed to updating its position by a certain amount in the update() method)
So what you could do is to override the didiSimulatePhysics() method (which is called after the SpriteKit game engine has done all the physics calculations and moved all the nodes) and if your ship is off screen (which you could do by seeing if it's position is outside the screne's x & y boundaries, or by using the intersectsRect method to see if. The ship's frame no longer overlaps the screen frame) and if it is, simply wrap it to the other side of the screen.

how to spawn a SKnode on a loop that is in a different class - swift

I am creating a shooting game and i am trying to create a firing method but my current method only spawns one bullet at a time when clicked. I have my bullet defined as a SKnode subclass and calling it in my touches began method in my gameScene.swift. Here's how
class Bullet1: SKNode {
var Bullet1:SKSpriteNode = SKSpriteNode()
var Bullet1Animaton:SKAction?
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
override init () {
super.init()
}
func createBullet1s (){
let wait:SKAction = SKAction.waitForDuration( CFTimeInterval(0.25))
let block:SKAction = SKAction.runBlock(createBullet1)
let seq2:SKAction = SKAction.sequence( [block, wait])
SKAction.repeatActionForever(seq2)
self.runAction(seq2, withKey:"droneAction")
}
func createBullet1() {
Bullet1 = SKSpriteNode(imageNamed: "bullet.png")
Bullet1.xScale = 0.5
Bullet1.yScale = 0.5
Bullet1.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet1.size)
Bullet1.physicsBody?.categoryBitMask = PhysicsCategory.bullet1
Bullet1.physicsBody?.contactTestBitMask = PhysicsCategory.enemy1
Bullet1.physicsBody?.affectedByGravity = false
Bullet1.physicsBody?.dynamic = true
self.addChild(Bullet1)
self.name = "Bullet1"
}
}
class GameScene: SKScene, SKPhysicsContactDelegate {
override func touchesBegan(touches: Set<UITouch>, withEvent
event:UIEvent?) {
override func touchesBegan(touches: Set<UITouch>, withEvent
event:UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
let node = nodeAtPoint(location)
if (CGRectContainsPoint(joystick.frame, location)) {
stickActive = true
fireWeapon = true
}else{
stickActive = false
}
if stickActive == true && fireWeapon == true{ if fireWeapon == false
{}
let bulletAction = SKAction.runBlock({
let v = CGVector(dx: location.x - self.joystickBase.position.x, dy:
location.y - self.joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)
print(deg + 180) // prints degree in debugging
self.hero.zRotation = angle - 1.57079633 //rotates with angle of joy
stic
let bullet1:Bullet1 = Bullet1()
bullet1.createBullet1()
bullet1.position = CGPoint (x: self.hero.position.x , y:
self.hero.position.y) // Bullet spawn # anchor point
let xDist:CGFloat = sin(angle - 1.5709633) * 35 //bullet spawn location
let yDist:CGFloat = cos(angle - 1.5709633) * 35 //bullet spawn location
bullet1.position = CGPointMake( self.hero.position.x - xDist,
self.hero.position.y + yDist) //spawning bullet # defined location
let xDistMove:CGFloat = sin(angle - 1.5709633) * 700 //Bullet max x
distance
let yDistMove:CGFloat = cos(angle - 1.5709633) * 700 //Bullet max y
distance
let bulletMovementY = SKAction.moveToY(self.hero.position.y +
yDistMove, duration: 1) //Bullet y speed
let bulletMovementX = SKAction.moveToX(self.hero.position.x -
xDistMove, duration: 1) //Bullet x speed
bullet1.runAction(bulletMovementY)
bullet1.runAction(bulletMovementX)
self.addChild(bullet1)
})
let wait = SKAction.waitForDuration(0.25)
let completeSequence = SKAction.sequence([bulletAction, wait])
let repeatShootingActionForever =
SKAction.repeatActionForever(completeSequence)
self.runAction(repeatShootingActionForever, withKey: "BulletAction")
}
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 - joystickBase.position.x, dy:
location.y - joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)
let length: CGFloat = joystickBase.frame.size.height / 2
print(deg + 180) // prints degree in debugging
joystick.position = location
hero.zRotation = angle - 1.57079633 //rotates with angle of joy stick
if (CGRectContainsPoint(joystickBase.frame, location)) {
}
override func touchesMoved(touches: Set<UITouch>, withEvent event:
UIEvent?) {
for touch in touches {
let location = touch.locationInNode(self)
if stickActive == true && fireWeapon == true{ if fireWeapon == false
{}
let v = CGVector(dx: location.x - joystickBase.position.x, dy:
location.y - joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)//converts radains to degrees and
generates a degree when touchesmove == true
let length: CGFloat = joystickBase.frame.size.height / 2 //sets maximum
distance for joystick ball
print(deg + 180) // prints degree in debugging
joystick.position = location
self.hero.zRotation = angle - 1.57079633 //rotates with
angle of joy stick
let xDist:CGFloat = sin(angle - 1.5709633) * 35 //bullet spawn location
let yDist:CGFloat = cos(angle - 1.5709633) * 35 //bullet spawn location
bullet1.position = CGPointMake( self.hero.position.x - xDist,
self.hero.position.y + yDist) //spawning bullet # defined location
let xDistMove:CGFloat = sin(angle - 1.5709633) * 700 //Bullet max x
distance
let yDistMove:CGFloat = cos(angle - 1.5709633) * 700 //Bullet max y
distance
let bulletMovementY = SKAction.moveToY(self.hero.position.y +
yDistMove, duration: 1) //Bullet y speed
let bulletMovemenX = SKAction.moveToX(self.hero.position.x - xDistMove,
duration: 1) //Bullet x speed
bullet1.runAction(bulletMovementY)
bullet1.runAction(bulletMovemenX)
}}
as you can see i need to call my bullet method in my touches began since the bullet spawn location is coordinated with a joystick. The current method only spawn 1 bullet when clicked instead of creating a new bullet every 0.25 seconds when the joystick is held down like i attempted in my createBullet1s method. If anyone can help me out i would be very grateful! thank you
if the values have to be new calculated for every bullet then something like this would be the way to go i guess (add self. before every node Name):
if stickActive == true && fireWeapon == true{ if fireWeapon == false
{}
let shootingAction = SKAction.runBlock({
let v = CGVector(dx: location.x - joystickBase.position.x, dy:
location.y - joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)//converts radains to degrees and
generates a degree when touchesmove == true
print(deg + 180)
let bullet1:Bullet1 = Bullet1()
self.bullet1.createBullet1s()
self.bullet1.position = CGPoint (x: self.hero.position.x , y: self.hero.position.y)
let xDist:CGFloat = sin(angle - 1.5709633) * 35
let yDist:CGFloat = cos(angle - 1.5709633) * 35
self.bullet1.position = CGPointMake(self.hero.position.x - xDist,self.hero.position.y + yDist)
let xDistMove:CGFloat = sin(angle - 1.5709633) * 700
let yDistMove:CGFloat = cos(angle - 1.5709633) * 700
let bulletMovementY = SKAction.moveToY(self.hero.position.y + yDistMove,
duration: 1)
let bulletMovemenX = SKAction.moveToX(self.hero.position.x - xDistMove,
duration: 1)
let wait:SKAction = SKAction.waitForDuration(CFTimeInterval(0.25))
self.addChild(self.bullet1)
let groupMovement = SKAction.group([bulletMovementY,bulletMovementX])
let removeNode = SKAction.removeFromParent()
let sequenceMovementWithWait = SKAction.sequence([wait,groupMovement,removeNode])
self.hero.runAction(sequenceMovementWithWait)
self.hero.zRotation = angle - 1.57079633
})
let repeatShootingActionForever = SKAction.repeatActionForever(shootingAction)
self.runAction(repeatShootingActionForever, withKey: "BulletAction"))
if the values can stay the same for each bullet maybe something like this would be enough:
let bulletAction = SKAction.runBlock({
self.addChild(self.bullet1)
let groupAction = SKAction([bulletMovementY,bulletMovementX])
let removeNode = SKAction.removeFromParent()
let finalSequenceWithWait = SKAction.sequence([wait,groupAction,removeNode])
self.bullet1.runAction(finalSequenceWithWait)
})
let repeatBulletActionForever = SKAction.repeatActionForever(bulletAction)
self.runAction(repeatBulletActionForever, withKey: "BulletAction")
For both ways i would say in TouchesEnded:
self.removeActionForKey("BulletAction")
Edit:
Okay .. You have a SKNode subclass where you add the bullets (SpriteNodes) to the SKNode. By the way - it's complicated because you gave the SpriteNodes and the SKNode the same name. However, the bullets (SpriteNodes) gets added to the SKNode and the SKNode gets added in the GameScene. Now we "spawn" and "remove" this SKNode in the gamescene, but we only add SpriteNodes to the SKNode and we don't remove them there.
Do you want multiple bullets at the same time to be visible in the GameScene? If you need only 1 bullet at a time visible (but repeated) i would say maybe it would help to remove the Bullet (SpriteNode) from your SKNode like we did in the GameScene.
Imagine that:
- GameScene creates a SKNode. (your class Bullet1)
- SKNode creates Child SpriteNode
- GameScene removes the SKNode.
- GameScene creates another SKNode. (again your class Bullet1)
- SKNode wants to create Child SpriteNode
-> Error: SpriteNode already exists.
So we need to edit your class.
//class Bullet1
func createBullet1() {
if Bullet1.parent == nil{
Bullet1 = SKSpriteNode(imageNamed: "bullet.png")
Bullet1.xScale = 0.5
Bullet1.yScale = 0.5
Bullet1.physicsBody = SKPhysicsBody(rectangleOfSize: Bullet1.size)
Bullet1.physicsBody?.categoryBitMask = PhysicsCategory.bullet1
Bullet1.physicsBody?.contactTestBitMask = PhysicsCategory.enemy1
Bullet1.physicsBody?.affectedByGravity = false
Bullet1.physicsBody?.dynamic = true
self.addChild(Bullet1)
self.name = "Bullet1"
}
else{
Bullet1.removeFromParent()
}
}
Oh, and we need to wait 0.25secs before adding a new SKNode so that the animation is done. In your GameScene edit this part:
let repeatShootingActionForever = SKAction.repeatActionForever(shootingAction)
self.runAction(repeatShootingActionForever, withKey: "BulletAction"))
to this:
let wait = SKAction.waitForDuration(0.25)
let runShootingAction = SKAction.runBlock(shootingAction)
let completeSequence = SKAction.sequence([wait,runShootingAction])
let repeatShootingActionForever = SKAction.repeatActionForever(completeSequence)
self.runAction(repeatShootingActionForever, withKey: "BulletAction"))
I bet there are better solutions for that, im not an expert in Swift, too. You have to test that .. sorry.
Another edit:
You did not tell which zRotation the bullet should have.
Try this (where you have bulletAction = SKAction.runBlock):
let bulletAction = SKAction.runBlock({
let v = CGVector(dx: location.x - self.joystickBase.position.x, dy: location.y - self.joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)//converts radains to degrees and generates a degree when touchesmove == true
print(deg + 180) // prints degree in debugging
let bullet1:Bullet1 = Bullet1()
bullet1.position = CGPoint (x: self.hero.position.x , y: self.hero.position.y) // Bullet spawn # anchor point
bullet1.zRotation = angle - 1.57079633 //rotates with angle of joy stick
let xDist:CGFloat = sin(angle - 1.5709633) * 35 //bullet spawn location
let yDist:CGFloat = cos(angle - 1.5709633) * 35 //bullet spawn location
bullet1.position = CGPointMake( self.hero.position.x - xDist, self.hero.position.y + yDist) //spawning bullet # defined location
bullet1.createBullet1()
let xDistMove:CGFloat = sin(angle - 1.5709633) * 700 //Bullet max x distance
let yDistMove:CGFloat = cos(angle - 1.5709633) * 700 //Bullet max y distance
let bulletMovementY = SKAction.moveToY(self.hero.position.y+yDistMove, duration: 1) //Bullet y speed
let bulletMovemenX = SKAction.moveToX(self.hero.position.x -xDistMove, duration: 1) //Bullet x speed
let wait:SKAction = SKAction.waitForDuration( CFTimeInterval(0.25))
// let bulletFire = SKAction.sequence([wait,bulletMovemenX, bulletMovementY])
self.addChild(bullet1)
bullet1.runAction(wait)
bullet1.runAction(bulletMovementY)
bullet1.runAction(bulletMovemenX)
self.hero.zRotation = angle - 1.57079633 //rotates with angle of joy stick
})
Last Edit:
I can't see in your updated method the bullet1.zRotation so i guess you want only the hero to Change the zRotation.
However, if the zRotation only changes in touchesBeganand not in touchesMoved i would say you should make angle a global variable which can be changed in both methods. Maybe this helps, because hero gets his zRotation depending on the value of angle (i think).
Make angle a global variable and add a variable which tells you if your touch is moved or not:
class GameScene: SKScene, SKPhysicsContactDelegate{
var angle = CGFloat() // It should be a CGFloat, right?
var touchesMovedOn = false
Edit this part from touchesBegan:
let bulletAction = SKAction.runBlock({
let v = CGVector(dx: location.x - self.joystickBase.position.x, dy: location.y - self.joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)
print(deg + 180) // prints degree in debugging
self.hero.zRotation = angle - 1.57079633 //rotates with angle of joystic
to this:
let bulletAction = SKAction.runBlock({
let v = CGVector(dx: location.x - self.joystickBase.position.x, dy: location.y - self.joystickBase.position.y)
if touchesMovedOn == false{
angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
}
let deg = angle * CGFloat(180 / M_PI)
print(deg + 180) // prints degree in debugging
self.hero.zRotation = angle - 1.57079633 //rotates with angle of joystic
Edit this part from touchesMoved:
let v = CGVector(dx: location.x - joystickBase.position.x, dy: location.y - joystickBase.position.y)
let angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)//converts radains to degrees and generates a degree when touchesmove == true
let length: CGFloat = joystickBase.frame.size.height / 2 //sets Maximum distance for joystick ball
print(deg + 180) // prints degree in Debugging to this:
to this:
touchesMovedOn = true
let v = CGVector(dx: location.x - joystickBase.position.x, dy: location.y - joystickBase.position.y)
angle = atan2(v.dy, v.dx) //where angle is defined "in radians"
let deg = angle * CGFloat(180 / M_PI)//converts radains to degrees and generates a degree when touchesmove == true
let length: CGFloat = joystickBase.frame.size.height / 2 //sets Maximum distance for joystick ball
print(deg + 180) // prints degree in Debugging to this:
Add this line in touchesEnded
touchesMovedOn = false
If this not helps i really Need the Project as i would have to test it myself where the Problem is. So i hope it works now. Best luck ! And as i said, im sure there are better Solutions for this, so in the future you should look out to make the code better. ;-)