Jump to content

  • Log In with Google      Sign In   
  • Create Account


Like
0Likes
Dislike

Polymorphism in Angelscript

By Xavier Shay | Published Nov 05 2004 11:05 AM in Game Programming

void angelscript script type function functions class scripting language scripting
If you find this article contains errors or problems rendering it unreadable (missing images or files, mangled code, improper text formatting, etc) please contact the editor so corrections can be made. Thank you for helping us improve this resource

Summary

Natively, Angelscript does not support inheritance. This article shows a method that can be used with Angelscript that allows virtual function
overrides to be written in Angelscript, effectively allowing you to create derived classes in script.


It is assumed the reader is familiar with the basics of Angelscript, and can register objects and functions with the Angelscript engine.


This article was written for Angelscript v1.7. Proper support for objects is planned for later versions, so with any luck this article will one day become obsolete.


As good a place as any

In our game we have a player that can collide with items. Each item will affect the player in a different way. For simplicity, our player class will only have one variable: health. We could
conceivably use the following classes:



// C++

class CPlayer {

public:

  float health;

};



class CItem {

public:

  virtual void collidePlayer(CPlayer *p);

};


In C++ it would be trivial to derive classes from CItem for each item type. But say we wanted the item behaviours to be defined in script. We are unable to use derived classes, so we must find
another method.


Let's work backwards and start with what we would like the script to look like:



// Angelscript

bool item_init() {

  registerItemType("health");

  return true;

}



void health_collidePlayer(CPlayer *p) {

  p->health += 15;

}


That looks peachy. Obviously, we will need a static variable to hold the item types, and a member variable to hold the type of the item. The collidePlayer function will look at the itemType and
call the appropriate function in script. Note that it is not actually necessary to register the item types, but this allows us to implement some sort of error checking (ie. Forbid creation of
non-existent item types).



// C++

class CItem {

public:

  static void registerItem(const char *itemName);

  static string itemTypes;

  

  string type;  

  

  virtual void collidePlayer(CPlayer *p);

}



void CItem::registerItem(const char *itemName) {

  CItem::itemNames.push_back(itemName);

}



void CItem::collidePlayer(CPlayer *p) {

  string func;

  list<asdword *="*"> args;



  CItem *thisPtr;

  

  thisPtr = this;

  func = itemType + "_collidePlayer";  // Function to call

  

  args.push_back((asDWORD *)&thisPtr);

  args.push_back((asDWORD *)&p);

  eng.callScript(func, &args);

}



CItem::registerItem("health"); // Or we could just call the item_init() function in script



a = new CItem("health"); // See example code for this constructor

p = new CPlayer;



p->health = 100;



a->collidePlayer(p);  // p->health will now be 115


Members Eat Free

That's all well and great, but Bob the stingy level designer desires to add health items that only give 5 health. He could create a new item type, but Bob also happens to be lazy. If the health
item could have a member variable "amount", Bob could create as many different health types as pleased him without ever having to create an item type of his own.


For this we are going to use an STL map. In the item type constructor, it will "register" its member variables, similar to how Javascript objects work. For simplicity, we will just allow variables
of type "float".



// Angelscript



// Called from the CItem constructor

void health(CItem *this) {

  this->registerFloat("health");

}



void health_collidePlayer(CItem *this, CPlayer *p) {

  p->health += this->getFloat("health");

}



// C++

class CItem {

public:

  /* as before */

  

  map<string, float="float"> vars;

  

  // See sample code for implementations of the following functions

  void registerFloat(const char *varname);    // Adds key to vars

  void setFloat(const char *varname, float value);

  float getFloat(const char *varname);

}



CItem::registerItem("health"); // Or we could just call the item_init() function in script



a = new CItem("health"); // See example code for this constructor

p = new CPlayer;



a->setFloat("health", 10);

p->health = 100;



a->collidePlayer(a, p);  // p->health will now be 110


Note that now we are using object specific variables, we must now pass a "this" pointer to our functions so that we can access member functions.


Stepping Stones

  • A handy byproduct of this method is that if we want to create an item type in C++, we only need to override all the functions that would normally call script functions, and the interface will beexactly the same, so we can mix and match at whim.
  • Angelscript cannot call virtual functions, so if you are planning on calling collidePlayer from script, you will need to write a wrapper function that simply calls this->collidePlayer().
  • Support for different types of member variables could possibly be generalised with the use of a Dator class.

Have a look at the sample code for additional insights. A MSVC 6.0 project file is included, but it should be portable. You will require
the angelscript library.


Xavier Shay
spam@noreality.net






Comments

Note: Please offer only positive, constructive comments - we are looking to promote a positive atmosphere where collaboration is valued above all else.




PARTNERS