A screenshot of my game
I want to make a game (with Spritekit) where you can use the left dpad to move the player around in a tile map which already works. With the right one you can aim at opponents which works too. Althought I enabled multiple touch only one controller works at the same time.
Joystick means the same as dpad.
import SpriteKit
import GameplayKit
class GameScene: SKScene {
//These are just the touch functions
//touch functions
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for _ in touches {
if touches.first!.location(in: cam).x < 0 {
moveStick.position = touches.first!.location(in: cam)
}
if touches.first!.location(in: cam).x > 0 {
shootStick.position = touches.first!.location(in: cam)
}
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for _ in touches {
if touches.first!.location(in: cam).x < 0 {
moveStick.moveJoystick(touch: touches.first!)
}
if touches.first!.location(in: cam).x > 0 {
shootStick.waponRotate(touch: touches.first!)
}
}
}
open override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for _ in touches {
resetMoveStick()
resetShootStick()
}
}
open override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for _ in touches {
resetMoveStick()
resetShootStick()
}
}
// update function
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
let jSForce = moveStick.velocityVector
self.player.position = CGPoint(x: self.player.position.x + jSForce.dx,
y: self.player.position.y + jSForce.dy)
cam.position = player.position
}
}
As KnightOfDragon pointed out, you are using .first. That means your code is looking for the first touch in your scene and then going from there. Your game won't let you use both joysticks at the same time because you won't let them be used at the same time.
These if-statements that you have in your various touch functions:
for _ in touches {
if touches.first!.location(in: cam).x < 0 {
}
if touches.first!.location(in: cam).x > 0 {
}
}
Should look like this:
for touch in touches {
let location = touch.location(in: self)
if location.x < 0 {
moveStick.moveJoystick(touch: location)
}
if if location.x > 0 {
shootStick.waponRotate(touch: location)
}
}
This should fix any errors you have.
Related
I am working on an arrow shooting game - The player need to shoot 3 arrows to a moving target (the target moves from left to right). When an arrow hits the target it should move with it (left to right). The most obvious thing to do would be to change the arrow parent to the target. From some reason its causing me some troubles -
I tried --- arrow.move(toParent:target) and I don't see the arrow on the screen even after I set a new location for it
If I simply --- target.addChild(arrow) I get a failure since I did not remove the arrow from its parent (which is the scene in this case)
When I --- arrow.removeFromParent() and then target.addChild(arrow) its causing other arrows to collide with each other and I still don't see the arrow on the screen.
This is my code -
class GameScene: SKScene, SKPhysicsContactDelegate {
var target:SKSpriteNode?
var arrows = [SKSpriteNode]()
var arrowContactPoint:CGPoint?
let noCategory:UInt32 = 0
let arrowCategory:UInt32 = 0b1
let targetCategory:UInt32 = 0b1 << 1
let obstacleCategory:UInt32 = 0b1 << 2
override func didMove(to view: SKView) {
allowCollisionDetection()
setTarget()
moveTargetFromSideToSide()
newArrow()
}
func didBegin(_ contact: SKPhysicsContact) {
let categoryBitMaskBodyA:UInt32 = contact.bodyA.categoryBitMask
let categoryBitMaskBodyB:UInt32 = contact.bodyB.categoryBitMask
if ((categoryBitMaskBodyA == targetCategory && categoryBitMaskBodyB == arrowCategory) || (categoryBitMaskBodyA == arrowCategory && categoryBitMaskBodyB == targetCategory)) {
arrowContactPoint = contact.contactPoint
arrowCollideWithTarget()
} else if (categoryBitMaskBodyA == obstacleCategory || categoryBitMaskBodyB == obstacleCategory) {
let obstacleNode:SKNode = ((categoryBitMaskBodyA == arrowCategory) ? contact.bodyA.node! : contact.bodyB.node)!
arrowCollideWithObstacle(obstacle:obstacleNode)
} else if (categoryBitMaskBodyA == arrowCategory && categoryBitMaskBodyB == arrowCategory) {
newGame()
} else {
print("Something went wrong")
}
newArrow()
}
func touchDown(atPoint pos : CGPoint) {
}
func touchMoved(toPoint pos : CGPoint) {
}
func touchUp(atPoint pos : CGPoint) {
shootArrow()
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchDown(atPoint: t.location(in: self)) }
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchMoved(toPoint: t.location(in: self)) }
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func touchesCancelled(_ touches: Set<UITouch>, with event: UIEvent?) {
for t in touches { self.touchUp(atPoint: t.location(in: self)) }
}
override func update(_ currentTime: TimeInterval) {
// Called before each frame is rendered
}
func allowCollisionDetection() {
self.physicsWorld.contactDelegate = self
}
func setTarget() {
target = self.childNode(withName: "target") as? SKSpriteNode
//Set the target bit mask, it's tag
target?.physicsBody?.categoryBitMask = targetCategory
//Set with which objects the target collide
target?.physicsBody?.collisionBitMask = noCategory
//Set to which coliision we want to responde/handle - didBegin will get triggered
target?.physicsBody?.contactTestBitMask = arrowCategory
}
func moveTargetFromSideToSide() {
let moveRight = SKAction.moveBy(x: frame.size.width - (target?.size.width)!, y: 0, duration: 2)
let moveLeft = SKAction.moveBy(x: -(frame.size.width - (target?.size.width)!), y: 0, duration: 2)
let moveBackAndForth = SKAction.repeatForever(SKAction.sequence([moveRight, moveLeft]))
target?.run(moveBackAndForth)
}
func newArrow() {
let arrow = SKSpriteNode(imageNamed: "arrow1")
let arrowTexture = SKTexture(imageNamed: "arrow1")
arrow.position = CGPoint.zero
self.addChild(arrow)
arrow.physicsBody = SKPhysicsBody(texture: arrowTexture, size: arrowTexture.size())
arrow.physicsBody?.isDynamic = true
arrow.physicsBody?.allowsRotation = true
arrow.physicsBody?.affectedByGravity = false
arrow.physicsBody?.friction = 0.2
arrow.physicsBody?.restitution = 0.2
arrow.physicsBody?.linearDamping = 0.1
arrow.physicsBody?.angularDamping = 0.1
arrow.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
arrow.physicsBody?.categoryBitMask = arrowCategory
arrow.physicsBody?.collisionBitMask = noCategory
arrow.physicsBody?.contactTestBitMask = arrowCategory | obstacleCategory | targetCategory
arrows.append(arrow)
}
func shootArrow(){
print("shootArrow")
arrows.last!.physicsBody?.applyImpulse(CGVector(dx: 0, dy: 80))
}
func arrowCollideWithTarget() {
print("arrowCollideWithTarget")
arrows.last!.physicsBody?.velocity = CGVector(dx: 0, dy: 0)
arrows.last!.move(toParent: target!)
}
func arrowCollideWithObstacle(obstacle:SKNode) {
print("arrowCollideWithObstacle")
arrows.last!.removeFromParent()
arrows.removeLast()
}
func newGame() {
print("New Game")
for i in 0 ..< (arrows.count) {
arrows[i].removeFromParent()
}
arrows.removeAll()
}
}
What eventually solved it for me was using this method ---
move(toParent: )
This is my code -
func arrowCollideWithTarget() {
arrows.last!.move(toParent:target!)
}
I am trying to move sprite to the right when I press a button on the screen. However, when I try doing it I only have a solution to move the sprite to a certain point. So... I want the sprite to move to the right forever or until i do something else.
This is in Xcode using Swift in SpriteKit.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
for touch : AnyObject in touches{
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
let tappeNodeName = tappedNode.name
if tappeNodeName == "Btn"{
player.physicsBody?.velocity = CGVectorMake(0, 0)
let action = SKAction.applyImpulse(CGVectorMake(400, 0), duration: 1)
player.runAction(SKAction.repeatActionForever(action))
print("Touched!")
}
}
}
You can simply move your node to the right untill it does exist the scene
class GameScene: SKScene {
private var player: SKSpriteNode!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
if tappedNode.name == "Btn"{
let moveToRight = SKAction.moveToX(self.frame.width + player.frame.width, duration: 5)
player.runAction(moveToRight)
}
}
}
Update: constant speed
If you want the player to move with constant speed you can use this code.
As you can see I am calculating the duration using space / speed. You just need to find the best constant value for speed for your scenario.
class GameScene: SKScene {
private var player: SKSpriteNode!
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let touch = touches.first else { return }
let pointInTouch = touch.locationInNode(self)
let tappedNode = nodeAtPoint(pointInTouch)
let deltaX = self.frame.width + player.frame.width - player.position.x
let speed: CGFloat = 10 // <-- change this to find the best value for you
let duration: NSTimeInterval = NSTimeInterval(deltaX / speed)
if tappedNode.name == "Btn"{
let moveToRight = SKAction.moveByX(deltaX, y:0, duration: duration)
player.runAction(moveToRight)
}
}
}
I was testing my game on my iPhone and I realized that I have a problem with the home button. When I pause the game and load another app the game is not paused and continues when the app is not on screen.
Here's the code:
class GameScene: SKScene, SKPhysicsContactDelegate{
var PauseButton = SKSpriteNode(imageNamed: "pause")
var Resume = SKSpriteNode(imageNamed: "resume1")
}
init(size: CGSize, won: Bool) {
super.init(size: size)
//pause
PauseButton.position = CGPointMake(330, 700)
PauseButton.zPosition = 3;
PauseButton.size = CGSizeMake(40, 40);
PauseButton.name = "PauseButton"
self.addChild(PauseButton)
//resume
Resume = SKSpriteNode(imageNamed: "resume1")
Resume.position = CGPointMake(520, 450)
Resume.zPosition = 100
Resume.setScale(1.3)
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
/* Called when a touch begins */
show = self.scene as SKScene!
for touch in touches {
let Location:CGPoint = touch.locationInNode(self)
let Node:SKNode = self.nodeAtPoint(Location)
if(Node == PauseButton){
self.show.paused = true
PauseButton.removeFromParent()
self.addChild(Resume)
}
if(Node == Resume){
Resume.removeFromParent()
self.show.paused = false
self.addChild(PauseButton)
}
How do I get the game to pause when I press the home button?
Pause the view not the scene.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
guard let view = view else {
return
}
view.paused = !view.paused
}
SpriteKit will auto Resume when you comeback to your app. so you need :
Step 1 : create a variable :
var wasPaused : Bool = false
Step 2 : change it in your pause/resume function :
wasPaused = true // pause func
wasPaused = false // resume fun
Step 3 : in update fun of your scene:
override func update(currentTime: CFTimeInterval) {
if isPausing{
self.paused = true
return
}
}
I am creating a game like Doodle Jump (without the accelerometer).
I have been trying to figure this out, but I can't seem to make it run as smoothly as I've been hoping.
Here is my code for my touches functions:
override func touchesBegan(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
}
override func touchesMoved(touches: Set<NSObject>, withEvent event: UIEvent) {
for touch: AnyObject in touches {
let touchLocation = touch.locationInNode(self)
lastTouch = touchLocation
}
}
override func touchesEnded(touches: Set<NSObject>, withEvent event: UIEvent) {
lastTouch = nil
}
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
let impulseVector = CGVector(dx: 400, dy: 400)
player.physicsBody?.applyImpulse(impulseVector)
}
}
From what I'm gathering, you want to change the impulseVector that is applied to the player based on where the touch is. I would imagine the code looking something like this:
// At the top of your code
let scale = 0.5
// Update function
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
let xOffset = (touch.x - player.position.x)*scale
let yOffset = (touch.y - player.position.y)*scale
let impulseVector = CGVector(dx: xOffset, dy: yOffset)
player.physicsBody?.applyImpulse(impulseVector)
}
}
This will pull the player node up with more force the further away it is from the touch. If you are trying to pull the player with the same amount of force, no matter where they are (which may be more likely in this case), you would do something like this:
// At the top of your code
let xPlayerForce = 20
let yPlayerForce = 30
// Update function
override func update(currentTime: CFTimeInterval) {
if let touch = lastTouch {
var xForce = 0.0
var yForce = 0.0
let xTouchOffset = (touch.x - player.position.x)
let yTouchOffset = (touch.y - player.position.y)
if xTouchOffset > 0.0 {
xForce = xPlayerForce
} else if xTouchOffset < 0.0 {
xForce = -xPlayerForce
} // else we do nothing
if yTouchOffset > 0.0 {
yForce = yPlayerForce
} // here you can choose whether you want it to push
// the player node down, using similar code from the
// above if statement
let impulseVector = CGVector(dx: xForce, dy: yForce)
player.physicsBody?.applyImpulse(impulseVector)
}
}
I have coded a starry sky as follows. Now I would like to remove a star when a user touches it. The following code however removes all the stars on the sky. How can I access a single star node to manipulate it?
override func didMoveToView(view: SKView) {
for(var i = 0; i < stars ; i++) {
planetsLayer.addChild(createPlanet(view))
}
self.addChild(planetsLayer)
}
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(planetsLayer)
let touchedLayer = nodeAtPoint(location)
let touchedNode = nodeAtPoint(location)
touchedNode.removeFromParent()
}
func createPlanet() -> SKShapeNode {
...
var shapeNode = SKShapeNode();
...
return shapeNode
}
Give your planet nodes a name when you create them, and check for this name when you remove them. This will keep you from deleting other nodes that are not planets.
override func touchesBegan(touches: NSSet, withEvent event: UIEvent) {
/* Called when a touch begins */
for touch in touches {
let location = touch.locationInNode(planetsLayer)
let touchedNode = nodeAtPoint(location)
if touchedNode.name == "planet" {
touchedNode.removeFromParent()
}
}
}
func createPlanet() -> SKShapeNode {
...
var shapeNode = SKShapeNode()
shapeNode.name = "planet"
...
return shapeNode
}