Latest version:
Tramway SDK 0.0.9
Github
Quick links
Home
Get Started

Basic scripting


In order to follow this guide, you might need the following:

  • Basic computer programming skills
    • Previous experience in object-oriented programming will prove to be very useful
  • Basic understanding of Lua or other Pascal-derived languages
  • General understanding of how game engines and similar software works

Reminder about Lua syntax


Anyway, here's a small reminder of how Lua works.

-- this is a comment

-- creating a global variable
number_first = 420

-- creating a local variable
local number_second = 69

-- printing stuff to the console
print(420 + 69)

-- Lua only has if/else statements. no switches
if 420 == 69 then
    -- ...
elseif 420 + 69 == 489 then
    -- ...
else
    -- ...
end

-- function
function add_special_number(x)
    return x + 420
end

-- Lua has tables, which are similar to
-- JavaScript objects. they function as
-- dynamic dictionaries, arrays, as objects
-- with methods, etc.

-- array
array = {}
array[1] = 420
array[2] = 69

-- dynamic dictionary
dict = {}
dict["hello"] = "world"
-- identical to previous statement
dict.hello = "world"

-- object
object = {}
object.number = 420
object.add = function(self, x)
    return self.number + x
end

-- if using the colon symbol, you can
-- call the method and the self parameter
-- will be filled in automatically
number = object:add(69)

What does the init script do?


First, let's go through the init.lua file and see what it does. It can be found in the /scripts/ directory in the project template.

print("\n\nHello! [...]\n")
-- ...

This part just prints some text to the console.

-- Retitling the main window.
tram.ui.SetWindowTitle("Teapot Explorer v1.0")
tram.ui.SetWindowSize(640, 480)

This part sets the title of the window, which is opened automatically on program start. It also sets the size of the window.

-- Setting up the global lighting.
tram.render.SetSunColor(tram.math.vec3(0.0, 0.0, 0.0))
tram.render.SetSunDirection(tram.math.DIRECTION_FORWARD)
local ambient = tram.math.vec3(0.1, 0.1, 0.1)
tram.render.SetAmbientColor(ambient)
tram.render.SetScreenClearColor(tram.render.COLOR_BLACK)

This section will set up the scene lighting. All of these functions accept a vec3 table. The tram.math.vec3(x, y, z) function constructs a vec3 table.

-- Move the camera a bit away from the origin.
local position = tram.math.DIRECTION_FORWARD * -1.2
tram.render.SetViewPosition(position)

By default, all new 3D models get created at the origin, which is the coordinates (0.0, 0.0, 0.0). The view also starts at this position, so we move it a bit back, so that it doesn't end up in the teapot.

-- Setting up a light so that you can see something.
scene_light = tram.components.Light()
scene_light:SetColor(tram.render.COLOR_WHITE)
scene_light:SetLocation(tram.math.vec3(5.0, 5.0, 5.0))
scene_light:Init()

Since the previous bit of code set the directional light's color to zero, it won't contribute to the scene's illumination, so here we create a light, so that you can actually see the teapot.

-- Adding a teapot to the scene.
teapot = tram.components.Render()
teapot:SetModel("teapot")
teapot:Init()

Finally, we add the 3D model of the teapot to the scene.

-- This vector here will contain teapot euler angle rotation in radians.
local teapot_modifier = tram.math.vec3(0.0, 0.0, 0.0)

In order to rotate the teapot, we need to set up its initial rotation.

-- This function will be called every tick.
tram.event.AddListener(tram.event.TICK, function()
  if tram.ui.PollKeyboardKey(tram.ui.KEY_LEFT)
   or tram.ui.PollKeyboardKey(tram.ui.KEY_A) then
    teapot_modifier = teapot_modifier
	 - tram.math.vec3(0.01, 0.0, 0.0)
  end
  
  if tram.ui.PollKeyboardKey(tram.ui.KEY_RIGHT)
   or tram.ui.PollKeyboardKey(tram.ui.KEY_D) then
    teapot_modifier = teapot_modifier
	 + tram.math.vec3(0.01, 0.0, 0.0)
  end
	
  if tram.ui.PollKeyboardKey(tram.ui.KEY_UP)
   or tram.ui.PollKeyboardKey(tram.ui.KEY_W) then
    teapot_modifier = teapot_modifier
	 - tram.math.vec3(0.01, 0.0, 0.0)
  end
	
  if tram.ui.PollKeyboardKey(tram.ui.KEY_DOWN)
   or tram.ui.PollKeyboardKey(tram.ui.KEY_S) then
    teapot_modifier = teapot_modifier
	 + tram.math.vec3(0.01, 0.0, 0.0)
  end
	
  teapot:SetRotation(tram.math.quat(teapot_modifier))
end)

We need to execute the rotation logic during runtime. A good way to do it is to add an Event Listener and set it to listen to the Tick event, which is emitted at regular intervals.

The code for the logic itself will check if the arrow keys are being pressed, and if they are, it will modify the rotation of the teapot. After all of the keys are checked and a new rotation is computed, it is assigned to the teapot's 3D model.

Since all rotations use quaternions, we can pass the tram.math.quat() quaternion constructor a vec3, which will be interpreted as a vector containing euler angles, i.e. the rotations around the x, y and z axis.

Teapot coloring.


The default white teapot color is rather uninteresting. Let's change it to something more interesting.

Add this line of code somewhere after the teapot variable is initialized:

teapot:SetColor(tram.render.COLOR_PINK)

All of the color definitions are just vec3s, so we can pass them in as the colors. The only thing to remember is that each color value needs to be in the range from 0.0 to 1.0.


The teapot has been colored pink.

Exercise


Look up the hex or integer color values for your favorite color. Convert them into the range from 0.0 to 1.0 for each color channel and assign them to the teapot.

At this point, you are probably very proud of your teapot coloring skills. Press the F12 key on your keyboard. This will save a screenshot of the teapot in your project directory. Print it out and mail it to your friends!

Let's try doing something more interesting with the colors. Place these lines of code somewhere in the update function:

local r = math.sin(tram.GetTickTime())
local g = math.sin(tram.GetTickTime())
local b = math.sin(tram.GetTickTime())

local color = tram.math.vec3(r, g, b)

-- the r, g, b values are in the range
-- from -1.0 to 1.0 due to the sin()
-- function, so we need to re-map them
-- to the 0.0 to 1.0 range for colors

color = color + 1.0
color = color * 0.5

teapot:SetColor(color)


The teapot fades from white to black and then back to white.

We can make this even more interesting. Let's add a different phase shift to each of the color channels.

local r = math.sin(tram.GetTickTime() + 1.0)
local g = math.sin(tram.GetTickTime() + 2.0)
local b = math.sin(tram.GetTickTime() + 4.0)


The teapot cycles through all of the colors.

Exercise


Now that you can dynamically change a vec3 for colors, how about dynamically changing the teapot's position and rotation?

Exercise


Maybe try changing the color of the scene's light.

Exercise


The code for controlling the teapot's rotation is already set up. Maybe add the option to not only be able to change the rotation of the teapot, but also its position?