📜 ⬆️ ⬇️

Creating an editor quest and dialogs for the Unreal engine: Part 2 technical aspects

image

Hello, my name is Dmitry. I create computer games on the Unreal Engine as a hobby. Today I will continue to talk about the plugin for editing quests and dialogues. In the previous article I told how to use the plugin, today I will tell you what you need to know so that the plugin can interact with the game world and its interface.

First of all, to connect the plugin to your project, you need to move the StoryGraphPlugin folder from the plugins folder to the directories folder of your project \ plugins. You may have to re-compile the game if the Unreal engine versions do not match.

But even after you create an Asset StoryGraph you will need to perform some manipulations. All subject objects that you place on the map should be derived from certain objects, here is their list:
')
1) ACharecter_StoryGraph - the base class for characters has blueprint methods:
ChangeState - The method changes the character's state in this case from live to dead, that is, in the blueprint, you need to create a function that, when a character dies, will switch its state, and then StoryGraph will understand that the character has died.
OpenDialog () - Blueprint method that you need to call to start a dialogue with this character.
GetObjectName () - Blueprint method that returns the name of the character defined in StoryGraph, if you have placed several StoryGraph objects at the level and the name of this character is different in each of them, the function will return the name from the first StoryGraph.
GetMessegeFromStoryGraph () - The Blueprint event occurs when a Send Message node is activated in StoryGraph, which sends a message to this character.

2) APlaceTrigger_StoryGraph - The base class for a trigger has blueprint methods:
GetPlaceTriggerType () - Needed to find out in which mode the trigger works.
ChangeState () - Similar to the method from ACharecter_StoryGraph, only states other (UnActive, Active). It should be used in the mode of operation of the UnInteractive trigger.
Activate () - This method is not an analogy of the previous one, because it just does not work in UnInteractive mode but works in the other two. In the Interactive mode, it activates the trigger, and in the AdvanceInteractive mode it opens the interaction dialog box with the trigger.
Like the previous class, this one has the blueprint methods GetObjectName () and GetMessegeFromStoryGraph ().

3) AInventoryItem_StoryGraph - The base class for the subject of the inventory. In addition to the above mentioned GetObjectName () and GetMessegeFromStoryGraph (), this class has a method
PickUp () - Which moves an item to the character's inventory.

4) AOtherActor_StoryGraph - Class for objects not belonging to the above categories. Only GetObjectName () and GetMessegeFromStoryGraph () do not have their own methods.

5) ALevelScriptActor_StoryGraph - The base class for LevelBluprint has one Blueprint event
GetMessegeFromStoryGraph () - Which is called when the node is activated Send message to level blueprint

In addition to objects on the map, StoryGraph interacts with the interface elements, here is their list:

1) AHUD_StoryGraph - The base class for your HUD. Has methods:
EndGame () - Blueprint event that is called when the Game Over node is triggered.
PrintQuestPhaseOnScreen () - A blueprint event that is triggered when the node triggers Print quest phase on screen.
OpenDialogEvent () - A blueprint event is called when one of the characters calls OpenDialog ().
OpenPlaceTriggerMessagesEvent () - Same for the trigger.
ChangeLocalization () - Changes the language in the game.
GetCurrentLocalization () - Returns the current language.

2) UGameScreen_StoryGraphWidget - The base class for the widget located on the game screen. This widget displays messages for the player as well as the so-called DefaultAnswer that the player sees if the response

3) UJurnal_StoryGraphWidget - Widget displays currently active quests on the screen.

4) URadar_StoryGraphWidget - Radar it needs to be placed in the GameScreen widget and it will display targets

5) UDialog_StoryGraphWidget - Displays the dialog window or the interaction window with the trigger.

6) UInventory_StoryGraphWidget - Widget displays the inventory in which the subject objects are located.

After that, everything will work as it should.

Problems encountered


Now I’ll tell you about the problems that I had to solve in order to write this plugin.

The first problem was that the level in the Unreal Engine is a closed system; all pointers of objects that are at the level must point to objects at the level and objects outside the level cannot have pointers pointing to objects at the level. And this is a problem because the StoryGraph object is out of level, but its objects need to be brought to the level objects. Fortunately, there are so-called lazy pointers (TAssetPtr) in the Unreal engine. They are lazy because they can refer to objects that are not currently loaded, and also with the help of them you can refer to objects of a level from objects that are outside of it.

The second problem for me was the fact that the graph object (UEdGraph) cannot be used in the game. That is, the game is compiled and even packaged, but when you try to run this game, it will simply crash with an error. Therefore, I simply stack the nodes into an array and on the graph I place an “adapter object” UProxyNodeBase which simply redirects method calls to my nodes when editing in the editor. And this object itself is located in the editorial module, therefore, when packing the game, it is not taken into account.

Well, when I wrote a plugin, I tried to compile it in Development mode and get this:

Errors
UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void * __cdecl operator new(unsigned __int64)" (??2@YAPEAX_K@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void * __cdecl operator new(unsigned __int64,struct std::nothrow_t const &)" (??2@YAPEAX_KAEBUnothrow_t@std@@@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void __cdecl operator delete(void *)" (??3@YAXPEAX@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void __cdecl operator delete(void *,struct std::nothrow_t const &)" (??3@YAXPEAXAEBUnothrow_t@std@@@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void * __cdecl operator new[](unsigned __int64)" (??_U@YAPEAX_K@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void * __cdecl operator new[](unsigned __int64,struct std::nothrow_t const &)" (??_U@YAPEAX_KAEBUnothrow_t@std@@@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void __cdecl operator delete[](void *)" (??_V@YAXPEAX@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void __cdecl operator delete[](void *,struct std::nothrow_t const &)" (??_V@YAXPEAXAEBUnothrow_t@std@@@Z) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "void __cdecl UELinkerFixupCheat(void)" (?UELinkerFixupCheat@@YAXXZ) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "bool const GIsDebugGame" (?GIsDebugGame@@3_NB) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "class FFixedUObjectArray * & GObjectArrayForDebugVisualizers" (?GObjectArrayForDebugVisualizers@@3AEAPEAVFFixedUObjectArray@@EA) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "bool GIsGameAgnosticExe" (?GIsGameAgnosticExe@@3_NA) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "wchar_t * GInternalGameName" (?GInternalGameName@@3PA_WA) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "wchar_t const * const GForeignEngineDir" (?GForeignEngineDir@@3PEB_WEB) already defined in StoryTestProgect.cpp.obj
1>UE4-StoryGraphPluginRuntime.lib(Module.StoryGraphPluginRuntime.cpp.obj) : error LNK2005: "struct FNameEntry * * * GFNameTableForDebuggerVisualizers_MT" (?GFNameTableForDebuggerVisualizers_MT@@3PEAPEAPEAUFNameEntry@@EA) already defined in StoryTestProgect.cpp.obj


I didn’t understand where these errors came from, it turned out everything just had to be replaced in StoryGraphPluginRuntime.cpp

This:

 IMPLEMENT_PRIMARY_GAME_MODULE(FStoryGraphPluginRuntime, StoryGraphPluginRuntime, "StoryGraphPluginRuntime"); 

On this:

 IMPLEMENT_MODULE(FStoryGraphPluginRuntime, StoryGraphPluginRuntime); 

And it all worked. But after that I wanted to compile the plugin in Shipping mode and then again errors got in, I unfortunately did not save them, but their essence was that I didn’t put ";" and the compiler pointed to the files of the engine itself. In general, everything turned out again just. To output the log, you need to declare a new category.

 DECLARE_LOG_CATEGORY_EXTERN(StoryGraphPluginRuntime, All, All) DEFINE_LOG_CATEGORY(StoryGraphPluginRuntime) 


But in the Development and Development Editor mode everything works without a semicolon, but in the Shipping mode you need to write like this:

 DECLARE_LOG_CATEGORY_EXTERN(StoryGraphPluginRuntime, All, All); DEFINE_LOG_CATEGORY(StoryGraphPluginRuntime); 

And all this is aggravated by the fact that the compiler, in case of an error, does not refer to these lines, but in general to the engine library.

That's all

→ Here are the sources
→ Link to demo

As well as a link to a previous article: Creating an editor of quests and dialogues for the Unreal engine: Part 1 plugin description

Source: https://habr.com/ru/post/318386/


All Articles