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++
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

编程快速参考

Introduction
介绍#

Basic Scene Objects
基本场景对象#

In terms of UNIGINE, node is a basic type from which all types of scene objects are inherited. Some of them appear visually: Objects, Decals, and Effects they all have surfaces to represent their geometry (mesh), while others (Light Sources, Players, etc.) are invisible.对于 UNIGINE 而言,节点 是一种基本类型,所有类型的场景对象都继承自该类型。 其中一些在视觉上出现:ObjectsDecalsEffects,它们 都有 surfaces 来表示它们的几何体(网格),而其他的(Light Sources玩家等)是不可见的。

Each node has a transformation matrix, which encodes position, rotation, and scale of the node in the world.每个节点有一个变换矩阵的编码位置,旋转,世界上节点的规模。

All scene objects added to the scene regardless of their type are called nodes.所有添加到场景中的场景对象,无论其类型,都称为节点。

附加信息:

Coordinate System
坐标系统#

The 3D space in UNIGINE is represented by the right-handed Cartesian coordinate system: X and Y axes form a horizontal plane, Z axis points up. When exporting an animation from 3D editors, Y is considered a forward direction.Unigine中的3D空间用右手笛卡尔坐标系表示:XY轴形成一个水平面,Z轴指向上方 . 当 exporting 从 3D 编辑器中导出动画时,Y 被视为正向。

Coordinate System坐标系统

Positive rotation angle sets the rotation counterclockwise. It corresponds to the right-hand rule: if you set right hand thumb along the axis, other fingers wrapped will show rotation direction.积极的旋转角度设置逆时针旋转。它对应于右手法则:如果你设置的右手拇指沿轴,其他手指包裹将显示旋转的方向。

Rotation Directions旋转的方向

附加信息:

  1. For more information on UNIGINE node types, see Built-in Node Types section.有关 UNIGINE 节点类型的更多信息,请参阅 内置节点类型 部分。
  2. For more information on managing nodes via API, see Node-Related Classes section.有关通过API管理节点的更多信息,请参见节点相关类小节。

Logging and Printing Messages to Console
日志和打印消息到控制台#

Printing messages to the log file and console helps to monitor overall progress of execution of your application and report errors which can be used in debugging. Log class makes it possible to print formatted string messages to the log file and the console. The code below demonstrates how to print various types of messages:打印消息日志文件和控制台监控整体进度的执行应用程序和报告错误,可以用于调试。Log类可以打印格式化字符串消息日志文件和控制台。下面的代码演示了如何打印各种类型的消息:

注意
To enable displaying messages in the onscreen overlay use the following command: console_onscreen 1要启用在屏幕覆盖显示消息,使用以下命令:console_onscreen 1
源代码 (C++)
using namespace Unigine;

// auxiliary variables for messages
const 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", ID);

	// 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);

Additional information:附加信息:

  • For more information on the console, see Console article.有关控制台的更多信息,请参阅Console文章。
  • For more information on the Log class, see Log class article.Log类的更多信息,请参见Log类文章。

Saving and Loading a World
保存和加载一个世界#

Some applications manage a single world, while other require several worlds to be managed. In any case, it is very useful to know how to save our current world and load some other. In order to solve this task, we should use the World class, which is designed as a singleton.有些应用程序管理单个世界,而其他应用程序则需要管理多个世界。在任何情况下,知道如何保存当前世界和加载其他世界是非常有用的。为了解决这个任务,我们应该使用World类,它被设计成一个单例。

注意
In order to use the World class, include the UnigineWorld.h library.为了使用World类,包括UnigineWorld.h图书馆。
源代码 (C++)
#include <UnigineWorld.h>

using namespace Unigine;
/* .. */

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

We can also do the same via the console by using the Console class, which is also designed as a singleton.我们也可以通过 console,通过使用Console类来实现同样的功能,它也被设计为单例。

注意
In order to use the Console class, include the UnigineConsole.h library.为了使用Console类,包括UnigineConsole.h图书馆。
源代码 (C++)
#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");

Additional information:附加信息:

  • For more information on managing worlds via API, see the World class article.管理世界通过API的更多信息,请参阅World类文章。
  • For more information on the console and available commands, see Console article.有关控制台和可用命令的更多信息,请参阅 console 文章。
  • For more information on managing the console via API, see Console class article.管理控制台通过API的更多信息,参见Console类文章。
  • For more information on managing world nodes that are to be saved via API, see the methods of the Node class.有关管理将通过API保存的世界节点的更多信息,请参阅Node类的方法。

Closing the Application
关闭应用程序#

Any application needs to be closed at some moment. To close your application you should use the Engine class.任何应用程序都需要在某个时刻关闭。要关闭您的应用程序,您应该使用Engine类。

To close the application the following code is to be used:要关闭应用程序,需要使用以下代码:

源代码 (C++)
using namespace Unigine;

/* .. */
		
// closing the application 
Engine::get()->quit();

Creating and Deleting Nodes at Runtime
在运行时创建和删除节点#

Nodes can be created and deleted at runtime almost as easy as in the Editor. The basic set of actions is as follows:在运行时创建和删除节点几乎和在编辑器中一样简单。基本的动作集如下:

  • Creation. To create a node we should declare a smart pointer for the type of the node we are going to create and call a constructor of the corresponding class providing construction parameters if necessary.创建。我们应该创建一个节点为节点的类型声明一个智能指针我们要创建和调用相应的类的构造函数在必要时提供施工参数。
  • Deletion. To delete a node we simply call the deleteLater() method for the node we are going to remove.删除>
源代码 (C++)
// creating a node of the NodeType named nodename
<NodeType>Ptr nodename = <NodeType>::create(<construction_parameters>);
	
// removing the node
nodename.deleteLater();

Now let us illustrate the process of creating and deleting a node using a simple static mesh (ObjectMeshStatic) by loading a mesh from an asset as an example.现在让我们以从资源加载网格为例,说明使用简单静态网格 (ObjectMeshStatic) 创建和删除节点的过程。

源代码 (C++)
int AppWorldLogic::init()
{

	//create an ObjectMeshStatic node from an fbx model imported to the data/fbx/ folder using the Editor
	ObjectMeshStaticPtr my_object = ObjectMeshStatic::create("fbx/model.fbx/model.mesh");

	// removing the node
	my_object.deleteLater();

	return 1;
}

Additional information:附加信息:

  • You can create primitives via code using the Primitives class.您可以创建原语通过代码使用Primitives类。
  • For more information on managing world nodes, see the methods of the Node class.有关管理世界节点的更多信息,请参阅Node类的方法。

Creating and Setting Up a Camera
创建和设置一个相机#

A camera is a viewport to the world, without it you actually won't be able to see anything. Cameras in UNIGINE are managed using players. When you add a new player, it creates a camera and specifies controls, masks, postprocess materials for this camera.一个 camera是一个对世界的视口,没有它你实际上将看不到任何东西。相机在UNIGINE使用players管理。当你添加一个新的播放器时,它会创建一个camera,并指定控件mask ,该相机的后期处理材质。

In order to set a new player as active one we should use the Game class which is designed as a singleton.为了设置一个新球员活跃我们应该使用Game设计成一个单例类。

注意
In order to use the Game class and PlayerSpectator class we must include the UnigineGame.h and the UniginePlayers.h libraries.为了使用Game类和PlayerSpectator类,我们必须包含UnigineGame.hUniginePlayers.h库。

The following code illustrates creation of a PlayerSpectator and setting it as the active game camera.以下代码说明了 PlayerSpectator 的创建并将其设置为活动游戏摄像机。

注意
By default the player uses a standard set of controls which can be overridden if necessary.默认情况下,玩家使用的是一套标准的控制方式,如果需要的话,可以重写这些控制方式。
源代码 (C++)
#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::Vec3(-1.6f, -1.7f, 1.7f));

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

Additional information:附加信息:

  • For more information on players, see the Players article.有关玩家的更多信息,请参阅 players 文章。
  • For more information on players API, see the Players-Related Classes article.有关播放器 API 的更多信息,请参阅 播放器相关类 文章。
  • For more information on the Game class, see the Game class article.有关Game类的更多信息,请参阅Game类文章。

Creating and Setting up Light Sources
创建和设置光源#

Lighting is the basis of every scene defining colors and final look of your objects. Lights in UNIGINE are created the same way as all nodes.照明是每个场景定义颜色和你的对象的最终外观的基础。UNIGINE中的灯的创建方式与所有节点相同。

Let us consider creation of a world light source as an example:让我们考虑建立一个世界光源为例:

源代码 (C++)
#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;
}

Additional information:附加信息:

  • For more information on light sources, see the Light sources article.有关光源的更多信息,请参阅 光源 文章。
  • For more information on light sources API, see the Lights-Related Classes article.有关光源API的更多信息,请参阅灯相关类文章。

Creating, Applying and Deleting Materials at Runtime
在运行时创建、应用和删除素材#

Materials assigned to particular surfaces of a node determine how the node is to be rendered. They implement the shaders and control what options, states, parameters of different types and textures are used to render the node during the rendering passes. To manage materials we use the following two classes:材质分配给节点的特定表面决定了节点的渲染方式。 他们实现 shaders 并控制 optionsstates参数和不同类型的纹理 用于在 渲染通道 期间渲染节点。 为了管理材质,我们使用以下两个类:

  • Materials class which represents an interface for managing loaded materials.Materials类代表一个接口来管理材质加载。
  • Material class which is used to manage each individual material.Material类,用于管理每个单独的材质。
注意
In order to use materials we must include the UnigineMaterials.h library为了使用材质我们必须包括UnigineMaterials.h

The following code can be used to create a new material inherited from the mesh_base material.可以使用下面的代码来创建一个新的材质继承了mesh_base材质。

源代码 (C++)
#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::findManualMaterial("Unigine::mesh_base");

	// inherit a new child material from it
	MaterialPtr my_mesh_base = mesh_base->inherit();
	
	// save it to  "materials/my_mesh_base0.mat"
	my_mesh_base->createMaterialFile("materials/my_mesh_base0.mat");

	// 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.mat" material to the surface 0 of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterialPath("materials/my_mesh_base0.mat", 0);
	
	// assigning a "my_mesh_base0.mat" material to all surfaces of the my_mesh ObjectMeshDynamic node
	my_mesh->setMaterialPath("materials/my_mesh_base0.mat", "*");


}

int AppWorldLogic::shutdown() 
{
	// deleting the material "materials/my_mesh_base0.mat"
	Materials::removeMaterial(Materials::findMaterialByPath("materials/my_mesh_base0.mat")->getGUID());

	return 1;
}

Additional information:附加信息:

  • For more information on creating and editing materials via API, see the Material class article.有关通过API创建和编辑素材的更多信息,请参阅Material类文章。
  • For more information on managing loaded materials via API, see the Materials class article.管理加载材质通过API的更多信息,参见Materials类文章。
  • For more information on materials files formats, see the Materials Files section.有关材质文件格式的详细信息,请参阅 材质文件 部分。
  • For more information on materials parameters, see the materials files in the %UNIGINE_SDK_BROWSER_INSTALLATION_FOLDER%/sdks/%CURRENT_SDK%/data/core/materials/default/ folder.有关物料参数的更多信息,请参见%UNIGINE_SDK_BROWSER_INSTALLATION_FOLDER%/sdks/%CURRENT_SDK%/data/core/materials/default/文件夹中的物料文件。

Managing Existing Scene Objects
管理现有的场景对象#

Not all content in the world is created at runtime, so we should be able to operate with nodes that already exist. How do we get pointers to existing objects in order to manage them? This is where the World class comes into play again. Basically, there are two ways we can get a pointer to a certain node using the methods of the World class:不是世界上所有的内容是在运行时创建的,所以我们应该能够操作节点已经存在。我们怎么得到现有对象的指针来管理他们吗?这就是World类再次发挥作用。基本上,有两种方法我们可以得到一个指向某个节点World类的使用方法:

注意
In order to be able to use the methods of the World class we must include the UnigineWorld.h library.为了能够使用World类的方法,我们必须包含UnigineWorld.h库。

These methods return a NodePtr value, which is a pointer to the base class, but in order to perform operations with a certain object (e.g. ObjectMeshDynamicPtr) we need to perform downcasting (i.e. convert from a pointer-to-base to a pointer-to-derived).这些方法返回一个NodePtr值,这是一个指向基类的指针,但是为了与某个对象执行操作(例如 ObjectMeshDynamicPtr)我们需要执行 的向下类型转换(即转换从pointer-to-base pointer-to-derived)。

Sometimes you may also need to perform upcasting (i.e. convert from a pointer-to-derived to a pointer-to-base), in this case you can use the derived class itself. The code below demonstrates the points described above.有时你可能还需要执行 upcasting(即从指针到派生类转换为指针到基类),在这种情况下,你可以使用派生类本身。下面的代码演示了上面描述的要点。

源代码 (C++)
#include <UnigineWorld.h>
using namespace Unigine;
/* .. */

// 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;

There are the following ways to get a component from a node:有以下方法让一个组件从一个节点:

源代码 (C++)
// 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);

Additional information:附加信息:

  • For more information on working managing smart pointers, see the Working with Smart Pointers article.有关管理智能指针的更多信息,请参阅使用智能指针一文。
  • For more information see the World class article.有关更多信息,请参阅World类文章。

Performing Basic Transformations (Move, Rotate, Scale)
执行基本的转换(移动、旋转、规模)#

Every node has a transformation matrix, which encodes position, rotation, and scale of the node in the world. If a node is added as a child of another node, it has a transformation matrix that is related to its parent node. That is why the Node class has different functions: getTransform(), setTransform() and getWorldTransform(), setWorldTransform() that operate with local and world transformation matrices respectively. The following code illustrates how to perform basic node transformations:每个节点都有一个转换矩阵,它对节点在世界中的位置、旋转和规模进行编码。如果一个节点被添加为另一个节点的子节点,则它有一个与其父节点相关的转换矩阵。这就是为什么Node类有不同的函数:getTransform(), setTransform()getWorldTransform(), setWorldTransform(),它们分别与局部和世界变换矩阵进行操作。下面的代码演示了如何执行基本的节点转换:

源代码 (C++)
// injecting Unigine namespace to the global namespace
using namespace Unigine;
using namespace Unigine::Math;

// move the node by X, Y, Z units along the corresponding axes
node->setWorldPosition(node->getWorldPosition() + Vec3(X, Y, Z));

// move the node by one unit along the Y axis
node->worldTranslate(0.0f, 1.0f, 0.0f);

// rotate the node around the axis (X, Y, Z) by the Alpha angle 
node->setWorldRotation(node->getWorldRotation() * quat(Vec3(X, Y, Z), Alpha));

// rotate the node around X, Y, and Z axes by the corresponding angle (angle_X, angle_Y, angle_Z)
node->setWorldRotation(node->getWorldRotation() * quat(angle_X, angle_Y, angle_Z));

// rotate the node by 45 degrees along the Z axis
node->worldRotate(0.0f, 0.0f, 45.0f);

// orient the node using a direction vector and a vector pointing upwards
node->setWorldDirection(vec3(0.5f, 0.5f, 0.0f), vec3_up, AXIS_Y);

// setting node scale to Scale_X, Scale_Y, Scale_Z along the corresponding axes
node->setWorldScale(vec3(Scale_X, Scale_Y, Scale_Z));

// setting new transformation matrix to scale the node 2 times along all axes, rotate it by 45 degrees around the Z-axis and move it by 1 unit along all axes
Mat4 transform = Mat4(translate(vec3(1.0f, 1.0f, 1.0f)) * rotate(quat(0.0f, 0.0f, 1.0f, 45.0f)) * scale(vec3(2.0f)));

// setting node transformation matrix relative to its parent
node->setTransform(transform);

// setting node transformation matrix relative to the world origin
node->setWorldTransform(transform);

Additional information:附加信息:

Making the Game Process Framerate-independent
Framerate-independent使游戏过程#

As the frame rate of our application may vary (i.e. the AppWorldLogic::update() method will be called more or less frequently) depending on hardware, we should do something to ensure that certain actions are performed at the same time periods regardless of the frame rate (e.g. change something once per second etc). To make your game frame rate independent you can use a scaling multiplier (the time in seconds it took to complete the last frame) returned by the following methods:由于我们的应用程序的帧速率可能会有所不同(即 AppWorldLogic::update() 方法会或多或少地被调用 )取决于硬件,我们应该做一些事情来确保某些动作在相同的时间段内执行,而不管帧速率如何(例如每秒更改一次等)。 为了使您的游戏帧速率独立,您可以使用以下方法返回的缩放乘数(完成最后一帧所需的时间,以秒为单位):

  • Engine::getIfps() returns the inverse FPS value for your application.Engine::getIfps()返回应用程序的反FPS值。
  • Game::getIfps() returns the scaled inverse FPS value. This class is to be used when you want to speed up, slow down or pause rendering, physics or game logic.Game::getIfps()返回缩放的反FPS值。这个类是用来当你想加快,放慢或暂停渲染,物理或游戏逻辑。

To change the transformations you can use the following code:要更改转换,您可以使用以下代码:

源代码 (C++)
// AppWorldLogic.cpp
#include <UnigineGame.h>

// injecting Unigine namespace to the global namespace
using namespace Unigine;

/* .. */

int AppWorldLogic::update() {
	
	// getting an inverse FPS value (the time in seconds it took to complete the last frame)
	float ifps = Game::getIFps();
	
	// moving the node up by 0.3 units every second instead of every frame
	node->worldTranslate(Math::Vec3(0.0f, 0.0f, 0.3f * ifps));

	return 1;
}
/* .. */

To perform some changes once in a certain period of time you can use the following code:执行一些变化一旦在一定的时间内你可以使用下面的代码:

源代码 (C++)
#include <UnigineGame.h>

// injecting Unigine namespace to the global namespace
using namespace Unigine;
// AppWorldLogic.cpp

	const float INTERVAL_DURATION = 5;		// interval duration
	float elapsed_time = INTERVAL_DURATION;	// current time left to make changes
	
/* .. */

int AppWorldLogic::update() {
	
	// getting an inverse FPS value (the time in seconds it took to complete the last frame)
	float ifps = Game::getIFps();

	// checking if it's time to make changes
	if (elapsed_time < 0.0f)
	{
		
		/* .. DO SOME CHANGES .. */

		// resetting elapsed time counter
		elapsed_time = INTERVAL_DURATION;
	}

	// decreasing elapsed time counter
	elapsed_time -= ifps;
	
	return 1;
}
/* .. */

Additional information:附加信息:

  • For more information on the Game class, see the Game class article.Game类的更多信息,参见Game类文章。
  • For more information on the Engine class, see the Engine class article.有关Engine类的更多信息,请参阅Engine类文章。

Managing Intersections
管理十字路口#

Intersections are widely used in 3D applications. In UNIGINE there are three main types of intersections: intersection 在3D应用中被广泛使用。在UNIGINE中有三种主要的交叉口类型:

But there are some conditions to detect intersections with the surface: 但是有一些条件来检测与表面的相交:

  1. The surface is enabled.表面是启用的。
  2. The surface has a material assigned.该表面已指定了一种材质。
  3. Per-surface Intersection flag is enabled.每个表面Intersection标志启用。

    注意
    You can set this flag to the object's surface by using the Object.setIntersection() function.您可以使用Object.setIntersection()函数将该标志设置为对象的表面。

The code below illustrates several ways of using world intersections:下面的代码说明了几种使用世界交点的方法:

  • to find all nodes intersected by a bounding box找出与边界框相交的所有结点
  • to find all nodes intersected by a bounding sphere找到所有节点分割的边界范围
  • to find all nodes intersected by a bounding frustum找出与边界截锥相交的所有结点
  • to find the first object intersected by a ray找到第一个对象分割的射线
源代码 (C++)
#include <UnigineGame.h>

#include <UnigineWorld.h>

// injecting Unigine namespace to the global namespace
using namespace Unigine;

int listNodes(Vector<Ptr<Node>>& nodes, const char* intersection_with)
{
	Log::message("Total number of nodes intersecting a %s is: %i \n", intersection_with, nodes.size());

	for (int i = 0; i < nodes.size(); i++)
	{
		Log::message("Intersected node: %s \n", nodes.get(i)->getName());
	}

	// clearing the list of nodes 
	nodes.clear();

	return 1;
}

int AppWorldLogic::update()
{

	// getting a player pointer
	PlayerPtr player = Game::getPlayer();

	// creating a vector to store intersected nodes
	Vector<Ptr<Node>> nodes;

	//-------------------------- FINDING INTERSECTIONS WITH A BOUNDING BOX -------------------------

	// initializing a bounding box with a size of 3 units, located at the World's origin 
	WorldBoundBox boundBox(Math::Vec3(0.0f), Math::Vec3(3.0f));

	// finding nodes intersecting a bounding box and listing them if any
	if (World::getIntersection(boundBox, nodes))
		listNodes(nodes, "bounding box");

	//------------------------- FINDING INTERSECTIONS WITH A BOUNDING SPHERE ------------------------

	// initializing a bounding sphere with a radius of 3 units, located at the World's origin 
	WorldBoundSphere boundSphere(Math::Vec3(0.0f), 3.0f);

	// finding nodes intersecting a bounding sphere and listing them if any
	if (World::getIntersection(boundSphere, nodes))
		listNodes(nodes, "bounding sphere");

	//------------------------- FINDING INTERSECTIONS WITH A BOUNDING FRUSTUM -----------------------

	// initializing a bounding frustum with a  frustum of the player's camera
	WorldBoundFrustum boundFrustum(player->getCamera()->getProjection(), player->getCamera()->getModelview());

	// finding ObjectMeshStaticNodes intersecting a bounding frustum and listing them if any
	if (World::getIntersection(boundFrustum, Node::OBJECT_MESH_STATIC, nodes))
		listNodes(nodes, "bounding frustum");

	//---------------- FINDING THE FIRST OBJECT INTERSECTED BY A RAY CAST FROM P0 to P1 --------------

	// initializing points of the ray from player's position in the direction pointed by the mouse cursor 
	Math::ivec2 mouse = Input::getMousePosition();
	Math::Vec3 p0 = player->getWorldPosition();
	Math::Vec3 p1 = p0 + Math::Vec3(player->getDirectionFromMainWindow(mouse.x, mouse.y)) * 100;

	//creating a WorldIntersection object to store the information about the intersection
	WorldIntersectionPtr intersection = WorldIntersection::create();

	// casting a ray from p0 to p1 to find the first intersected object
	ObjectPtr obj = World::getIntersection(p0, p1, 1, intersection);

	// print the name of the first intersected object and coordinates of intersection point if any
	if (obj)
	{
		Math::Vec3 p = intersection->getPoint();
		Log::message("The first object intersected by the ray at point (%f, %f, %f) is: %s \n ", p.x, p.y, p.z, obj->getName());
	}

	return 1;
}

Additional information:附加信息:

  • For more information on intersections, see the Intersections article.有关交叉口的更多信息,请参阅 Intersections 文章。
  • For more information on managing World intersections via API, see the World class article.有关通过API管理World交集的更多信息,请参阅World类文章。
  • For more information on managing Game intersections via API, see the Game class article.有关通过API管理Game交集的更多信息,请参阅Game类文章。
  • For more information on managing Physics intersections via API, see the Physics class article.有关管理的更多信息Physics路口通过API,看到Physics类文章。

Getting and Managing User Inputs
获取和管理用户输入#

The majority of applications are designed to interact with the user. In UNIGINE you can manage user inputs using the following classes:大多数应用程序被设计成与用户交互。在UNIGINE中,你可以使用以下类来管理用户输入:

The following code illustrates how to use Input class to get mouse coordinates in case if a right mouse button was clicked and to close the application if "q" key was pressed (ignoring this key if the console is opened):下面的代码演示了如何使用Input类获取鼠标坐标,以防如果鼠标右键点击关闭应用程序如果"q"键被按下(忽略这个关键如果控制台打开):

源代码 (C++)
#include <UnigineInput.h>

#include <UnigineConsole.h>

int AppWorldLogic::update()
{

	// if right mouse button is clicked
	if (Input::isMouseButtonDown(Input::MOUSE_BUTTON_RIGHT))
	{
		Math::ivec2 mouse = Input::getMousePosition();
		// 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())
	{
		Engine::get()->quit();
	}

	return 1;
}

The following code illustrates how to use Controls class to handle keyboard input:下面的代码演示了如何使用Controls类来处理键盘输入:

源代码 (C++)
#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;
}

The following code illustrates how to use ControlsApp class to map keys and buttons to states and then to handle user input:下面的代码演示了如何使用ControlsApp类将键和按钮映射到状态,然后处理用户输入:

源代码 (C++)
#include <UnigineGame.h>

int AppWorldLogic::init()
{

	// remapping states to other keys and buttons
	ControlsApp::setStateKey(Controls::STATE_FORWARD, Input::KEY_PGUP);
	ControlsApp::setStateKey(Controls::STATE_BACKWARD, Input::KEY_PGDOWN);
	ControlsApp::setStateKey(Controls::STATE_MOVE_LEFT, Input::KEY_L);
	ControlsApp::setStateKey(Controls::STATE_MOVE_RIGHT, Input::KEY_R);
	ControlsApp::setStateMouseButton(Controls::STATE_JUMP, Input::MOUSE_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;
}

Additional information:附加信息:

  • For more information on managing user inputs using Input class, see the Input class article.有关使用Input类管理用户输入的更多信息,请参阅Input class文章。
  • For more information on managing user inputs using Controls class, see the Controls class article.有关使用Controls类管理用户输入的更多信息,请参阅Controls class文章。
  • For more information on managing user inputs using ControlsApp class, see the ControlsApp class article.更多关于使用ControlsApp类管理用户输入的信息,看到ControlsApp class文章。

Creating User Interface
创建用户界面#

In UNIGINE a Graphical User Interface (GUI) is composed of different types of widgets added to it. Basically, there are two ways of creating GUI:在 UNIGINE 中,图形用户界面 (GUI) 由添加到其中的不同类型的 widgets 组成。 基本上,有两种创建 GUI 的方法:

  • By adding widgets to the system GUI (Unigine user interface) that is rendered on top of application window.通过添加小部件到系统GUI (Unigine用户界面),呈现在应用程序窗口的顶部。
  • By adding widgets to a GUI object positioned in the world. In this case, any postprocessing filter can be applied.通过将小部件添加到位于世界中的 GUI Object。 在这种情况下,可以应用任何后处理过滤器。

To add elements to the system GUI you should use the Gui class.要向系统GUI添加元素,您应该使用Gui类。

注意
In order to use GUI widgets we must include the UnigineUserInterface.h library.为了使用GUI部件我们必须包括UnigineUserInterface.h图书馆。

There are 2 ways to create the GUI layout:有两种方法来创建GUI布局:

The following code demonstrates how to add a label and a slider to the system GUI:下面的代码演示了如何添加一个标签和一个滑块系统界面:

源代码 (C++)
#include <UnigineUserInterface.h>

// injecting Unigine namespace to the global namespace
using namespace Unigine;

GuiPtr gui;

int AppWorldLogic::init()
{

	// getting a GUI pointer
	gui = Gui::getCurrent();

	// 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;
}

In order to use GUI elements we must specify handlers for various events (click, change, etc.). The following code demonstrates how to set event handlers:为了使用GUI元素,我们必须为各种事件(单击、更改等)指定处理程序。下面的代码演示如何设置事件处理程序

源代码 (C++)
#include <UnigineUserInterface.h>

// injecting Unigine namespace to the global namespace
using namespace Unigine;

GuiPtr gui;

// EventConnections class instance to manage event subscriptions
EventConnections econnections;

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

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

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

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

int AppWorldLogic::init()
{

	/* .. */

	// getting a GUI pointer
	gui = Gui::getCurrent();

	// subscbribing for the clicked event with the onButton1Clicked function as a handler
	buttonwidget1->getEventClicked().connect(econnections, onButton1Clicked);

	// subscbribing for the clicked event with the onButton2Clicked function as a handler
	buttonwidget2->getEventClicked().connect(econnections, this, &AppWorldLogic::onButton2Clicked);

	buttonwidget1->getEventClicked().connect(econnections, this, &AppWorldLogic::onButtonDelClicked);

	// setting AppWorldLogic::onSliderChanged method as a changed event handler for a widget_slider
	widget_slider->getEventChanged().connect(econnections, this, &AppWorldLogic::onSliderChanged);
	/* .. */

	return 1;
}

int AppWorldLogic::shutdown()
{

	// removing all event subscriptions somewhere on shutdown
	econnections.disconnectAll();

	return 1;
}

Additional information:附加信息:

  • For more information on UI files, see the UI files article.有关UI文件的更多信息,请参阅 UI文件文章。
  • For more information on GUI-related API, see the GUI-related classes section.就API的更多信息,参见GUI-related classes部分。
  • For more information on the Gui class, see the Gui class article.有关Gui类的更多信息,请参阅Gui class文章。
  • For more information on handling events, see the Events Handling article.有关处理事件的更多信息,请参阅 事件处理 文章。

Playing Sound and Music
播放声音和音乐#

声源#

The SoundSource class is used to create directional sound sources. To create a sound source, create an instance of the SoundSource class and specify all required settings:SoundSource 类用于创建定向声源。 要创建声源,请创建 SoundSource 类的实例并指定所有必需的设置:

源代码 (C++)
// 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();

环境资源#

To play ambient background music create an instance of the AmbientSource class, specify all required parameters and enable it. Make sure to import the sound asset to the project.要播放环境背景音乐,创建一个AmbientSource类的实例,指定所有必需的参数并启用它。确保将声音资源导入到项目中。

注意
For an ambient source to be played, a player is always required. In case an ambient source needs to be played when neither a world, nor the editor are loaded, a player, as well as the sound source (see code below), should be created in the SystemLogic.Init() method; otherwise, no sound will be heard.对于要播放的环境源,始终需要 player。 如果在既不加载世界也不加载编辑器的情况下需要播放环境声源,则应在SystemLogic.Init()方法中创建播放器和声音源(参见下面的代码); 否则,将听不到任何声音。
源代码 (C++)
// 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();

Additional information:附加信息:

  • For more information on directional sound, see the SoundSource class article.有关定向声音的更多信息,请参见SoundSource类文章。
  • For more information on ambient sound, see the AmbientSource class article.有关环境声的更多信息,请参见AmbientSource类文章。

Setting Up Physics
设置物理#

An object should have a body and a shape to be affected by gravity and to collide with other physical objects:object 应该有一个 body 和一个受重力影响的 shape 并与其他物理对象碰撞:

源代码 (C++)
// cube 
ObjectMeshStaticPtr box = ObjectMeshStatic::create("core/meshes/box.mesh");

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

Catching Nodes with World Triggers
捕获节点与世界触发器#

A World trigger triggers events when any nodes (colliders or not) get inside or outside of them. The trigger can detect a node of any type by its bounding box. The trigger reacts to all nodes (default behavior).World trigger任何节点时触发回调(对撞机)内部或外部。任何类型的触发器可以检测到一个节点的边界框。触发器对所有节点(默认行为)。

The handler function of World Trigger is actually executed only when the next engine function is called: that is, before updatePhysics() (in the current frame) or before update() (in the next frame) - whatever comes first.World Trigger的回调函数实际上只在调用下一个引擎函数时执行:也就是说,在updatePhysics()(在当前帧中)之前或在update()(在下一帧中)之前——不管前面是什么。

注意
If you have moved some nodes and want to execute handler functions based on changed positions in the same frame, you need to call updateSpatial() first.如果你有一些节点移动,想要回调的基础上改变了位置在同一帧,需要调用updateSpatial()第一。

You can subscribe for Enter and Leave events with handler functions to be executed when a node enters or leaves the World Trigger. A handler function must receive a Node as its first argument.您可以订阅EnterLeave事件,并在节点进入或离开>World Trigger时执行处理函数。回调函数必须接收 Node作为它的第一个参数。

源代码 (C++)
// EventConnections class instance to manage event subscriptions
EventConnections econnections;

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

// implement the leave event handler
void AppWorldLogic::leave_event_handler(const 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 event handler to be executed when a node enters the world trigger
	trigger->getEventEnter().connect(econnections, this, &AppWorldLogic::enter_event_handler);

	// add the leave event handler to be executed when a node leaves the world trigger
	trigger->getEventLeave().connect(econnections, this, &AppWorldLogic::leave_event_handler);

	return 1;
}

int AppWorldLogic::shutdown()
{

	// removing all event subscriptions somewhere on shutdown
	econnections.disconnectAll();

	return 1;
}
最新更新: 2024-12-13
Build: ()