3D-Studio 3.1 exporter
gMax Exporter
Named Hierarchy Browser
nmax for RL SDK
Quake 2 Interpolator
Diverse Jme Mangalore Nebula Project1 Project2 Tabletop

Moving on terrain

This tutorial was based on nebula 1 and might be quite out of date by now.

In this article I will present a simple means to move objects on a nflatterrainnode. The example will be kept simple and presented as a tcl script. The steps will go by:

  • creating the input mapping
  • designing the scene
  • explaining the move
  • creating the height script
  • creating the move scripts
  • talking about improvements
You would perhaps do it in a different order, and you are free to do so.

First of all I need some kind of input mapping, so you will know when to move your object in which direction. For this purpose I use the input server together with script events (if you need more information about script event take a look at the nInputserver article). So let us create a mapping for forward, backward, left and right turn, etc.

Example of a tcl script event mapping

        sel /sys/servers/input


          .map mouse0:btn1.pressed orbit

          .map mouse0:btn0.pressed pan

          .map keyb0:shift.pressed dolly

          .map keyb0:up.pressed "script:fo"

          .map keyb0:down.pressed "script:aft"

          .map keyb0:left.pressed "script:left"

          .map keyb0:right.pressed "script:right"




Back on top...

Please remember that a new mappping overwrites your old mappings, so add any additional scripts you use (I added the pan, dolly and orbit state events for the nobserver for example).
Now that I have the input mapping I lay out the scenery. My scene consists of a n3dnode for the terrain called grid1 (taken directly from the flatterain tcl example) and a n3dnode for my player object. I removed the translation and rotation of the flatterain grid to keep it simple.

Part of the scene layout, parts ommited

        sel /usr/scene

        new n3dnode player

          sel player

          # txyz setting can be left out

          .txyz 0.0 0.4 0.0


          # define your data for the player model ...


          sel ..


        new n3dnode grid1

          sel grid1

          # can also be ommited

          .txyz 0.0 0.0 0.0


          # define the data for flatterain


          sel ..


To be honest my model has its center 40 centimeters above the feet, so I placed it 0.4 unit higher. you can do this to adjust your models too. If you model's local 0/0/0/ coordinte (local origin) lies between the feet of the model you do not need this adjustment.
But this is a good point to start talking about the coordinate system. Imagine the origin of your coordinate systen lies directly in front of you. So x will extend from your origin to the right, y will be going up and z will be further to you front. So if you are standing on the floor the floor will be on the x/z plane. But our world is not a plane but rough terrain, so y will mark the height on this terrain.


Back on top...

So how will the model move ? One part is to get te height above the terrain from nflatterrainnode, the second part is to compute the movement along the plane. Let's start with the height above terrain.
For this purpose we create a tcl procedure called honter (height on terrain) which is placed before the input mapping. I like placing procedures at the top of my files and the nebula commands below. The basic working is this: Get the model's x/z coordinates, query the nflatterrainnode for the height at position x/z, return the height value.

Example of tcl code to get height from nflatttainnode


        #honter returns the height at position of player

        proc honter { } {

          set con [expr  65535 / [/usr/scene/grid1/ter.getradius]  ]

          set wrld [ /usr/scene/player.gett ]

          set lx [ expr $con * [lindex $wrld 0]]

          set lz [ expr $con * [lindex $wrld 2]]

          set ly [ /usr/scene/grid1/ter.getheight $lx $lz]

          set wrldy [expr $ly / $con]

          return $wrldy



  • set con [expr 65535 / [/usr/scene/grid1/ter.getradius] ]
    this line computes a conversion value stored in variable con. con = 65535 / terrain radius

  • set wrld [ /usr/scene/player.gett ]
    variable wrld will store the x/y/z coordinates from the model

  • set lx [ expr $con * [lindex $wrld 0]]
    lx (local x) will store the x coordinate contained in variable wrld

  • set lz [ expr $con * [lindex $wrld 2]]
    lz (local z) will store the z coordinate contained in variable wrld

  • set ly [ /usr/scene/grid1/ter.getheight $lx $lz]
    this call will get the height at location x/z on the terrain. you can look it up in the documentation of the nflatterrain commands

  • set wrldy [expr $ly / $con]
    this is a necessary conversion to real world values and will be stored in wrldy. con = height / con

  • return $wrldy
    the last instruction will return the height value from the honter function


Back on top...

This is the first step to get a model moving on terrain. The next part needs little bit of trigonometry, but it will be very simple. Take a look at the image below.

sinus and cosinus

Now imagine you are the model and this page is the plane you are standing on. Your location is right in the middle of the circle, that means at x=0 and z=0. The circle is a unit circle, that means its radius is 1. Further imagine you are looking right at the x-axis so your shoulders are aligned along the z-axis. To move forward you would just need to move on along the x-axis (increase your models x value). To move backward you would move back on the x-axis (decrease your models x value).
The case gets a little bit complicated when you are not facing any axis but as it is layed out on the image above. If you are looking along the fat black line you see that the x (the red line) and z values (the green line) to reach the point on the circle are uneven values. That's where trigonometry get in. The cosinus function returns the x value we need to move on and the sinus function returns the z value we will move on. The only thing needed is the angle we are facing.

But this is not the whole truth. You have to flip the sign for your sinus value to get the correct result, otherwise your model will turn left but walk right. The following code moves the player forward.

Example code to move an object to the front

        #tcl procedure to move a model to the direction it is facing

        proc fo { } {

          set ly [lindex [ /usr/scene/player.getr ] 1]

          set lx [lindex [ /usr/scene/player.gett ] 0]

          set lz [lindex [ /usr/scene/player.gett ] 2]

          set ly [expr (0.0174532925 * $ly)]

          set lx [expr $lx + cos($ly)/10]

          set lz [expr $lz - sin($ly)/10]

          /usr/scene/player.tx $lx

          /usr/scene/ $lz

          set ht [ honter ]

          /usr/scene/player.ty $ht




Back on top...

  • set ly [lindex [ /usr/scene/player.getr ] 1]
    getr returns a list of rotation coordinates in degrees. I just want the middle of the three values, the rotation around the y-axis. This value is stored in ly.
  • set lx [lindex [ /usr/scene/player.gett ] 0]
    gett returns a list of translation coordiates. I want the first of the three values, the x translation. The value is sstored in lx.
  • set lz [lindex [ /usr/scene/player.gett ] 2]
    Same as above but now the z translation which is sstored in lz.
  • set ly [expr (0.0174532925 * $ly)]
    ly is in degrees, but the tcl functions sin/coss want radians so I convert degrees to radians.
  • set lx [expr $lx + cos($ly)/10]
    Here is the motion. As the move function gets called several times, I divide the value by 10.
  • set lz [expr $lz - sin($ly)/10]
    Same as above. Notice the flip of sign.
  • /usr/scene/player.tx $lx
    Assign the player the new position data for x.
  • /usr/scene/ $lz
    Assign the player the new position data for z.
  • set ht [ honter ]
    Now I call the height on terrain function to get the height at my current position.
  • /usr/scene/player.ty $ht
    Assign the player the adjusted height value.

To move the player backward just flip the signs of the sin and cos functions from the example above. Another thing you might be interrested in is the code to let your model turn. It is just adjusting the rotation along the y-axis. To turn left I increase the rotation by 3 degrees, to turn right I decrease the ry value of the model by 3 degrees. One little adjustment I additionally do is to subtract 360 degrees if the rotation is above 360 and add 360 if the rotation value is lower than -360.

Example of a tcl script to rotate a model to the right.

        # rotate the model to the right

        proc right { } {

          set ly [lindex [ /usr/scene/m1/mesh1.getr ] 1]

          if { $ly > 360 } {

            set ly [expr $ly - 360]


          if { $ly < -360 } {

            set ly [expr $ly + 360]


          /usr/scene/m1/mesh1.ry [expr $ly - 3]



The following picture shows the result with the quake 2 interpolator model.

Image of a Quake 2 model on a terrain slope


Back on top...

So if you have the basic movement going it is time for improvements. One thing is to improve the script calls, many calls are redundant, or can be optimized. The next thing is the parametrization. The code shown above will only move the player model, it would be much cooler to pass the path of the n3dnode and perform the move on the passed n3dnode. The current code only moves the model, but you must create the illusion of motion. To reach this you should trigger a walk animation when moving. And how about strafe movements.... but that's all let up to you, not for my article, and if there should be any open questions drop me a mail.



Back to Table of Contents.

Something missing? Mail to cc

WoW Importer for Max
The World of Warcraft tool for 3D Studio has been updated. It now converts model files from WoW Client version 2.x (upto 2.7) and displays correct animations for multi mesh models. The script can be found here....
nGUI explained
If you ever wanted some more details on the nebula2 nGUI System you can find it in the nGUI System article.
Mangalore entity ID's
If you need information about the mangalore entity ID usage have a look here..
Mangalore Articles
Added a new section about the mangalore game framework from radonlabs. The section contains some articles about my experience with mangalore. Read more here:
Free models
Finally some free models for the Radonlabs SDK. You can download them here.