Indie game storeFree gamesFun gamesHorror games
Game developmentAssetsComics
SalesBundles
Jobs
Tags

Tilemancer

Procedural pixel-art tile creator · By Led

How to create custom nodes Sticky Locked

A topic by Led created Mar 21, 2016 Views: 3,100 Replies: 7
This topic was locked by Led Apr 03, 2019

Development on Tilemancer has stopped. Read more about why and discover its spiritual successor here:
https://itch.io/t/432485/the-end-of-a-journey-the-start-of-another

Thank you all for the support!

Viewing posts 1 to 4
Developer (3 edits)

Nodes and presets in Tilemancer are programmed in Lua (view it's license here), enabling users to create or download custom ones.

This tutorial will teach you how a nodes and presets work, assuming you already have some programming knowledge.

Nodes

Located in the "Nodes" folder, each node is a single ".lua" file.

To be recognized by Tilemancer as a node, it must contain two functions:

function init()
end

function apply()
end

Although it does nothing, it loads as a default node.

"init" is where the node will be initialized. You can set its properties with the following calls:


setName(name)

Sets the node's name to "name".

setDesc(desc)

Set the node's description (shown when hovering the cursor) to "desc".

setSize(w, h)

Sets the node's width and height to "w" and "h", respectfully.

addInput(name, y)

Adds an input labeled as "name" at "y" coordinate relative to the top of the node.

addParameter(name, desc, y, default, min, max, p)

Adds a parameter with the name and description set to "name" and "desc", respectfully, at coordinate "y" relative to the top of the node, with default, minimum and maximum values as "default", "min" and "max", respectfully.

"p" is optional, but if set to "true", a % sign will be displayed.

addInputParameter(name, desc, y, default, min, max, p)

Same thing as previous, but will add an input along with it. Parameter's values will be replaced by those of the input once it is linked to an output.

"p" is optional, but if set to "true", a % sign will be displayed.

addOutput(y)

Adds an output at "y" coordinate relative to the top of the node.


"apply" is where the node will do it's job. Called whenever it needs updating.

You can put it to work with the following calls:


getTileSize()

Returns the tile's size in pixels.

getValue(index, x, y, d)

Each call to "addInput", "addParameter" or "addInputParameter" places the newly added component in a new index, starting at 0. This function will fetch the value at the index specified by "index".

In case "index" points to a input which has a connection, it will return the normalized (0-1) RGB values of the input's contents at pixel ("x", "y"). If the point ("x", "y") is outside the texture's bounds, it will wrap around.

In case "index" points to a parameter, it will return the parameter's value divided by "d".
If the parameter is a parameter/input hybrid added by "addInputParameter" and it's input is connected, the function will return values from the input's contents.

setPixel(index, x, y, r, g, b)

Each call to "addOutput" places the newly added output in a new index, starting at 0. This function selects the output at "index", and sets it's pixel at ("x", "y") to the normalized (0-1) RGB values specified by "r", "g" and "b". If the point ("x", "y") is outside the texture's bounds, it will wrap around.


Here's a node that will output red:

function init()
    setName("Test Node")
    setDesc("Outputs red")
    setSize(100, 24+64+8)
    addOutput(56) --output at index 0
end

function apply()
    tileSize = getTileSize()
    for x=0, tileSize-1 do
        for y=0, tileSize-1 do
            setPixel(0, x, y, 1, 0, 0) --sets all pixels in output at index 0 to red (1, 0, 0)
        end
    end
end

img_1

Presets

Located in the "Presets" folder, each preset is also a single ".lua" file.

To be recognized by Tilemancer as a preset, it must contain the same two functions:

function init()
end

function apply()
end

Although it does nothing, it will be listed as a preset in Tilemancer.

"init" is where the node will be initialized. You can set its properties with the following calls:


setName(name)

Sets the parameter's name to "name".

setDesc(desc)

Set the parameter's description (shown when hovering the cursor) to "desc".


"apply" will run once the user clicks the preset, adding all the nodes and setting up connections and parameters.

You can do that with these calls:


addNode(filename, x, y)

Creates a node with the name of "filename" centered at positon ("x", "y").

setParameter(node, parameter, value)

Sets parameter at index "parameter" inside the node at index "node" to "value".

addConnection(nodeA, output, nodeB, input)

Adds a connection between the output at index "output" inside the node at index "nodeA" and the input at index "input" inside the node at index "nodeB".


Here's a preset that will create a "Bricks" node, and link it to a "Lighting" node

function init()
    setName("Test Preset")
    setDesc("Sets up bricks")
end

function apply()
    addNode("bricks.lua", -60, 0) --node at index 0
    addNode("lighting.lua", 60, 0) --node at index 1
    addConnection(0, 0, 1, 0) --adds a connection between "bricks"'s output and "lighting"'s input
end

img_2

Is there any documentation for addCRamp()/colorize() ?

Developer

I didn't include them because they are used in very specific cases and wouldn't be useful for custom nodes.

"addCRamp(y)" adds the Colorizer's color ramp at vertical position "y", and "colorize()" is a hardcoded function which will color the texture at input 0 with the values from the first color ramp. In case a custom node uses this and its layout doesn't match that of the Colorizer (having no inputs or no color ramps), the program might just crash.

So selecting colors for e.g. replacing 2 colors in a texture is not possible yet?

Developer

Colorizer was meant to be a final node. You can't do much with a colored texture since most filters act upon heightmaps.

You could easily let the user select values for replacement in heightmaps by using parameters, due to the fact that they're grayscale and parameters hold a single value, but it's not possible yet to let the user select a color from their palette as the replacement, because the palette-bound color picker only works coupled with the Colorizer.

Mind explaining what you have in mind, exactly?

(2 edits)

Why should you use y-position? Can't it be automatically, so you don't have to add numbers everytime?

Developer

I let users specify the y position because it gives them more freedom when designing their node.

(1 edit)

Why not make automation an option? If value is positive, use it. If it’s negative (-1), generate it (put every element at a subsequent position, and when init ends, get the height).

Developer locked this topic