This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
基础
专业(SIM)
UnigineEditor
界面概述
资源工作流程
Version Control
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
使用范例
C#
UnigineScript
统一的Unigine着色器语言 UUSL (Unified UNIGINE Shader Language)
Plugins
File Formats
材质和着色器
Rebuilding the Engine Tools
GUI
双精度坐标
应用程序接口
Animations-Related Classes
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
VR-Related Classes
创建内容
内容优化
材质
Material Nodes Library
Miscellaneous
Input
Math
Matrix
Textures
Art Samples
Tutorials

Function Export

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.

Unigine API supports export of:

  • Pure functions
  • Methods of specific objects as pure functions

See also#

An example can be found in <UnigineSDK>/source/samples/Api/Scripts/Functions/ directory.

Function Export Example#

Pure functions and object methods that are exported can take up to 9 arguments.

Notice
The object whose methods are exported should not be destroyed while it is used by the engine in a script.

Below is an example of function and method export.

  1. Create a pointer to an external function via MakeExternFunction(). For object methods, useMakeExternObjectFunction().
  2. Register the function or a method via Unigine::Interpreter::addExternFunction().
  3. All functions are exported into a global namespace. To limit the scope of the exported function or a method, use library namespace.
Source code (C++)
#include <UnigineApp.h>
#include <UnigineConsole.h>
#include <UnigineEngine.h>
#include <UnigineInterface.h>
#include <UnigineLogic.h>
#include <UnigineString.h>
#include <UnigineWorld.h>

using namespace Unigine;
using namespace Unigine::Math;


//////////////////////////////////////////////////////////////////////////
// User defined functions
//////////////////////////////////////////////////////////////////////////

/*
 */
Variable my_sum(const Variable &v0, const Variable &v1)
{
	if (v0.getType() == Variable::INT && v1.getType() == Variable::INT)
	{
		Log::message("my_sum(%d,%d): called\n", v0.getInt(), v1.getInt());
		return Variable(v0.getInt() + v1.getInt());
	}

	if (v0.getType() == Variable::STRING && v1.getType() == Variable::STRING)
	{
		Log::message("my_sum(%s,%s): called\n", v0.getString(), v1.getString());
		return Variable((String(v0.getString()) + "+" + String(v1.getString())).get());
	}

	Log::message("my_sum(%s,%s): called\n", v0.getTypeName().get(), v1.getTypeName().get());

	return Variable("unknown");
}

/*
 */
float my_mul(float a, float b)
{
	Log::message("my_mul(%g,%g): called\n", a, b);

	return a * b;
}

/*
 */
float my_dot(const vec3 &v0, const vec3 &v1)
{
	Log::message("my_dot((%g,%g,%g),(%g,%g,%g)): called\n", v0.x, v0.y, v0.z, v1.x, v1.y, v1.z);

	return dot(v0, v1);
}

//////////////////////////////////////////////////////////////////////////
// User defined class member functions
//////////////////////////////////////////////////////////////////////////

/*
 */
class MyApplication
{
public:
	MyApplication()
		: seed(1)
	{
	}

	void init(int s = 1)
	{
		Log::message("MyApplication::init(%d) called\n", s);
		seed = s;
	}

	void shutdown()
	{
		Log::message("MyApplication::shutdown() called\n");
		seed = 1;
	}

	int update()
	{
		seed = (seed * 3877 + 29573) % 139968;
		return seed;
	}

	int get() const
	{
		return seed;
	}

private:
	int seed;
};

//////////////////////////////////////////////////////////////////////////
// System logic class
//////////////////////////////////////////////////////////////////////////

class AppSystemLogic : public SystemLogic
{
public:
	AppSystemLogic() {}
	virtual ~AppSystemLogic() {}
	
	int init() override
	{
		App::setUpdate(1);
		// load the world with a script file in UnigineScript using our functions
		World::loadWorld("functions");

		return 1;
	}
};
//////////////////////////////////////////////////////////////////////////
// Main
//////////////////////////////////////////////////////////////////////////
 int main(int argc, char **argv)
{
	// singleton
	MyApplication my_application;

	// export functions
	Interpreter::addExternFunction("my_sum", MakeExternFunction(&my_sum, ",1"));
	Interpreter::addExternFunction("my_mul", MakeExternFunction(&my_mul));
	Interpreter::addExternFunction("my_dot", MakeExternFunction(&my_dot));

	// export class member functions
	Interpreter::addExternLibrary("my_application");
	Interpreter::addExternFunction("my_application.init", MakeExternObjectFunction(&my_application, &MyApplication::init, "1"));
	Interpreter::addExternFunction("my_application.shutdown", MakeExternObjectFunction(&my_application, &MyApplication::shutdown));
	Interpreter::addExternFunction("my_application.update", MakeExternObjectFunction(&my_application, &MyApplication::update));
	Interpreter::addExternFunction("my_application.get", MakeExternObjectFunction(&my_application, &MyApplication::get));

	// init engine
	EnginePtr engine(argc, argv);

	// enter main loop
	AppSystemLogic system_logic;
	engine->main(&system_logic, NULL, NULL);

	return 0;
}

Access from Scripts#

After the registration, the exported functions and methods can be used in a script written in UnigineScript (loaded in AppSystemLogic::init() above):

Source code (UnigineScript)
/*
 */
int init()
{
	/////////////////////////////////

	log.message("\nFunctions:\n\n");

	// my_sum(1) with default second argument
	log.message("result is: %s\n\n", typeinfo(my_sum(1)));

	// my_sum(1,2)
	log.message("result is: %s\n\n", typeinfo(my_sum(1, 2)));

	// my_sum("begin","end")
	log.message("result is: %s\n\n", typeinfo(my_sum("begin", "end")));

	// my_sum(1,"end")
	log.message("result is: %s\n\n", typeinfo(my_sum(1, "end")));

	// my_mul(16,64)
	log.message("result is: %s\n\n", typeinfo(my_mul(16, 64)));

	// my_dot(vec3(1.0f,2.0f,3.0f),vec3(4.0f,5.0f,6.0f))
	log.message("result is: %s\n\n", typeinfo(my_dot(vec3(1.0f, 2.0f, 3.0f), vec3(4.0f, 5.0f, 6.0f))));

	/////////////////////////////////

	log.message("Member functions:\n\n");

	// default argument
	my_application.init();

	// manual argument
	my_application.init(100);

	// update application
	for (int i = 0; i < 4; i++)
		log.message("%d: %d\n", i, my_application.update());

	// shutdown
	my_application.shutdown();

	/////////////////////////////////

	// show console
	engine.console.setActivity(1);

	return 1;
}

Output#

The following result will be printed into the console:

Output
Functions:

my_sum(1,1): called
result is: int: 2

my_sum(1,2): called
result is: int: 3

my_sum(begin,end): called
result is: string: "begin+end"

my_sum(int,string): called
result is: string: "unknown"

my_mul(16,64): called
result is: float: 1024

my_dot((1,2,3),(4,5,6)): called
result is: float: 32

Member functions:

MyApplication::init(1) called
MyApplication::init(100) called
0: 137337
1: 46850
2: 128527
3: 42672
MyApplication::shutdown() called

Default Argument Values#

If you want to export functions with default values for their arguments, you can specify them as the last argument of MakeExternFunction(). The order of values should be the same as the order of arguments in the function declaration, and the values should be comma-separated. For example:

Source code (C++)
void foo(const char *a,float b) { }

// To specify default values:
Interpreter::addExternFunction("foo",MakeExternFunction(&foo,"\"Unigine\",0.4"));

If you want to specify default values not for all arguments, simply omit those that do not have defaults, but don't forget commas:

Source code (C++)
// To specify only required default value:
Interpreter::addExternFunction("foo",MakeExternFunction(&foo,",0.4"));

Expression as a Default Argument#

Besides constants, you can also specify expressions as default values.

  • Expressions are evaluated before the function is registered.
  • An expression provided as a default argument should return a value of the same type as the corresponding argument. Type conversions are not supported, the only exception being a two-way conversion between float and int.

Overloading Caveats#

Unlike C++, UnigineScript is not a strongly typed language. In C++ code you can have two or more functions with the same name that return a value of the same type, but accept different types of arguments (or the number of arguments differs). If you attempt to export them into scripts simply by registering function names, MakeExternFunction() will fail.

To make registration work, you need to explicitly specify:

  1. Return value type
  2. Arguments types
Source code (C++)
void foo(int a) { }
void foo(float a) { }

/* Interpreter::addExternFunction("foo",MakeExternFunction(&foo));  
 * This expression will not work since Interpreter cannot choose between functions.
 */
   
// To register void foo(int a), use:   
Interpreter::addExternFunction("foo",MakeExternFunction<void,int>(&foo));

// To register void foo(float a), use:
Interpreter::addExternFunction("foo",MakeExternFunction<void,float>(&foo));

Let's say we have got a more difficult situation: the first arguments of functions are of the same type. In such case, following syntax is used for registration:

Source code (C++)
void foo(int a) { }
void foo(int a,int b) { }

/* Interpreter::addExternFunction("foo",MakeExternFunction<void,int>(&foo)); 
 * This expression will not work since both functions take an integer as a first argument.
 */

// To register void foo(int a), use:
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int))&foo)); 
 
// To register void foo(int a,int b), use:
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int,int))&foo)); 

/* Interpreter::addExternFunction("foo",MakeExternFunction<void,int,int>(&foo));
 * This expression will also allow to register void foo(int a,int b).
 */

Overloading Exported Methods#

If ambiguity exists for class static methods names, it is resolved in a similar way as for functions.

Source code (C++)
class Class {

	public:
	
		static int foo(float a) { return 1; }
        static int foo(int a) { return 1; }
		static void foo(int a,int b) { return 1; }
};

/*
*/

// To register foo(float a):
Interpreter::addExternFunction("foo",MakeExternFunction((int (*)(float))&Class::foo));
// To register foo(int a):
Interpreter::addExternFunction("foo",MakeExternFunction((int (*)(int))&Class::foo));
// To register foo(int a,int b):
Interpreter::addExternFunction("foo",MakeExternFunction((void (*)(int,int))&Class::foo));
Last update: 2021-07-09
Build: ()