VR Input System (EasyInputs)

Hand Parameter
All input functions that require a hand use: Enum.Hand.Left or Enum.Hand.Right

Grip & Trigger

Input.GetGripButtonDown(hand)
Returns true if the grip button is pressed
hand - Enum.Hand.Left or Enum.Hand.Right
Returns: boolean
Input.GetGripButtonFloat(hand)
Returns analog grip value (useful for grip strength)
Returns: float (0.0 to 1.0)
Input.LeftGrip() / Input.RightGrip()
Convenience shortcuts for grip values
Returns: float (0.0 to 1.0)
Input.GetTriggerButtonDown(hand)
Returns true if trigger button is pressed
Returns: boolean
Input.GetTriggerButtonFloat(hand)
Returns analog trigger value
Returns: float (0.0 to 1.0)
Input.GetTriggerButtonTouched(hand)
Returns true if trigger is being touched (Quest only)
Returns: boolean
Input.LeftTrigger() / Input.RightTrigger()
Convenience shortcuts for trigger values
Returns: float (0.0 to 1.0)

Face Buttons (A, B, X, Y)

Input.GetPrimaryButtonDown(hand)
A button (right hand) or X button (left hand)
Returns: boolean
Input.GetSecondaryButtonDown(hand)
B button (right hand) or Y button (left hand)
Returns: boolean
Input.AButton() / Input.BButton()
Right controller buttons (A and B)
Returns: boolean
Input.XButton() / Input.YButton()
Left controller buttons (X and Y)
Returns: boolean
Input.GetPrimaryButtonTouched(hand)
Returns true if primary button is being touched
Returns: boolean
Input.GetSecondaryButtonTouched(hand)
Returns true if secondary button is being touched
Returns: boolean

Thumbstick

Input.GetThumbStick2DAxis(hand)
Get thumbstick position as Vector2 (for movement/turning)
Returns: Vector2 (x: -1 to 1, y: -1 to 1)
Input.LeftStick() / Input.RightStick()
Convenience shortcuts for thumbstick values
Returns: Vector2
Input.GetThumbStickButtonDown(hand)
Returns true if thumbstick is clicked down
Returns: boolean
Input.GetThumbStickButtonTouched(hand)
Returns true if thumbstick is being touched
Returns: boolean

Menu & System

Input.GetMenuButtonDown(hand)
Returns true if menu button is pressed
Returns: boolean

Device Tracking

Input.GetDeviceVelocity(hand)
Get controller velocity (useful for swing detection)
Returns: Vector3
Input.LeftControllerPosition()
Get left controller world position
Returns: Vector3
Input.RightControllerPosition()
Get right controller world position
Returns: Vector3
Input.LeftControllerRotation()
Get left controller rotation
Returns: Quaternion
Input.RightControllerRotation()
Get right controller rotation
Returns: Quaternion
Input.HeadsetPosition()
Get VR headset position
Returns: Vector3
Input.HeadsetRotation()
Get VR headset rotation
Returns: Quaternion

Haptics

Input.Vibrate(hand, amplitude, duration)
Trigger haptic feedback on controller
hand - Which controller
amplitude - Vibration strength (0.0 to 1.0)
duration - Duration in seconds
Example: VR Input Usage
-- Check if right trigger is pulled
if Input.RightTrigger() > 0.5 then
  print("Trigger pulled!")
end

-- Get thumbstick for movement
local stick = Input.LeftStick()
print("Stick X: " .. stick.x .. " Y: " .. stick.y)

-- Check button press
if Input.AButton() then
  print("A button pressed!")
  Input.Vibrate(Enum.Hand.Right, 0.5, 0.1)
end

-- Detect controller swing
local velocity = Input.GetDeviceVelocity(Enum.Hand.Right)
if velocity.magnitude > 2.0 then
  print("Fast swing detected!")
end

GameObject Management

GameObject.New(name)
Create a new empty GameObject
name - GameObject name
Returns: GameObject
GameObject.Find(name)
Find a GameObject by name in the scene
Returns: GameObject or nil
GameObject.FindWithTag(tag)
Find first GameObject with specified tag
Returns: GameObject or nil
GameObject.FindGameObjectsWithTag(tag)
Find all GameObjects with specified tag
Returns: GameObject array
GameObject.CreatePrimitive(type)
Create a primitive shape
type - Enum.Primitive.Sphere, Cube, Cylinder, Capsule, Plane, Quad
Returns: GameObject
GameObject.Destroy(obj)
Destroy a GameObject (happens at end of frame)
GameObject.DestroyImmediate(obj)
Destroy a GameObject immediately
GameObject.DontDestroyOnLoad(obj)
Keep GameObject alive between scene loads
Example: Creating Objects
-- Create a sphere
local sphere = GameObject.CreatePrimitive(Enum.Primitive.Sphere)
sphere.transform.position = Vector3.New(0, 2, 5)
sphere.transform.localScale = Vector3.New(2, 2, 2)

-- Find and modify existing object
local player = GameObject.Find("Player")
if player then
  print("Found player at: " .. player.transform.position)
end

-- Find all enemies
local enemies = GameObject.FindGameObjectsWithTag("Enemy")
print("Found " .. #enemies .. " enemies")

Triggers & Collisions

Trigger System

Trigger.Add(gameObject)
Add trigger component to GameObject (auto-adds collider as trigger)
Returns: LuaTrigger component
Trigger.SetTag(trigger, tag)
Filter triggers to only detect specific tag
Trigger.OnEnter(trigger, callback)
Set callback when something enters trigger
callback - function(otherGameObject)
Trigger.OnExit(trigger, callback)
Set callback when something exits trigger
Trigger.OnStay(trigger, callback)
Set callback while something stays in trigger (called every frame)

Collision System

Collision.Add(gameObject)
Add collision component (auto-adds collider and rigidbody)
Returns: LuaCollision component
Collision.SetTag(collision, tag)
Filter collisions to only detect specific tag
Collision.OnEnter(collision, callback)
Set callback when collision starts
Collision.OnExit(collision, callback)
Set callback when collision ends
Collision.OnStay(collision, callback)
Set callback while collision continues
Example: Trigger Zone
-- Create a trigger zone
local zone = GameObject.CreatePrimitive(Enum.Primitive.Cube)
zone.transform.position = Vector3.New(0, 0, 5)
zone.transform.localScale = Vector3.New(5, 5, 5)

local trigger = Trigger.Add(zone)
Trigger.SetTag(trigger, "Player")

Trigger.OnEnter(trigger, function(other)
  print("Player entered zone!")
  Input.Vibrate(Enum.Hand.Right, 1.0, 0.2)
end)

Trigger.OnExit(trigger, function(other)
  print("Player left zone!")
end)
Example: Physics Collision
-- Create a bouncing ball
local ball = GameObject.CreatePrimitive(Enum.Primitive.Sphere)
ball.transform.position = Vector3.New(0, 5, 0)

local collision = Collision.Add(ball)

Collision.OnEnter(collision, function(other)
  print("Ball hit: " .. other.name)
  Input.Vibrate(Enum.Hand.Right, 0.5, 0.1)
end)

Physics & Raycasting

Physics.Raycast(origin, direction, maxDistance)
Cast a ray and detect hits
origin - Vector3 starting point
direction - Vector3 direction (should be normalized)
maxDistance - Maximum ray distance
Returns: table with {hit, point, normal, distance, collider, gameObject}
Physics.SphereCast(origin, radius, direction, maxDistance)
Cast a sphere and detect hits
Returns: table with {hit, point, normal, distance, gameObject}
Physics.OverlapSphere(position, radius)
Find all colliders within a sphere
Returns: Collider array
Example: Raycast from Controller
-- Cast a ray forward from right controller
local controllerPos = Input.RightControllerPosition()
local forward = Vector3.Forward()

local hit = Physics.Raycast(controllerPos, forward, 10)

if hit.hit then
  print("Hit object: " .. hit.gameObject.name)
  print("Distance: " .. hit.distance)
  print("Hit point: " .. hit.point)
else
  print("Nothing hit")
end
Example: Area Detection
-- Find all objects near player
local playerPos = Input.HeadsetPosition()
local nearby = Physics.OverlapSphere(playerPos, 5.0)

print("Found " .. #nearby .. " nearby objects")
for i, collider in ipairs(nearby) do
  print("Object: " .. collider.gameObject.name)
end

Vectors & Math

Vector3

Vector3.New(x, y, z)
Create a new Vector3
Returns: Vector3
Vector3.Distance(a, b)
Calculate distance between two points
Returns: float
Vector3.Lerp(a, b, t)
Linear interpolation between two vectors
t - 0.0 to 1.0
Returns: Vector3
Vector3.Zero() / One() / Up() / Down() / Left() / Right() / Forward() / Back()
Static vector constants

Vector2

Vector2.New(x, y)
Create a new Vector2
Vector2.Distance(a, b)
Calculate distance between two 2D points
Vector2.Lerp(a, b, t)
Linear interpolation

Color

Color.New(r, g, b, a)
Create a color (values 0.0 to 1.0)
Color.Red() / Green() / Blue() / White() / Black() / Yellow() / Cyan() / Magenta()
Color presets

Random

Random.Range(min, max)
Random float between min and max
Random.RangeInt(min, max)
Random integer between min and max (exclusive)
Example: Vector Math
local pos1 = Vector3.New(0, 0, 0)
local pos2 = Vector3.New(5, 0, 5)

local distance = Vector3.Distance(pos1, pos2)
print("Distance: " .. distance)

-- Interpolate between positions
local midpoint = Vector3.Lerp(pos1, pos2, 0.5)
print("Midpoint: " .. midpoint)

-- Random position
local randomPos = Vector3.New(
  Random.Range(-10, 10),
  Random.Range(0, 5),
  Random.Range(-10, 10)
)

Time & Coroutines

Time.time()
Get time since game started
Returns: float (seconds)
Time.deltaTime()
Get time since last frame
Returns: float (seconds)
Time.timeScale()
Get current time scale
Time.SetTimeScale(scale)
Set game speed (1.0 = normal, 0.5 = half speed, 2.0 = double speed)
wait(seconds)
Pause execution for specified time (use in coroutines)
StartCoroutine(name, function)
Start a named coroutine (can be stopped later)
StopCoroutine(name)
Stop a running coroutine by name
Example: Coroutine Timer
StartCoroutine("countdown", function()
  for i = 5, 1, -1 do
    print("Countdown: " .. i)
    wait(1.0)
  end
  print("GO!")
end)

-- Stop it early if needed
-- StopCoroutine("countdown")
Example: Slow Motion
-- Activate slow motion
Time.SetTimeScale(0.5)

-- After 3 seconds, return to normal
StartCoroutine("slowmo", function()
  wait(3.0)
  Time.SetTimeScale(1.0)
end)

Networking (Photon)

Multiplayer RPCs
The networking system uses Photon's RaiseEvent system. Each RPC needs a unique ID between 0-154.

RPC Management

EventManager.Register(name, id, callback)
Register a new RPC that can be called over the network
name - Unique RPC name
id - Unique ID (0-154)
callback - function(data) called when RPC is received
EventManager.Call(name, receivers, data)
Call an RPC across the network
name - RPC name to call
receivers - Enum.Receivers.All, Others, or MasterClient
data - Data to send (string, number, table, etc.)
EventManager.GetAvailableId()
Get the next available RPC ID
Returns: integer (or -1 if all slots taken)

Game State

game.IsMasterClient()
Check if this client is the master client (host)
Returns: boolean
game.IsRunning()
Check if the game is running (not in editor)
Returns: boolean
Example: Simple Multiplayer System
-- Register an RPC for spawning objects
EventManager.Register("SpawnCube", 1, function(data)
  local pos = Vector3.New(data.x, data.y, data.z)
  local cube = GameObject.CreatePrimitive(Enum.Primitive.Cube)
  cube.transform.position = pos
  print("Cube spawned at: " .. pos)
end)

-- When A button pressed, spawn cube for everyone
if Input.AButtonDown() then
  local spawnData = {
    x = Random.Range(-5, 5),
    y = 2,
    z = Random.Range(-5, 5)
  }
  EventManager.Call("SpawnCube", Enum.Receivers.All, spawnData)
end
Example: Master Client Authority
-- Only master client can trigger events
if game.IsMasterClient() then
  EventManager.Register("GameStart", 2, function(data)
    print("Game starting!")
  end)
  
  -- Start game for all players
  if Input.YButtonDown() then
    EventManager.Call("GameStart", Enum.Receivers.All, nil)
  end
end

Audio

Audio Components
Audio sources can play sounds in 3D space with distance attenuation and spatial audio.
gameObject.AddComponent(AudioSource)
Add an audio source component to a GameObject
Returns: AudioSource
audioSource.Play()
Play the audio clip
audioSource.Stop()
Stop playing audio
audioSource.Pause()
Pause audio playback
audioSource.volume
Get or set volume (0.0 to 1.0)
audioSource.pitch
Get or set pitch (0.5 to 2.0, 1.0 is normal)
audioSource.loop
Get or set looping (boolean)
audioSource.spatialBlend
Get or set 2D/3D blend (0.0 = 2D, 1.0 = 3D)
Example: 3D Spatial Audio
-- Create a sound emitter in 3D space
local emitter = GameObject.New("SoundEmitter")
emitter.transform.position = Vector3.New(5, 1, 5)

local audio = emitter.AddComponent(AudioSource)
audio.volume = 0.8
audio.spatialBlend = 1.0 -- Full 3D audio
audio.loop = true
audio.Play()

Lighting

gameObject.AddComponent(Light)
Add a light component to a GameObject
Returns: Light
light.type
Light type: Enum.LightType.Spot, Point, Directional, or Area
light.color
Light color (Color)
light.intensity
Light brightness (float)
light.range
Light range for point and spot lights (float)
light.spotAngle
Spot light cone angle (0-180 degrees)
Example: Dynamic Lighting
-- Create a pulsing point light
local lightObj = GameObject.New("DynamicLight")
lightObj.transform.position = Vector3.New(0, 3, 0)

local light = lightObj.AddComponent(Light)
light.type = Enum.LightType.Point
light.color = Color.New(1.0, 0.5, 0.0) -- Orange
light.range = 10

-- Pulse the light
StartCoroutine("pulse", function()
  while true do
    for i = 0, 20 do
      light.intensity = 1 + math.sin(Time.time() * 3)
      wait(0.05)
    end
  end
end)
Example: Flashlight
-- Attach spotlight to controller
local flashlight = GameObject.New("Flashlight")
local light = flashlight.AddComponent(Light)
light.type = Enum.LightType.Spot
light.color = Color.White()
light.intensity = 2
light.range = 15
light.spotAngle = 45

-- Update position every frame
StartCoroutine("updateFlashlight", function()
  while true do
    flashlight.transform.position = Input.RightControllerPosition()
    flashlight.transform.rotation = Input.RightControllerRotation()
    wait(0)
  end
end)

Particles

gameObject.AddComponent(ParticleSystem)
Add a particle system component
Returns: ParticleSystem
particleSystem.Play()
Start emitting particles
particleSystem.Stop()
Stop emitting particles
particleSystem.Pause()
Pause particle emission
particleSystem.Clear()
Remove all existing particles
Example: Particle Effects
-- Create explosion effect
local explosion = GameObject.New("Explosion")
explosion.transform.position = Vector3.New(0, 1, 5)

local particles = explosion.AddComponent(ParticleSystem)
particles.Play()

-- Destroy after 2 seconds
StartCoroutine("cleanupExplosion", function()
  wait(2.0)
  GameObject.Destroy(explosion)
end)

Complete Examples

Example 1: VR Grab System

VR Object Grabbing
-- Track grabbed object
local grabbedObject = nil
local grabOffset = Vector3.Zero()

-- Check for grab each frame
StartCoroutine("grabUpdate", function()
  while true do
    local gripValue = Input.RightGrip()
    
    if gripValue > 0.7 and grabbedObject == nil then
      -- Cast ray from controller
      local controllerPos = Input.RightControllerPosition()
      local forward = Vector3.Forward()
      local hit = Physics.Raycast(controllerPos, forward, 2)
      
      if hit.hit then
        grabbedObject = hit.gameObject
        grabOffset = grabbedObject.transform.position - controllerPos
        Input.Vibrate(Enum.Hand.Right, 0.3, 0.1)
      end
    elseif gripValue < 0.3 and grabbedObject ~= nil then
      -- Release object
      grabbedObject = nil
      Input.Vibrate(Enum.Hand.Right, 0.2, 0.05)
    end
    
    -- Update grabbed object position
    if grabbedObject ~= nil then
      local controllerPos = Input.RightControllerPosition()
      grabbedObject.transform.position = controllerPos + grabOffset
    end
    
    wait(0)
  end
end)

Example 2: Target Shooting Gallery

Shooting Gallery with Score
local score = 0
local targets = {}

-- Spawn targets
for i = 1, 5 do
  local target = GameObject.CreatePrimitive(Enum.Primitive.Sphere)
  target.transform.position = Vector3.New(
    Random.Range(-5, 5),
    Random.Range(1, 3),
    10
  )
  target.transform.localScale = Vector3.New(0.5, 0.5, 0.5)
  
  -- Make target red
  local renderer = target.GetComponent(Renderer)
  renderer.material.color = Color.Red()
  
  table.insert(targets, target)
end

-- Shooting logic
StartCoroutine("shootingLoop", function()
  while true do
    if Input.RightTrigger() > 0.9 then
      local pos = Input.RightControllerPosition()
      local rot = Input.RightControllerRotation()
      local forward = Vector3.Forward() -- Transform by rotation
      
      local hit = Physics.Raycast(pos, forward, 50)
      
      if hit.hit then
        -- Check if we hit a target
        for i, target in ipairs(targets) do
          if target == hit.gameObject then
            score = score + 1
            print("Hit! Score: " .. score)
            GameObject.Destroy(target)
            table.remove(targets, i)
            Input.Vibrate(Enum.Hand.Right, 1.0, 0.2)
            break
          end
        end
      end
      
      wait(0.2) -- Fire rate delay
    end
    
    wait(0)
  end
end)

Example 3: Multiplayer Spawner

Synchronized Object Spawning
-- Register RPC for spawning
EventManager.Register("NetworkSpawn", 10, function(data)
  local obj = GameObject.CreatePrimitive(data.type)
  obj.transform.position = Vector3.New(data.x, data.y, data.z)
  obj.transform.localScale = Vector3.New(data.scale, data.scale, data.scale)
  
  local renderer = obj.GetComponent(Renderer)
  renderer.material.color = Color.New(data.r, data.g, data.b)
end)

-- Spawn objects when button pressed
StartCoroutine("spawnLoop", function()
  while true do
    if Input.AButtonDown() then
      local spawnData = {
        type = Enum.Primitive.Cube,
        x = Random.Range(-5, 5),
        y = 5,
        z = Random.Range(-5, 5),
        scale = Random.Range(0.5, 2.0),
        r = Random.Range(0, 1),
        g = Random.Range(0, 1),
        b = Random.Range(0, 1)
      }
      
      EventManager.Call("NetworkSpawn", Enum.Receivers.All, spawnData)
    end
    
    wait(0)
  end
end)

Example 4: Teleportation System

VR Teleport Mechanic
local teleportMarker = GameObject.CreatePrimitive(Enum.Primitive.Cylinder)
teleportMarker.transform.localScale = Vector3.New(1, 0.1, 1)
teleportMarker.SetActive(false)

local renderer = teleportMarker.GetComponent(Renderer)
renderer.material.color = Color.New(0, 1, 0, 0.5) -- Green transparent

local player = GameObject.Find("Player")

StartCoroutine("teleportSystem", function()
  while true do
    local stick = Input.LeftStick()
    
    if stick.y > 0.8 then
      -- Show teleport marker
      local controllerPos = Input.LeftControllerPosition()
      local forward = Vector3.Forward()
      
      local hit = Physics.Raycast(controllerPos, forward, 10)
      
      if hit.hit then
        teleportMarker.SetActive(true)
        teleportMarker.transform.position = hit.point
      end
    else
      -- Teleport on release
      if teleportMarker.activeSelf then
        player.transform.position = teleportMarker.transform.position
        teleportMarker.SetActive(false)
        Input.Vibrate(Enum.Hand.Left, 0.5, 0.1)
      end
    end
    
    wait(0)
  end
end)