A big mechanic of a venture I’m engaged on goes to contain dragging objects together with your fingers and having the thing being dragged work together correctly with physics, pushing different objects away.
I created this proof of idea with 5 inexperienced containers meant to be hit by a purple field that follows the finger location (in debug mode marked in blue) and makes use of a spring joint.
The tough bit right here is that I would like the physics physique for the purple field I’m transferring to not be static. So I must in some way transfer it in a method the physics simulation can react to. I’ve heard of individuals making use of the fingers motion as velocity to the physics node however I don’t anticipate this to work practically as easily as if I might simulate a really tight rubber band which damps shortly in some way.
Points with transferring with velocity:
- When you push the purple field into an object that object pushes again and because of this the purple field is now not below your finger and is regularly additional away.
Points with spring:
- Would not really feel strongly sufficient connected to the dragging node
- Oscillations/vibrations/loops and so on
See gif and code.
Are there values of damping/frequency/friction/mass that may get this to really feel good? As a be aware I’m not certain why however the blue drag deal with node does collide with the inexperienced containers despite the fact that it’s static and set to not collide… Undecided why.
class GameScene: SKScene {
let field = {
let node = DraggableNode(shade: .purple, measurement: CGSize(width: 50, top: 50))
node.place = CGPointMake(150, 150)
node.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, top: 50))
node.physicsBody?.allowsRotation = false
node.physicsBody?.affectedByGravity = false
node.physicsBody?.isDynamic = true
return node
}()
override func didMove(to view: SKView) {
physicsBody = SKPhysicsBody(edgeLoopFrom: body)
addChild(field)
// Add smaller containers to collide with
let targetCount = 5
let targetSize = CGSize(width: 30, top: 30)
let targetY = measurement.top * 0.75
for i in 0..<targetCount {
let fraction = (CGFloat(i) + 1) / CGFloat(targetCount + 1)
let targetX = measurement.width * fraction
let goal = SKSpriteNode(shade: .inexperienced, measurement: targetSize)
goal.place = CGPoint(x: targetX, y: targetY)
goal.physicsBody = SKPhysicsBody(rectangleOf: targetSize)
goal.physicsBody?.affectedByGravity = false
goal.physicsBody?.allowsRotation = false
addChild(goal)
}
}
override func touchesBegan(_ touches: Set<UITouch>, with occasion: UIEvent?) {
guard let contact = touches.first else { return }
let location = contact.location(in: self)
field.dragPosition = location
}
override func touchesMoved(_ touches: Set<UITouch>, with occasion: UIEvent?) {
guard let contact = touches.first else { return }
let location = contact.location(in: self)
field.dragPosition = location
}
override func touchesEnded(_ touches: Set<UITouch>, with occasion: UIEvent?) {
field.endDrag()
}
override func touchesCancelled(_ touches: Set<UITouch>, with occasion: UIEvent?) {
field.endDrag()
}
}
class DraggableNode: SKSpriteNode {
let debug: Bool = true
var dragPosition: CGPoint = .zero {
didSet {
updateDragConnection()
}
}
personal var dragHandleNode: SKNode? = nil
personal var dragJoint: SKPhysicsJointSpring? = nil
personal func updateDragConnection() {
if dragHandleNode == nil {
beginDrag()
}
dragHandleNode?.place = dragPosition
}
func beginDrag() {
assert(dragHandleNode == nil && dragJoint == nil)
guard let scene = self.scene else {
assertionFailure("Have to be added to the scene for this to work")
return
}
let deal with: SKNode
if debug {
deal with = SKSpriteNode(shade: .blue, measurement: CGSize(width: 10, top: 10))
} else {
deal with = SKNode()
}
deal with.physicsBody = SKPhysicsBody(circleOfRadius: 1)
deal with.physicsBody?.isDynamic = false
deal with.physicsBody?.affectedByGravity = false
deal with.physicsBody?.allowsRotation = false
// TODO: I meant this to imply that the blue dot wouldn't collide with another objects
deal with.physicsBody?.collisionBitMask = 0
deal with.physicsBody?.contactTestBitMask = 0
dragHandleNode = deal with
scene.addChild(deal with)
deal with.place = dragPosition
guard let handleBody = deal with.physicsBody else {
assertionFailure("Surprising subject. Deal with has no physics physique")
return
}
guard let nodeBody = self.physicsBody else {
assertionFailure("Draggable node should have a physics physique")
return
}
let distinction = CGPointMake(dragPosition.x - self.dragPosition.x, dragPosition.y - self.dragPosition.y)
let joint = SKPhysicsJointSpring.joint(withBodyA: handleBody, bodyB: nodeBody, anchorA: dragPosition, anchorB: CGPointMake(place.x - distinction.x, place.y - distinction.y))
// Try at fast dampening and being tied to the thing
joint.damping = 1.8
joint.frequency = 2.5
scene.physicsWorld.add(joint)
dragJoint = joint
}
func endDrag() {
guard let scene = self.scene else { return }
if let joint = dragJoint {
scene.physicsWorld.take away(joint)
dragJoint = nil
}
dragHandleNode?.removeFromParent()
dragHandleNode = nil
}
}
// A pattern SwiftUI making a GameScene and sizing it
// at 300x400 factors
struct ContentView: View {
var scene: SKScene {
let scene = GameScene()
scene.measurement = CGSize(width: 300, top: 400)
scene.scaleMode = .fill
return scene
}
var physique: some View {
SpriteView(scene: scene, debugOptions: .showsPhysics)
.body(width: 300, top: 400)
.ignoresSafeArea()
}
}
#Preview {
ContentView()
}
With velocity the purple field you’re "dragging" is shortly away out of your finger
With Spring there are undesirable oscillations/loops/jiggles