Read data from File - Some kind of dynamic level

Hi everyone,

I promise I was searching quit a while, but found nothing so far.

What am I looking for?
I want to have some kind of dynamic level BP Script so that I can:

  • Open some kind of custom “Open File Dialog” so that the User can chose a File on his drive - but this is not the Main Problem here (from the other side - a good “Handle User input in Blueprint Tutorial would be great also”)
  • Then I want to read the Data contained in the File - Text or Byte based - here the tricky part begins. I haven’t found any documentation on this topic and also none of the BP functions seems to be able to do that.
  • The last part is easy - as the data in the File is well structured I will spawn some Geometry/Static Meshes etc. on various locations.

As you see, this is more about something like a “viewer” (I want to use it for some Visualization, not for a game). But I think that this could be also useful, for developers that want to have a “Level editor” inside their games - then they can have their cool and fancy Levels that come shipped with the game itself - and with this approach they could easily create also an “map editor” that saves a simple byte or text File and then read it during run-time dynamically into the custom level. This would be for sure fine for the most of simple Top down or sidescroller games with (for simplicity) cubic tiles.

Let me give you an example.
We have some cubic tiles/meshes/assets prepared (but in our case in 3d):

  • “G” - is an Ground Brick
  • “D” - is an destructible Brick
  • “?” - is the ? Box
  • “.” - is empty space

In our Text File that we want to Load we have then (it is not perfect but when you see the Final outcome you should get an idea what I mean):



…?D…
…?DDDDDD…
…GG…
…GG…GGGGGG…
GGGGGGGGGGGGGGGGGGG
GGGGGGGGGGGGGGGGGGG

And as we load this Text file it will create the following “Level” (lets ignore for now the clouds and the green pipe):
7450.gif

Is it in general possible to interact/manipulate Files from Blueprints?
Is there any chance for this in Blueprint or am I forced to use C++?
Can someone point me in the right direction please?

Thank you,
Best regards,
Jur4nd

One first&last Bump.

For now I’m pretty sure that this is not possible only with Blueprint but any comments or ideas would be appreciated

I’m afraid we don’t have any functions currently exposed to Blueprints for opening/reading arbitrary files. That has some security/stability concerns, as well as making packaging of your game much more error-prone.

Thank you JamesG - I will try some C++ for that problem

I’ve loaded tilemaps created in Tiled into my game. I save them as JSON and parse that JSON with C++ into an array of integers in a function. I’m calling the function in a blueprint’s construction script to be able to customize the level in the editor, but you could also load it on runtime of course. In the blueprint I’m adding instanced static meshes that represent the level based on the tilemap array.

If that sounds useful for you, I could post the code when I’m home.

anteevy, yes, that sounds good :D.

In general it fits perfectly that what I was asking for, but in a far more elegant way.
I really would like to see the code that you have written for that, then I could try to modify it to get an simple kind of “ingame” level Editor. If that will work then I will make an short Tutorial on that - I think it will be useful for various small Top down or sidescrollers.

First, a warning: This is my first code in the UE4 so there may be better ways of achieving this. :smiley:

My tilemap class is derived from AActor. My blueprint is a subclass of this class and calls the ReadTilemapFile function/node defined in this class and then reads the FloorTiles array for spawning the instanced static meshes.

In the .h, don’t forget:


#include "Json.h"

Make BP aware of the function:


UFUNCTION(BlueprintCallable, Category = "Tilemap")
	void ReadTilemapFile(FString LevelName);

As I just have to store “is floor / is no floor” per tile in my case, I use a bool array (FloorTiles). Width and height of the tilemap is parsed, too (needed when building the level in the blueprint).


UPROPERTY()
	TArray<bool> FloorTiles;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tilemap")
	int32 TilemapSizeX;

	UPROPERTY(VisibleAnywhere, BlueprintReadOnly, Category = "Tilemap")
	int32 TilemapSizeY;

This is the function that reads from the Tiled .json file.


void ATilemap::ReadTilemapFile(FString LevelName)
{
	if (LevelName.IsEmpty())
	{
		return;
	}
	
	// remove all tile information from previous parsing
	FloorTiles.Empty();

	// read level json file into json object
	FString FullPath = FPaths::GameDir() + "ContentRaw/Tilemaps/" + LevelName + ".json";
	FString JsonStr;
	FFileHelper::LoadFileToString(JsonStr, *FullPath);
	TSharedRef<TJsonReader<TCHAR>> JsonReader = FJsonStringReader::Create(JsonStr);
	TSharedPtr<FJsonObject> JsonObject = MakeShareable(new FJsonObject());
	bool serialized = FJsonSerializer::Deserialize(JsonReader, JsonObject);

	if (!serialized)
	{
		return;
	}

	// read tile data from json
	TArray<TSharedPtr<FJsonValue>> LayersArray = JsonObject->GetArrayField(TEXT("layers"));
	TSharedPtr<FJsonObject> LayersObject = LayersArray[0]->AsObject();
	TArray<TSharedPtr<FJsonValue>> TilesArray = LayersObject->GetArrayField(TEXT("data"));

	// parse read tile array into floor tilemap
	for (int i = 0; i < TilesArray.Num(); i++)
	{
		int32 TileVal = (int32)TilesArray*->AsNumber();
		FloorTiles.Add(TileVal == 1);
	}

	// read tilemap size x and y from json
	TilemapSizeX = (int32)LayersObject->GetNumberField(TEXT("width"));
	TilemapSizeY = (int32)LayersObject->GetNumberField(TEXT("height"));
}

Actually I’m not using the FloorTiles bool array directly but convert it into another array of a custom struct that stores more information for each tile - more specific, whether it is a outer corner tile, inner corner, wall or floor tile and its rotation (I calculate that by checking the neighbor tiles of each tile). The blueprint then spawns the according instanced static mesh. But I don’t think you’ll need that for your purpose.

Just for reference, this is the parsed json:


{ "height":100,
 "layers":
        {
         "data":[0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, (and so on...)],
         "height":100,
         "name":"Kachelebene 1",
         "opacity":1,
         "type":"tilelayer",
         "visible":true,
         "width":100,
         "x":0,
         "y":0
        }],
 "orientation":"orthogonal",
 "properties":
    {

    },
 "tileheight":32,
 "tilesets":
        {
         "firstgid":1,
         "image":"Floor.png",
         "imageheight":128,
         "imagewidth":128,
         "margin":0,
         "name":"Floor",
         "properties":
            {

            },
         "spacing":0,
         "tileheight":32,
         "tilewidth":32
        }],
 "tilewidth":32,
 "version":1,
 "width":100
}

Currently I’m only using the data, width and height attributes.

Maybe this would have been stuff for a separate tutorial thread… :smiley: Also, feel free to use this in your tutorial!

We do support importing .CSV files for creating both CurveTables (a table rows that define curve data) and DataTables (table of data whose column’s data types can be user defined). The format the .csv files are in isn’t well documented yet, nor is the process of defining the type of data for the columns in a Data Table, but I’m hoping to write a blog post on it sometime in the near future.

Right now this is a C++ feature, but we’re looking into making it more accessible from Blueprints and require less programming (or preferably none), but we have a fair amount of work to do in this regard.

I’ll try to remember to post here when I write that blog post.

Thanks!