Platform Maker C++/Python 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)  # Corrected function call to pass MinRot and MaxRot

Rotation = RandomiseRotation(MinRotation, MaxRotation)

##print(Rotation)

# Define the function to spawn the blueprint actor
def spawn_blueprint_actor(blueprint_path, x, y, z):
    # Load the blueprint
    blueprint = unreal.EditorAssetLibrary.load_blueprint_class(blueprint_path)

    if blueprint:
        # Spawn the actor
        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:
        # Split the line by commas and remove leading/trailing whitespaces
        items = line.strip().split(',')
        data_list.extend(items)  # Append the individual coordinates to data_list

# Iterate over the list of coordinates and spawn cubes
for i in range(0, len(data_list), 3):
    x, y, z = data_list[i:i+3]  # Extract x, y, z coordinates
    spawned_actor = spawn_blueprint_actor(blueprint_path,float(x), float(y), float(z))  # Spawn cube at the coordinates

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"

bool UTextFileEditor::SaveTextToCSVFile(FString SaveDirectory, FString FileName, const FString& SaveText, bool AllowOverWriting, bool AppendText)
{
    // Set complete file path
    FString FullPath = FPaths::Combine(SaveDirectory, FileName);

    // Check if overwriting is allowed and if the file already exists
    if (!AllowOverWriting && FPlatformFileManager::Get().GetPlatformFile().FileExists(*FullPath))
    {
        return false; // File already exists and overwriting is not allowed
    }

    FString FinalString;

    if (AppendText)
    {
        // Read existing text from the file
        FString ExistingText;
        if (FFileHelper::LoadFileToString(ExistingText, *FullPath))
        {
            // Concatenate existing text with new text
            FinalString = ExistingText + SaveText;
        }
        else
        {
            // Failed to read existing text, use only the new text
            FinalString = SaveText;
        }
    }
    else
    {
        // If AppendText is false, use only the new text
        FinalString = SaveText;
    }

    // Save FinalString to file
    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.