Introduction to GameMonkey Script Part 2
Embedding GameMonkey
Constructor with ParametersAt present, this type is pretty useless as there is no way of accessing the data the Vector contains. The decision about how to access the data is an important one; shall we allow the user to access it via named properties (e.g.: x, y, z members) or via an array of floats. I shall answer this question shortly, but first we need a way of initialising the data on construction of the type. As in the C++ class, we want to allow the user to specify a constructor with several parameters to initialise the data in the vector as well as providing a default constructor. namespace gmVector { int GM_CDECL default_constructor( gmThread *a_thread ) { // Create a native object with default params Vector *p = new Vector(); a_thread->PushNewUser( p, Type ); return GM_OK; } /// This is the constructor for passed data items int GM_CDECL data_constructor( gmThread *a_thread ) { // Check for a valid number of parameters if (a_thread->GetNumParams() != 3 ) return GM_EXCEPTION; // Loop through and grab the params, checking their types float v[3]; for (int i = 0; i < 3; ++i) { switch (a_thread->Param(i).m_type) { case GM_INT: v[i] = a_thread->Param(i).m_value.m_int; break; case GM_FLOAT: v[i] = a_thread->Param(i).m_value.m_float; break; default: a_thread->GetMachine()->GetLog().LogEntry( "Vector: Param %d error - expected int or float", i ); return GM_EXCEPTION; } } // Create a native object with default params Vector *p = new Vector( v[0], v[1], v[2] ); // Return to GM a_thread->PushNewUser( p, Type ); return GM_OK; } /// Entry point for the library; this is effectively the constructor int GM_CDECL libentry( gmThread *a_thread ) { // Delegate the appropriate call based on the arg count switch (a_thread->GetNumParams()) { case 0: return default_constructor( a_thread ); case 3: return data_constructor( a_thread ); }; // Not handled, log an error and return an exception a_thread->GetMachine()->GetLog().LogEntry( "Vector: Bad number of parameters passed" ); return GM_EXCEPTION; } }; // end namespace gmVector
I have adjusted the libentry function to delegate the actual constructor operation based on the number of parameters passed. The default_constructor function is the same as the previous libentry function, but in data_constructor you can see that I engage in an operation to retrieve the parameters from the thread. This is exactly the same as if you were binding a regular function, so I won't dwell on how it works. The new constructor validates that the parameters are either integers or floats and stores them for passing to the native Vector class' constructor. The data constructor could be extended to allow for copy construction of objects, allowing you to pass a variable of your Vector type and use it to create a copy of itself. This is important to remember as GameMonkey Script user-bound variables are reference types; if you assign a variable with another they will reference each other and any changes made to one will be visible in the other. As a learning exercise, I will leave the addition of the copy constructor as an extension for you to pursue on your own - you should be able to code it yourself with the information I've given you so far. If you get stuck you may refer to example vector_2a.cpp for my implementation. Now that there's some actual data in the vector object you need to allow scripts to retrieve it for use in other operations. It is at this point that we must decide the data access method; I have chosen to provide the members x, y z as this is familiar to many people and I feel best represents how I will use the vector in script. As I said previously, the decision is entirely yours and the methods I will describe can be adapted to use the indexed access method if you choose. |