Modding, Mission Design, and Coding > The Scripting Workshop

Mini Tutorial - Create Your Own Object Types

(1/1)

Nuke:
Nuke Tutorial 1 - Create Your Own Object Types

In this tutorial i will cover hot to create a 2d vector object. Why? Because anyone making a hud will want to use primarily 2d coords. Sure, you could use the built in ba.createVector() function. but using a 3d vector, leaving the z field at some arbitrary value, only increases the math by 50% on +,-,*,/ operations on that vector, not to mention 50% more memory. Why dont we use a table (if you didnt know a table is what lua calls an array)? Well in a way we are but a regular table its just a storage point, you cant say multiply one table by another. To do so you would need to an operation on each indice. To acces the variables you would have to use an index. Such things can be cumbersome. Here are some examples of object vs table.

--- Code: ---
--declaration with a table
vec  = {100,100}

--declaration with a function
vec = vec_2d(100,100)

--varable access
vec2 = vec
vec2 = vec

vec_x = vec
vec_y = vec

--with object
vec2 = vec

vec_x = vec.x
vec_y = vec.y

--multiplying a table
vecsquare  , vecsquare = vec * vec , vec * vec

--with an object
vecsquare = vec * vec

--- End code ---

As you can see, an object will make your code easyer to write, and to read. It will also take up much less space.

To set up your own type, you need 2 things. The first thing you need is a function to create an object of the new type. The second thing you need is a metatable. Now you need both for this to work.  The metatable, contains what are called meta methods. In essence it binds certain operators to functions. This makes objects really powerfull. You could for example bind an operator to a function to normalize our vector if we wanted to. But for now your basic 4 operators will do. Ok, lets write some code, starting with our function.

--- Code: -----this is the function we will call to create a new vec_2d object, im writing it so that it can take either 2 numbers, a table, or another vec_2d

vec_2d = function(xy,ey)   --xy is your vec_2d or your first number, ey is an extra y incase the first is of type 'number' and not type 'table'
local v = {}  --remember an object is really just a table with named entries, this is the table

if getmetatable(xy) == vec_2d_mt then       --getmetatable() returns the object name or nil if its not an object
v.x = xy.x       --this is where we set up named table components
v.y = xy.y
elseif type(xy) == 'table' then    --type(), is usefull, it tells us what kind of variable were using
v.x = xy
v.y = xy
elseif type(xy) == 'number' and type(ey) == 'number' then
v.x, v.y = xy, ey        --only time where ey is used, it is an optional thing, you only need to fill it in if youre using numbers
else
v.x, v.y = 0, 0            --this is usefull if we want to create a blank object
end

setmetatable(v, vec_2d_mt)  --this function sets the metatable for this object, more on this later

return v
end

--- End code ---

That function was pretty straight forward. Note i used some built - in lua functions. in other words it will run in any lua 5.0 implementation. I havent touched a single freespace handel/function yet. Infact i wrote and tested this in a stand alone interpreter, and it works in freespace just as well.

Ok, now by itself that function wont do much. It might even crash. For it to work we need to define vec_2d_mt. Now its not a function its a table with functions in it. Look below.

--- Code: ---
vec_2d_mt = {            --this starts out just like a table
__add = function(v1,v2) return vec_2d(v1.x + v2.x, v1.y + v2.y) end,  --our add function, called when vec+vec is used
__sub = function(v1,v2) return vec_2d(v1.x - v2.x, v1.y - v2.y) end,  --our subtract function
__unm = function(v) return vec_2d(-v.x,-v.y) end,  --this one is called when -vec is used, note that theres only one argument
__mul = function(v1,v2)   --ive set multiply up to use either a number or a another vec, in any order
if type(v1) == 'number' then
return vec_2d(v1 * v2.x, v1 * v2.y)
elseif type(v2) == 'number' then
return vec_2d(v1.x * v2, v1.y * v2)
else
return vec_2d(v1.x * v2.x, v1.y * v2.y)
end
end,
__div = function(v1,v2)    ---and div is about the same, note the position of the argument corasponds with that numbers position in vec/vec
if type(v1) == 'number' then
return vec_2d(v1 / v2.x, v1 / v2.y)
elseif type(v2) == 'number' then
return vec_2d(v1.x / v2, v1.y / v2)
else
return vec_2d(v1.x / v2.x, v1.y / v2.y)
end
end
}

--- End code ---

Now the parts starting out with double underscores __, like __mul, are references to various operators, this one being for *. There are more you can use, theres a list of them here. Remember when writing that that its a table. Put commas between your functions.

And thats it. If you just want to use the type, just paste those into the game init hook. Heres some code you can put in the hud hook use to test to make sure all that stuff works.

--- Code: --- v = {5,5}
vec1 = vec_2d(100,100)
vec2 = vec_2d(v)
vec2 = vec_2d(vec2 * 5)
vec2 = vec2 / 2
vec2 = vec1 + vec2
gr.drawLine(vec1.x,vec1.y, vec2.x,vec2.y)

--- End code ---

If the code doesnt work, its probibly cause i messed it up writing the comments for the tutorial, so let me know if it dont work. Coders, if i messed up any terminology, feel free to correct me. I only figued this out yesterday. :D