Puzzle 1

The first puzzle room will continue to practice using vectors. Especially using the size of an offset vector to determine the distance between two objects.

Puzzle Overview

The first puzzle riddle is as follows:

Karl will only chase Kate
if other suitors are nearby

This means that the player must stand near the Kate statue in order for the Karl statue to move over to Kate. Once they are close enough, the first puzzle will be complete and open the door to the second puzzle room.

Scripting Overview

Each puzzle room will have its own server script to run its logic and determine if certain conditions are met. There will be common logic required by all the puzzles so it makes sense to use an API script. This will allow sharing functions across multiple scripts and avoid unnecessary repetition of code.

What is an API?

API stands for application programming interface. In Core, it is a special script that can be loaded into other scripts using a custom property. This is useful because multiple scripts can load the API and access shared properties, functions, and other code.

API Example

Here’s a quick example of how an API works.

  • The API script saves its properties and functions to a table and returns it at the bottom.
  • The API script is in the Project Content window, but NOT in the Hierarchy.
  • A second script (in the Hierarchy) has the API as a custom property.
  • The second script uses the require function to access the API table of properties and functions.

Create an API Script

In the Project Content window, right click and add a new script. Name the script PuzzleAPI.

Create a Server Script

In the Hierarchy, add a new script named Puzzle1Logic to the Server Context within the Scripts folder.

Add Custom Properties

The Puzzle1Logic 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 1 group. This will contain the objects required by the script. Then open the Properties window with the Puzzle1Logic 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 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.

Solution Distance

The SolutionDistance custom property is the minimum amount of distance of the two statues required for the puzzle to be completed.

It is also the minimum distance of the player and Statue 2 in order for Statue 1 to begin moving.

Starting Code

To start, all the custom properties will need to be assigned to variables. The Trigger will detect when a player enters and exits the room. There will also be a Tick function that will constantly call another function named MoveStatue if there is a player in the room. This function will later be filled in with the puzzle logic.

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 TRIGGER = script:GetCustomProperty("Trigger"):WaitForObject()
local SOLUTION_DISTANCE = script:GetCustomProperty("SolutionDistance")
local MOVE_SPEED = script:GetCustomProperty("MoveSpeed")

local player = nil

local function MoveStatue(player)
   print(player.name .. " is in Puzzle Room 1.")
end

function Tick()
   if player then
      MoveStatue(player)
   end
end

local function OnBeginOverlap(trigger, other)
   if other:IsA("Player") then
      player = other
   end
end

local function OnEndOverlap(trigger, other)
   if other:IsA("Player") then
      player = nil
   end
end

TRIGGER.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 first room, the Event Log window should print a message repeatedly until the player exits.

Missing Code

The MoveStatue function will need to check the distance of the player and statues. It will also need to calculate the velocity vector to move Statue 1 towards Statue 2.

This will be the pseudocode for the Puzzle 1 logic:

API Functions

Before implementing the puzzle logic, let’s add some helpful functions to the PuzzleAPI script. The puzzle will need to know the distance between two objects and the velocity vector from one object to another.

In the Project Content window, double click the PuzzleAPI script to open the Script Editor window. Add the following code:

local API = {}

function API.GetOffsetVector(obj1, obj2)
   local obj1Pos = obj1:GetWorldPosition()
   local obj2Pos = obj2:GetWorldPosition()
   return Vector2.New(obj2Pos) - Vector2.New(obj1Pos)
end

function API.GetDistance(obj1, obj2)
   local offset = API.GetOffsetVector(obj1, obj2)
   return offset.size
end

function API.GetTargetVelocity(obj, target, speed)
   local offset = API.GetOffsetVector(obj, target)
   local offsetNormalized = offset:GetNormalized()
   return Vector3.New(offsetNormalized * speed, 0)
end

return APICode language: Lua (lua)

Converting between Vector2 and Vector3

In the GetOffsetVector function, the two position vectors being subtracted are being converted to a Vector2. This is because the distance does not need to consider the Z axis. It is as if the vectors are being calculated on a 2D plane using the top-down view of the 3D project.

In the GetTargetVelocty function, the Vector2 offset vector is being converted back to a Vector3 because the move function (will be added in future steps) is expecting a Vector3 even if the Z component is 0.

MoveStatue Function

With the pseudocode planned and API functions created, the puzzle logic is now ready to be implemented. The statue will be moved and stopped using CoreObject functions.

Open the Puzzle1Logic script. Add the following code inside the MoveStatue function.

local function MoveStatue(player)
   local playerDistance = API.GetDistance(player, STATUE_2)
   local statueDistance = API.GetDistance(STATUE_1, STATUE_2)
   local velocity = API.GetTargetVelocity(STATUE_1, STATUE_2, MOVE_SPEED)

   if statueDistance <= SOLUTION_DISTANCE then
      STATUE_1:StopMove()
      Events.Broadcast("OpenPuzzleDoor", "1")
   elseif playerDistance <= SOLUTION_DISTANCE then
      STATUE_1:MoveContinuous(velocity)
   else
      STATUE_1:StopMove()
   end
endCode language: Lua (lua)

Preview the Project

Save the scripts and preview the project. The first statue should move towards the second statue if the player is nearby. Eventually, the puzzle door should open once the statues get close enough.

Post a comment

Leave a Comment

Scroll to Top