Last active
April 22, 2025 05:44
-
-
Save jweinst1/28e8137a10572ed01e48e5d0b0645ff7 to your computer and use it in GitHub Desktop.
playground game of blocks in swift with contacts and jumps and joints
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//: A SpriteKit based Playground | |
import PlaygroundSupport | |
import SpriteKit | |
let sceneView = SKView(frame: CGRect(x:0 , y:0, width: 640, height: 480)) | |
class GameOverScene : SKScene { | |
var label = SKLabelNode(fontNamed: "Chalkduster") | |
override func didMove(to view: SKView) { | |
self.scaleMode = .aspectFill | |
label.position = CGPoint(x:0,y:0) | |
label.text = "Game Over" | |
addChild(label) | |
} | |
} | |
struct Platform { | |
let height:Int | |
let width:Int | |
let x:Int | |
let y:Int | |
init(height: Int, width: Int, x: Int, y: Int) { | |
self.height = height | |
self.width = width | |
self.x = x | |
self.y = y | |
} | |
} | |
struct PlatformGen { | |
let height:Int | |
let width:Int | |
let spaceMin:Int | |
let spaceMax:Int | |
var lastX:Int | |
var lastY:Int | |
init(height: Int, width: Int, spaceMin: Int, spaceMax: Int, startX:Int = 0, startY:Int = 0) { | |
self.height = height | |
self.width = width | |
self.spaceMin = spaceMin | |
self.spaceMax = spaceMax | |
self.lastX = startX | |
self.lastY = startY | |
} | |
mutating func next() -> Platform { | |
// print(Int.random(in: 1..<100)) | |
let nextX = Int.random(in: (self.lastX + self.spaceMin)...(self.lastX + self.spaceMax)) | |
let nextY = Int.random(in: (self.lastY + self.spaceMin)...(self.lastY + self.spaceMax)) | |
self.lastX = nextX; | |
self.lastY = nextY; | |
return Platform(height: self.height, width: self.width, x: nextX, y: nextY) | |
} | |
} | |
class GameScene: SKScene, @preconcurrency SKPhysicsContactDelegate { | |
private var touchingGround : Bool = true | |
private var timeLabel : SKLabelNode! | |
private var playerNode : SKShapeNode! | |
private var ground : SKShapeNode! | |
private var spinnyNode4 : SKShapeNode! | |
private var cameraNode : SKCameraNode! | |
private var scoreCount: Int = 0 | |
private var platforms: [SKShapeNode] = [] | |
func didBegin(_ contact: SKPhysicsContact) { | |
guard let nodeA = contact.bodyA.node else { return } | |
guard let nodeB = contact.bodyB.node else { return } | |
if nodeB.physicsBody?.categoryBitMask == 0b1 { | |
print("began ground contact") | |
touchingGround = true | |
// use contact point to make a joint | |
let newJoint = SKPhysicsJointFixed.joint(withBodyA: nodeA.physicsBody!, bodyB: nodeB.physicsBody!, anchor: contact.contactPoint) | |
physicsWorld.add(newJoint) | |
//nodeA.run(.moveTo(y: 200, duration: 0)) | |
} | |
/*if nodeA.name == "me" && nodeB.name == "op" { | |
//nodeB.removeFromParent() | |
// causes a fall through | |
nodeB.physicsBody?.collisionBitMask = 0 | |
}*/ | |
} | |
func didEnd(_ contact: SKPhysicsContact) { | |
// todo, an ending contact | |
guard let nodeA = contact.bodyA.node else { return } | |
guard let nodeB = contact.bodyB.node else { return } | |
if nodeB.physicsBody?.categoryBitMask == 0b1 { | |
// ground contact | |
print("ended ground contact") | |
touchingGround = false | |
} | |
} | |
override func didMove(to view: SKView) { | |
physicsWorld.contactDelegate = self | |
cameraNode = SKCameraNode() | |
addChild(cameraNode) | |
camera = cameraNode | |
// Get label node from scene and store it for use later | |
/*let fadeInOut = SKAction.sequence([.fadeIn(withDuration: 2.0), | |
.wait(forDuration: 0.8), | |
.fadeOut(withDuration: 2.0)])*/ | |
//label.run(.repeatForever(fadeInOut)) | |
playerNode = SKShapeNode(rectOf: CGSize(width:40, height:40)) | |
playerNode.lineWidth = 5 | |
playerNode.name = "me" | |
playerNode.position = CGPoint(x:0, y:0) | |
playerNode.strokeColor = NSColor(white: 0.0, alpha: 1.0) | |
playerNode.fillColor = NSColor(red: 0.0, green: 1.0, blue: 0.0, alpha: 1.0) | |
playerNode.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:40, height:40)) | |
//spinnyNode2.physicsBody?.affectedByGravity = false; | |
playerNode.physicsBody?.friction = 0.1; | |
playerNode.physicsBody!.contactTestBitMask = 0b011 | |
playerNode.physicsBody!.collisionBitMask = 0b1 | |
addChild(playerNode) | |
timeLabel = SKLabelNode(fontNamed: "Chalkduster") | |
timeLabel.fontSize = 50 | |
timeLabel.text = String(scoreCount) | |
timeLabel.horizontalAlignmentMode = .right | |
timeLabel.color = SKColor.white | |
timeLabel.position = CGPoint(x:0, y:playerNode.position.y + 300) | |
addChild(timeLabel) | |
ground = SKShapeNode(rectOf: CGSize(width:300, height:40)) | |
ground.lineWidth = 5 | |
ground.position = CGPoint(x:0, y:-120) | |
ground.strokeColor = NSColor(white: 0.0, alpha: 1.0) | |
ground.fillColor = NSColor(red: 1.0, green: 1.0, blue: 0.0, alpha: 1.0) | |
ground.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:300, height:40)) | |
//spinnyNode3.physicsBody?.isDynamic = false; | |
ground.physicsBody?.isDynamic = false; | |
ground.physicsBody?.categoryBitMask = 0b1 // for the ground | |
addChild(ground) | |
// causes ground to move over time | |
ground.run(.repeatForever(.move(by: CGVector(dx: -5, dy: 0), duration: 0.1))) | |
spinnyNode4 = SKShapeNode(rectOf: CGSize(width:40, height:40)) | |
spinnyNode4.name = "op" | |
spinnyNode4.lineWidth = 5 | |
spinnyNode4.position = CGPoint(x:100, y:0) | |
spinnyNode4.strokeColor = NSColor(white: 1.0, alpha: 1.0) | |
spinnyNode4.fillColor = NSColor(red: 0.0, green: 0.5, blue: 0.5, alpha: 1.0) | |
spinnyNode4.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width:40, height:40)) | |
spinnyNode4.physicsBody!.categoryBitMask = 0b010 | |
spinnyNode4.physicsBody!.collisionBitMask = 0b1 | |
//spinnyNode4.physicsBody?.mass = 100.0 | |
addChild(spinnyNode4) | |
spinnyNode4.run(.repeatForever(.move(by: CGVector(dx: -2, dy: 0), duration: 0.1))) | |
/* can be used for back and forth movement | |
let fadeAndRemove = SKAction.sequence([.wait(forDuration: 0.5), | |
.fadeOut(withDuration: 0.5), | |
.removeFromParent()]) | |
spinnyNode.run(.repeatForever(.rotate(byAngle: CGFloat(Double.pi), duration: 1))) | |
spinnyNode.run(fadeAndRemove)*/ | |
} | |
@objc static override var supportsSecureCoding: Bool { | |
// SKNode conforms to NSSecureCoding, so any subclass going | |
// through the decoding process must support secure coding | |
get { | |
return true | |
} | |
} | |
func touchDown(atPoint pos : CGPoint) { | |
/*guard let n = spinnyNode.copy() as? SKShapeNode else { return } | |
n.position = pos | |
n.strokeColor = SKColor.green | |
addChild(n)*/ | |
if touchingGround { | |
//spinnyNode2.physicsBody?.applyImpulse(CGVector(dx:0, dy:80)) | |
physicsWorld.remove(playerNode.physicsBody!.joints[0]) | |
playerNode.run(.applyForce(CGVector(dx:0, dy:300), duration: 0.2)) | |
} | |
} | |
func touchMoved(toPoint pos : CGPoint) { | |
/*guard let n = self.spinnyNode.copy() as? SKShapeNode else { return } | |
n.position = pos | |
n.strokeColor = SKColor.blue | |
addChild(n)*/ | |
} | |
func touchUp(atPoint pos : CGPoint) { | |
/*guard let n = spinnyNode.copy() as? SKShapeNode else { return } | |
n.position = pos | |
n.strokeColor = SKColor.red | |
addChild(n)*/ | |
} | |
override func mouseDown(with event: NSEvent) { | |
touchDown(atPoint: event.location(in: self)) | |
} | |
override func mouseDragged(with event: NSEvent) { | |
touchMoved(toPoint: event.location(in: self)) | |
} | |
override func mouseUp(with event: NSEvent) { | |
touchUp(atPoint: event.location(in: self)) | |
} | |
override func update(_ currentTime: TimeInterval) { | |
// Called before each frame is rendered | |
cameraNode.position = playerNode.position | |
timeLabel.position = CGPoint(x:playerNode.position.x, y:playerNode.position.y + 300) | |
} | |
} | |
// Load the SKScene from 'GameScene.sks' | |
if let scene = GameScene(fileNamed: "GameScene") { | |
// Set the scale mode to scale to fit the window | |
scene.scaleMode = .aspectFill | |
// Present the scene | |
sceneView.presentScene(scene) | |
} | |
PlaygroundSupport.PlaygroundPage.current.liveView = sceneView |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment