Erstellen Sie Ihren eigenen Bearbeitungsmodus in Unreal Engine

  • Tutorial


Hallo, mein Name ist Dmitry. Ich mache Computerspiele auf der Unreal Engine als Hobby. Mein Spiel hat eine prozedural erzeugte Welt, aber um ein interessanteres Level zu erstellen, habe ich mich dafür entschieden, vordefinierte Abschnitte des Levels zu verwenden. Es stellt sich jedoch die Frage, wie die Bereiche festgelegt werden sollen, in denen der Pegel erzeugt wird, und wo nicht. Dazu habe ich meinen eigenen Bearbeitungsmodus erstellt, unter der Katze werde ich deren Erstellung beschreiben, die Quelle am Ende des Artikels.


Beginnen wir also mit der Erstellung eines neuen Bearbeitungsmodus:
class FEdModeCustom : public FEdMode
{
public:
	FEdModeCustom();
	~FEdModeCustom();
	virtual void Tick(FEditorViewportClient* ViewportClient,float DeltaTime) override;
	// FEdMode interface
	virtual bool UsesToolkits() const override;
	void Enter() override;
	void Exit() override;
	virtual bool InputKey( FEditorViewportClient* InViewportClient, FViewport* InViewport, FKey InKey, EInputEvent InEvent ) override;
	virtual bool HandleClick(FEditorViewportClient* InViewportClient, HHitProxy *HitProxy, const FViewportClick &Click ) override;
	virtual void Render(const FSceneView* View, FViewport* Viewport, FPrimitiveDrawInterface* PDI) override; //FEdMode: Render elements for the Draw tool
	virtual bool IsSelectionAllowed(AActor* InActor, bool bInSelection) const override; //Check to see if an actor can be selected in this mode - no side effects
	virtual void ActorSelectionChangeNotify() override; //Called when the currently selected actor has changed
	virtual bool ShouldDrawWidget() const override;
	virtual bool InputDelta(FEditorViewportClient* InViewportClient, FViewport* InViewport, FVector& InDrag, FRotator& InRot, FVector& InScale) override;
	// End of FEdMode interface
	//Render
	void DrawPrevewGird(FPrimitiveDrawInterface* PDI);
	void DrawBlankCells(FPrimitiveDrawInterface* PDI);
	class ATestAct* EditorModel;
	void ForceRealTimeViewports(const bool bEnable, const bool bStoreCurrentState);
	TSharedPtr GetActiveTool() const { return ActiveTool; }
	void SetActiveTool(TSharedPtr ActiveTool);
	void ApplyBrush(FEditorViewportClient* ViewportClient);
	static FEditorModeID EM_EdModeCustom;
private:
	UMaterial* OverlayMaterial;
	void UpdateGridCursorPosition(const FSceneView* View, FViewport* Viewport);
	ATestAct* FindTestActorOnScen();
	TSharedPtr ActiveTool;
	FIntVector GridCursorPosition; 
	bool bToolActive;
	class SCustomEdit* EditPanel;
};

Enter und Exit - werden beim Aufrufen und Verlassen des Bearbeitungsmodus aufgerufen. Beim Aufrufen des Bearbeitungsmodus erstellen wir Modusoberflächenelemente, die auf der Registerkarte angezeigt werden. Diese Elemente sind in der Datei SCustomEdit.h beschrieben, ich werde sie hier nicht zitieren.

InputKey - wird aufgerufen, wenn Ereignisse von Eingabegeräten empfangen werden, dh wenn sie auf die Schaltfläche geklickt, das Ereignis IE_Pressed empfangen, freigegeben und IE_Released empfangen haben.

Rendern - zeichnet eine Szene.

IsSelectionAllowed - Bestimmt, ob Objekte ausgewählt werden können oder nicht.

ActorSelectionChangeNotify - Wird aufgerufen, wenn die Auswahl immer noch stattgefunden hat

ShouldDrawWidget - Legt fest, ob das Widget auf dem Objekt gezeichnet werden soll oder nicht (Das Widget besteht aus drei farbigen Pfeilen, die auf dem ausgewählten Objekt angezeigt werden, wenn Sie es verschieben.)

InputDelta - Verhindert, dass sich die Kamera bewegt, wenn eine der Maustasten gedrückt wird.

Für zusätzlichen Komfort habe ich zwei Tools erstellt. Das erste Werkzeug ist Malen. Mit diesem Werkzeug wählen wir Zellen aus, für die wir keine Ebene erzeugen müssen (Umschalttaste kehrt das Werkzeug um). Das zweite Werkzeug ist Auswählen: Mit diesem Werkzeug können Sie Elemente in ausgewählten Zellen anordnen. Jedes Werkzeug hat seine eigenen Methoden Render () und InputKey (), die wir aus den entsprechenden Bearbeitungsmodusmethoden aufrufen.

Aber einfach so, der neue Bearbeitungsmodus wird nicht angezeigt. Damit es angezeigt wird, müssen Sie es in der StartupModule () -Methode registrieren.
void FLevelDrawEditorModule::StartupModule()
{
	FCustomCommands::Register();
	FEditorModeRegistry::Get().RegisterMode
		(
			FEdModeCustom::EM_EdModeCustom,
			FText::FromString("Level Draw"),
			FSlateIcon(FEditorStyle::GetStyleSetName(), "LevelEditor.ViewOptions", "LevelEditor.ViewOptions.Small"),
			true, 400
		);
	//Registrate detail pannel costamization for TestActor
	FTestActDetailCustomization::RegestrateCostumization();
}


Danach haben wir hier ein solches Menü:
Bild

Jetzt ist es am wichtigsten, die empfangenen Informationen irgendwo zu speichern. Informationen werden im TestAct-Objekt gespeichert. Es muss auf der Bühne platziert werden, wenn mehrere TestActs auf der Bühne platziert sind. Sie können zwischen ihnen wechseln, indem Sie auf die entsprechenden Symbole klicken.
UCLASS()
class LEVELDRAW_API ATestAct : public AActor
{
	GENERATED_BODY()
public:	
	ATestAct(); 
	UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = Gird)
		FVector GridCellSize;
	UPROPERTY(Transient)
		UModeSettings* ModeSettings;
	UPROPERTY()
		FLevelMap LevelMap;
private_subobject:
#if WITH_EDITORONLY_DATA
	UPROPERTY()
		UBillboardComponent* SpriteComponent;
#endif //WITH_EDITORONLY_DATA
};

Also, was wir haben:

GridCellSize - Dieser Parameter bestimmt die Größe der Zellen, die angezeigt werden.

ModeSettings - Die Parameter dieses Objekts werden im Eigenschaftenfenster des Bearbeitungsmodus angezeigt. Sie legt zwei Punkte fest, mit denen Sie Ihr Raster markieren können. Außerdem werden der Name eines bestimmten TestAct und eine Schaltfläche zum Löschen von Daten angezeigt.

Sie fragen sich vielleicht, warum ich dieses Objekt an TestAct und nicht an den Bearbeitungsmodus selbst angehängt habe? Tatsache ist, dass der Bearbeitungsmodus nur "lebt", wenn Sie dorthin wechseln. Und wenn ich nur dieses Objekt an den Bearbeitungsmodus anhängen würde. Diese Einstellungen werden jedes Mal zurückgesetzt, wenn in einen anderen Modus gewechselt wurde.

Darüber hinaus stellen Sie möglicherweise fest, dass der Zeiger auf das UModeSettings-Objekt als UPROPERTY (Transient) angegeben wird. Der Parameter Transient verhindert, dass der Editor das UModeSettings-Objekt speichert, wenn auf die Schaltfläche Speichern geklickt wird. Aber wofür ist UPROPERTY?

Wichtig: Unreal Egine verfügt über einen Garbage Collector, der alle Objekte löscht, wenn kein einziger als UPROPERTY definierter Zeiger auf dieses Objekt verweist. Das Objekt kann auch mit dem Flag RF_RootSet geschützt werden.
YourObjectInstance->SetFlags(RF_RootSet);

All dies gilt natürlich nur für Objekte, die von UObject abgeleitet sind. Wenn das Objekt kein UObject-Nachkomme ist, wird es vom Garbage Collector nicht bedroht.

LevelMap - In diesem Objekt werden Informationen zu den Zellen gespeichert. Dieses Objekt besteht aus vier zweidimensionalen Arrays, eines für jedes Viertel der Ebene. Hier wird der Parameter UPROPERTY bereits benötigt, damit beim Klicken auf die Schaltfläche zum Speichern der Karte das FLevelMap-Objekt gespeichert wird.

SpriteComponent - Nur ein Link zu dem Symbol, das auf dem Editor-Bildschirm angezeigt wird, wenn das ATestAct-Objekt auf der Karte platziert wird.

Das ist eigentlich alles für weitere Studien. Ich empfehle, die Quelle zu betrachten.

Das Quellcode-Projekt ist hier .

Jetzt auch beliebt: