Trying to understand the code behind Paper2D

Hello!

I’m trying to implement some new features for PaperTileset class, which is used to create tiles to create PaperTilemaps later on. I’m not sure, but in my mind PaperTileset has shortage of some neccessary functions.

I am trying to create procedural map and thus I need to use code to create my 2D procedural map. However, I can’t even access the tile texture straight from the PaperTileset class.
Instead I need to create UPaperTilemap in UE4 editor, use all the tiles from my tileset there so I could access them in code through the just created tilemap. Then create UPaperTileMapComponent, set this tilemap as its’ tilemap. Finally, I can access my tile textures.

Accessing textures can be seen in this picture below:
a4660e809a9e780402d3f9f612b8167a.png

My question:
How does FPaperTileInfo hold the texture information of the tile. If I understand this, I could create a getter for getting tile texture directly in the tileset, but it’s too confusing…
I don’ t especially understand the EPaperTileFlags enum, what does it do?


enum class EPaperTileFlags : uint32
{
	FlipHorizontal = (1U << 31),
	FlipVertical = (1U << 30),
	FlipDiagonal = (1U << 29),

	TileIndexMask = ~(7U << 29),
};

// This is the contents of a tile map cell
USTRUCT(BlueprintType, meta=(HasNativeBreak="Paper2D.TileMapBlueprintLibrary.BreakTile", HasNativeMake="Paper2D.TileMapBlueprintLibrary.MakeTile"))
struct FPaperTileInfo
{
	GENERATED_USTRUCT_BODY()

	// The tile set that this tile comes from
	UPROPERTY(EditAnywhere, Category=Sprite)
	UPaperTileSet* TileSet;

	// This is the index of the current tile within the tile set
	UPROPERTY(EditAnywhere, Category=Sprite)
	int32 PackedTileIndex;

	FPaperTileInfo()
		: TileSet(nullptr)
		, PackedTileIndex(INDEX_NONE)
	{
	}

	inline bool operator==(const FPaperTileInfo& Other) const
	{
		return (Other.TileSet == TileSet) && (Other.PackedTileIndex == PackedTileIndex);
	}

	inline bool operator!=(const FPaperTileInfo& Other) const
	{
		return !(*this == Other);
	}

	bool IsValid() const
	{
		return (TileSet != nullptr) && (PackedTileIndex != INDEX_NONE);
	}

 	inline int32 GetFlagsAsIndex() const
 	{
		return (int32)(((uint32)PackedTileIndex) >> 29);
 	}

	inline void SetFlagsAsIndex(uint8 NewFlags)
	{
		const uint32 Base = PackedTileIndex & (int32)EPaperTileFlags::TileIndexMask;
		const uint32 WithNewFlags = Base | ((NewFlags & 0x7) << 29);
		PackedTileIndex = (int32)WithNewFlags;
	}

	inline int32 GetTileIndex() const
	{
		return PackedTileIndex & (int32)EPaperTileFlags::TileIndexMask;
	}

	inline bool HasFlag(EPaperTileFlags Flag) const
	{
		return (PackedTileIndex & (int32)Flag) != 0;
	}

	inline void ToggleFlag(EPaperTileFlags Flag)
	{
		if (IsValid())
		{
			PackedTileIndex ^= (int32)Flag;
		}
	}

	inline void SetFlagValue(EPaperTileFlags Flag, bool bValue)
	{
		if (IsValid())
		{
			if (bValue)
			{
				PackedTileIndex |= (int32)Flag;
			}
			else
			{
				PackedTileIndex &= ~(int32)Flag;
			}
		}
	}
};

If you need any more information, feel free to mention it.
Thanks in advance!

If I understand what your after, then I think some code I was playing within a previous project might help. I’ll just paste the relevant section verbatim, with some extra comments. You probably won’t find it useful as is, but it should show you how to go about using maps, tileinfo etc


// Create the tilemap component for this actor at construct time
_tileMap = CreateDefaultSubobject<UPaperTileMapComponent>(TEXT("TileMap"));
_tileMap->AttachToComponent(scene, FAttachmentTransformRules::KeepRelativeTransform);

// Load existing map and tileset assets.  Tilemap itself is empty, but I had configured it with 3 layers.
// You can add layers in code, but I found it easier to set up things like tint, transparency etc in the Editor
UPaperTileMap* map = Cast<UPaperTileMap>(StaticLoadObject(UPaperTileMap::StaticClass(), this, TEXT("PaperTileMap'/Game/Assets/Map/MainTileMap.MainTileMap'")));
UPaperTileSet* set = Cast<UPaperTileSet>(StaticLoadObject(UPaperTileSet::StaticClass(), this, TEXT("PaperTileSet'/Game/Assets/Map/MainTileset.MainTileset'")));

// In the Editor, I had used the Userdata for each tile in the tileset 
// to give it a human readable name (rather than just using the numeric ID)
// Here I loop through each tile in the tileset, and create a TMap (_mapTiles) associating
// the Userdata name with a FPaperTileInfo struct referencing that Tile ID.
// This makes it easy to refer to each tile later in the code
for (int i = 0; i < set->GetTileCount(); i++)
{
	if (set->GetTileUserData(i) != NAME_None)
	{
		FPaperTileInfo a;
		a.TileSet = set;
		a.PackedTileIndex = i;
		_mapTiles.Add(set->GetTileUserData(i).ToString(), a);
	}	
}

// Now do a similar thing for layers, using a TMap so I can easily get
// layer ID from the name I gave the tilemap layer in the Editor.
for (UPaperTileLayer* layer : map->TileLayers)
{
	_mapLayers.Add(layer->LayerName.ToString(), layer->GetLayerIndex());

}

// Set the actor's TileMap component to use this map
_tileMap->SetTileMap(map);

// Required to allow using SetTile()
_tileMap->MakeTileMapEditable();

// Set the required size, then loop through every tile in the map
// and set the default value.  The "Terrain" layer are all set
// to tiles whose Userdata is "GrassA", and the Visibility layer
// is set to "FogFull" tiles
_tileMap->ResizeMap(MapWidth, MapHeight);
for (int x = 0; x < MapWidth; x++)
	for (int y = 0; y < MapHeight; y++)
	{
		_tileMap->SetTile(x, y, _mapLayers"Terrain"], _mapTiles"GrassA"]);
		_tileMap->SetTile(x, y, _mapLayers"Visibility"], _mapTiles"FogFull"]);
	}


So in short, a FPaperTileInfo struct must be told which tileset asset it refers to, and then set the PackedTileIndex to reference a specific tile ID in that set. Then you can pass that TileInfo, along with the layer ID, to map SetTile()

I hope that helps a bit

However, a big, big caveat I found and could not solve at the time, is that I could find no way to re-build the collision info after creating/altering the TileMap in code. TileMapComponent has a RebuildCollision() function, but that never worked for me. Perhaps it’s changed in 4.13, or if you find a way to do it I’d love to know!

That said, depending on the style of game, you may not even need to have collision shapes defined on each tile anyway.

Hi @Rasponien , you can access the texture of a particular tile much easier. For example:
You have UPapertileSet* TileSetRef //A specific TileSet is selected in the Blueprints

//Create FPaperTileInfo struct
FPaperTileInfo Info;
Info.TileSet = TileSetRef; //Set your own TileSet for struct
info.PackedTileIndex = Index of Tile; //Select the desired Tile

DONE!! After this you can use Info struct in, for example YourTileMap->SetTile(Xcord, Ycord, layer, Info)