This page has been translated automatically.
Video Tutorials
Interface
Essentials
Advanced
How To
Professional (SIM)
UnigineEditor
Interface Overview
Assets Workflow
Settings and Preferences
Working With Projects
Adjusting Node Parameters
Setting Up Materials
Setting Up Properties
Lighting
Landscape Tool
Sandworm
Using Editor Tools for Specific Tasks
Extending Editor Functionality
Built-in Node Types
Nodes
Objects
Effects
Decals
Light Sources
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
Programming
Fundamentals
Setting Up Development Environment
Usage Examples
C++
C#
UnigineScript
UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
Materials and Shaders
Rebuilding the Engine Tools
GUI
Double Precision Coordinates
API
Containers
Common Functionality
Controls-Related Classes
Engine-Related Classes
Filesystem Functionality
GUI-Related Classes
Math Functionality
Node-Related Classes
Objects-Related Classes
Networking Functionality
Pathfinding-Related Classes
Physics-Related Classes
Plugins-Related Classes
IG Plugin
CIGIConnector Plugin
Rendering-Related Classes
Content Creation
Content Optimization
Materials
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials
Warning! This version of documentation is OUTDATED, as it describes an older SDK version! Please switch to the documentation for the latest SDK version.
Warning! This version of documentation describes an old SDK version which is no longer supported! Please upgrade to the latest SDK version.

Adding Scripts to the Project

Warning
The scope of applications for UnigineScript is limited to implementing materials-related logic (material expressions, scriptable materials, brush materials). Do not use UnigineScript as a language for application logic, please consider C#/C++ instead, as these APIs are the preferred ones. Availability of new Engine features in UnigineScript (beyond its scope of applications) is not guaranteed, as the current level of support assumes only fixing critical issues.

Starting coding your project is simple with the use of UnigineScript language (no compilation is required).

For this tutorial we are going to get a primitive object (a box) from a script and add game logic to rotate it.

Step 1. Add The Primitive Object To The World#

  1. Run the project with the loaded UnigineEditor via the SDK Browser.

  2. In the Menu Bar, choose Create -> Primitive -> Box to create a box object.

  3. In Create Box window that opens, specify the size of the box and click OK.

  4. Place the box somewhere in the world.

  5. By default, the added node is named Cuboid. Right-click it in the World Hierarchy window and rename it box.

  6. In the Node tab of the Parameters window, change the position of the box to 0.0, 0.0, 1.0.

  7. In the Menu Bar, click File -> Save World or press CTRL + S to save the world.

Step 2. Add Script Logic#

There are two methods to add a script to the object:

Method 1: By Editing the .usc World Script File#

To add logic that will rotate the box, you should modify the <your_project_name>.usc world script file in a plain text editor.

Notice
The .usc files do not require compilation, as they are interpreted by the Engine itself during its initialization.

  1. Open the project folder via the SDK Browser.

    Notice
    If there is no Open Folder button, choose Other Actions -> Open Folder.
  2. Open <your_project_name>.usc script file located in the data directory by using plain text editor.

    Source code (UnigineScript)
    #include <core/unigine.h>
    // This file is in UnigineScript language.
    // World script, it takes effect only when the world is loaded.
    
    int init() {
    	// Write here code to be called on world initialization: initialize resources for your world scene during the world start.
    	
    	Player player = new PlayerSpectator();
    	player.setPosition(Vec3(0.0f,-3.401f,1.5f));
    	player.setDirection(Vec3(0.0f,1.0f,-0.4f));
    	engine.game.setPlayer(player);
    	return 1;
    }
    
    // start of the main loop
    int update() {
    	// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.
    	
    	return 1;
    }
    
    int postUpdate() {
    	// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.
    	
    	return 1;
    }
    
    int updatePhysics() {
    	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
    	// The engine calls updatePhysics() with the fixed rate (60 times per second by default) regardless of the FPS value.
    	// WARNING: do not create, delete or change transformations of nodes here, because rendering is already in progress.
    	
    	return 1;
    }
    // end of the main loop
    
    int shutdown() {
    	// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
    	
    	return 1;
    }

    The world script contains the following functions by default:

    • init() function is used to create objects and initialize all other necessary resources on the world load.
    • update() function is used to code project logic and executed every frame.
    • postUpdate() function is used for correction purposes: it implements logic that is executed after updating the node's state.
    • updatePhysics() function is used to code physics simulation logic
    • shutdown() function is used to code project logic and executed when the world is unloaded.

    The following part of the init() function code creates a new free-flying game camera that collides with objects (but does not push or interact with them). Read more about the Engine functions.

    Source code (UnigineScript)
    // create a new spectator player
    Player player = new PlayerSpectator();
    
    // place it in the specified point
    player.setPosition(Vec3(0.0f,-3.401f,1.5f));
    
    // turn it in the specified direction
    player.setDirection(Vec3(0.0f,1.0f,-0.4f));
    
    // set the player as default one
    engine.game.setPlayer(player);
    Notice
    Comments were added to explain the meaning of each line of the code.
  3. Add a variable to handle the required box node before the init() function.
    Warning
    We do NOT recommend you to create global variables for the real project.
    Source code (UnigineScript)
    Node box; // add a box node
    
    int init() {
    	/* ... */
    	}
  4. Put this code into the init() function to get the box node.
    Source code (UnigineScript)
    Node box; // add a box node	
    
    int init() {
    	/* ... */
    
    	// get the node by the specified name
    	box = engine.world.getNodeByName("box");
    
    	return 1;
    }
    Notice
    Though nodes can be handled by any of the scripts (world, system or editor one) and UnigineEditor (that loads and stores all the nodes from the .world file), they should be owned only by one of them. Otherwise, such nodes can cause Engine crash or memory leak problems.
    See Memory Management article for details.
  5. Set the node transformation in the update() function. Note that it is necessary to scale the rotation angle each frame with the frame duration (because it's different for each individual frame) to get constant angular velocity.
    Source code (UnigineScript)
    /* ... */
    
    int update() {
    
    	// check whether the node exists
    	if(box != NULL) {
    
    		// get the frame duration
    		float ifps = engine.game.getIFps();
    
    		// set the angle of rotation
    		float angle = ifps * 90.0f;
    
    		// get the current transformation of the node and apply rotation
    		mat4 transform = box.getTransform() * rotateZ(angle);
    
    		// set new transformation to the node
    		box.setTransform(transform);
    	}
    	return 1;
    }
    Thus, the resulting script is:
    Source code (UnigineScript)
    #include <core/unigine.h>
    // This file is in UnigineScript language.
    // World script, it takes effect only when the world is loaded.
    
    Node box; // add a box node	
    
    int init() {
    	// Write here code to be called on world initialization: initialize resources for your world scene during the world start.
    
    	Player player = new PlayerSpectator();
    	player.setPosition(Vec3(0.0f,-3.401f,1.5f));
    	player.setDirection(Vec3(0.0f,1.0f,-0.4f));
    	engine.game.setPlayer(player);
    
    	// get the node by the specified name
    	box = engine.world.getNodeByName("box");
    
    	return 1;
    }
    
    // start of the main loop
    int update() {
    	// Write here code to be called before updating each render frame: specify all graphics-related functions you want to be called every frame while your application executes.
    	// check whether the node exists
    	if(box != NULL) {
    
    		// get the frame duration
    		float ifps = engine.game.getIFps();
    
    		// set the angle of rotation
    		float angle = ifps * 90.0f;
    
    		// get the current transformation of the node and apply rotation
    		mat4 transform = box.getTransform() * rotateZ(angle);
    
    		// set new transformation to the node
    		box.setTransform(transform);
    	}
    	return 1;
    }
    
    int postUpdate() {
    	// The engine calls this function before rendering each render frame: correct behavior after the state of the node has been updated.
    
    	return 1;
    }
    
    int updatePhysics() {
    	// Write here code to be called before updating each physics frame: control physics in your application and put non-rendering calculations.
    	// The engine calls updatePhysics() with the fixed rate (60 times per second by default) regardless of the FPS value.
    	// WARNING: do not create, delete or change transformations of nodes here, because rendering is already in progress.
    
    	return 1;
    }
    // end of the main loop
    
    int shutdown() {
    	// Write here code to be called on world shutdown: delete resources that were created during world script execution to avoid memory leaks.
    
    	return 1;
    }
  6. Save all the changes in the <your_project_name>.usc world script file.
  7. In the Menu Bar of UnigineEditor, choose File -> Reload World or run the world_reload console command.

  8. Check the result.

Method 2: By Using WorldExpression Objects#

You can add the script to the box by using the WorldExpression object.

  1. In the Menu Bar, choose Create -> Logic -> Expression to create the WorldExpression object.

  2. Place the World Expression box somewhere in the world. The added World Expression node will appear in the World Hierarchy.

  3. Choose the added WorldExpression node in the World Hierarchy window and go to the Node tab of the Parameters window. Here change the position to 1.0, -1.0, 0.5.

    Now you have the primitive box and the World Expression object on a plane.

  4. Specify the Update Distance Limit for the WorldExpression node - it is the maximum distance from the camera up to which the expression shall be executed. Let's set it to 10 units.

  5. Choose the WorldExpression node in the World Hierarchy and go to the World Expression section of the Parameters window.
  6. Put the following code into the Source field.
    Source code (UnigineScript)
    {
    	// get the WorldExpression node via its internal function
    	Node worldExpression = getNode();
    	
    	// get the frame duration
    	float ifps = engine.game.getIFps();
    
    	// set the angle of rotation
    	float angle = ifps * 90.0f;
    
    	// get the current transformation of the node and apply rotation
    	mat4 transform = worldExpression.getTransform() * rotateZ(angle);
    
    	// set new transformation to the node
    	worldExpression.setTransform(transform);
    }
    Warning
    Curly braces are mandatory!

    Other ways of attaching scripts to the World Expression object you can read here.

  7. Make the box node a child of the WorldExpression node by dragging it in the World Hierarchy.

    Now the box node is the child of the WorldExpression node. It means that the box object inherits all expression transformations of the World Expression object.
    Notice
    All the child nodes of the WorldExpression node inherit expression transformations.
    Our expression shall be executed (i.e. the transformation shall be updated according to the script) as long as the distance from the camera to the WorldExpression node does not exceed the specified Update Distance Limit)
  8. Check the result.

If you reset the position of the box node to its parent position, you will get the same result as in the Method 1.

Last update: 2022-03-10
Build: ()