The second puzzle room will combine vectors and rotations. It will mainly focus on detecting the players world rotation and look rotation.
Puzzle Overview
The second puzzle riddle is as follows:
Karl will make his move only when suitors turn their back to him AND keep their eye on the prize
This means the player must have their back turned to Karl and be looking at the Treasure in order for Karl to move. The Treasure will have a VFX that will only play if the player is looking at it.
Create a Server Script
In the Hierarchy, add a new script named Puzzle2Logic to the Server Context within the Scripts folder.
Add Custom Properties
The Puzzle2Logic script will need several properties to detect if the player is in the room, move the statues, and use the API script.
Expand the Escape The Room Template and find the Puzzle 2 group. This will contain the objects required by the script. Then open the Properties window with the Puzzle2Logic script selected.
Add the following custom properties:
From the Project Content window, drag and drop the PuzzleAPI script as a custom property.
Drag and drop the Statue 1 object as a custom property.
Drag and drop the Statue 2 object as a custom property.
Drag and drop the Treasure object as a custom property.
Drag and drop the Trigger object as a custom property.
Add a new custom property of type float, set the name to SolutionDistance, and value to 300.
Add a new custom property of type float, set the name to MoveSpeed, and value to 500.
Starting Code
The starting code is similar to the first puzzle, except there is a variable sparklesPlaying that will track if the Treasure VFX is currently playing.
Double click the Puzzle1Logic script to open the Script Editor window. Add the following code:
local API = require(script:GetCustomProperty("PuzzleAPI"))
local STATUE_1 = script:GetCustomProperty("Statue1"):WaitForObject()
local STATUE_2 = script:GetCustomProperty("Statue2"):WaitForObject()
local TREASURE = script:GetCustomProperty("Treasure"):WaitF
local TRIGGER = script:GetCustomProperty("Trigger"):WaitForObject()
local SOLUTION_DISTANCE = script:GetCustomProperty("SolutionDistance")
local MOVE_SPEED = script:GetCustomProperty("MoveSpeed")
local player = nillocal sparklesPlaying = falselocalfunctionMoveStatue(player)print(player.name .. " is in Puzzle Room 2.")
endfunctionTick()if player then MoveStatue(player)
endendlocalfunctionOnBeginOverlap(trigger, other)if other:IsA("Player") then player = other
endendlocalfunctionOnEndOverlap(trigger, other)if other:IsA("Player") then player = nilendendTRIGGER.beginOverlapEvent:Connect(OnBeginOverlap)
TRIGGER.endOverlapEvent:Connect(OnEndOverlap)
Code language:Lua(lua)
Preview the Project
Save the script and preview the project. When the player enters the second room, the Event Log window should print a message repeatedly until the player exits.
Missing Code
The MoveStatue function will need to use the dot product of the player and Statue 1 offset vector and the player forward facing vector. It will also need to use raycasting to detect if the player’s vision is pointed at the Treasure object group.
This will be the pseudocode for the Puzzle 2 logic:
API Functions
The API script will need three new functions to be used in the second puzzle room. The first function will return the dot product of a player’s forward vector compared to the offset vector to another object. This calculated number will signify if the player is facing another object. The second function is calculating the two vectors required by a raycast of a player’s vision line. The third function is using a raycast to detect if the player is looking at an object belonging to a group of a certain name.
In the Project Content window, double click the PuzzleAPI script to open the Script Editor window. Add the following code:
local API = {}
functionAPI.GetOffsetVector(obj1, obj2)local obj1Pos = obj1:GetWorldPosition()
local obj2Pos = obj2:GetWorldPosition()
return Vector2.New(obj2Pos) - Vector2.New(obj1Pos)
endfunctionAPI.GetDistance(obj1, obj2)local offset = API.GetOffsetVector(obj1, obj2)
return offset.size
endfunctionAPI.GetTargetVelocity(obj, target, speed)local offset = API.GetOffsetVector(obj, target)
local offsetNormalized = offset:GetNormalized()
return Vector3.New(offsetNormalized * speed, 0)
endfunctionAPI.GetFacingDotProduct(obj1, obj2)local obj1Forward = obj1:GetWorldRotation() * Vector3.FORWARD
local offset = API.GetOffsetVector(obj1, obj2)
local offsetNormalized = Vector3.New(offset:GetNormalized(), 0)
return obj1Forward .. offsetNormalized
endfunctionAPI.GetPlayerLookAtVectors(player, length)local playerPos = player:GetWorldPosition()
local headOffset = Vector3.New(0, 0, 100)
local headPos = playerPos + headOffset
local playerLookRotation = player:GetLookWorldRotation()
local playerLookForward = playerLookRotation * Vector3.FORWARD * length
return headPos, headPos + playerLookForward
endfunctionAPI.LookingAtGroup(player, group)local v1, v2 = API.GetPlayerLookAtVectors(player, 3000)
local hitResult = World.Raycast(v1, v2, {ignorePlayers = true})
if hitResult and hitResult.other and group:IsAncestorOf(hitResult.other) thenreturntrueendreturnfalseendreturn API
Code language:Lua(lua)
Raycasting
The World.Raycast function will return a HitResult object if the raycast collides with any object going from the starting vector position to the ending vector position. This is useful for checking a player’s vision line or checking a projectile collision.
Display the Treasure VFX
Using the raycasting function from the API will detect if the player is looking at the treasure. The effect should be played if it is not currently playing and the player is looking at the treasure. The effect should be stopped if the effect is playing and the player is not looking at the treasure.
Open the Puzzle2Logic script. Add this code to the MoveStatue function:
localfunctionMoveStatue(player)local lookingAtTreasure = API.LookingAtGroup(player, TREASURE)
ifnot sparklesPlaying and lookingAtTreasure then
Events.BroadcastToPlayer(player, "PlaySparkle")
sparklesPlaying = trueendif sparklesPlaying andnot lookingAtTreasure then
Events.BroadcastToPlayer(player, "StopSparkle")
sparklesPlaying = falseendendCode language:Lua(lua)
Preview the Project
Save the script and preview the project. Try toggling the special effect by looking at the treasure and then away.
Display the Dot Product
The dot product of the player offset vector to Statue 1 and its forward facing vector will return a number between -1 and 1. If the number is positive, then the player is facing in the general direction of the statue. If it is negative, then the player is facing away.
Open the Puzzle2Logic script. Add this code to the MoveStatue function:
localfunctionMoveStatue(player)local lookingAtTreasure = API.LookingAtGroup(player, TREASURE)
ifnot sparklesPlaying and lookingAtTreasure then Events.BroadcastToPlayer(player, "PlaySparkle")
sparklesPlaying = trueendif sparklesPlaying andnot lookingAtTreasure then Events.BroadcastToPlayer(player, "StopSparkle")
sparklesPlaying = falseendlocal dotProduct = API.GetFacingDotProduct(player, STATUE_1)
local isFacingAwayFromStatue = dotProduct < 0print("Player facing away from statue is " .. tostring(isFacingAwayFromStatue))
print("The dot product is " .. tostring(dotProduct))
endCode language:Lua(lua)
Preview the Project
Save the script and preview the project. Check the Event Log window to see if the player’s direction is correctly recognizing whether it is facing the statue.
Finish the Code
Now that both conditions are being checked, it is time to put it all together to control the Statue 1 movement and puzzle door opening.
Open the Puzzle2Logic script. Add this code to the MoveStatue function:
localfunctionMoveStatue(player)local lookingAtTreasure = API.LookingAtGroup(player, TREASURE)
ifnot sparklesPlaying and lookingAtTreasure then Events.BroadcastToPlayer(player, "PlaySparkle")
sparklesPlaying = trueendif sparklesPlaying andnot lookingAtTreasure then Events.BroadcastToPlayer(player, "StopSparkle")
sparklesPlaying = falseendlocal dotProduct = API.GetFacingDotProduct(player, STATUE_1)
local isFacingAwayFromStatue = dotProduct < 0local targetDistance = API.GetDistance(STATUE_1, STATUE_2)
local velocity = API.GetTargetVelocity(STATUE_1, STATUE_2, MOVE_SPEED)
if targetDistance <= SOLUTION_DISTANCE then STATUE_1:StopMove()
Events.Broadcast("OpenPuzzleDoor", "2")
elseif isFacingAwayFromStatue and lookingAtTreasure then STATUE_1:MoveContinuous(velocity)
else STATUE_1:StopMove()
endendCode language:Lua(lua)
This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.
Strictly Necessary Cookies
Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.
If you disable this cookie, we will not be able to save your preferences. This means that every time you visit this website you will need to enable or disable cookies again.
3rd Party Cookies
This website uses Google Analytics to collect anonymous information such as the number of visitors to the site, and the most popular pages.
Keeping this cookie enabled helps us to improve our website.
Please enable Strictly Necessary Cookies first so that we can save your preferences!