Can you help to write a code able to pick an element a bit far from me, then bring it near to me, flick it a bit and then send it back to its original position when I release it?
Thanks a lot, Christophe
How did we do? We’d love to know your thoughts on this year’s conference. Take the survey here
Can you help to write a code able to pick an element a bit far from me, then bring it near to me, flick it a bit and then send it back to its original position when I release it?
Thanks a lot, Christophe
Hi @cuo001
There are many ways to do this. Here's a snippet to get you started. It uses SpatialEventGesture and a hand AnchorEntity. When a person begins the pinch gesture, the code appends the entity to an anchor entity attached to the index finger, mantaining its world position, then animates the entity to a position near the index finger. When the pinch ends, the code appends the entity to its previous parent and animates the entity to its previous position.
Create a custom component to store the state related to the custom interaction.
struct SummonComponent: Component {
let leftAnchor: AnchorEntity
let rightAnchor: AnchorEntity
var isHolding = false
var parentEntity: Entity?
var previousTransform: Transform?
}
Implement the interaction.
struct ImmersiveView: View {
@State var spatialTrackingSession = SpatialTrackingSession()
var body: some View {
RealityView { content in
// Create the entity to summon.
let box = ModelEntity(mesh: .generateBox(size: 0.15), materials: [SimpleMaterial(color: .red, isMetallic: true)])
box.name = "Box"
box.generateCollisionShapes(recursive: false)
box.components.set(InputTargetComponent())
box.position = [0, 1.2, -1]
content.add(box)
// Create entities to track a person's index fingers.
let rightFingerTip = AnchorEntity(.hand(.right, location: .indexFingerTip))
content.add(rightFingerTip)
let leftFingerTip = AnchorEntity(.hand(.left, location: .indexFingerTip))
content.add(leftFingerTip)
box.components.set(SummonComponent(leftAnchor: leftFingerTip, rightAnchor: rightFingerTip))
}
.task {
// Start a spatial tracking session otherwise `preservingWorldTransform` won't work.
guard HandTrackingProvider.isSupported else { return }
let configuration = SpatialTrackingSession.Configuration(
tracking: [.hand])
await spatialTrackingSession.run(configuration)
}
.gesture(
SpatialEventGesture()
.onChanged { events in
for event in events {
// When a person closes their fingers to pinch...
if event.kind == .indirectPinch,
let entity = event.targetedEntity,
var summonComponent = entity.components[SummonComponent.self],
summonComponent.isHolding == false {
// store the entity's parent and current transform. You'll use this information to return the entity when the person opens their fingers.
summonComponent.parentEntity = entity.parent
summonComponent.previousTransform = entity.transform
summonComponent.isHolding = true
// Find the anchor entity attached to the person's index finger.
let anchor = event.chirality == .right ? summonComponent.rightAnchor : summonComponent.leftAnchor
// Append the entity to the finger anchor.
// Preserve the world transform so the entity does not move.
anchor.addChild(entity, preservingWorldTransform: true)
// Calculate an offset for the entity so it's slightly in front of the index finger.
let xOffset:Float = event.chirality == .right ? -0.08 : 0.08
// Define a target transform for the entity that positions it near a person's finger and scales it.
let targetTransform = Transform(scale: .init(repeating: 0.5), translation: [xOffset, 0, 0])
// Animate the entity to the new transform.
entity.move(to: targetTransform, relativeTo: anchor, duration: 0.3)
entity.components.set(summonComponent)
}
}
}
.onEnded { events in
for event in events {
// When the person opens their fingers...
if event.kind == .indirectPinch,
let entity = event.targetedEntity,
var summonComponent = entity.components[SummonComponent.self],
summonComponent.isHolding == true,
let parentEntity = summonComponent.parentEntity,
let previousTransform = summonComponent.previousTransform {
// append the entity to its previous parent.
parentEntity.addChild(entity, preservingWorldTransform: true)
// Animate it to its previous transform.
entity.move(to: previousTransform, relativeTo: parentEntity, duration: 0.3)
summonComponent.isHolding = false
summonComponent.previousTransform = nil
summonComponent.parentEntity = nil
entity.components.set(summonComponent)
}
}
}
)
}
}