global::TriangleMesh is a Futile display node whose geometry comes from vertices and triangle indices. Use it when a sprite needs a shape that changes at draw time, such as a ribbon, beam, or flat marker. The node still needs an owner, a camera container, and a cleanup path.

flowchart TD
    A[IDrawable owner] --> B[SpriteLeaser]
    B --> C[TriangleMesh]
    C --> D[Camera FContainer]
    D --> E[Remove on owner cleanup]

Choose the segment count from the geometry

TriangleMesh.MakeLongMesh(1, false, false) creates four vertices and two triangles. This is the correct size for one rectangle with two endpoints. MakeLongMesh(2, false, false) creates eight vertices and six triangles. Code that chooses two segments but assigns only vertices 0 through 3 leaves the remaining geometry without the intended positions.

The helper with three arguments uses the Futile_White element. MakeLongMeshAtlased chooses RainWorld_White and assigns atlas coordinates. Element availability and the final appearance still depend on the active Futile setup.

The public MeshMarker example creates its mesh in IDrawable.InitiateSprites:

sLeaser.sprites = new FSprite[]
{
    TriangleMesh.MakeLongMesh(1, false, false)
};
AddToContainer(sLeaser, rCam, null);

One SpriteLeaser owns one mesh for each camera that registers the drawable. Drawable lifetime explains why the mesh belongs in sLeaser.sprites instead of a static plugin field.

Move all four corners

The example records the player’s main body chunk position when a fresh jump press creates the marker. It does not move the player or change any BodyChunk field. During each draw, it subtracts camPos and assigns a fixed 24 by 4 rectangle:

var mesh = (TriangleMesh)sLeaser.sprites[0];
Vector2 center = origin - camPos;
mesh.MoveVertice(0, center + new Vector2(-12f, -2f));
mesh.MoveVertice(1, center + new Vector2(-12f, 2f));
mesh.MoveVertice(2, center + new Vector2(12f, -2f));
mesh.MoveVertice(3, center + new Vector2(12f, 2f));

For this helper and segment count, vertices 0 and 1 form the left endpoint and vertices 2 and 3 form the right endpoint. MoveVertice(int, Vector2) writes the selected vertex and marks the mesh dirty. If code edits the public vertices array directly, call Refresh() afterward so Futile knows to rebuild the rendered data. PopulateRenderLayer() copies the vertices, UV coordinates, and colors selected by the triangle list into the render layer.

The marker uses camera coordinates because it lives in the camera’s Foreground container. A moving world object should interpolate its previous and current world positions before subtracting camPos. See Camera coordinates for that calculation.

Attach and remove the node

MeshMarker.AddToContainer removes the mesh from its current parent, then adds it to the requested container or rCam.ReturnFContainer("Foreground"). Container choice controls draw ordering and needs a visual check in the supported rooms and camera modes.

The marker is an UpdatableAndDeletable. It destroys itself after 30 update calls. Its DrawSprites method calls sLeaser.CleanSpritesAndRemove() when the marker is slated for deletion or its room no longer matches the camera room. Thirty updates is a counter, not a promised elapsed duration. Simulation and physics explains why update counts do not imply one fixed cadence in seconds.

The example compiled with C# 7.3 against the installed build references through examples/build.ps1 -Example MeshMarker. It has not been loaded into Rain World. Test both triangles for visibility, camera movement, room changes, multiple cameras, pause behavior, process changes, and mod disabling before reusing it in a released mod.

Sources

  • Rain World v1.11.8, Steam app 312520, build 22785462. Assembly-CSharp.dll SHA-256 B6BE1D4E18CE219D21091B51564CB6A11C1E4106B41DE903EB8E58849CB16FDB.
  • Assembly-CSharp.dll: global::TriangleMesh, global::TriangleMesh.MakeLongMesh(System.Int32,System.Boolean,System.Boolean), global::TriangleMesh.MoveVertice(System.Int32,UnityEngine.Vector2), global::TriangleMesh.Refresh(), and global::TriangleMesh.PopulateRenderLayer().
  • Compiled MeshMarker example
  • Runtime reference records the assembly and compilation baseline.