using { /Fortnite.com/Devices } using { /Verse.org/Simulation } using { /Verse.org/Simulation/Tags } using { /Verse.org/Assets } using { /UnrealEngine.com/Temporary/SpatialMath } using { /UnrealEngine.com/Temporary/Diagnostics } # THE DRIVABLE MINI-EXPERIENCE — capstone of "Scene Graph: Car Primitive". # # Every piece you earned across the domain, on one timeline: # * Part 1 the CAR PRIMITIVE — a tagged prop we move from Verse. # * Part 2 COLOUR — SetMaterial swaps the whole paint job. # * Part 3 ENTER -> FIRST PERSON — a mutator zone + first-person camera. # * here DRIVE — Input Trigger devices (throttle / steer) move the car # while a player is seated, plus a paint cycle on a fourth input. # # How it drives: the car is a prop primitive, so we move it ourselves with # MoveTo. Holding the throttle key advances the car along its own forward; the # steer keys rotate it. We do this in a `spawn`ed loop that runs only # while a driver is in the zone, reading each Input Trigger's `IsHeld[Agent]` # every tick. That keeps the controls responsive without authoring any custom # input action (which is Epic-internal). # # The car body + the entry zone + the paint materials are discovered/wired so # you can re-skin the whole experience without touching code. car_body_tag := class(tag) {} car_drivable_capstone_device := class(creative_device): # Walk-in zone that "seats" the driver. @editable SeatZone : mutator_zone_device = mutator_zone_device{} # First-person view for the driver. @editable DriverCamera : gameplay_camera_first_person_device = gameplay_camera_first_person_device{} # Throttle (hold to go), and steer left / right Input Triggers. @editable Throttle : input_trigger_device = input_trigger_device{} @editable SteerLeft : input_trigger_device = input_trigger_device{} @editable SteerRight : input_trigger_device = input_trigger_device{} # Paint-cycle input + two paint materials (optional asset slots). @editable PaintInput : input_trigger_device = input_trigger_device{} @editable PaintA : ?material = false @editable PaintB : ?material = false # Drive tuning. @editable Speed : float = 350.0 # cm advanced per tick of throttle @editable TurnRadians : float = 0.06 # radians turned per tick of steering # Runtime state. var Driving : logic = false # a driver is currently seated var ShowingA : logic = true # which paint is on the car now OnBegin() : void = SeatZone.AgentEntersEvent.Subscribe(OnSeat) SeatZone.AgentExitsEvent.Subscribe(OnUnseat) PaintInput.PressedEvent.Subscribe(OnPaint) Print("CarCapstone: seat zone, camera, drive + paint inputs wired.") OnSeat(Agent : agent) : void = if (not Driving?): DriverCamera.AddTo(Agent) Throttle.Register(Agent) SteerLeft.Register(Agent) SteerRight.Register(Agent) PaintInput.Register(Agent) set Driving = true Print("CarCapstone: driver seated -> first person + controls live.") spawn { DriveLoop(Agent) } OnUnseat(Agent : agent) : void = DriverCamera.RemoveFrom(Agent) Throttle.Unregister(Agent) SteerLeft.Unregister(Agent) SteerRight.Unregister(Agent) PaintInput.Unregister(Agent) set Driving = false Print("CarCapstone: driver left -> controls released.") # Cycle paint on the tagged car body. The two slots are optional, so we pick # whichever one is showing-next and only repaint if it's actually filled. OnPaint(Agent : agent) : void = if (ShowingA?): set ShowingA = false if (Paint := PaintB?): RepaintCar(Paint) else: set ShowingA = true if (Paint := PaintA?): RepaintCar(Paint) RepaintCar(Paint : material) : void = for (Obj : FindCreativeObjectsWithTag(car_body_tag)): if (Body := creative_prop[Obj]): Body.SetMaterial(Paint, ?Index := 0) Print("CarCapstone: repainted.") # Runs while a driver is seated: each ~tick, read held inputs and move the car. DriveLoop(Agent : agent) : void = loop: if (not Driving?): break MoveTaggedCar(Agent) Sleep(0.06) # Advance and/or turn every tagged car body based on what the driver holds. MoveTaggedCar(Agent : agent) : void = for (Obj : FindCreativeObjectsWithTag(car_body_tag)): if (Body := creative_prop[Obj]): spawn { StepCar(Agent, Body) } StepCar(Agent : agent, Body : creative_prop) : void = Current : transform = Body.GetTransform() var NextRot : rotation = Current.Rotation # Steering rotates the car about its up axis (Z). if (SteerLeft.IsHeld[Agent]): set NextRot = NextRot.ApplyWorldRotationZ(-TurnRadians) if (SteerRight.IsHeld[Agent]): set NextRot = NextRot.ApplyWorldRotationZ(TurnRadians) var NextPos : vector3 = Current.Translation # Throttle advances along the (possibly newly-rotated) forward. if (Throttle.IsHeld[Agent]): Forward : vector3 = NextRot.GetLocalForward() set NextPos = Current.Translation + Forward * Speed Body.MoveTo(NextPos, NextRot, 0.06)