How can I simulate shrink and stretch a rope, I want to joint two sprite nodes and when the getting closer the rope should be stretched and shrunk once they go to the original position, I used a lot of way using Limit and Spring joints but I really don't know how to animate the rope to accomplish the shrink and stretch.
Attempt 1
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
self.addAnchor()
self.addRope()
self.addCharacter()
let jointOneFixed = SKPhysicsJointFixed.joint(withBodyA: anchor.physicsBody!, bodyB: rope.physicsBody!, anchor: anchor.position)
self.physicsWorld.add(jointOneFixed)
let jointTwoFixed = SKPhysicsJointFixed.joint(withBodyA: rope.physicsBody!, bodyB: currentCharacter.physicsBody!, anchor: CGPoint(x: rope.frame.midX, y: rope.frame.minY))
self.physicsWorld.add(jointTwoFixed)
limitJoint = SKPhysicsJointLimit.joint(withBodyA: anchor.physicsBody!, bodyB: currentCharacter.physicsBody!, anchorA: anchor.position, anchorB: currentCharacter.position)
//limitJoint.maxLength = self.size.height - 1000
self.physicsWorld.add(limitJoint)
}
func addAnchor(){
anchor.position = CGPoint(x: self.size.width / 2, y: self.size.height + 1)
anchor.anchorPoint = CGPoint(x: 0.5, y: 0.5)
anchor.setScale(1)
anchor.zPosition = 2
anchor.name = "anchor"
anchor.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: anchor.size.width, height: anchor.size.height))
anchor.physicsBody?.isDynamic = false
anchor.physicsBody?.affectedByGravity = false
anchor.physicsBody?.allowsRotation = true
self.addChild(anchor)
}
func addRope() {
rope.position = CGPoint(x: anchor.position.x, y: anchor.position.y - 175)
rope.setScale(0.65)
rope.zPosition = 1
rope.name = "rope"
rope.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: rope.size.width, height: rope.size.height))
rope.physicsBody?.affectedByGravity = false;
rope.physicsBody?.mass = 0.5;
rope.physicsBody?.allowsRotation = true
self.addChild(rope)
}
func addCharacter() {
let characterName: String = UserDefaults.standard.string(forKey: "current_character")!
currentCharacter = SKSpriteNode(imageNamed: characterName);
currentCharacter.position = CGPoint(x: self.size.width / 2, y: self.size.height - 400)
currentCharacter.anchorPoint = CGPoint(x: 0.5, y: 0.5)
currentCharacter.setScale(0.50)
currentCharacter.zPosition = 2
currentCharacter.name = "character"
currentCharacter.physicsBody = SKPhysicsBody(circleOfRadius: max(currentCharacter.size.width / 2, currentCharacter.size.height / 2))
currentCharacter.physicsBody?.affectedByGravity = false
currentCharacter.physicsBody?.isDynamic = true
currentCharacter.physicsBody?.allowsRotation = true
self.addChild(currentCharacter)
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let moveDown = SKAction.moveBy(x: 0, y: -100, duration:0.1)
currentCharacter.run(moveDown)
}
Attempt 2
Is almost the same except instead of create a Limit joint I created a Spring joint
override func didMove(to view: SKView) {
self.physicsWorld.contactDelegate = self
self.addAnchor()
self.addRope()
self.addCharacter()
let jointOneFixed = SKPhysicsJointFixed.joint(withBodyA: anchor.physicsBody!, bodyB: rope.physicsBody!, anchor: anchor.position)
self.physicsWorld.add(jointOneFixed)
let jointTwoFixed = SKPhysicsJointFixed.joint(withBodyA: rope.physicsBody!, bodyB: currentCharacter.physicsBody!, anchor: CGPoint(x: rope.frame.midX, y: rope.frame.minY))
self.physicsWorld.add(jointTwoFixed)
let springJoint = SKPhysicsJointSpring.joint(withBodyA: anchor.physicsBody!, bodyB: currentCharacter.physicsBody!, anchorA: anchor.position, anchorB: currentCharacter.position)
//springJoint.frequency = 1.0
//springJoint.damping = 0
self.physicsWorld.add(springJoint)
}
Related
I'm new to swift 5.
I printed out the self.size.width in GameScene and the result is 677.0
I printed out the self.size.width from another class - lets say Ground and the result is 4002.0
I'm confused, please help.
Thanks a lot.
GameScene.swift:
import SpriteKit
class GameScene: SKScene {
let cam = SKCameraNode()
let bee = SKSpriteNode()
let ground = Ground()
override func didMove(to view: SKView) {
self.anchorPoint = .zero
//self.backgroundColor = UIColor(red: 0.4, green: 0.6, blue: 0.95, alpha: 1.0)
self.camera = cam
self.addingTheFlyingBee()
self.addBackground()
let bee2 = Bee()
bee2.position = CGPoint(x: 325, y: 325)
self.addChild(bee2)
let bee3 = Bee()
bee3.position = CGPoint(x: 200, y: 325)
self.addChild(bee3)
ground.position = CGPoint(x: -self.size.width * 2, y: 0)
ground.size = CGSize(width: self.size.width * 6, height: 0)
ground.createChildren()
self.addChild(ground)
}
override func didSimulatePhysics() {
self.camera!.position = bee.position
}
func addBackground() {
let bg = SKSpriteNode(imageNamed: "background-menu")
bg.position = CGPoint(x: 220, y: 220)
bg.zPosition = -1
self.addChild(bg)
}
func addingTheFlyingBee() {
bee.position = CGPoint(x: 250, y: 250)
bee.size = CGSize(width: 38, height: 34)
self.addChild(bee)
let beeAtlas = SKTextureAtlas(named: "Enemies")
let beeFrames : [SKTexture] = [
beeAtlas.textureNamed("bee"),
beeAtlas.textureNamed("bee-fly")
]
let flyAction = SKAction.animate(with: beeFrames, timePerFrame: 0.14)
let beeAction = SKAction.repeatForever(flyAction)
bee.run(beeAction)
let pathLeft = SKAction.moveBy(x: -200, y: -10, duration: 2)
let pathRight = SKAction.moveBy(x: 200, y: 10, duration: 2)
let flipTextureNegative = SKAction.scaleX(to: -1, duration: 0)
let flipTexturePositive = SKAction.scaleX(to: 1, duration: 0)
let flightOfTheBee = SKAction.sequence([ pathLeft, flipTextureNegative, pathRight, flipTexturePositive])
let neverEndingFlight = SKAction.repeatForever(flightOfTheBee)
bee.run(neverEndingFlight)
}
Ground.swift:
import Foundation
import SpriteKit
class Ground: SKSpriteNode, GameSprite {
var textureAtlas: SKTextureAtlas = SKTextureAtlas(named: "Environment")
var initialSize = CGSize.zero
func createChildren() {
self.anchorPoint = CGPoint(x: 0, y: 1)
let texture = textureAtlas.textureNamed("ground")
var tileCount: CGFloat = 0
let tileSize = CGSize(width: 35, height: 300)
while tileCount * tileSize.width < self.size.width {
let tileNode = SKSpriteNode(texture: texture)
tileNode.size = tileSize
tileNode.position.x = tileCount * tileSize.width
tileNode.anchorPoint = CGPoint(x: 0, y: 1)
self.addChild(tileNode)
tileCount += 1
}
}
func onTap() {}
}
I don't really understand the question. Since your scene width is 677.0px and your ground
width should be 6 * scene width so 4062px.
Is your question "Why is ground node width 4002px instead of 4062px?" or "What is the difference between those properties?"
If you question is the second one then this should be an answer:
SKScene.size.width will return width of the current scene while SKSpriteNode.size.width will return width of the target node in your case "ground" node.
self.size.width will return width of the current class you're editing.
So in GameScene.swift file if you call self.size.width inside a class GameScene: SKScene it will return GameScene width.
In Ground.swift file calling self.size.width inside a class Ground: SKSpriteNode, GameSprite will return Ground width.
I hope this answers your question.
I'm working on a 2-d Platformer style game. I'm very new to IOS and Swift development. I'm attempting to use a button (a different node) to move my character across the screen from left to right. It works fine until I reach the halfway point then it speeds up dramatically and letting go of the touch of the button doesn't always stop it. Sometimes it requires another touch. Also the background doesn't appear to keep up with the player. Once the player hits mid screen the background should shift as the player continues moving.
I've pieced what I've done together from multiple tutorials SO examples but I'm stuck on this point.
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE PLAYER ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
override func didMove(to view: SKView) {
/* Setup your scene here */
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
leftMoveButton.position.x = 64
leftMoveButton.position.y = 64
leftMoveButton.name = "moveLeft"
addChild(leftMoveButton)
rightMoveButton.position.x = 124
rightMoveButton.position.y = 64
rightMoveButton.name = "moveRight"
addChild(rightMoveButton)
jumpButton.position.x = 771
jumpButton.position.y = 64
jumpButton.name = "jumpButton"
addChild(jumpButton)
fireButton.position.x = 836
fireButton.position.y = 64
fireButton.name = "fireButton"
addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
//let moveAction = SKAction.moveBy(x: 3, y: 0, duration: 1)
//let repeatForEver = SKAction.repeatForever(moveAction)
//let seq = SKAction.sequence([moveAction, repeatForEver])
//run the action on your ship
//player.run(seq)
player.position.x = player.position.x + 3
setViewpointCenter(player.position)
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
if rightMoveButton.contains(touch.location(in: self)) {
forwardMarch = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
if jumpButton.contains(touch.location(in: self)) {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: (centerOfView.x - actualPosition.x) * 3, y: centerOfView.y - actualPosition.y)
tileMap!.position = viewPoint
}
func setupScene() {
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
physicsWorld.gravity = CGVector(dx: 0, dy: -9.8)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
let point = tileMap!.calculateAccumulatedFrame()
print (point)
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
//print (wallGIDs)
//let node = walls
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
addChild(player)
}
}
Holding the rightMoveButton should move at a consistent pace to the right. As the player gets to mid screen the view point of the background should shift until it reaches the end of the background at which point the player can move off screen and complete the level. Releasing the button should allow the player to stop.
You can move the scene as the character moves by creating an SKCameraNode. You then can choose when to move the camera to create the right affect. Make sure to set the camera as your game’s camera. You can learn about cameras here. As for the speeding up, I assume it has something to do with your physicsBody. If you’re interested about player movement, you could look here or here. There are many other great videos and sites online, which you could find by searching “SpriteKit player movement.”
Eli Front's answer pointed me in the right direction. I still have some issues with this code but the answer to my question was using SKCamera node as Eli pointed out. I just wanted to post the code that works so if anyone has a similar question there's a code example as far as camera moving with the player. The speed issue was essentially an optical illusion with the background moving too fast.
Note that the controls are children of the camera. That is so they move with the camera as well otherwise as your node moves the controls would move off screen.
import UIKit
import SpriteKit
import GameKit
class StoryModeScene: SKScene, SKPhysicsContactDelegate {
var tileMap = JSTileMap(named: "legend1Level1.tmx")
var tileSize:CGSize!
var xPointsToMovePerSecond:CGFloat = 0
var rightMoveButton = SKSpriteNode(imageNamed: "right-move")
var leftMoveButton = SKSpriteNode(imageNamed: "left-move")
var jumpButton = SKSpriteNode(imageNamed: "a-button")
var fireButton = SKSpriteNode(imageNamed: "b-button")
var forwardMarch:Bool = false
var mightAsWellJump:Bool = false
var onGround:Bool = true
//CREATE THE Player ATLAS FOR ANIMATION
let playerAtlas = SKTextureAtlas(named:"legend1")
var playerSprites = Array<Any>()
var player = SKSpriteNode(imageNamed: "legend1")
var repeatActionPlayer = SKAction()
let cam = SKCameraNode()
var previousUpdateTime: TimeInterval = 0
override func didMove(to view: SKView) {
// SETUP CAMERA
self.camera = cam
scene?.addChild(cam)
cam.position.x = 448
cam.position.y = 212
setupScene()
addPlayer()
//PREPARE TO ANIMATE THE PLAYER AND REPEAT THE ANIMATION FOREVER
let animatedPlayer = SKAction.animate(with: self.playerSprites as! [SKTexture], timePerFrame: 0.1)
self.repeatActionPlayer = SKAction.repeatForever(animatedPlayer)
// SETUP CONTROLS
leftMoveButton.position.x = -338
leftMoveButton.position.y = -112
leftMoveButton.name = "moveLeft"
leftMoveButton.zPosition = 5
cam.addChild(leftMoveButton)
rightMoveButton.position.x = -278
rightMoveButton.position.y = -112
rightMoveButton.name = "moveRight"
cam.addChild(rightMoveButton)
jumpButton.position.x = 278
jumpButton.position.y = -112
jumpButton.name = "jumpButton"
jumpButton.zPosition = 5
cam.addChild(jumpButton)
fireButton.position.x = 338
fireButton.position.y = -112
fireButton.name = "fireButton"
cam.addChild(fireButton)
}
override func update(_ currentTime: TimeInterval) {
if (forwardMarch) {
if player.position.x > 448 && player.position.x < 1800 {
cam.position.x = player.position.x
} else if player.position.x >= 1800 {
cam.position.x = 1800
}
setViewpointCenter(player.position)
player.position.x = player.position.x + 3
}
if (mightAsWellJump) {
let jumpForce = CGPoint(x: 0.0, y: 310.0)
let jumpCutoff: Float = 150.0
if mightAsWellJump && onGround {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx + jumpForce.x, dy: player.physicsBody!.velocity.dy + jumpForce.y)
onGround = false
} else if !mightAsWellJump && player.physicsBody!.velocity.dy > CGFloat(jumpCutoff) {
player.physicsBody!.velocity = CGVector(dx: player.physicsBody!.velocity.dx, dy: CGFloat(jumpCutoff))
}
player.position = CGPoint(x: player.position.x, y: player.position.y + 5);
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in (touches) {
let positionInScene = touch.location(in: self)
let touchedNode = self.atPoint(positionInScene)
if let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = true
player.texture = SKTexture(imageNamed: "legend1_jump")
}
if name == "moveRight" {
player.physicsBody!.velocity.dy = 0.0
forwardMarch = true
self.player.run(repeatActionPlayer)
}
}
}
}
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 let name = touchedNode.name {
if name == "jumpButton" {
mightAsWellJump = false
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
}
if name == "moveRight" {
player.removeAllActions()
player.texture = SKTexture(imageNamed: "legend1")
forwardMarch = false
}
}
}
}
func setViewpointCenter(_ position: CGPoint) {
var x = max(position.x, size.width / 2)
var y = max(position.y, size.height / 2)
x = min(x, (tileMap!.mapSize.width * tileMap!.tileSize.width) - size.width / 2)
y = min(y, (tileMap!.mapSize.height * tileMap!.tileSize.height) - size.height / 2)
let actualPosition = CGPoint(x: CGFloat(x), y: CGFloat(y))
let centerOfView = CGPoint(x: size.width / 2, y: size.height / 2)
let viewPoint = CGPoint(x: centerOfView.x - actualPosition.x, y: centerOfView.y - actualPosition.y)
if (actualPosition.x > 1800) {
tileMap!.position = tileMap!.position
} else {
tileMap!.position = viewPoint
}
}
func setupScene() {
// ADD PLAYER SPRITES #TODO MAKE THIS DYNAMIC
playerSprites.append(playerAtlas.textureNamed("legend1_0"))
playerSprites.append(playerAtlas.textureNamed("legend1_1"))
playerSprites.append(playerAtlas.textureNamed("legend1_2"))
playerSprites.append(playerAtlas.textureNamed("legend1_3"))
playerSprites.append(playerAtlas.textureNamed("legend1_4"))
playerSprites.append(playerAtlas.textureNamed("legend1_5"))
backgroundColor = UIColor(red: 165.0/255.0, green: 216.0/255.0, blue: 255.0/255.0, alpha: 1.0)
anchorPoint = CGPoint(x: 0, y: 0)
position = CGPoint(x: 0, y: 0)
// let point = tileMap!.calculateAccumulatedFrame()
tileMap!.position = CGPoint(x: 0, y: 0)
addChild(tileMap!)
addFloor()
}
func addFloor() {
for a in 0..<Int(tileMap!.mapSize.width) {
for b in 0..<Int(tileMap!.mapSize.height) {
// let layerInfo:TMXLayerInfo = tileMap!.layers.firstObject as! TMXLayerInfo
let point = CGPoint(x: a, y: b)
let walls = tileMap!.layerNamed("Walls")
let wallInfo:TMXLayerInfo = walls!.layerInfo
let wallGIDs = wallInfo.layer.tileGid(at: wallInfo.layer.point(forCoord: point))
if wallGIDs > 0 {
let node = wallInfo.layer.tile(atCoord: point)
node!.physicsBody = SKPhysicsBody(rectangleOf: node!.size)
node!.physicsBody?.isDynamic = false
}
}
}
}
func addPlayer() {
tileSize = tileMap?.tileSize
player.position = CGPoint(x: tileSize.width + player.size.width/2, y: tileSize.height + player.size.height*8)
let rect = CGRect(origin: CGPoint(x: 0,y :0), size: CGSize(width: 50, height: 95))
player.physicsBody = SKPhysicsBody(rectangleOf: rect.size)
player.physicsBody!.velocity = CGVector(dx: 0.0, dy: 0.0)
player.physicsBody!.isDynamic = true
player.physicsBody!.restitution = 0
player.physicsBody!.allowsRotation = false
player.physicsBody!.friction = 1.0
player.physicsBody!.mass = 1.0
addChild(player)
}
}
As I mentioned I still have some issues like the player node floating/flying when I jump/run at the same time. The 1st jump is the correct height and all subsequent jumps are smaller etc but I'm working on figuring those out.
I have a sprite node connected to another sprite node with a pin joint, anchored as such. I am trying to simulate this, where the green sprite is being moved with a finger:
https://i.stack.imgur.com/limMh.gif
This is what I have so far, but it's not rotating correctly:
body.name = "body"
body.size = CGSize(width: 10, height: 70)
body.physicsBody = SKPhysicsBody(rectangleOf: body.size)
body.physicsBody?.isDynamic = false
self.addChild(body)
leftArm.name = "leftArm"
leftArm.size = CGSize(width: 50, height: 50)
leftArm.position = CGPoint(x: -24, y: 0)
leftArm.physicsBody = SKPhysicsBody(rectangleOf: leftArm.size)
leftArm.physicsBody?.isDynamic = true
self.addChild(leftArm)
let leftArmBodyJoint = SKPhysicsJointPin.joint(withBodyA: leftArm.physicsBody!,
bodyB: body.physicsBody!,
anchor: CGPoint(x: self.body.frame.midX, y: self.body.frame.midY))
physicsWorld.add(leftArmBodyJoint)
let location = touches.first?.location(in: self)
let nodeTouched = atPoint(location!)
if nodeTouched.name == "leftArm" {
print("Began touch on left arm")
touchedLeftArm = nodeTouched
isFingerOnLeftArm = true
self.startPoint = touches.first!.location(in: self)
}
if isFingerOnLeftArm{
let newPoint = touches.first!.location(in: self)
let calculatedPoint = CGPoint(x: newPoint.x - startPoint.x, y: newPoint.y - startPoint.y)
leftArm.position = CGPoint(x: leftArm.position.x + calculatedPoint.x, y: leftArm.position.y + calculatedPoint.y)
let radians = atan2(newPoint.x - leftArm.position.x, newPoint.y - leftArm.position.y)
leftArm.zRotation = -radians//this rotates the player
self.startPoint = newPoint
}
Thanks for any help.
I have a pong game in a Swift Playground that I made by following a tutorial online but my assistant editor won't show anything! Where is my pong game!? My code is below the image, which shows that nothing is showing up.
import SpriteKit
import PlaygroundSupport
// Declare some global constants
let width = 800 as CGFloat
let height = 1200 as CGFloat
let racketHeight = 150 as CGFloat
let ballRadius = 20 as CGFloat
// Three types of collision objects possible
enum CollisionTypes: UInt32 {
case Ball = 1
case Wall = 2
case Racket = 4
}
// Racket direction
enum Direction: Int {
case None = 0
case Up = 1
case Down = 2
}
// Make a SpriteKit scene
class gameScene: SKScene, SKPhysicsContactDelegate {
let racketSpeed = 500.0
var direction = Direction.None
var score = 0
var gameRunning = false
// Screen elements
var racket: SKShapeNode?
var ball: SKShapeNode?
let scoreLabel = SKLabelNode()
// Initialize objects during first start
override func sceneDidLoad() {
super.sceneDidLoad()
scoreLabel.fontSize = 40
scoreLabel.position = CGPoint(x: width/2, y: height - 100)
self.addChild(scoreLabel)
createWalls()
createBall(position: CGPoint(x: width / 2, y: height / 2))
createRacket()
startNewGame()
self.physicsWorld.contactDelegate = self
}
// Create the ball sprite
func createBall(position: CGPoint) {
let physicsBody = SKPhysicsBody(circleOfRadius: ballRadius)
ball = SKShapeNode(circleOfRadius: ballRadius)
physicsBody.categoryBitMask = CollisionTypes.Ball.rawValue
physicsBody.collisionBitMask = CollisionTypes.Wall.rawValue | CollisionTypes.Ball.rawValue | CollisionTypes.Racket.rawValue
physicsBody.affectedByGravity = false
physicsBody.restitution = 1
physicsBody.linearDamping = 0
physicsBody.velocity = CGVector(dx: -500, dy: 500)
ball!.physicsBody = physicsBody
ball!.position = position
ball!.fillColor = SKColor.white
}
// Create the walls
func createWalls() {
createWall(rect: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: ballRadius, height: height)))
createWall(rect: CGRect(origin: CGPoint(x: 0, y: 0), size: CGSize(width: width, height: ballRadius)))
createWall(rect: CGRect(origin: CGPoint(x: 0, y: height - ballRadius), size: CGSize(width: width, height: ballRadius)))
}
func createWall(rect: CGRect) {
let node = SKShapeNode(rect: rect)
node.fillColor = SKColor.white
node.physicsBody = getWallPhysicsbody(rect: rect)
self.addChild(node)
}
// Create the physics objetcs to handle wall collisions
func getWallPhysicsbody(rect: CGRect) -> SKPhysicsBody {
let physicsBody = SKPhysicsBody(rectangleOf: rect.size, center: CGPoint(x: rect.midX, y: rect.midY))
physicsBody.affectedByGravity = false
physicsBody.isDynamic = false
physicsBody.collisionBitMask = CollisionTypes.Ball.rawValue
physicsBody.categoryBitMask = CollisionTypes.Wall.rawValue
return physicsBody
}
// Create the racket sprite
func createRacket() {
racket = SKShapeNode(rect: CGRect(origin: CGPoint.zero, size: CGSize(width: ballRadius, height: racketHeight)))
self.addChild(racket!)
racket!.fillColor = SKColor.white
let physicsBody = SKPhysicsBody(rectangleOf: racket!.frame.size, center: CGPoint(x: racket!.frame.midX, y: racket!.frame.midY))
physicsBody.affectedByGravity = false
physicsBody.isDynamic = false
physicsBody.collisionBitMask = CollisionTypes.Ball.rawValue
physicsBody.categoryBitMask = CollisionTypes.Racket.rawValue
physicsBody.contactTestBitMask = CollisionTypes.Ball.rawValue
racket!.physicsBody = physicsBody
}
// Start a new game
func startNewGame() {
score = 0
scoreLabel.text = "0"
racket!.position = CGPoint(x: width - ballRadius * 2, y: height / 2)
let startLabel = SKLabelNode(text: "Game Over")
startLabel.position = CGPoint(x: width / 2, y: height / 2)
startLabel.fontSize = 160
self.addChild(startLabel)
// Animated countdown
let fadeIn = SKAction.fadeIn(withDuration: 0.5)
let fadeOut = SKAction.fadeOut(withDuration: 0.5)
startLabel.text = "3"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "2"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "1"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.text = "0"
startLabel.run(SKAction.sequence([fadeIn, fadeOut]), completion: {
startLabel.removeFromParent()
self.gameRunning = true
self.ball!.position = CGPoint(x: 30, y: height / 2)
self.addChild(self.ball!)
})
})
})
})
}
// Handle touch events to move the racket
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
for touch in touches {
let location = touch.location(in: self)
if location.y > height / 2 {
direction = Direction.Up
} else if location.y < height / 2{
direction = Direction.Down
}
}
}
// Stop racket movement
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
direction = Direction.None
}
// Game loop:
var dt = TimeInterval(0)
override func update(_ currentTime: TimeInterval) {
if gameRunning {
super.update(currentTime)
checkGameOver()
if dt > 0 {
moveRacket(dt: currentTime - dt)
}
dt = currentTime
}
}
// Move the racket up or down
func moveRacket(dt: TimeInterval) {
if direction == Direction.Up && racket!.position.y < height - racketHeight {
racket!.position.y = racket!.position.y + CGFloat(racketSpeed * dt)
} else if direction == Direction.Down && racket!.position.y > 0 {
racket!.position.y = racket!.position.y - CGFloat(racketSpeed * dt)
}
}
// Check if the ball is still on screen
// Game Over animation
func checkGameOver() {
if ball!.position.x > CGFloat(width) {
gameRunning = false
ball!.removeFromParent()
let gameOverLabel = SKLabelNode(text: "Game Over")
gameOverLabel.position = CGPoint(x: width / 2, y: height / 2)
gameOverLabel.fontSize = 80
self.addChild(gameOverLabel)
// Game Over animation
let rotateAction = SKAction.rotate(byAngle: CGFloat(M_PI), duration: 1)
let fadeInAction = SKAction.fadeIn(withDuration: 2)
gameOverLabel.run(SKAction.repeat(rotateAction, count: 2))
gameOverLabel.run(SKAction.scale(to: 0, duration: 2.5), completion: {
gameOverLabel.removeFromParent()
self.startNewGame()
})
}
}
// Detect collisions between ball and racket to increase the score
func didBegin(_ contact: SKPhysicsContact) {
if contact.bodyA.categoryBitMask == CollisionTypes.Racket.rawValue || contact.bodyB.categoryBitMask == CollisionTypes.Racket.rawValue {
score += 1
scoreLabel.text = String(score)
}
}
}
//Initialize the playground and start the scene:
let skView = SKView(frame: CGRect(origin: CGPoint.zero, size: CGSize(width: width, height: height)))
let scene = gameScene(size: skView.frame.size)
skView.presentScene(scene)
PlaygroundPage.current.liveView = skView
This happens to me sometimes. Playgrounds with SpriteKit just would not show anything in the Timeline view - you do have the Timeline view open, right? If you don't, tap the "Show the Assistant editor" button on the toolbar to open the Timeline view.
If you have the Timeline view open and nothing shows, try shutting down Xcode and restarting it. That generally resolves this issue for me.
I have a sprite node which moves left to right with user touch.
However currently it will exit the screen, I want to add a node either side so if the sprite node touches the node on either side it hugs it and remains there until user touch to make it travel the opposite direction.
This is what I thought of doing but it isn't working currently.
let shipTexture = SKTexture(imageNamed: "ship.png")
ship = SKSpriteNode(texture: shipTexture)
ship.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
ship.zPosition = 3
ship.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 30, height: 100))
ship.physicsBody!.isDynamic = true
ship.physicsBody?.collisionBitMask = 0b1
ship.physicsBody?.contactTestBitMask = 0b1
ship.physicsBody!.collisionBitMask = 0b1
ship.physicsBody?.affectedByGravity = false
self.addChild(ship)
let side = SKNode()
side.position = CGPoint(x: self.frame.width / 2, y: self.frame.midY)
side.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: -240, y: -160, width: 480, height: 320))
side.physicsBody!.isDynamic = false
side.physicsBody?.collisionBitMask = 0b1
side.physicsBody?.contactTestBitMask = 0b1
side.physicsBody!.collisionBitMask = 0b1
self.addChild(side)
func didBegin(_ contact: SKPhysicsContact) {
print("Collision")
}
}
//var moveLeft = SKAction.moveBy(x: 800, y: 0, duration: 2)
//frame.size.width
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
ship.removeAllActions()
switch direction ?? .left {
case .left:
ship.run(SKAction.moveBy(x: -frame.size.width, y: 0, duration: 3))
case .right:
ship.run(SKAction.moveBy(x: frame.size.width, y: 0, duration: 3))
}
direction = direction == nil || direction == .right ? .left : .right
}
I got it. The reason is - you use action to move your node, but should use physics - force and impulse
Try this one:
import SpriteKit
import GameplayKit
var ship = SKSpriteNode()
var bg = SKSpriteNode()
class GameScene: SKScene, SKPhysicsContactDelegate {
override func didMove(to view: SKView) {
let bgTexture = SKTexture(imageNamed: "bg.png")
let moveBGanimation = SKAction.move(by: CGVector(dx: 0, dy: -bgTexture.size().height), duration: 4)
let shiftBGAnimation = SKAction.move(by: CGVector(dx: 0, dy: bgTexture.size().height), duration: 0)
let moveBGForever = SKAction.repeatForever(SKAction.sequence([moveBGanimation, shiftBGAnimation]))
var i: CGFloat = 0
while i < 3 {
bg = SKSpriteNode(texture: bgTexture)
bg.position = CGPoint(x: self.frame.midX, y: bgTexture.size().height * i)
bg.size.width = self.frame.width
bg.run(moveBGForever)
self.addChild(bg)
i += 1
}
let shipTexture = SKTexture(imageNamed: "ship.png")
ship = SKSpriteNode(texture: shipTexture)
ship.position = CGPoint(x: self.frame.midX, y: self.frame.midY)
ship.zPosition = 3
ship.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 30, height: 100))
ship.physicsBody!.isDynamic = true
ship.physicsBody?.collisionBitMask = 0b1
ship.physicsBody?.contactTestBitMask = 0b1
ship.physicsBody!.categoryBitMask = 0b1
ship.physicsBody?.affectedByGravity = false
self.addChild(ship)
let side = SKNode()
side.position = CGPoint(x: 0, y: 0)
side.physicsBody = SKPhysicsBody(edgeLoopFrom: CGRect(x: -self.frame.width/2, y: -self.frame.height/2, width: self.frame.width, height: self.frame.height))
side.physicsBody!.isDynamic = false
side.physicsBody?.collisionBitMask = 0b1
side.physicsBody?.contactTestBitMask = 0b1
side.physicsBody!.categoryBitMask = 0b1
self.addChild(side)
self.physicsWorld.contactDelegate = self
func didBegin(_ contact: SKPhysicsContact) {
print("Collision")
}
}
//var moveLeft = SKAction.moveBy(x: 800, y: 0, duration: 2)
//frame.size.width
enum Direction: Int {
case left = 0
case right
}
var direction: Direction?
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
ship.removeAllActions()
switch direction ?? .left {
case .left:
ship.physicsBody?.applyImpulse(CGVector(dx: -20, dy: 0))
//ship.run(SKAction.moveBy(x: -frame.size.width, y: 0, duration: 3))
case .right:
ship.physicsBody?.applyImpulse(CGVector(dx: 20, dy: 0))
//ship.run(SKAction.moveBy(x: frame.size.width, y: 0, duration: 3))
}
direction = direction == nil || direction == .right ? .left : .right
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
}
override func update(_ currentTime: TimeInterval) {
}
}