2021-07-07

运行时逻辑

每个基于UNIGINE的应用程序都有其生命周期,它由某些阶段组成,其中某些阶段执行一次,其他阶段每帧重复一次。简而言之,这些阶段如下:
在这里插入图片描述

UNIGINE具有三个主要逻辑组件,它们每个都有一组功能(名为 init(), update(), postUpdate()等),其中包含要在引擎工作周期的相应阶段执行的操作。这些组件是:

  • System Logic是在整个应用程序生命周期中运行的代码(即使在世界之间切换时,其范围也存在)。对于使用UnigineScript编写的应用程序,系统逻辑将写入系统脚本文件(unigine.usc)。对于使用C ++的应用程序,将创建AppSystemLogic.cpp,对于C#应用程序,将创建AppSystemLogic.cs。该文件存储在项目的source/文件夹中。它具有已实现的方法将逻辑代码放入其中。

  • World Logic是虚拟世界的逻辑。该逻辑仅在加载世界后才生效。对于使用UnigineScript编写的应用程序,世界逻辑将写入 world脚本文件(以您的项目命名的*.usc),并与相应的世界一起加载和卸载。对于使用C ++的应用程序,将创建AppWorldLogic.cpp,对于C#应用程序,将创建AppWorldLogic.cs。该文件存储在项目的source/文件夹中,并在整个引擎运行时保持加载状态。它具有已实现的方法将逻辑代码放入其中。

  • Editor Logic是编辑器的逻辑。该逻辑仅在加载UnigineEditor时有效。您应该创建一个编辑器逻辑文件,以将逻辑代码放入其中,并使用命令行参数将其添加。

Debug打印

using namespace Unigine;

// auxiliary variables for messages
char *file_name = "file.txt";
int ID = 10;
	
// reporting an error message
Log::error("Loading mesh: can't open \"%s\" file\n", file_name);

// reporting a message
Log::message("-> Added %d UI elements.\n", 10);
	
// reporting a warning message
Log::warning("ID of the \"%s\" file: %d.\n", file_name, ID);
	
// reporting a fatal error message to the log file and closing the application
Log::fatal("FATAL ERROR reading \"%s\" file!\n", file_name);

保存当前world,打开另一个world

#include <UnigineWorld.h>

using namespace Unigine;
/* .. */

// loading world from the my_world.world file
World::loadWorld("my_world");

或者如下代码

#include <UnigineConsole.h>

using namespace Unigine;
/* .. */

// saving current world to the my_world.world file
Console::run("world_save my_world");

// loading world from the my_world.world file
Console::run("world_load my_world");

关闭程序

#include <UnigineApp.h>
using namespace Unigine;

/* .. */
		
// closing the application 
App::exit();

创建Nodes

注意这里我们使用到了智能指针来避免资源泄露

// creating a node of the NodeType named nodename
<NodeType>Ptr nodename = <NodeType>::create(<construction_parameters>);
	
// removing the node
nodename.deleteLater();

创建以及设置相机

通过player设置相机,当你创建了一个player,那么你同时也创建了一个相机

#include <UnigineGame.h>
using namespace Unigine;

/* ... */

int AppWorldLogic::init() 
{
	// creating a new PlayerSpectator instance
	PlayerSpectatorPtr playerSpectator = PlayerSpectator::create();

	// setting necessary parameters: FOV, ZNear, ZFar, view direction vector and position.
	playerSpectator->setFov(90.0f);
	playerSpectator->setZNear(0.1f);
	playerSpectator->setZFar(10000.0f);
	playerSpectator->setViewDirection(Math::vec3(0.0f, 1.0f, 0.0f));
	playerSpectator->setWorldPosition(Math::dvec3(-1.6f, -1.7f, 1.7f));

	// setting the player as a default one via the Game singleton instance
	Game::setPlayer(playerSpectator);
	
	return 1;
}

创建光源

创建光源就像创建普通Node一样

#include <UnigineLights.h>
using namespace Unigine;

int AppWorldLogic::init()
{
	// creating a world light source and setting its color to white
	LightWorldPtr sun = LightWorld::create(Math::vec4(1.0f, 1.0f, 1.0f, 1.0f));

	// setting light source's parameters (intensity, disable angle, scattering type, name and rotation)
	sun->setName("Sun");
	sun->setDisableAngle(90.0f);
	sun->setIntensity(1.0f);
	sun->setScattering(LightWorld::SCATTERING::SCATTERING_SUN);
	sun->setWorldRotation(Math::quat(86.0f, 30.0f, 300.0f));

	return 1;
}

运行期添加材料

#include <UnigineMaterials.h>
#include <UnigineObjects.h>
using namespace Unigine;

/* .. */

int AppWorldLogic::init() 
{

	// creating a box (ObjectMeshDynamic node)
	MeshPtr mesh = Mesh::create();
	mesh->addBoxSurface("box_surface", Math::vec3(1.5f, 1.5f, 1.5f));
	ObjectMeshDynamicPtr my_mesh = ObjectMeshDynamic::create(mesh);

	// getting the base mesh_base material to inherit from
	MaterialPtr mesh_base = Materials::findMaterial("mesh_base");
	// creating a new child material of the mesh_base named "my_mesh_base0"
	MaterialPtr my_mesh_base = mesh_base->inherit("my_mesh_base0");
	
	// setting the albedo color of the material to red
	my_mesh_base->setParameterFloat4("albedo_color", Math::vec4(255, 0, 0, 255));

	// assigning a "my_mesh_base0" material to the surface 0 of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterial("my_mesh_base0", 0);
	
	// assigning a "my_mesh_base0" material to all surfaces of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterial("my_mesh_base0", "*");


}

int AppWorldLogic::shutdown() 
{
	// deleting the material named "my_mesh_base0"
	Materials::removeMaterial(Materials::findMaterial("my_mesh_base0")->getGUID());

	return 1;
}

管理已经存在的场景对象

并不是所有的场景对象都是使用代码创建的,所以我们需要一种操作/管理这些已经存在的场景对象的方法, World类提供了两种获取这类对象指针的方法,如下:

getNodeByName() method - when we know node's name
getNodeByID() method - when we know the node's ID

例子:

// find a pointer to node by a given name
NodePtr baseptr = World::getNodeByName("my_meshdynamic");

// cast a pointer-to-derived from pointer-to-base with automatic type checking
ObjectMeshDynamicPtr derivedptr = checked_ptr_cast<ObjectMeshDynamic>(baseptr);

// static cast (pointer-to-derived from pointer-to-base)
ObjectMeshDynamicPtr derivedptr = static_ptr_cast<ObjectMeshDynamic>(World::getNodeByName("my_meshdynamic"));

// upcast to the pointer to the Object class which is a base class for ObjectMeshDynamic
ObjectPtr object = derivedptr;

// upcast to the pointer to the Node class which is a base class for all scene objects
NodePtr node = derivedptr;

获取Node上的部件

// get the component assigned to a node by type "MyComponent"
MyComponent* my_component = ComponentBase::getComponent<MyComponent>(node);

// // do the same by using the function of the Component System
MyComponent* my_component = ComponentSystem::get()->getComponent<MyComponent>(color_zone);

执行基础的转换(move, rotate, scale)

每个Node都有一个转换矩阵,这个矩阵决定了该Node在世界中的位置、角度和比例缩放。如果将一个Node node1加入到另一个Node node2中,那么node1的转换矩阵就是相对于node2的

获取和管理用户输入

// AppWorldLogic.cpp

#include <UnigineApp.h>
#include <UnigineConsole.h>
#include <UnigineInput.h>

/* .. */

int AppWorldLogic::update() 
{
	// if right mouse button is clicked
	if (Input::isMouseButtonDown(Input::MOUSE_BUTTON_RIGHT))
	{
		Math::ivec2 mouse = Input::getMouseCoord();
		// report mouse cursor coordinates to the console
		Log::message("Right mouse button was clicked at (%d, %d)\n", mouse.x, mouse.y);
	}
	
	// closing the application if a 'Q' key is pressed, ignoring the key if the console is opened
	if (Input::isKeyDown(Input::KEY_Q) && !Console::isActive())
	{
		App::exit();
	}

	return 1;
}

/* .. */

或者

// AppWorldLogic.cpp

#include <UnigineApp.h>
#include <UnigineConsole.h>

/* .. */

int AppWorldLogic::update() 
{
	// if right mouse button is clicked
	if (App::clearMouseButtonState(App::BUTTON_RIGHT))
	{
		// report mouse cursor coordinates to the console
		Log::message("Right mouse button was clicked at (%d, %d)\n", App::getMouseX(), App::getMouseY());
	}
	
	// closing the application if a 'Q' key is pressed, ignoring the key if the console is opened
	if (App::getKeyState("q") && !Console::isActive())
	{
		App::exit();
	}

	return 1;
}

/* .. */

使用Controls处理用户输入

// AppWorldLogic.cpp

#include <UnigineGame.h>

/* .. */

int AppWorldLogic::update() 
{

	// getting current controls
	ControlsPtr controls = Game::getPlayer()->getControls();

	// checking controls states and reporting which buttons were pressed
	if (controls->clearState(Controls::STATE_FORWARD) || controls->clearState(Controls::STATE_TURN_UP))
	{
		Log::message("FORWARD or UP key pressed\n");
	}
	else if (controls->clearState(Controls::STATE_BACKWARD) || controls->clearState(Controls::STATE_TURN_DOWN))
	{
		Log::message("BACKWARD or DOWN key pressed\n");
	}
	else if (controls->clearState(Controls::STATE_MOVE_LEFT) || controls->clearState(Controls::STATE_TURN_LEFT))
	{
		Log::message("MOVE_LEFT or TURN_LEFT key pressed\n");
	}
	else if (controls->clearState(Controls::STATE_MOVE_RIGHT) || controls->clearState(Controls::STATE_TURN_RIGHT))
	{
		Log::message("MOVE_RIGHT or TURN_RIGHT key pressed\n");
	}

	return 1;
}

/* .. */

键位映射和处理

// AppWorldLogic.cpp

#include <UnigineGame.h>

/* .. */

int AppWorldLogic::init() 
{
	// remapping states to other keys and buttons
	ControlsApp::setStateKey(Controls::STATE_FORWARD, App::KEY_PGUP);
	ControlsApp::setStateKey(Controls::STATE_BACKWARD, App::KEY_PGDOWN);
	ControlsApp::setStateKey(Controls::STATE_MOVE_LEFT, 'l');
	ControlsApp::setStateKey(Controls::STATE_MOVE_RIGHT, 'r');
	ControlsApp::setStateButton(Controls::STATE_JUMP, App::BUTTON_LEFT);

	return 1;
}

int AppWorldLogic::update() 
{
	if (ControlsApp::clearState(Controls::STATE_FORWARD))
	{
		Log::message("FORWARD key pressed\n");
	}
	else if (ControlsApp::clearState(Controls::STATE_BACKWARD))
	{
		Log::message("BACKWARD key pressed\n");
	}
	else if (ControlsApp::clearState(Controls::STATE_MOVE_LEFT))
	{
		Log::message("MOVE_LEFT key pressed\n");
	}
	else if (ControlsApp::clearState(Controls::STATE_MOVE_RIGHT))
	{
		Log::message("MOVE_RIGHT key pressed\n");
	}
	else if (ControlsApp::clearState(Controls::STATE_JUMP))
	{
		Log::message("JUMP button pressed\n");
	}

	return 1;
}

/* .. */

创建用户接口

在Unigine中,一个GUI是由许多不同类型的widget组成的,创建GUI的方法:

  • 添加widget到系统Gui上
  • 添加widget到世界的一个Gui上

创建Gui的两种办法:

  • 使用代码从Gui相关类中获取
  • 使用Ui文件

添加lable和slider到系统Ui上

#include <UnigineUserInterface.h>
using namespace Unigine;
GuiPtr gui;

int AppWorldLogic::init() 
{
	// getting a GUI pointer
	gui = Gui::get();

	// creating a label widget and setting up its parameters
	WidgetLabelPtr widget_label = WidgetLabel::create(gui, "Label text:");
	widget_label->setToolTip("This is my label!");
	widget_label->arrange();
	widget_label->setPosition(10, 10);

	// creating a slider widget and setting up its parameters
	WidgetSliderPtr widget_slider = WidgetSlider::create(gui, 0, 360, 90);
	widget_slider->setToolTip("This is my slider!");
	widget_slider->arrange();
	widget_slider->setPosition(100, 10);

	gui->addChild(widget_label, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);
	gui->addChild(widget_slider, Gui::ALIGN_OVERLAP | Gui::ALIGN_FIXED);

	return 1;
}

为了运用Gui element,我们需要为不同的event定义不同的handler

#include <UnigineUserInterface.h>
using namespace Unigine;

GuiPtr gui;

/// function to be called when button1 is clicked
int onButton1Clicked()
{
	/* .. */
}

/// method to be called when button2 is clicked
int AppWorldLogic::onButton2Clicked()
{
	/* .. */
}

/// method to be called when delete button is clicked
int AppWorldLogic::onButtonDelClicked()
{
	/* .. */
}

/// method to be called when slider position is changed
int AppWorldLogic::onSliderChanged()
{
	/* .. */
}
		
int AppWorldLogic::init()
{

	/* .. */
	
	// getting a GUI pointer
	gui = Gui::get();

	// setting onButton1Clicked function as a clicked event handler for a buttonwidget1 using a CallbackBase variable
	Unigine::CallbackBase *button1_clicked_callback_function = MakeCallback(onButton1Clicked);
	buttonwidget1->addCallback(Gui::CLICKED, button1_clicked_callback_function);

	// setting AppWorldLogic::onButton2Clicked method as a clicked event handler for a buttonwidget2 using a CallbackBase variable
	Unigine::CallbackBase *button2_clicked_callback_method = MakeCallback(this, &AppWorldLogic::onButton2Clicked);
	buttonwidget2->addCallback(Gui::CLICKED, button2_clicked_callback_method);
	
	buttonwidget1->addCallback(Gui::CLICKED, MakeCallback(this, &AppWorldLogic::onButtonDelClicked));

	// setting AppWorldLogic::onSliderChanged method as a changed event handler for a widget_slider
	widget_slider->addCallback(Gui::CHANGED, MakeCallback(this, &AppWorldLogic::onSliderChanged));
	/* .. */
	
}

声音源

// create a new sound source using the given sound sample file
SoundSourcePtr sound = SoundSource::create("sound.mp3");

// disable sound muffling when being occluded
sound->setOcclusion(0);
// set the distance at which the sound gets clear
sound->setMinDistance(10.0f);
// set the distance at which the sound becomes out of audible range
sound->setMaxDistance(100.0f);
// set the gain that result in attenuation of 6 dB
sound->setGain(0.5f);
// loop the sound
sound->setLoop(1);
// start playing the sound sample 
sound->play();

环绕声

为了播放环绕声,我们需要一个player。当播放环绕声的时候,player和声音源对象都需要使用SystemLogic.Init() 来创建

// create a player so that an ambient sound source is played
PlayerSpectatorPtr player = PlayerSpectator::create();
player->setPosition(Vec3(0.0f, -3.401f, 1.5f));
player->setViewDirection(vec3(0.0f, 1.0f, -0.4f));
Game::setPlayer(player);

// create the ambient sound source
AmbientSourcePtr sound = AmbientSource::create("sound.mp3");

// set necessary sound settings
sound->setGain(0.5f);
sound->setPitch(1.0f);
sound->setLoop(1);
sound->play();

设置Physics

任何一个对象都应该有shape和body,他们被重力影响,并与其他对象碰撞

// cube 
MeshPtr meshBox = Mesh::create();
meshBox->addBoxSurface("box_surface", Math::vec3(1.0f));
ObjectMeshStaticPtr box = ObjectMeshStatic::create(meshBox);

// create a body and a shape based on the mesh
BodyRigidPtr bodyBox = BodyRigid::create(box);
ShapeBoxPtr shapeBox = ShapeBox::create(bodyBox, Math::vec3(1.0f));

使用 world trigger来catch Nodes

使用world trigger我们可以知道什么时候有哪个Nodes是进入到这个world trigger还是走出这个world trigger,检测方式是通过bounding box来实现的,默认对所有的nodes都有效。

这些函数在update函数或者 updatePhysics()之前被调用

如果你想要在同一帧画面中移动某个object然后调用它的回调,那么需要使用 updateSpatial()

// implement the enter callback
void AppWorldLogic::enter_callback(NodePtr node)
{
	Log::message("\nA node named %s has entered the trigger\n", node->getName());
}

// implement the leave callback
void AppWorldLogic::leave_callback(NodePtr node)
{
	Log::message("\nA node named %s has left the trigger\n", node->getName());
}

WorldTriggerPtr trigger;

int AppWorldLogic::init() {

	// create a world trigger node
	trigger = WorldTrigger::create(Math::vec3(3.0f));
	
	// add the enter callback to be fired when a node enters the world trigger
	trigger->addEnterCallback(MakeCallback(this, &AppWorldLogic::enter_callback));

	// add the leave callback to be fired when a node leaves the world trigger
	trigger->addLeaveCallback(MakeCallback(this, &AppWorldLogic::leave_callback));

	return 1;
}

通常,一级节点的坐标和方位按【全球(世界)】坐标系定义,而它们子节点的坐标和方位则按各自父节点的【本地】坐标系来定义(前提是未选择其它选项)。 这就使得我们只需变换和旋转根节点就可以轻松完成整个层级树分支的变换和旋转。

【全球(世界)】坐标系的原点被设置在Scene Center(场景中心)。 而本地坐标系的原点就是父节点的【The Pivot Point(轴心点)】。

surface和平面是没有关系的,不同的LOD你就可以看成具有多个surface,通过Editor添加的Node只具有一个surface

节点表面的【数量】取决于网格是如何从三维编辑器中被导出的,并且该数量不能在Runtime(运行时)中被动态更改。 每个节点表面都可以添加单独的DIP来调用GPU

节点与它们的节点表面共享常用选项。 如有所需,每个节点表面都可以选择Enabled(启用)或Disabled(禁用)渲染

节点表面的层级顺序可以被自由更改(方法是按住【ALT】键的同时拖拽节点)。 不过,节点表面自身不能被添加,删除,或是在UnigineEditor中以其它方式被更改。 造成这种情况的原因是,对象在外部三维编辑应用中被创建的过程中其表面就已经形成了。 因此要更改它们,就必须重新输出对象。
You can assign a separate material and property to each surface.

对象可以在不需要交互的地方,它也可以参与到Physical Simulation(物理仿真)。 仿真引擎既要行之有效,同时又要保持整体高帧速,因此它的经度并不高。 这样设计的目的就是为了能在对象之间进行Collision Detection(碰撞检测)。

为此,对象应该被分配Body(物理实体),由它来描述对象的行为以及其所有具有的物理属性。 例如,它既可以是能一直保持固体形态的未变形Rigid Body(刚体),也可以是能被折叠和撕裂的Cloth(布料)。

不过,这种body还不能满足对象进行交互。 我们需要能估计出对象实体体积的某种基本物理实体 — Shape。 Unigine引擎提供有这么几类Shape:Box(盒体),Sphere(球体),Capsule(胶囊),Cylinder(缸体),Convex Hull(凸包),或任意的网格形状。 这些形状被用来计算对象之间的碰撞。

材料

Nodes without materials are rendered red in the scene. The material stores information on how a node is to be rendered; it is in fact a set of properties (states, options, parameters) and assets (2D, 3D textures), based on which surfaces are rendered. The material should be assigned to the node surface.

Even if no material is assigned to the node surfaces, the node can still participate in collisions and intersections.

To manage rendering of surfaces with no materials assigned, use the viewport mask for these surfaces.

In UNIGINE, there are two types of materials:

  • Read-only base materials created by programmers and stored in *.basemat files. A UNIGINE-based project includes a set of default base materials.
  • Editable user materials inherited from the base materials or from other user materials and stored in *.mat files. Such materials are created by 3D artists and override properties of parent materials.

In UNIGINE, there are two types of materials:

  • Read-only base materials created by programmers and stored in *.basemat files. A UNIGINE-based project includes a set of default base materials.
  • Editable user materials inherited from the base materials or from other user materials and stored in *.mat files. Such materials are created by 3D artists and override properties of parent materials.

A set of user material properties cannot differ from the base material ones: the user material inherited from the base material only overrides all its options, states, textures and parameters.

材质层级

在Unigine引擎中,材质以层级形式组织,就像节点一样

默认情况下,Unigine引擎提供了一系列基本材质,它们永远位于材质层级的最顶端。 如果您需要扩展基本材质的属性或对其修改,那可以通过从必需的基本材质继承来创建新材质:基本材质会将自身的所有属性都传给所继承的材质

属性

要想将对象正确地融入虚拟世界中,光是确定其方位,固有特性和外观还不够。 属性指明了对象的行为表现以及它与其它对象和场景环境间的交互方式。

A property is a “material” for application logic represented by a set of logic-related parameters. Properties can be used to build components to extend the functionality of nodes.

You can define conditions for parameters of the property to be available/unavailable in UnigineEditor.

UNIGINE’s Properties system includes:

  • Manual properties implemented and modified manually by programmers. A manual property at the top of the hierarchy is called a base property. There are two built-in read-only base properties: node_base and surface_base.
  • User properties inherited from the manual ones and adjusted via the UnigineEditor by 3D artists.

Properties can be assigned to both the whole node and a single surface:

  • If assigned a node, properties can specify additional settings that extend the built-in ones (e.g., they can be used to specify if a node is interactive and whether it is a switch). For a character, properties can be used to specify health points or gold amount.
  • If assigned to a surface, a property can specify certain parameters that can be used during physical interaction with the surface. For example, the property can indicate the type of material assigned to the surface (wood, metal, plastic, etc.).
  • 一个node可以对应多个属性
  • 一个属性只能对应一个node

If you need to assign a property to a single surface, it must be inherited from the surface_base property.

建议从node_base属性继承将分配给节点的属性。但是,您也可以将任何自定义基属性或其子属性分配给节点。

Each property is stored in a separate *.prop file, except for the internal properties.

属性体系:

All inherited and non-overridden parameters will be updated automatically, if they are updated in the parent property.

The Properties hierarchy is based on GUIDs: all properties are referred to using GUIDs, even the base and manual ones (the GUIDs for such properties are generated at run time and are uniquely determined by their names). However, only user properties store their GUIDs explicitly: a GUID is generated automatically at the user property creation and is written to the corresponding *.prop file.

Properties in the hierarchy can be reparented, renamed, cloned, inherited, or removed in a single click.

Execution Sequence

Unigine’s Application Logic System has three main concepts of logic:

  • System logic - the logic of the application. You can implement your logic that will be performed during application life cycle. Your custom logic can be put in the system script file (by using UnigineScript API only), or you can inherit SystemLogic class and implement your logic (C++ and C# APIs).
    UnigineScript unigine.usc system script file is created automatically in the your project’s folder. When you create a new C++ / C# project, it has already inherited system logic class with implemented methods to put your logic code inside.

  • World logic - the logic of the world - here you should put the logic of the virtual scene. The logic takes effect when the world is loaded. You can put your logic inside the world script file (by using UnigineScript API only), or you can inherit WorldLogic class and implement your logic (C++ and C# APIs).
    The world script *.usc file is automatically created with the new world and has the name of your project. When you create a new C++ / C# project, it has already inherited world logic class with implemented methods to put your logic code inside.

  • Editor logic - the logic of the editor. The logic takes effect only when the editor is loaded. You can put your logic inside the editor script file (by using UnigineScript API only), or you can inherit EditorLogic class and implement your logic (C++ and C# APIs).When you create a new C++ / C# project, it has already inherited editor logic class with implemented methods to put your logic code inside.

In case of inheriting *Logic classes (C++ / C#), implemented methods will be called right after corresponding scripts’ methods.

The UNIGINE engine internal code and the application logic are executed in the pre-defined order:

Initialization. During this stage, the required resources are prepared and initialized. As soon as these resources are ready for use, the engine enters the main loop.
Main loop. When UNIGINE enters the main loop, all its actions can be divided into three stages, which are performed one by one in a cycle:
Update stage containing all logic of your application that is performed every frame
Rendering stage containing all rendering-related operations, physics simulation calculations, and pathfinding
Swap stage containing all synchronization operations performed in order to switch between the buffers
This cycle is repeated every frame while the application is running.

Shutdown. When UNIGINE stops execution of the application, it performs operations related to the application shutdown and resource cleanup.
Read this article to know where to put your logic code.

Also, read the Execution Sequence and Logic System articles to know the detailed workflow of the Unigine engine.

The UNIGINE engine internal code and the application logic are executed in the pre-defined order:

Initialization. During this stage, the required resources are prepared and initialized. As soon as these resources are ready for use, the engine enters the main loop.

Main loop. When UNIGINE enters the main loop, all its actions can be divided into three stages, which are performed one by one in a cycle:
Update stage containing all logic of your application that is performed every frame
Rendering stage containing all rendering-related operations, physics simulation calculations, and pathfinding
Swap stage containing all synchronization operations performed in order to switch between the buffers
This cycle is repeated every frame while the application is running.

Shutdown. When UNIGINE stops execution of the application, it performs operations related to the application shutdown and resource cleanup.

将逻辑应用于对象

For an object to be conveniently integrated into application logic, it is required to specify the set of user-defined parameters and the way the object will behave and interact with other objects and the scene environment.

By assigning properties (for C++). Properties can be used on their own for accessing nodes and files or as an integral part of C++ Component System to extend the functionality of nodes. While the property represents a tag for logic and provides a set of user-defined parameters, the logic component integrates a node, a C++ class, containing logic implementation, and a property.

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。
完整版:https://download.csdn.net/download/qq_27595745/89522468 【课程大纲】 1-1 什么是java 1-2 认识java语言 1-3 java平台的体系结构 1-4 java SE环境安装和配置 2-1 java程序简介 2-2 计算机中的程序 2-3 java程序 2-4 java类库组织结构和文档 2-5 java虚拟机简介 2-6 java的垃圾回收器 2-7 java上机练习 3-1 java语言基础入门 3-2 数据的分类 3-3 标识符、关键字和常量 3-4 运算符 3-5 表达式 3-6 顺序结构和选择结构 3-7 循环语句 3-8 跳转语句 3-9 MyEclipse工具介绍 3-10 java基础知识章节练习 4-1 一维数组 4-2 数组应用 4-3 多维数组 4-4 排序算法 4-5 增强for循环 4-6 数组和排序算法章节练习 5-0 抽象和封装 5-1 面向过程的设计思想 5-2 面向对象的设计思想 5-3 抽象 5-4 封装 5-5 属性 5-6 方法的定义 5-7 this关键字 5-8 javaBean 5-9 包 package 5-10 抽象和封装章节练习 6-0 继承和多态 6-1 继承 6-2 object类 6-3 多态 6-4 访问修饰符 6-5 static修饰符 6-6 final修饰符 6-7 abstract修饰符 6-8 接口 6-9 继承和多态 章节练习 7-1 面向对象的分析与设计简介 7-2 对象模型建立 7-3 类之间的关系 7-4 软件的可维护与复用设计原则 7-5 面向对象的设计与分析 章节练习 8-1 内部类与包装器 8-2 对象包装器 8-3 装箱和拆箱 8-4 练习题 9-1 常用类介绍 9-2 StringBuffer和String Builder类 9-3 Rintime类的使用 9-4 日期类简介 9-5 java程序国际化的实现 9-6 Random类和Math类 9-7 枚举 9-8 练习题 10-1 java异常处理 10-2 认识异常 10-3 使用try和catch捕获异常 10-4 使用throw和throws引发异常 10-5 finally关键字 10-6 getMessage和printStackTrace方法 10-7 异常分类 10-8 自定义异常类 10-9 练习题 11-1 Java集合框架和泛型机制 11-2 Collection接口 11-3 Set接口实现类 11-4 List接口实现类 11-5 Map接口 11-6 Collections类 11-7 泛型概述 11-8 练习题 12-1 多线程 12-2 线程的生命周期 12-3 线程的调度和优先级 12-4 线程的同步 12-5 集合类的同步问题 12-6 用Timer类调度任务 12-7 练习题 13-1 Java IO 13-2 Java IO原理 13-3 流类的结构 13-4 文件流 13-5 缓冲流 13-6 转换流 13-7 数据流 13-8 打印流 13-9 对象流 13-10 随机存取文件流 13-11 zip文件流 13-12 练习题 14-1 图形用户界面设计 14-2 事件处理机制 14-3 AWT常用组件 14-4 swing简介 14-5 可视化开发swing组件 14-6 声音的播放和处理 14-7 2D图形的绘制 14-8 练习题 15-1 反射 15-2 使用Java反射机制 15-3 反射与动态代理 15-4 练习题 16-1 Java标注 16-2 JDK内置的基本标注类型 16-3 自定义标注类型 16-4 对标注进行标注 16-5 利用反射获取标注信息 16-6 练习题 17-1 顶目实战1-单机版五子棋游戏 17-2 总体设计 17-3 代码实现 17-4 程序的运行与发布 17-5 手动生成可执行JAR文件 17-6 练习题 18-1 Java数据库编程 18-2 JDBC类和接口 18-3 JDBC操作SQL 18-4 JDBC基本示例 18-5 JDBC应用示例 18-6 练习题 19-1 。。。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值