IntroductionCryon is a scripting language designed for game developers.It's aim is to make game development simpler and faster. It is object oriented and its syntax is inspired from JAVA and C#. The scripting engine runs on Windows and several Unixes, but currently, the graphics libraries relies on DirectX and works only on Windows. A JAVA, C# or C++ knowledge is necessary to read this document. Language features- java-like syntax- value and reference objects - typesafe enums - static/dynamic arrays - associative arrays - in/out/inout parameters - easy operators overloading - javadoc style autodocumentation - inheritance - access limitation - methods affectation - conditional compilation - easy localization - easy game packaging - extendable |
Java-like syntaxThe Cryon language looks familiar to Java, C++ and C# developers :Example of class declaration : class CTest { protected int i; public void inc() { i = i +1 ;} }There is no non-object 'intrinsic' types. All types are actual structs and classes > int i=3; > i.print(); 3There is no 'new' operator. Objects are created by calling directly the constructor : class CInt { protected int i; public CInt( int val ) { i = val; } // constructor } CInt myInt = CInt(4); // myInt.i = 4 CInt myInt2(4); // same resultBlock comments can be nested : /* outer comment block class MyClass { /* inner comment block ... */ } */ Value and reference objectsValue objects means that the variable represents the object value.Declaring the variable creates the object. Value objects are declared with struct keyword. struct complex { float a; float b; void print() { string s=a+" + "+b+" i "; s.print(); } } > complex c; // object created here > c.print(); 0 + 0 i > complex d=c; // d takes the same value as c > d.a=3.14; // modifying d doesn't affect c > d.print(); 3.14 + 0 i > c.print(); 0 + 0 iReference objects means that the variable represents a reference on the object. Declaring the variable doesn't create the object. A constructor has to be called. The object is destroyed as soon as there is no more references on it. class CInt { int i; } > CInt a; // uninitialized reference > a.print(); line 1 Interpreter error : cannot access member 'print' on an undefined object > a = CInt(); // call a constructor and get a reference on the object created > a.print(); { i=0; } > CInt b = a; // b is another reference on the same object > b.i=3; > a.print(); // modifying b affects a { i=3; } Typesafe enumsEnums are collections of int, string or float values.They are used when some method parameter need to restrict possible values. Enums behave like classes with static members. By default, enums are integers starting with 0 : > enum EGunType { LASER, PLASMA, PROTON } > EGunType g = EGunType.LASER; > g.print(); EGunType.LASER > EGunType.PROTON.print(); EGunType.PROTONBut we can also enumerate strings or floats : > enum EGunName extends string { LASER="laser gun", PLASMA="plasma cannon", PROTON="proton torpedo" }Enums automatically have a constructor with their base type. The constructor checks illegal values : > EGunType g = EGunType(1); // g is set to EGunType.PLASMA > EGunName n = EGunName("proton torpedo"); > EGunType g = 12; line 1 Interpreter error : cannot convert int to EGunType g=12; ====^ > EGunType g = EGunType(12); line 2 Interpreter error : cannot convert int value '12' to enum EGunType g = EGunType(12); ================^ Static and dynamic arraysArrays are reference objects. You must call a constructor to build them.Static arrays have a fixed size : > int[3] tab = int[3](); // or int[3] tab(); > tab[2]=5; > tab.print(); { 0, 0, 5} > tab[5]=12; line 1 Interpreter error : cannot access element 5 on int[3] array.Dynamic arrays can grow during execution : > int[] tab2(); > tab2[0]=5; > tab2.size().print(); 1 > tab2[2]=7; > tab2.print(); { 5, 0, 7} > tab2.size().print(); 3You can also use dynamic arrays as stacks : > int[] tab3(); > tab3.push(7); > tab3.top().print(); 7 > tab3.pop().print(); 7 > tab3.isEmpty().print(); trueYou can loop through elements of an array with the foreach/index keywords : foreach (int value in tab2) { string s = "tab2 [ " + index + " ] = " + value; s.print(); } Associative arraysAssociative array have an index which is not an int :> int[string] tab4(); > tab4["key1"] = 8; > tab4["key2"] = 10; > tab4["key1"].print(); 8Any struct or class can be used as an index. You can use an associative array as a map : > tab4.put("key3",12); > tab4.get("key2").print(); 10 in/out/inout parametersBy default, all method parameters are 'in' parameters.Objects used as 'in' parameters cannot be modified by the method. Parameters can also be 'out' or 'inout'. 'out' parameters are values returned by the method. 'inout' parameters are 'in' parameters which are modified by the method. 'out' or 'inout' keyword must be specified when the method is called. class CTest { static void func(int i) { i.print(); } // same as void func(in int i) static void func2( out int i ) { i = 4; } static void func3(inout i) { i.print(); i = 4; } } > CTest.func(4); 4 > CTest.func2(5); line 1 Interpreter error : no method void func2(int) in class CTest > CTest.func2(out 5); line 1 Interpreter error : cannot use value 5 as out parameter > int j=7; CTest.func2(out j); j.print(); 4 > j=7; CTest.func3(inout j); 7 > j.print(); 4 Easy operators overloadingOperators are static methods with one or two parameters.Unary operators : struct complex { float a; float b; static complex operator - (complex c) { complex res; res.a = - c.a; res.b = - c.b; return res; } } > complex c={1.0,2.0}; > complex d = -c; // unary - operator used > d.print(); { a = -1; b = -2; }Binary operators : static complex operator - (complex c1, complex c2) { complex res; res.a = c1.a - c2.a; res.b = c1.b - c2.b; return res; } > complex e = c - d; > e.print(); { a = 2; b = 4; } Javadoc-style autodocumentationComments can be put inside code to generate a HTML class reference.Class definitions with autodocumentation can also be viewed inside the interpreter. Classes, fields and methods can be documented. Comments use no special tags. The class reference from this documentation has been generated this way. /** class description */ class CMyClass { /** field description */ int myField; /** method description */ void myFunc() {} } > CSystem.printClass("CMyClass"); /** class description */ class CMyClass extends object { /** field description */ public int myField; // public void print(); ( inherited from object ) // static public string operator +( object leftOper, string rightOper ); ( inherited from object ) /** method description */ public void myFunc(); } InheritanceStructs (or classes) can extend one (and only one) other struct (or classe).Classes cannot extend struct and structs cannot extend classes. /** star at a given position */ class CStar { vector pos; } /** moving star */ class CMovingStar extends CStar { vector speed; void update() { pos = pos + speed; } }Note on inheritance and affectation : you can affect a descendant class to one of its base classes reference. This code : CStar star = CMovingStar();is correct, since a moving star is a star. Yet, this code : CMovingStar star = CStar();will fail because a star is not necessary a moving star. access limitationBy default, all fields and methods are public.A protected field or method is only visible by current class and its descendants. A private field or method is only visible by current class. A readonly field can only be modified by current class and its descendants. struct CAccess { int i; // public field. same as 'public int i;' readonly int j; // readonly field protected int k; // protected field private int l; // private field } > CAccess ac; > ac.i=3; // ok > ac.j.print(); // ok 0 > ac.j=4; // error cannot modify readonly field line 2 Interpreter error : cannot change readonly field CAccess.j. ac.j=4; // error cannot modify readonly field ====^ > ac.k.print(); // error cannot access protected field line 1 Interpreter error : cannot access protected or private field k in class CAccess ac.k.print(); // error cannot access protected field ===^ > ac.l.print(); // error cannot access private field line 1 Interpreter error : cannot access protected or private field l in class CAccess ac.l.print(); // error cannot access private field ===^ struct CDesc extends CAccess { void func() { i=3; // ok j=4; // ok k=5; // ok l=6; // error cannot access father's private field } } line 8 Compiler error : cannot acces father's private field l l=6; // error cannot access father's private field abstract, static and final classesAn abstract class cannot extend a non abstract class.Abstract classes may or may not contain abstract methods. Objects cannot be created from abstract classes. Arrays of abstract class can be created. abstract class CWeapon { abstract void impact( CShip sh ); } class CLaserGun extends CWeapon { void impact( CShip sh ) { if ( sh.shield > 0 ) sh.shield = sh.shield - 5; else sh.hull = sh.hull - 5; } } class CMissile extends CWeapon { void impact( CShip sh ) { sh.hull = sh.hull - 10; } } > CWeapon[] weapons(); > weapons[0] = CLaserGun(); > weapons[1] = CMissile(); > weapons[0].impact(myShip); weapons[1].impact(myShip);A static class contains only static fields. Thus, the keyword static can be omitted inside the class declaration. A non static class cannot extend a static class. A static class cannot extend a non static class. static class CUniverse { void update() { ... } void draw() { ... } } > CUniverse.update();A final class cannot be extended. Methods affectationIt is sometimes easier to change an object's method than creating a new class.In standard object oriented languages, you have to create an abstract class and as many extending classes as needed. In Cryon, an object method can be redefined at runtime. Note that method affectation, as other affectations, ends with a ";" class CEventListener { // this is an empty method, not ABSTRACT ! void handleEvent( int event ) {} } CEventListener myListener(); myListener.handleEvent(int event) = { event.print(); };Methods can also be affected inside other methods : static class CListenerHandler { void setPrinter( CEventListener lis ) { lis.handleEvent(int event) = { event.print(); }; } } > CListenerHandler.setPrinter(myListener); Conditional compilationYou can enable/disable features in your code with the version/ifversion keywords :// uncomment to enable debug // version("debug"); ifversion("debug") { class DebugLoger { ... } } class MyClass { ifversion("debug") { void debugFunc() { ... } } void func () { ifversion("debug") { ... debug code ... } } }You can regroup versions flags in complex versions with defversion : // version definitions defversion("commercial") { version("canCompile"); version("canDebug"); version("canProfile"); } defversion("trial") { version("canCompile"); } defversion("pro") { version("commercial"); version("canCrypt"); } // enable one version version("trial"); // conditional code ifversion("canCompile") { ... } ifversion("trial") { ... } Easy localizationThe cryon language does not contains a 'char' type.Strings litteral values can use double-quotes or single-quote : string s1='localizable string'; string s2="constant string";If you don't envisage to localize your program, use double-quotes everywhere. If you want to localize the program, you have to take care of the syntax. Any string that will be translated must use single-quotes. You can see the list of localizable strings by typing this command in the console : > CLocale.printStrings(); { "localizable string" };Note that s2 has not been registered as a localizable string. To add the support to another language, first create a template with following command : CLocale.saveStrings("fr.cs","CFrenchTable","francais");The first parameter is the name of the .cs file to be generated. The second is the name of the class to be created in this file. The third is the user-readable language name. This will generate a fr.cs file. All you have to do is to translate the strings inside. For the string s1 previously declared, the file would look like this : class CFrenchTable { static void main(string[] args) { string[] CFrenchTable_table= { "localizable string" }; CLocale.addStringTable(CFrenchTable_table,"francais"); } }To add french support to the program, just replace "localizable string" by "chaine traductible" in this file. Now we can add french support by using the new fr.cs file : using "fr.cs";We can switch from one language to another with CLocale.setLanguage method : > s1.print(); localizable string > CLocale.setLanguage("francais"); > s1.print(); chaine traductibleIf you want to be able to add other languages without modifying your program, put all languages .cs files in a directory (for example scripts/lang) and just put using "script/lang";somewhere in your program. This will parse all .cs files in the script/lang directory. You can get a list of available languages with string[] CLocale.getLanguages() Easy game packagingYou may not want your game distribution to contain lots of small .cs files.To avoid this, the Cryon virtual machine allows you to put your files in an archive in the Cryon main directory. Supported archive formats are : - .tgz (.tar.gz) - .tar - .zip Any file inside thoses archives are accessible as if they were actually on the filesystem. ExtendableNative (C++) classes from external dll can be added to the Cryon language.All Cryon classes, including the base structs (int, float, string, bool) are created using this extension mechanism. There are currently four libraries : - sclib : scripting engine + base classes - graphlib : 3D rendering classes - guilib : user controls + toolkit (buttons, scrollbar, ...) - dblib : lightweight database library based on SQLite 3 Some others are in development or planned : - soundlib : as it sounds! - ailib : artificial intelligence agents library - physlib : physical engine (collision detection, cinematics) - netlib : multiplayer + network utilities |