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

从虚幻引擎迁移到UNIGINE:编程

Game logic in an Unreal Engine project is implemented via Blueprint Script / C++ components. You got used to determine actor's behavior by writing event functions like InitializeComponent() and TickComponent().Unreal Engine项目中的游戏逻辑是通过Blueprint Script / C++ components实现的。您习惯于通过编写事件函数(例如InitializeComponent()TickComponent())来确定演员的行为。

In UNIGINE, you can create projects based on C++, C#, and UnigineScript API. A visual scripting feature is being under research and development at the moment.在UNIGINE中,您可以基于C ++,C#和UnigineScript API创建项目。目前正在研究视觉脚本功能。

The traditional workflow implies the Application Logic has three basic components that have different lifetimes:传统的工作流程意味着应用逻辑具有三个具有不同生命周期的基本组件:

  • SystemLogic (the AppSystemLogic.cpp source file) exists during the application life cycle. SystemLogicAppSystemLogic.cpp源文件)在应用程序生命周期中存在。
  • WorldLogic (the AppWorldLogic.cpp source file) takes effect only when a world is loaded. WorldLogicAppWorldLogic.cpp源文件)仅在加载世界时才生效。
  • EditorLogic (the AppEditorLogic.cpp source file) takes effect only when a custom editor is loaded (there is a class derived from the EditorLogic class). EditorLogicAppEditorLogic.cpp源文件)仅在加载自定义编辑器(存在从EditorLogic类派生的类)时生效。

Check out the Programming Quick Start series to get started in traditional C++ programming in UNIGINE.看看编程快速入门系列开始使用UNIGINE中的传统C ++编程。

Regarding components, UNIGINE has quite a similar concept, which can be easily adopted — C++ Component System, which is safe and secure and ensures high performance. Logic is written in C++ classes derived from the ComponentBase class, based on which the engine generates a set of component's parameters — Property that can be assigned to any node in the scene. Each component has a set of functions (init(), update(), etc.), that are called by the corresponding functions of the engine's main loop.关于组件,UNIGINE具有非常相似的概念,可以很容易地采用- C ++组件系统,这是安全可靠的,并确保了高性能。逻辑是使用从ComponentBase类派生的C ++类编写的,基于该类,引擎将生成一组组件的参数-财产可以分配给场景中的任何节点。每个组件都有一组功能(init(), update()等),这些功能由引擎的相应功能调用主循环

From here on, this article covers primarily programming in C++ using the C++ Component System as a more natural and familiar workflow for Unreal Engine users. Check out the Using C++ Component System article for an example for beginners.从这里开始,本文主要介绍使用C ++组件系统进行C ++编程,这是Unreal Engine用户更为自然和熟悉的工作流程。看看使用C ++组件系统本文为初学者提供了示例。

Programming in UNIGINE using C++ is not much different from programming in Unreal Engine except that you need to make some preparations and create headers files for components as in the natural coding in C++. For example, let's compare how simple components are created in both engines. In UE4:使用C ++在UNIGINE中进行编程与在Unreal Engine中进行编程没有太大区别,除了像在C ++中进行自然编码一样,您需要做一些准备并为组件创建头文件。例如,让我们比较一下两个引擎中创建简单组件的方式。在UE4中:

源代码 (C++)
UCLASS()
class UMyComponent : public UActorComponent
{
    GENERATED_BODY()

    // Called after the owning Actor was created
    void InitializeComponent();

    // Called when the component or the owning Actor is being destroyed
    void UninitializeComponent();

    // Component version of Tick
    void TickComponent(float DeltaTime, enum ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction);
};

And in UNIGINE. To make the things work, you need to initialize the Component System in the AppSystemLogic.cpp file:并在UNIGINE中。为了使事情正常进行,您需要在AppSystemLogic.cpp文件中初始化组件系统:

源代码 (C++)
/* .. */
#include <UnigineComponentSystem.h>

/* .. */

int AppSystemLogic::init()
{
	// Write here code to be called on engine initialization.
	Unigine::ComponentSystem::get()->initialize();
	return 1;
}

And then you can write a new component.然后,您可以编写一个新组件。

MyComponent.h:

源代码 (C++)
#pragma once
#include <Unigine.h>
#include <UnigineComponentSystem.h>

using namespace Unigine;
class MyComponent : public ComponentBase
{
public:
	// declare the component
	COMPONENT(MyComponent, ComponentBase);

	// declare methods to be called at the corresponding stages of the execution sequence
	COMPONENT_INIT(init);
	COMPONENT_UPDATE(update);
	COMPONENT_SHUTDOWN(shutdown);

	// declare the name of the property to be used in the Editor
	PROP_NAME("my_component");

protected:
	void init();
	void update();
	void shutdown();
};

MyComponent.cpp:

源代码 (C++)
#include "MyComponent.h"

// register the component in the Component System
REGISTER_COMPONENT(MyComponent);

// called on component initialization
void MyComponent::init(){}

// called every frame
void MyComponent::update(){}

// called on component or the owning node is being destroyed
void MyComponent::shutdown(){}

After that, you need to perform the following steps:之后,您需要执行以下步骤:

  1. Build the application using the IDE.使用IDE生成应用程序。
  2. Run the application once to get the component property generated by the engine.运行应用程序一次获取引擎生成的组件属性。
  3. Assign the property to a node.分配节点的属性。
  4. Finally, you can check out its work by launching the application.最后,您可以通过启动应用程序来检查其工作。


To learn more about the execution sequence and how to build components, follow the links below:

要了解有关执行顺序以及如何构建组件的更多信息,请单击以下链接:

For those who prefer C#, UNIGINE allows creating C# applications using C# API and, if required, C# Component System.对于喜欢C#的用户,UNIGINE允许创建C#应用程序使用C#API,并在需要时使用C#组件系统

Writing Gameplay Code
编写游戏代码#

Printing to Console
打印到控制台#

Unreal Engine UNIGINE
源代码 (C++)
UE_LOG(LogTemp, Warning, TEXT("Your message"));
源代码 (C++)
Log::message("Debug info: %s\n", text);
Log::message("Debug info: %d\n", number);

See Also
也可以看看#

Loading a Scene
加载场景#

Unreal Engine UNIGINE
源代码 (C++)
UGameplayStatics::OpenLevel(GetWorld(), TEXT("MyLevelName"));
源代码 (C++)
World::loadWorld("YourSceneName");

Accessing Actor/Node from Component
从组件访问Actor /Node#

Unreal Engine UNIGINE
源代码 (C++)
MyComponent->GetOwner();
源代码 (C++)
NodePtr owning_node = node;

See Also
也可以看看#

Accessing a Component from the Actor/Node
从Actor / Node访问组件#

Unreal Engine:

源代码 (C++)
UMyComponent* MyComp = MyActor->FindComponentByClass<UMyComponent>();

UNIGINE:

源代码 (C++)
MyComponent* my_component = getComponent<MyComponent>(node);

Finding Actors/Nodes
查找Actor/Node#

Unreal Engine:

源代码 (C++)
// Find Actor by name (also works on UObjects)
AActor* MyActor = FindObject<AActor>(nullptr, TEXT("MyNamedActor"));

// Find Actors by type (needs a UWorld object)
for (TActorIterator<AMyActor> It(GetWorld()); It; ++It)
{
	AMyActor* MyActor = *It;
	// ...
}

UNIGINE:

源代码 (C++)
// Find a Node by name
NodePtr my_node = World::getNodeByName("my_node");

// Find all nodes having this name
Vector<NodePtr> nodes;
World::getNodesByName("test", nodes);

// Find the index of a direct child node
int index = node->findChild("child_node");
NodePtr direct_child = node->getChild(index);

// Perform a recursive descend down the hierarchy to find a child Node by name
NodePtr child = node->findNode("child_node", 1);

Casting From Type to Type
从类型到类型的转换#

Downcasting (from a pointer-to-base to a pointer-to-derived) is performed using different constructions. To perform Upcasting (from a pointer-to-derived to a pointer-to-base) you can simply use the instance itself.向下转换(从指向基础指针到指向派生的指针)是使用不同的构造执行的。要执行Upcasting (从派生指针到基础指针),您可以简单地使用实例本身。

Unreal Engine:

源代码 (C++)
UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass());
USphereComponent* SphereCollider = Cast<USphereComponent>(Primitive);
if (SphereCollider != nullptr)
{
// ...
}

UNIGINE:

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

Destroy Actor/Node
销毁Actor/Node#

Unreal Engine UNIGINE
源代码 (C++)
MyActor->Destroy();

// destroy the actor with 1 second delay
MyActor->SetLifeSpan(1);
源代码 (C++)
node.deleteLater(); // recommended option
//called between the current and the next frames

node.deleteForce(); // called during the same frame but unsafe

To perform deferred removal of a node in UNIGINE, you can create a component that will be responsible for the timer and deletion.要在UNIGINE中执行节点的延迟删除,您可以创建一个负责计时器和删除的组件。

Instantiating Actor / Node Reference
实例化Actor /Node参考#

In UE4, you create a clone of an actor the following way:UE4中,您可以通过以下方式创建actor的副本:

源代码 (C++)
AMyActor* CreateCloneOfMyActor(AMyActor* ExistingActor, FVector SpawnLocation, FRotator SpawnRotation)
{
UWorld* World = ExistingActor->GetWorld();
FActorSpawnParameters SpawnParams;
SpawnParams.Template = ExistingActor;
World->SpawnActor<AMyActor>(ExistingActor->GetClass(), SpawnLocation, SpawnRotation, SpawnParams);
}

In UNIGINE, you should use World::loadNode to load a hierarchy of nodes from a .node asset. In this case the hierarchy of nodes that was saved as a NodeReference will be added to the scene. You can refer to the asset either via a component parameter or manually by providing the virtual path to it:在UNIGINE中,应使用World::loadNode.node资源加载节点的层次结构。在这种情况下,将另存为NodeReference的节点层次结构添加到场景中。您可以通过以下方式引用资源:组件参数或通过提供虚拟路径对此:

源代码 (C++)
// MyComponent.h
PROP_PARAM(File, node_to_spawn);
源代码 (C++)
// MyComponent.cpp
/* .. */

void MyComponent::init() 
{

	// load a hierarchy of nodes from the asset
	NodePtr spawned = World::loadNode(node_to_spawn.get());
	spawned->setWorldPosition(node->getWorldPosition());

	NodePtr spawned_manually = World::loadNode("nodes/node_reference.node");
}

In case of using the approach of component parameters, you should also specify the .node asset:如果使用组件参数方法,则还应指定.node资源:

You can also spawn the NodeReference as a single node (without extracting the content) in the world:您还可以将NodeReference生成为世界上的单个节点(不提取内容):

源代码 (C++)
void MyComponent::update() 
{

	NodeReferencePtr nodeRef = NodeReference::create("nodes/node_reference_0.node");
}

Triggers
扳机 (Triggers)#

Unreal Engine:

源代码 (C++)
UCLASS()
class AMyActor : public AActor
{
GENERATED_BODY()

// My trigger component
UPROPERTY()
UPrimitiveComponent* Trigger;

AMyActor()
{
	Trigger = CreateDefaultSubobject<USphereComponent>(TEXT("TriggerCollider"));

	// Both colliders need to have this set to true for events to fire
	Trigger.bGenerateOverlapEvents = true;

	// Set the collision mode for the collider
	// This mode will only enable the collider for raycasts, sweeps, and overlaps
	Trigger.SetCollisionEnabled(ECollisionEnabled::QueryOnly);
}

virtual void NotifyActorBeginOverlap(AActor* Other) override;

virtual void NotifyActorEndOverlap(AActor* Other) override;
};

In UNIGINE, Trigger is a special built-in node type that raises events in certain situations:在UNIGINE中,Trigger是一种特殊的内置节点类型,在某些情况下会引发事件:

WorldTrigger is the most common type that can be used in gameplay. Here is an example on how to use it:WorldTrigger是可用于游戏中的最常见类型。这是有关如何使用它的示例:

源代码 (C++)
WorldTriggerPtr trigger;
EventConnectionId enter_event_connection;

// implement the enter event handler
void AppWorldLogic::enter_event_handler(const Unigine::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 Unigine::NodePtr &node) {
	Log::message("\nA node named %s has left the trigger\n", node->getName());
}

int AppWorldLogic::init()
{

	node = NodeDummy::create();
	node2 = NodeDummy::create();
	node2->setParent(node);
	node2->setName("child_node");

	// create a world trigger node
	trigger = WorldTrigger::create(Math::vec3(3.0f));

	// subscribe for the enter ID to remove subscription when necessary
	enter_event_connection = trigger->getEventEnter().connect(this, &AppWorldLogic::enter_event_handler);
	// subscribe for the leave event (when a node leaves the world trigger)
	trigger->getEventLeave().connect(this, &AppWorldLogic::leave_event_handler);

	return 1;
}

Input
输入#

UE4 Input:UE4输入:

源代码 (C++)
UCLASS()
class AMyPlayerController : public APlayerController
{
    GENERATED_BODY()

    void SetupInputComponent()
    {
        Super::SetupInputComponent();

        InputComponent->BindAction("Fire", IE_Pressed, this, &AMyPlayerController::HandleFireInputEvent);
        InputComponent->BindAxis("Horizontal", this, &AMyPlayerController::HandleHorizontalAxisInputEvent);
        InputComponent->BindAxis("Vertical", this, &AMyPlayerController::HandleVerticalAxisInputEvent);
    }

    void HandleFireInputEvent();
    void HandleHorizontalAxisInputEvent(float Value);
    void HandleVerticalAxisInputEvent(float Value);
};

UNIGINE:

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

You can also use the ControlsApp class to handle control bindings. To configure the bindings, open the Controls settings:您也可以使用ControlsApp类来处理控件绑定。要配置绑定,请打开Controls设置:

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

void MyInputController::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");
	}
}

Ray Tracing
光线追踪#

Unreal Engine:

源代码 (C++)
APawn* AMyPlayerController::FindPawnCameraIsLookingAt()
{
// You can use this to customize various properties about the trace
FCollisionQueryParams Params;
// Ignore the player's pawn
Params.AddIgnoredActor(GetPawn());

// The hit result gets populated by the line trace
FHitResult Hit;

// Raycast out from the camera, only collide with pawns (they are on the ECC_Pawn collision channel)
FVector Start = PlayerCameraManager->GetCameraLocation();
FVector End = Start + (PlayerCameraManager->GetCameraRotation().Vector() * 1000.0f);
bool bHit = GetWorld()->LineTraceSingle(Hit, Start, End, ECC_Pawn, Params);

if (bHit)
{
	// Hit.Actor contains a weak pointer to the Actor that the trace hit
	return Cast<APawn>(Hit.Actor.Get());
}

return nullptr;
}

In UNIGINE the same is handled by Intersections:在UNIGINE中,相同的处理方式为交叉口

源代码 (C++)
#include "MyComponent.h"

using namespace Unigine;
using namespace Math;

REGISTER_COMPONENT(MyComponent);

void MyComponent::init() 
{

	Visualizer::setEnabled(true);
}

void MyComponent::update() 
{

	ivec2 mouse = Input::getMousePosition();
	float length = 100.0f;
	Vec3 start = Game::getPlayer()->getWorldPosition();
	Vec3 end = start + Vec3(Game::getPlayer()->getDirectionFromMainWindow(mouse.x, mouse.y)) * length;

	// ignore surfaces that have certain bits of the Intersection mask enabled
	int mask = ~(1 << 2 | 1 << 4);

	WorldIntersectionNormalPtr intersection = WorldIntersectionNormal::create();

	ObjectPtr obj = World::getIntersection(start, end, mask, intersection);

	if (obj)
	{
		Vec3 point = intersection->getPoint();
		vec3 normal = intersection->getNormal();
		Visualizer::renderVector(point, point + Vec3(normal), vec4_one);
		Log::message("Hit %s at (%f,%f,%f)\n", obj->getName(), point.x, point.y, point.z);
	}
}
最新更新: 2024-12-13
Build: ()