A major mechanic of a undertaking I’m engaged on goes to contain dragging objects along with your fingers and having the item 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 crimson field that follows the finger location (in debug mode marked in blue) and makes use of a spring joint.
The difficult bit right here is that I want the physics physique for the crimson field I’m transferring to not be static. So I have to someway transfer it in a manner 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 count on this to work almost as easily as if I might simulate a really tight rubber band which damps rapidly someway.
Points with transferring with velocity:
- If you happen to push the crimson field into an object that object pushes again and because of this the crimson field is now not beneath your finger and is step by step additional away.
Points with spring:
- Would not really feel strongly sufficient hooked up to the dragging node
- Oscillations/vibrations/loops and many others
See gif and code.
Are there values of damping/frequency/friction/mass that may get this to really feel good? As a notice I’m not certain why however the blue drag deal with node does collide with the inexperienced containers though it’s static and set to not collide… Unsure why.
class GameScene: SKScene {
let field = {
let node = DraggableNode(coloration: .crimson, measurement: CGSize(width: 50, peak: 50))
node.place = CGPointMake(150, 150)
node.physicsBody = SKPhysicsBody(rectangleOf: CGSize(width: 50, peak: 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, peak: 30)
let targetY = measurement.peak * 0.75
for i in 0..<targetCount {
let fraction = (CGFloat(i) + 1) / CGFloat(targetCount + 1)
let targetX = measurement.width * fraction
let goal = SKSpriteNode(coloration: .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("Should be added to the scene for this to work")
return
}
let deal with: SKNode
if debug {
deal with = SKSpriteNode(coloration: .blue, measurement: CGSize(width: 10, peak: 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 supposed this to imply that the blue dot wouldn't collide with some other 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("Sudden difficulty. Deal with has no physics physique")
return
}
guard let nodeBody = self.physicsBody else {
assertionFailure("Draggable node will need to 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 item
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, peak: 400)
scene.scaleMode = .fill
return scene
}
var physique: some View {
SpriteView(scene: scene, debugOptions: .showsPhysics)
.body(width: 300, peak: 400)
.ignoresSafeArea()
}
}
#Preview {
ContentView()
}
With velocity the crimson field you might be "dragging" is rapidly away out of your finger
With Spring there are undesirable oscillations/loops/jiggles