Platform Maker Python/C++ Tool

https://github.com/QwertyQarrot/PlatformMaker


To kick things off, my goal for this tool was to facilitate the real-time design and blocking out of game levels. Instead of grappling with the uncertainty of block placement – pondering whether they’d be too high or too low – users could immerse themselves in gameplay, strategically positioning platforms underfoot as they played. Later, during the editing phase, these dynamically placed elements could seamlessly transition into the editor mode, allowing for further refinement. However, a notable challenge arose: changes made during gameplay weren’t automatically reflected in the editor environment. Consequently, I had to devise a method to externally store this data and integrate it back into the editor when needed.

Step 1: The Python


At the outset, I sought to leverage Python to develop a script capable of spawning platforms by extracting and parsing data from an external CSV file. The aim was to transform the extracted data into a usable format. However, lacking the setup for the C++ tool initially, I had to resort to employing a static CSV file containing predetermined values. Once this initial functionality was in place, my attention turned towards creating a user-friendly GUI within Unreal Engine to facilitate straightforward utilization of the tool.

import unreal
import sys
import random

Rotation = 0

MinRotation = int(sys.argv[1])
MaxRotation = int(sys.argv[2])

def RandomiseRotation(MinRot, MaxRot):
    return random.randint(MinRot, MaxRot)

Rotation = RandomiseRotation(MinRotation, MaxRotation)

print(Rotation)

def spawn_blueprint_actor(blueprint_path, x, y, z):
    blueprint = unreal.EditorAssetLibrary.load_blueprint_class(blueprint_path)

    if blueprint:
        spawned_actor = unreal.EditorLevelLibrary.spawn_actor_from_class(blueprint, unreal.Vector(x, y, z), unreal.Rotator(0, 0, 0 + Rotation))
        unreal.log_warning("Blueprint Actor spawned successfully.")
        return spawned_actor
    else:
        unreal.log_warning("Failed to load blueprint at path: {}".format(blueprint_path))
        return None

blueprint_path = "/Game/ViviChar/V3/Plane_Blueprint.Plane_Blueprint"

filename = r"F:\Documents (F)\Unreal Projects\Chaos_Car\Pythons\xyz.csv"
data_list = []
with open(filename, 'r') as file:
    for line in file:
        items = line.strip().split(',')
        data_list.extend(items)  

for i in range(0, len(data_list), 3):
    x, y, z = data_list[i:i+3]
    spawned_actor = spawn_blueprint_actor(blueprint_path,float(x), float(y), float(z))

print("Number of cubes spawned:", len(data_list) // 3)
print(Rotation)

Step 2: The C++


Next, I worked on getting Unreal Engine to send data to a CSV file. Since Unreal Engine couldn’t do this on its own, I dove into creating custom C++ code for the task. I found a helpful tutorial video by Dev Enabled to get started. Then, I built on this code to fit my project’s needs, especially in how it handled the data.

#pragma once

#include "CoreMinimal.h"
#include "Kismet/BlueprintFunctionLibrary.h"
#include "TextFileEditor.generated.h"

/**
 * 
 */
UCLASS()
class CHARACTERPROJECT_API UTextFileEditor : public UBlueprintFunctionLibrary
{
    GENERATED_BODY()

public:
    UFUNCTION(BlueprintCallable, Category = "FileEditor")
    static bool SaveTextToCSVFile(FString SaveDirectory, FString FileName, const FString& SaveText, bool AllowOverWriting = false, bool AppendText = false);


};
------------------------------------------------------------------------------
#include "TextFileEditor.h"
#include "Misc/FileHelper.h"
#include "HAL/PlatformFilemanager.h" //https://docs.unrealengine.com/4.26/en-US/API/Runtime/Core/Misc/FFileHelper/

bool UTextFileEditor::SaveTextToCSVFile(FString SaveDirectory, FString FileName, const FString& SaveText, bool AllowOverWriting, bool AppendText)
{

    FString FullPath = FPaths::Combine(SaveDirectory, FileName);

    if (!AllowOverWriting && FPlatformFileManager::Get().GetPlatformFile().FileExists(*FullPath))
    {
        return false;
    }

    FString FinalString;

    if (AppendText)
    {
        FString ExistingText;
        if (FFileHelper::LoadFileToString(ExistingText, *FullPath))
        {
            FinalString = ExistingText + SaveText;
        }
        else
        {
            FinalString = SaveText;
        }
    }
    else
    {
        FinalString = SaveText;
    }

    return FFileHelper::SaveStringToFile(FinalString, *FullPath);
}

Step 3: Turning it into a plugin


After successfully integrating Python and C++ to work together, I aimed to enhance the tool’s accessibility. To achieve this, I converted it into an Unreal plugin. This transformation enabled me to seamlessly incorporate the plugin into any Unreal project, ensuring its usability across different environments and projects.