Dr. Computer Science Dept. CSUS Overview Scripting Concepts Script Interpreters ( Engines ) Scripting Languages o JavaScript Basics Communicating with Scripts Using Scripts in Games Additional Scripting Engines 2
Scripting Using external code to alter game world structure or game play Common scripting languages : o JavaScript, Python, Lua o Others (Tcl, Scheme, Ruby, SmallTalk, VB ) Scripts must have access to game objects o Or at least, to a API Requires embedding an interpreter in game/game engine 3 Script Interpreters Game Engine Game Structures Internal Script Text Script Interpreter* Rhino (JavaScript) Jython (Python) LuaJ (Lua) External Script Files Queries & Modifications *Also known as a Script Engine 4
Using Script Engines import javax.script.scriptengine; import javax.script.scriptenginefactory; import javax.script.scriptenginemanager; import javax.script.scriptexception; /** This program lists installed the script engines, then fetches the JavaScript * engine and uses it to execute ( evaluate ) a simple one-line JavaScript program.*/ public class EnumerateScriptingEngines { public static void main(string[] args) { //get the Java script engine manager ScriptEngineManager factory = new ScriptEngineManager(); //get a list of the script engines on this platform List<ScriptEngineFactory> list = factory.getenginefactories(); System.out.println("Script Engine Factories found:"); for (ScriptEngineFactory f : list) { System.out.println(" Name = " + f.getenginename() + " language = " + f.getlanguagename() + " extensions = " + f.getextensions()); //get the JavaScript engine ScriptEngine jsengine = factory.getenginebyname("js"); //run a JavaScript program jsengine.eval("println ('Hello javascript') "); catch (ScriptException e) { System.out.println(e); 5 Executing Script Files // This code snippet gets a JavaScript engine and uses it to execute an external script ScriptEngineManager factory = new ScriptEngineManager(); ScriptEngine jsengine = factory.getenginebyname("js"); String scriptfilename = hello.js ; executescript(jsengine, scriptfilename);... //this method executes a JavaScript program from a file private void executescript(scriptengine engine, String scriptfilename) { FileReader filereader = new FileReader(scriptFileName) ; engine.eval(filereader); //execute all the script statements in the file filereader.close(); catch (FileNotFoundException e1) { System.out.println(scriptFileName + not found + e1); catch (IOException e2) { System.out.println( IO problem with + scriptfilename + e2); catch (ScriptException e3) { System.out.println( ScriptException in + scriptfilename + e3); catch (NullPointerException e4) { System.out.println ( Null ptr exception reading + scriptfilename + e4); ======================================== Contents of file hello.js : println( hello world ); 6
Comments JavaScript Basics Same as Java: // or /* */ (no JavaDoc /** */ form) Variables Declared with var (optional) Either global or local (inside a function) no class scope Syntax: same as Java (e.g. start with letter or _ ) o Cannot use reserved words (most Java reserved words, plus others) Typeless type determined by assigned value var i = 8; //i is an int var pi = 3.14159; //pi is a real var j = Hello ; //j is a string var k = 42 + is the answer ; //k is also a string var m = i < 10; //m is a Boolean = true 7 JavaScript Basics (cont.) Operators: same as Java (+, -, *, /, %, ==,!=, <, >, <=, >=, &&,,!, =) Control statements: if (cond) {... else {... for (var i=0; i<3; i++) {... while (cond) {...... catch(e){... // same as Java // almost Java // same as Java // same as Java Functions Global scope by default Defined with keyword function 8
Communicating With Scripts The Java Scripting API: javax.script.* Allows Java to: Pass data into a script: engine.put() Get data back from a script: engine.get() o Scripts can assign values to vars accessible by Java o Scripts also have a return value Allows scripts to: Get data from Java Pass data to Java Invoke methods in Java objects 9 Java/Script Communication Java code: 1 Script Engine int count=3; engine.put( count,count); int [] vals = {10,20,30; engine.put( vals, vals); Name Value count 3 vals ref FileReader fr = new FileReader( sums.js ); boolean result = (Boolean)engine.eval(fr); 2 sum 60 5 4 double sum = (Double) engine.get( sum ); System.out.println( Java + sum = + sum ) ; 3 6 JavaScript in file sums.js : println( count = ' + count); var sum = 0; for (var i=0; i<count; i++) { sum += vals[i]; println( JS sum = " + sum); sum > 50 ; //script return value 10
Gameworld Creation Script // CreateWorld.js -- a JavaScript file to create a game world scenegraph and // return it to the Java code //provide the script with access to Java & Sage classes importclass(packages.java.awt.color); importclass(packages.sage.scene.group); importclass(packages.sage.scene.shape.teapot); importclass(packages.sage.scene.shape.pyramid); importclass(packages.sage.scene.shape.line); importclass(packages.graphicslib3d.point3d); var rootnode = new Group(); //construct an (empty) scenegraph named rootnode var t1 = new Teapot(); //create a teapot and add it to the scenegraph t1.setcolor(java.awt.color.green); rootnode.addchild(t1); var p1 = new Pyramid(); //create a pyramid and add it to the scenegraph p1.translate(3,0,0); rootnode.addchild(p1); var origin = new Point3D(0,0,0); //define and add axes to the world var xend = new Point3D(20,0,0); var yend = new Point3D(0,20,0); var zend = new Point3D(0,0,20); var xaxis = new Line(origin, xend, Color.red, 3); rootnode.addchild(xaxis); var yaxis = new Line(origin, yend, Color.green, 3); rootnode.addchild(yaxis); var zaxis = new Line(origin, zend, Color.blue, 3); rootnode.addchild(zaxis); 11 Using the CreateWorld Script /** This demonstrates how to invoke an external JavaScript to construct a Game World. */ public class DemoInitWorld extends BaseGame { private SceneNode rootnode; // the root of the game world private String scriptname = "CreateWorld.js"; //the script to create the gameworld private File scriptfile; //the script file object public void initgame() { //...code here to create a script engine as before... scriptfile = new File(scriptName); // use the engine to execute the script FileReader fr = new FileReader(scriptFile); engine.eval(fr); fr.close(); catch (...) {... // get the scenegraph created by the script from the engine rootnode = (SceneNode) engine.get("rootnode"); addgameworldobject(rootnode); //...code here for other initialization, e.g. camera position, input hndlrs, etc. //other methods (e.g. update()) here 12
Dynamic Script Updating protected void update(float time) { // check if the script has been modified long modtime = scriptfile.lastmodified(); if (modtime > filelastmodifiedtime) { // yes it's been modified; re-read it filelastmodifiedtime = modtime; fr = new FileReader(scriptName); engine.eval(fr); fr.close(); catch (...) {... //replace the current scenegraph with the new one from the engine removegameworldobject(rootnode); rootnode = (SceneNode) engine.get("rootnode"); addgameworldobject(rootnode); //other update() steps here as usual, including the following: rootnode.updategeometricstate(time,true); // = super.update(time); 13 /** This demonstrates how game code can instantly respond to dynamic updates to scripts */ public class DemoInitWorld extends BaseGame { private SceneNode rootnode; // the root of the game world private String scriptname = "CreateWorld.js"; // the script to create the gameworld private File scriptfile; //the script file object private long filelastmodifiedtime = 0 ; //new protected void initgame() { //code here to read the script and save the rootnode it creates, as before Invoking Script Functions Define script function Load function into engine (using eval()) Cast engine as an Invocable object Use invokefunction() to call function Java code: FileReader fr = new FileReader( sayhello.js ); engine.eval(fr); //load script function //make the engine invocable Invocable invocableengine = (Invocable) engine ; //define argument to be passed to the function Object [] arg = { Rufus ; //invoke the function in the engine invocableengine.invokefunction( sayhello,arg); catch (NoSuchMethodException e1) {... catch (ScriptException e2) {... 14 JavaScript in file sayhello.js : function sayhello(name){ println("hello " + name); ScriptEngine f1( ) { f2( ) {
Example: Character Updating //File UpdateCharacter.js: a JavaScript function to apply updates to a game character function updatecharacter(character) { character.setcolor(java.awt.color.green); /** This class demonstrates how to use JavaScript functions to apply changes to * GameWorld nodes. It attaches a JavaScript function to a keyboard action; * hitting the SPACE key invokes the corresponding JavaScript function. */ public class DemoNodeChangingFunctions extends BaseGame { private SceneNode rootnode; // the root of the game world, defined by script private ScriptEngine engine; protected void initgame() { //code here to create a ScriptEngine, load script files, register them // (i.e., execute them using eval()), save the script names, and initialize // the script-created rootnode... initgameactions(); //other game-specific initialization here as necessary... protected void update(float time) { // code here to check each script by name to see if it has been modified, // reexecute it if so, and reinitialize the rootnode if needed (i.e., if the // CreateWorld.js script was one of the ones that was modified)... // code here for other updates as needed... rootnode.updategeometricstate(time,true); //continued... 15 // = super.update(time); Character Updating Ex. (cont.) //continued... private void initgameactions() { IInputManager im = getinputmanager(); //get InputMgr from parent class String kbname = im.getkeyboardname(); //build an action for updating a character IAction updatecharacter = new UpdateCharacterAction(); im.associateaction(kbname, Identifier.Key.SPACE, updatecharacter, pressonly); //an Action which invokes a script function private class UpdateCharacterAction extends AbstractInputAction { public void performaction(float time, Event e) { //cast the engine so it supports invoking functions Invocable invocableengine = (Invocable) engine ; //get the node which is to be updated SceneNode player = ((Group) rootnode).getchild("player1"); //invoke the updatecharacter script function on the player invocableengine.invokefunction("updatecharacter", player); catch (...) {... //end class 16
Using Other Script Engines //get the Lua engine ScriptEngine luaengine = factory.getenginebyextension(".lua"); //insert variable x with value 25 luaengine.put("x", 25); //run a Lua script to compute a function of x luaengine.eval("y = math.sqrt(x)"); catch (ScriptException e) { System.out.println(e); System.out.println("Hello Lua: " + "y=" + luaengine.get("y")); // construct a Python script String lsep = System.getProperty("line.separator"); String a = "import sys" + lsep; a += "import java.io as javaio" + lsep; a += "currentdir = javaio.file (\".\")" + lsep; a += "print \"Hello Python: Current directory : \" + currentdir.getcanonicalpath()" + lsep; //get the Python engine (Jython) ScriptEngine pythonengine = factory.getenginebyextension("py"); //run the Python script pythonengine.eval(a); catch (ScriptException e) { System.out.println(e); 17 Add l JavaScript Features Arrays Size defined by parentheses at declaration var foo = new Array(10); var bar = new Array(5); Indexing from zero and using brackets (like Java) foo[0] = 42; bar[4] = 99.9; Mixed element types allowed var stuff = new Array ( a string, 12, 98.6, true); Dynamically resizeable var colors = new Array(); colors[2] = red ; //colors has no elements //colors now has 3 elements // ( [0] and [1] == null ) Properties and methods length, indexof(), concat(), tostring(),... 18
Add l JavaScript Features (cont.) Built-in Objects : var currenttime = new Date(); var month = currenttime.getmonth() + 1; var day = currenttime.getdate(); var year = currenttime.getfullyear(); User-created Objects : var personobj=new Object(); personobj.firstname="john"; personobj.lastname="doe"; personobj.age=50; personobj.eyecolor="blue"; //properties ( fields ) are // created when defined 19 Add l JavaScript Features (cont.) User-defined Object Constructors: function person(firstname,lastname,age,eyecolor){ this.firstname = firstname; this.lastname = lastname; this.age = age; this.eyecolor = eyecolor; this.newlastname = newlastname; //method invocation function newlastname(new_lastname){ this.lastname = new_lastname; var myfather = new person("john","doe",50,"blue"); var mymother = new person("sally","rally",48,"green"); mymother.newlastname( Doe ); 20
Add l JavaScript Features (cont.) User-defined Object Constructors (another example): //object creation function function circle(r) { this.radius = r; //radius property this.area = getarea; //function invocation this.diameter = getdiameter; //function invocation function getarea() { var area = this.radius*this.radius*3.14; return area; function getdiameter() { var d = this.radius*2; return d; //function definition //function definition var mycircle = new circle(20); println("area = " + mycircle.area()); //println is a Rhino method println("diameter = " + mycircle.diameter()); 21