Physics Simulation
Welcome to Lesson 2.2! In this lesson, you'll master HoloScript's physics system to create realistic, interactive VR experiences.
Physics Fundamentals
HoloScript's physics system is built on a high-performance engine optimized for VR. Enable physics on any object with the @physics trait:
composition ball {
@physics
geometry: "sphere"
position: [0, 5, 0] // Will fall due to gravity
}Physics Properties
Mass
Mass determines how heavy an object is:
composition lightweight {
@physics { mass: 0.1 } // Light, easy to move
geometry: "sphere"
}
composition heavyweight {
@physics { mass: 10.0 } // Heavy, hard to move
geometry: "cube"
}Restitution (Bounciness)
Control how bouncy objects are:
// Super bouncy ball
composition bouncyBall {
@physics {
mass: 0.5
restitution: 0.95 // 0 = no bounce, 1 = perfect bounce
}
geometry: "sphere"
}
// Heavy, non-bouncy rock
composition rock {
@physics {
mass: 5.0
restitution: 0.1 // Barely bounces
}
geometry: "sphere"
}Friction
Control how slippery surfaces are:
// Ice block - very slippery
composition iceBlock {
@physics {
friction: 0.05
}
geometry: "cube"
}
// Rubber - high friction
composition rubber {
@physics {
friction: 0.9
}
geometry: "cube"
}Damping
Slow down movement and rotation over time:
composition dampedObject {
@physics {
linearDamping: 0.3 // Slow down movement
angularDamping: 0.5 // Slow down rotation
}
geometry: "cube"
}Body Types
Dynamic (Default)
Fully simulated, affected by forces:
composition dynamic {
@physics { type: "dynamic" }
geometry: "sphere"
}Static
Never moves, infinite mass:
composition floor {
@physics { type: "static" }
geometry: "plane"
scale: [20, 20, 1]
rotation: [-90, 0, 0]
}Kinematic
Controlled by animation, affects other objects:
composition platform {
@physics { type: "kinematic" }
geometry: "cube"
scale: [3, 0.2, 3]
animation move {
property: "position.y"
from: 0
to: 3
duration: 3000
loop: true
easing: "easeInOut"
}
}Collision Shapes
Auto-Generated
By default, collision matches geometry:
composition autoBox {
@physics
geometry: "cube" // Uses box collider
}
composition autoSphere {
@physics
geometry: "sphere" // Uses sphere collider
}Custom Colliders
Specify explicit collision shapes:
composition complex {
@physics {
collider: "box" // Box
collider: "sphere" // Sphere
collider: "capsule" // Capsule
collider: "mesh" // Exact mesh (expensive)
collider: "convex" // Convex hull
}
geometry: "models/character.glb"
}Compound Colliders
Combine multiple simple shapes:
composition character {
@physics {
colliders: [
{ type: "capsule", height: 1.8, radius: 0.3, center: [0, 0.9, 0] },
{ type: "sphere", radius: 0.2, center: [0, 1.8, 0] } // Head
]
}
geometry: "models/character.glb"
}Forces and Impulses
Apply Force
Apply continuous force:
composition rocket {
@physics { mass: 1.0 }
geometry: "cylinder"
onUpdate: {
if (this.isBoosting) {
physics.applyForce(this, [0, 50, 0]) // Upward thrust
}
}
}Apply Impulse
Apply instant velocity change:
composition ball {
@physics
@grabbable
@throwable
onRelease(event): {
// Add extra kick when released
physics.applyImpulse(this, event.velocity.multiply(1.5))
}
}Apply Torque
Spin the object:
composition spinner {
@physics
onGrab: {
physics.applyTorque(this, [0, 100, 0]) // Spin when grabbed
}
}Collision Detection
Collision Events
composition collider {
@physics
@collidable
onCollisionEnter(event): {
console.log("Hit:", event.other.name)
console.log("Force:", event.impulse)
console.log("Point:", event.contactPoint)
console.log("Normal:", event.contactNormal)
}
onCollisionStay(event): {
// Called every frame while colliding
}
onCollisionExit(event): {
console.log("Stopped touching:", event.other.name)
}
}Trigger Zones
Non-physical detection zones:
composition triggerZone {
@trigger
geometry: "cube"
scale: [3, 3, 3]
opacity: 0.2
onTriggerEnter(event): {
if (event.object.tag === "player") {
this.activateEvent()
}
}
}Raycasting
Cast rays to detect objects:
composition laser {
onUpdate: {
const hit = physics.raycast(
this.position, // Origin
this.forward, // Direction
100, // Max distance
["enemy", "environment"] // Layer mask
)
if (hit) {
laserEnd.position = hit.point
if (hit.object.tag === "enemy") {
hit.object.damage(10)
}
}
}
}Joints and Constraints
Fixed Joint
Lock objects together:
composition handle {
@physics
}
composition blade {
@physics
joint fixed {
connectedTo: handle
breakForce: 1000 // Breaks if force exceeds this
}
}Hinge Joint
Rotational constraint (doors, wheels):
composition door {
@physics { type: "dynamic" }
joint hinge {
connectedTo: doorFrame
anchor: [-0.5, 0, 0] // Pivot point
axis: [0, 1, 0] // Rotation axis
limits: [-90, 90] // Angle limits
motor: { speed: 2, force: 10 }
}
}Spring Joint
Elastic connection:
composition pendulum {
@physics
joint spring {
connectedTo: anchor
springConstant: 50
damping: 0.5
restLength: 2
}
}Complete Physics Example
composition "Physics Playground" {
environment {
gravity: [0, -9.81, 0]
}
// Static ground
composition ground {
@physics { type: "static" }
geometry: "plane"
scale: [20, 20, 1]
rotation: [-90, 0, 0]
material: { color: "#3a3a3a" }
}
// Bouncy balls
template BouncyBall {
params {
bounce: number = 0.8
ballColor: string = "#ff0000"
}
@physics {
mass: 0.5
restitution: params.bounce
}
@grabbable
@throwable
geometry: "sphere"
scale: 0.2
color: params.ballColor
onCollision(event): {
if (event.impulse > 2) {
audio.play("bounce.mp3", { volume: event.impulse / 10 })
}
}
}
object ball1 using BouncyBall {
position: [0, 3, -2]
bounce: 0.95
ballColor: "#ff0000"
}
object ball2 using BouncyBall {
position: [0.5, 4, -2]
bounce: 0.7
ballColor: "#00ff00"
}
// Dominoes
group dominoes {
for (let i = 0; i < 10; i++) {
composition domino {
@physics { mass: 0.1 }
geometry: "cube"
scale: [0.1, 0.5, 0.3]
position: [i * 0.4 - 2, 0.25, -3]
}
}
}
// Swinging pendulum
composition pendulumAnchor {
@physics { type: "static" }
position: [3, 4, -3]
scale: 0.1
}
composition pendulumBall {
@physics { mass: 2.0 }
@grabbable
geometry: "sphere"
scale: 0.3
position: [3, 2, -3]
joint spring {
connectedTo: pendulumAnchor
springConstant: 20
damping: 0.1
restLength: 2
}
}
}Performance Tips
- Use simple colliders - Box and sphere are fastest
- Limit active physics objects - Disable far objects
- Use layers wisely - Avoid unnecessary collision checks
- Sleep inactive objects - Let static objects sleep
// Optimize with collision layers
composition enemy {
@physics {
layer: "enemies"
collidesWith: ["player", "projectiles", "environment"]
}
}Quiz
- What property controls how bouncy an object is?
- What's the difference between force and impulse?
- How do you create a door hinge?
- What's a kinematic body?
- How do you detect collisions?
Answers
restitution(0-1)- Force is continuous, impulse is instant
- Use a hinge joint with axis and limits
- A physics body controlled by animation that affects other bodies
- Use
onCollisionEnter,onCollisionStay,onCollisionExitevents
Estimated time: 45 minutes
Difficulty: ⭐⭐ Intermediate
Next: Lesson 2.3 - Audio & Sound