The VScript Book

Chapter 3.1: Math and Vectors

Portal 2 is a 3D game built on physics and geometry. To manipulate anything in this world, from aiming a turret to calculating the distance to a button, you'll need to use math. VScript provides two core mathematical tools: the Vector data type and a built-in Math Library.

Vectors: Navigating 3D Space

A Vector is a container for three Float numbers: X, Y, and Z. It's the primary tool for working with positions, directions, and angles.

// Create a vector representing a position in the map.
local position = Vector(128, 256, -64)

// Access the individual components
printl("X coordinate: " + position.x) // Prints 128

Vector Math

You can perform math operations directly on vectors. This is incredibly useful for calculating new positions.

  • Addition/Subtraction: Adds or subtracts each component. Vector(10, 20, 30) + Vector(1, 2, 3) results in Vector(11, 22, 33).
  • Multiplication by a number: Scales the vector. Vector(10, 20, 30) * 2 results in Vector(20, 40, 60).

Note: VSquirrel's Vector type does not support direct division by a number (e.g., myVector / 2 will cause a script error). To achieve division, you must multiply by a fraction instead. For example, to halve a vector, you would write myVector * 0.5 or myVector * (1.0 / 2).

// Example: Moving a Cube
local cube = Entities.FindByName(null, "my_cube")
local startPos = cube.GetOrigin()
local offset = Vector(0, 0, 100) // We want to move it up by 100 units.
local endPos = startPos + offset
cube.SetOrigin(endPos)

More Than Just Position: Angles

Vectors are also used to represent an entity's rotation in 3D space. When used for angles, the components have special meanings:

  • X: Pitch (tilting up and down)
  • Y: Yaw (turning left and right)
  • Z: Roll (tilting side to side)
// Example: Rotating a Turret
local turret = Entities.FindByName(null, "turret_01")
local currentAngles = turret.GetAngles()

// We want to make the turret face 90 degrees to its right.
local rotation = Vector(0, 90, 0) // 90 degrees on the Yaw axis
local newAngles = currentAngles + rotation

// The SetAngles function needs three separate numbers, not one Vector object.
turret.SetAngles(newAngles.x, newAngles.y, newAngles.z)

Useful Vector Methods

Vectors have built-in functions that make common calculations easy.

  • .Length(): Returns the length of the vector (its distance from the world's center at Vector(0,0,0)).
  • (vec1 - vec2).Length(): A common and very useful trick to get the distance between two points.
// Example: Check if the player is close enough to a button
local player = GetPlayer()
local button = Entities.FindByName(null, "the_button")

local playerPos = player.GetOrigin()
local buttonPos = button.GetOrigin()

local distance = (playerPos - buttonPos).Length()

if (distance < 100) { // 100 is our activation range
    printl("Player is in range!")
}

The Global Math Library

Squirrel provides a set of standard mathematical functions that are always available. You don't need to include any special libraries to use them.

Rounding & Absolute Value

  • floor(x): Rounds a number down to the nearest whole number. floor(3.9) is 3.0.
  • ceil(x): Rounds a number up to the nearest whole number. ceil(3.1) is 4.0.
  • abs(x): Returns the absolute (non-negative) value. abs(-5) is 5.

Exponents & Logarithms

  • sqrt(x): Returns the square root of a number.
  • pow(base, exponent): Returns the `base` raised to the power of `exponent`. pow(2, 3) is 8.
  • log(x): Returns the natural logarithm of x.

Trigonometry

These functions are essential for working with angles and rotations. They all work with angles in radians, not degrees!

  • sin(rad), cos(rad), tan(rad): Standard trigonometric functions.
  • asin(x), acos(x), atan(x): Inverse trigonometric functions.
  • atan2(y, x): A very useful function that returns the angle in radians between the positive X-axis and the point (x, y).

Random Numbers

To add unpredictability to your maps, you can use random number generators. While old methods exist, the modern and recommended way is to use the built-in RandomInt() and RandomFloat() functions.

  • RandomInt(min, max): Returns a random whole number between min and max, including both of those values.
  • RandomFloat(min, max): Returns a random decimal number between min and max.
// Seed the generator ONCE at the very top of your script.
// Example 1: Choose a random announcement to play.
local choice = RandomInt(1, 3)
if (choice == 1) {
    printl("Playing announcement A...")
}
else if (choice == 2) {
    printl("Playing announcement B...")
}
else {
    printl("Playing announcement C...")
}

// Example 2: Drop a cube at a random horizontal position.
local randomX = RandomFloat(-200.0, 200.0)
local randomY = RandomFloat(-200.0, 200.0)
local spawnPosition = Vector(randomX, randomY, 512)
// Code to spawn a cube at spawnPosition...