This page has been translated automatically.
视频教程
界面
要领
高级
实用建议
专业(SIM)
UnigineEditor
界面概述
资源工作流程
版本控制
设置和首选项
项目开发
调整节点参数
Setting Up Materials
设置属性
照明
Sandworm
使用编辑器工具执行特定任务
如何擴展編輯器功能
嵌入式节点类型
Nodes
Objects
Effects
Decals
光源
Geodetics
World Nodes
Sound Objects
Pathfinding Objects
Players
编程
基本原理
搭建开发环境
使用范例
C++
C#
UnigineScript
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

编程快速参考

介绍#

基本场景对象#

对于 UNIGINE 而言,节点 是一种基本类型,所有类型的场景对象都继承自该类型。 其中一些在视觉上出现:ObjectsDecalsEffects,它们 都有 surfaces 来表示它们的几何体(网格),而其他的(Light Sources玩家等)是不可见的。

每个节点有一个变换矩阵的编码位置,旋转,世界上节点的规模。

所有添加到场景中的场景对象,无论其类型,都称为节点。

附加信息:

坐标系统#

Unigine中的3D空间用右手笛卡尔坐标系表示:XY轴形成一个水平面,Z轴指向上方 . 当 exporting 从 3D 编辑器中导出动画时,Y 被视为正向。

坐标系统

积极的旋转角度设置逆时针旋转。它对应于右手法则:如果你设置的右手拇指沿轴,其他手指包裹将显示旋转的方向。

旋转的方向

附加信息:

  1. 有关 UNIGINE 节点类型的更多信息,请参阅 内置节点类型 部分。
  2. 有关通过API管理节点的更多信息,请参见节点相关类小节。

日志和打印消息到控制台#

打印消息日志文件和控制台监控整体进度的执行应用程序和报告错误,可以用于调试。Log类可以打印格式化字符串消息日志文件和控制台。下面的代码演示了如何打印各种类型的消息:

注意
要启用在屏幕覆盖显示消息,使用以下命令: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);

附加信息:

  • 有关控制台的更多信息,请参阅Console文章。
  • Log类的更多信息,请参见Log类文章。

保存和加载一个世界#

有些应用程序管理单个世界,而其他应用程序则需要管理多个世界。在任何情况下,知道如何保存当前世界和加载其他世界是非常有用的。为了解决这个任务,我们应该使用World类,它被设计成一个单例。

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

using namespace Unigine;
/* .. */

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

我们也可以通过 console,通过使用Console类来实现同样的功能,它也被设计为单例。

注意
为了使用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");

附加信息:

  • 管理世界通过API的更多信息,请参阅World类文章。
  • 有关控制台和可用命令的更多信息,请参阅 console 文章。
  • 管理控制台通过API的更多信息,参见Console类文章。
  • 有关管理将通过API保存的世界节点的更多信息,请参阅Node类的方法。

关闭应用程序#

任何应用程序都需要在某个时刻关闭。要关闭您的应用程序,您应该使用Engine类。

要关闭应用程序,需要使用以下代码:

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

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

在运行时创建和删除节点#

在运行时创建和删除节点几乎和在编辑器中一样简单。基本的动作集如下:

  • 创建。我们应该创建一个节点为节点的类型声明一个智能指针我们要创建和调用相应的类的构造函数在必要时提供施工参数。
  • 删除>
源代码 (C++)
// creating a node of the NodeType named nodename
<NodeType>Ptr nodename = <NodeType>::create(<construction_parameters>);
	
// removing the node
nodename.deleteLater();

现在让我们以从资源加载网格为例,说明使用简单静态网格 (ObjectMeshStatic) 创建和删除节点的过程。

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

/* .. */
int AppWorldLogic::init() 
{
	// first, we're creating a mesh instance as it is required to call an ObjectMeshStatic constructor
	MeshPtr mesh = Mesh::create();

	// loading a mesh from an fbx model imported to the data/fbx/ folder using the Editor
	mesh->load("fbx/model.fbx/model.mesh");

	// creating a box surface with a given size to the mesh
	mesh->addBoxSurface("box_surface", Math::vec3(0.5f, 0.5f, 0.5f));

	//create an ObjectMeshStatic node
	ObjectMeshStaticPtr my_object = ObjectMeshStatic::create(mesh);

	// removing the node
	my_object.deleteLater();

	// clearing the mesh
	mesh.clear();

	return 1;
}

附加信息:

  • 您可以创建原语通过代码使用Primitives类。
  • 有关管理世界节点的更多信息,请参阅Node类的方法。

创建和设置一个相机#

一个 camera是一个对世界的视口,没有它你实际上将看不到任何东西。相机在UNIGINE使用players管理。当你添加一个新的播放器时,它会创建一个camera,并指定控件mask ,该相机的后期处理材质。

为了设置一个新球员活跃我们应该使用Game设计成一个单例类。

注意
为了使用Game类和PlayerSpectator类,我们必须包含UnigineGame.hUniginePlayers.h库。

以下代码说明了 PlayerSpectator 的创建并将其设置为活动游戏摄像机。

注意
默认情况下,玩家使用的是一套标准的控制方式,如果需要的话,可以重写这些控制方式。
源代码 (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;
}

附加信息:

  • 有关玩家的更多信息,请参阅 players 文章。
  • 有关播放器 API 的更多信息,请参阅 播放器相关类 文章。
  • 有关Game类的更多信息,请参阅Game类文章。

创建和设置光源#

照明是每个场景定义颜色和你的对象的最终外观的基础。UNIGINE中的灯的创建方式与所有节点相同。

让我们考虑建立一个世界光源为例:

源代码 (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;
}

附加信息:

  • 有关光源的更多信息,请参阅 光源 文章。
  • 有关光源API的更多信息,请参阅灯相关类文章。

在运行时创建、应用和删除素材#

材质分配给节点的特定表面决定了节点的渲染方式。 他们实现 shaders 并控制 optionsstates参数和不同类型的纹理 用于在 渲染通道 期间渲染节点。 为了管理材质,我们使用以下两个类:

  • Materials类代表一个接口来管理材质加载。
  • Material类,用于管理每个单独的材质。
注意
为了使用材质我们必须包括UnigineMaterials.h

可以使用下面的代码来创建一个新的材质继承了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;
}

附加信息:

  • 有关通过API创建和编辑素材的更多信息,请参阅Material类文章。
  • 管理加载材质通过API的更多信息,参见Materials类文章。
  • 有关材质文件格式的详细信息,请参阅 材质文件 部分。
  • 有关物料参数的更多信息,请参见%UNIGINE_SDK_BROWSER_INSTALLATION_FOLDER%/sdks/%CURRENT_SDK%/data/core/materials/default/文件夹中的物料文件。

管理现有的场景对象#

不是世界上所有的内容是在运行时创建的,所以我们应该能够操作节点已经存在。我们怎么得到现有对象的指针来管理他们吗?这就是World类再次发挥作用。基本上,有两种方法我们可以得到一个指向某个节点World类的使用方法:

注意
为了能够使用World类的方法,我们必须包含UnigineWorld.h库。

这些方法返回一个NodePtr值,这是一个指向基类的指针,但是为了与某个对象执行操作(例如 ObjectMeshDynamicPtr)我们需要执行 的向下类型转换(即转换从pointer-to-base pointer-to-derived)。

有时你可能还需要执行 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;

有以下方法让一个组件从一个节点:

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

附加信息:

  • 有关管理智能指针的更多信息,请参阅使用智能指针一文。
  • 有关更多信息,请参阅World类文章。

执行基本的转换(移动、旋转、规模)#

每个节点都有一个转换矩阵,它对节点在世界中的位置、旋转和规模进行编码。如果一个节点被添加为另一个节点的子节点,则它有一个与其父节点相关的转换矩阵。这就是为什么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);

附加信息:

  • 有关矩阵变换的更多信息,请参阅矩阵变换文章。

Framerate-independent使游戏过程#

由于我们的应用程序的帧速率可能会有所不同(即 AppWorldLogic::update() 方法会或多或少地被调用 )取决于硬件,我们应该做一些事情来确保某些动作在相同的时间段内执行,而不管帧速率如何(例如每秒更改一次等)。 为了使您的游戏帧速率独立,您可以使用以下方法返回的缩放乘数(完成最后一帧所需的时间,以秒为单位):

  • Engine::getIfps()返回应用程序的反FPS值。
  • Game::getIfps()返回缩放的反FPS值。这个类是用来当你想加快,放慢或暂停渲染,物理或游戏逻辑。

要更改转换,您可以使用以下代码:

源代码 (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;
}
/* .. */

执行一些变化一旦在一定的时间内你可以使用下面的代码:

源代码 (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;
}
/* .. */

附加信息:

  • Game类的更多信息,参见Game类文章。
  • 有关Engine类的更多信息,请参阅Engine类文章。

管理十字路口#

intersection 在3D应用中被广泛使用。在UNIGINE中有三种主要的交叉口类型:

  • World intersection - objectsnodes 的交集。
  • Physics intersection - 一个具有形状碰撞对象的交集。
  • Game intersection - 与 障碍 等寻路节点的交集。

但是有一些条件来检测与表面的相交:

  1. 表面是启用的。
  2. 该表面已指定了一种材质。
  3. 每个表面Intersection标志启用。

    注意
    您可以使用Object.setIntersection()函数将该标志设置为对象的表面。

下面的代码说明了几种使用世界交点的方法:

  • 找出与边界框相交的所有结点
  • 找到所有节点分割的边界范围
  • 找出与边界截锥相交的所有结点
  • 找到第一个对象分割的射线
源代码 (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;
}

附加信息:

  • 有关交叉口的更多信息,请参阅 Intersections 文章。
  • 有关通过API管理World交集的更多信息,请参阅World类文章。
  • 有关通过API管理Game交集的更多信息,请参阅Game类文章。
  • 有关管理的更多信息Physics路口通过API,看到Physics类文章。

获取和管理用户输入#

大多数应用程序被设计成与用户交互。在UNIGINE中,你可以使用以下类来管理用户输入:

下面的代码演示了如何使用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;
}

下面的代码演示了如何使用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;
}

下面的代码演示了如何使用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;
}

附加信息:

  • 有关使用Input类管理用户输入的更多信息,请参阅Input class文章。
  • 有关使用Controls类管理用户输入的更多信息,请参阅Controls class文章。
  • 更多关于使用ControlsApp类管理用户输入的信息,看到ControlsApp class文章。

创建用户界面#

在 UNIGINE 中,图形用户界面 (GUI) 由添加到其中的不同类型的 widgets 组成。 基本上,有两种创建 GUI 的方法:

  • 通过添加小部件到系统GUI (Unigine用户界面),呈现在应用程序窗口的顶部。
  • 通过将小部件添加到位于世界中的 GUI Object。 在这种情况下,可以应用任何后处理过滤器。

要向系统GUI添加元素,您应该使用Gui类。

注意
为了使用GUI部件我们必须包括UnigineUserInterface.h图书馆。

有两种方法来创建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;
}

为了使用GUI元素,我们必须为各种事件(单击、更改等)指定处理程序。下面的代码演示如何设置事件处理程序

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

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

GuiPtr gui;

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

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

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

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

	return 1;
}

附加信息:

  • 有关UI文件的更多信息,请参阅 UI文件文章。
  • 就API的更多信息,参见GUI-related classes部分。
  • 有关Gui类的更多信息,请参阅Gui class文章。
  • 有关处理事件的更多信息,请参阅 事件处理 文章。

播放声音和音乐#

声源#

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

环境资源#

要播放环境背景音乐,创建一个AmbientSource类的实例,指定所有必需的参数并启用它。确保将声音资源导入到项目中。

注意
对于要播放的环境源,始终需要 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();

附加信息:

  • 有关定向声音的更多信息,请参见SoundSource类文章。
  • 有关环境声的更多信息,请参见AmbientSource类文章。

设置物理#

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

捕获节点与世界触发器#

World trigger任何节点时触发回调(对撞机)内部或外部。任何类型的触发器可以检测到一个节点的边界框。触发器对所有节点(默认行为)。

World Trigger的回调函数实际上只在调用下一个引擎函数时执行:也就是说,在updatePhysics()(在当前帧中)之前或在update()(在下一帧中)之前——不管前面是什么。

注意
如果你有一些节点移动,想要回调的基础上改变了位置在同一帧,需要调用updateSpatial()第一。

您可以订阅EnterLeave事件,并在节点进入或离开>World Trigger时执行处理函数。回调函数必须接收 Node作为它的第一个参数。

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

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(this, &AppWorldLogic::enter_event_handler);
	
	// add the leave event handler to be executed when a node leaves the world trigger
	trigger->getEventLeave().connect(this, &AppWorldLogic::leave_event_handler);
	

	return 1;
}
最新更新: 2024-02-27
Build: ()