# Migrating to UNIGINE from Unreal Engine: Programming 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()***. 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. The *traditional* workflow implies the [Application Logic](../../code/fundamentals/execution_sequence/app_logic_system.md) has three basic components that have different lifetimes: - **SystemLogic** (the `AppSystemLogic.cpp` source file) exists during the application life cycle. - **WorldLogic** (the `AppWorldLogic.cpp` source file) takes effect only when a world is loaded. - **EditorLogic** (the `AppEditorLogic.cpp` source file) takes effect only when a custom editor is loaded (there is a class derived from the *EditorLogic* class). Check out the [Programming Quick Start](../../start/quick_start/intro_cpp.md) series to get started in traditional C++ programming in UNIGINE. Regarding components, UNIGINE has quite a similar concept, which can be easily adopted � [C++ Component System](../../principles/component_system/component_system_cpp/index.md), 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](../../principles/properties/index.md)** 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](../../code/fundamentals/execution_sequence/code_update.md). 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](../../code/usage/using_component_system/index.md)** article for an example for beginners. 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*: ```cpp 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: ```cpp /* .. */ #include /* .. */ 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:` ```cpp #pragma once #include #include 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:` ```cpp #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. 2. [Run the application](../../migration/from_ue/index.md#editor_play) once to get the component property generated by the engine. 3. [Assign](../../editor2/properties_settings/organizing_properties/index.md#assign_property) 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: - [Execution Sequence](../../code/fundamentals/execution_sequence/code_update.md) - [Using C++ Component System](../../code/usage/using_component_system/index.md) For those who prefer C#, UNIGINE allows creating [C# applications](../../code/csharp/application.md) using C# API and, if required, [C# Component System](../../principles/component_system/component_system_cs/index.md). ## Writing Gameplay Code ### Printing to Console | Unreal Engine | UNIGINE | |---|---| | ```cpp UE_LOG(LogTemp, Warning, TEXT("Your message")); ``` | ```cpp Log::message("Debug info: %s\n", text); Log::message("Debug info: %d\n", number); ``` | #### See Also - More types of messages in the *[Log](../../api/library/common/class.log_cpp.md)* class API - [Video tutorial](https://youtu.be/6MUeatvw9v0) demonstrating how to print user messages to console using C# Component System ### Loading a Scene | Unreal Engine | UNIGINE | |---|---| | ```cpp UGameplayStatics::OpenLevel(GetWorld(), TEXT("MyLevelName")); ``` | ```cpp World::loadWorld("YourSceneName"); ``` | ### Accessing Actor/Node from Component | Unreal Engine | UNIGINE | |---|---| | ```cpp MyComponent->GetOwner(); ``` | ```cpp NodePtr owning_node = node; ``` | #### See Also - [Video tutorial](https://youtu.be/KjEGI311NiA) demonstrating how to access nodes from components using C# Component System. ### Accessing a Component from the Actor/Node Unreal Engine: ```cpp UMyComponent* MyComp = MyActor->FindComponentByClass(); ``` UNIGINE: ```cpp MyComponent* my_component = getComponent(node); ``` ### Finding Actors/Nodes Unreal Engine: ```cpp // Find Actor by name (also works on UObjects) AActor* MyActor = FindObject(nullptr, TEXT("MyNamedActor")); // Find Actors by type (needs a UWorld object) for (TActorIterator It(GetWorld()); It; ++It) { AMyActor* MyActor = *It; // ... } ``` UNIGINE: ```cpp // Find a Node by name NodePtr my_node = World::getNodeByName("my_node"); // Find all nodes having this name Vector 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. Unreal Engine: ```cpp UPrimitiveComponent* Primitive = MyActor->GetComponentByClass(UPrimitiveComponent::StaticClass()); USphereComponent* SphereCollider = Cast(Primitive); if (SphereCollider != nullptr) { // ... } ``` UNIGINE: ```cpp // 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(baseptr); // static cast (pointer-to-derived from pointer-to-base) ObjectMeshDynamicPtr derivedptr = static_ptr_cast(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 | Unreal Engine | UNIGINE | |---|---| | ```cpp MyActor->Destroy(); // destroy the actor with 1 second delay MyActor->SetLifeSpan(1); ``` | ```cpp 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. ### Instantiating Actor / Node Reference In *UE4*, you create a clone of an actor the following way: ```cpp AMyActor* CreateCloneOfMyActor(AMyActor* ExistingActor, FVector SpawnLocation, FRotator SpawnRotation) { UWorld* World = ExistingActor->GetWorld(); FActorSpawnParameters SpawnParams; SpawnParams.Template = ExistingActor; World->SpawnActor(ExistingActor->GetClass(), SpawnLocation, SpawnRotation, SpawnParams); } ``` In UNIGINE, you should use *[World::loadNode](../../api/library/engine/class.world_cpp.md#loadNode_cstr_int_Node)* to load a hierarchy of nodes from a `.node` asset. In this case the hierarchy of nodes that was saved as a *[NodeReference](../../migration/from_ue/index.md#blueprint)* will be added to the scene. You can refer to the asset either via a [component parameter](../../api/library/common/logic/component_system/cpp/class.componentbase_cpp.md#parameters) or manually by providing the [virtual path](../../principles/filesystem/index_cpp.md#virtual_paths) to it: ```cpp // MyComponent.h PROP_PARAM(File, node_to_spawn); ``` ```cpp // 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: ![](unigine_drag_noderef.png) You can also spawn the *NodeReference* as a single node (without extracting the content) in the world: ```cpp void MyComponent::update() { NodeReferencePtr nodeRef = NodeReference::create("nodes/node_reference_0.node"); } ``` ## Triggers Unreal Engine: ```cpp UCLASS() class AMyActor : public AActor { GENERATED_BODY() // My trigger component UPROPERTY() UPrimitiveComponent* Trigger; AMyActor() { Trigger = CreateDefaultSubobject(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: - **[NodeTrigger](../../api/library/nodes/class.nodetrigger_cpp.md)** is used to track events for the *Trigger* node - event handlers are executed when the *Trigger* node is [enabled](../../api/library/nodes/class.nodetrigger_cpp.md#getEventEnabled_Event) or its [position](../../api/library/nodes/class.nodetrigger_cpp.md#getEventPosition_Event) has changed. - **[WorldTrigger](../../api/library/worlds/class.worldtrigger_cpp.md)** is used to track events for any node (collider or not) that gets [inside](../../api/library/worlds/class.worldtrigger_cpp.md#getEventEnter_Event) or [outside](../../api/library/worlds/class.worldtrigger_cpp.md#getEventLeave_Event) of it. - **[PhysicalTrigger](../../api/library/physics/class.physicaltrigger_cpp.md)** triggers events when physical objects get [inside](../../api/library/physics/class.physicaltrigger_cpp.md#getEventEnter_Event) or [outside](../../api/library/physics/class.physicaltrigger_cpp.md#getEventLeave_Event) of it. > **Notice:** *PhysicalTrigger* does not handle collision events, for that purpose [Bodies and Joints](../../code/fundamentals/events/index_cpp.md#physics) provide their own events. **WorldTrigger** is the most common type that can be used in gameplay. Here is an example on how to use it: ```cpp 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: ```cpp 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: ```cpp 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](../../api/library/controls/class.controlsapp_cpp.md)** class to handle control bindings. To configure the bindings, open the *[Controls](../../editor2/settings/controls/index.md)* settings: ```cpp 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: ```cpp 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(Hit.Actor.Get()); } return nullptr; } ``` In UNIGINE the same is handled by [Intersections](../../code/usage/intersections/index_cpp.md): ```cpp #include "MyComponent.h" #include #include #include #include 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); } } ```