A drawable implementing global::IDrawable supplies drawing behavior to a room camera. The camera owns the corresponding global::RoomCamera.SpriteLeaser, which holds the Futile sprites and other camera resources for that drawable. Treat the leaser as camera state, rather than as the drawable’s one permanent sprite array.

This article covers the camera side of the lifetime. The update and deletion rules for a global::UpdatableAndDeletable remain separate. Abstract and realized objects explains room registration and ordinary realized object lifetime.

flowchart TD
    A[Room camera registers an IDrawable] --> B[Camera constructs a SpriteLeaser]
    B --> C[InitiateSprites creates nodes]
    C --> D[ApplyPalette receives the current palette]
    D --> E[DrawUpdate calls DrawSprites]
    E -->|next frame| E
    E --> F[CleanSpritesAndRemove removes nodes and marks the leaser]
    F --> G[Camera drops the marked leaser from its list]

The four drawable methods

global::IDrawable defines four camera operations:

  • InitiateSprites creates the nodes stored by this leaser.
  • ApplyPalette applies the camera’s current global::RoomPalette.
  • DrawSprites updates node state for a rendered frame.
  • AddToContainer places the existing nodes in a requested FContainer.

Every method receives both a global::RoomCamera.SpriteLeaser and a global::RoomCamera. DrawSprites also receives timeStacker and camPos. This method shape is the first reason to avoid storing one sprite globally on the drawable. Camera resources arrive through the leaser passed to the call.

The leaser constructor calls InitiateSprites, then ApplyPalette with the camera’s current palette. If that camera has ripple data, the constructor may also replace basic shaders for the leaser’s sprite array. That extra handling belongs to the camera. A drawable should establish a complete, fixed sprite array during InitiateSprites and leave camera policy to the camera.

A minimal allocation can be as small as:

sLeaser.sprites = new FSprite[] { new FSprite("Futile_White") };

This snippet only illustrates ownership of the array. Futile_White is created during Futile initialization in the verified build. Custom element loading and naming need the checks in Atlases and shaders.

Registration belongs to each camera

The complete DrawMarker example creates a cosmetic UpdatableAndDeletable implementing IDrawable. A fresh jump press adds it to the current room. It rises for 30 update calls, then marks itself for deletion. Each leaser creates one cyan sprite using the built-in white element. Drawing interpolates the marker’s position and subtracts camPos. If the marker is deleted or belongs to another room, it calls CleanSpritesAndRemove() instead of drawing.

This original example compiled against build 22785462 with .NET SDK 10.0.400. It has not been run in the game. Test its appearance, room changes, multiple cameras, and cleanup. Its lifetime is counted in update calls, not a promised number of seconds.

global::RoomCamera.NewObjectInRoom(global::IDrawable) appends a new leaser to that camera’s private list. Its second overload can insert a drawable before another registered drawable. If the requested anchor is absent, that overload does not insert a replacement entry.

The source therefore supports a separate leaser for each camera. It does not establish how every split-screen setup registers a particular custom drawable. Test with every active camera before relying on shared state, draw order, or a single initialization call.

global::RoomCamera.DrawUpdate(System.Single,System.Single) walks the camera’s leasers from the end of the list. Each leaser update calls the drawable’s DrawSprites with that camera and its calculated camPos. The camera removes a leaser after deleteMeNextFrame has been set. Avoid changing the size or meaning of sLeaser.sprites during ordinary drawing unless the object’s design and cleanup path have been tested for that change.

Camera coordinates covers the interpolation and conversion from world coordinates to camera coordinates that normally belongs in DrawSprites.

Containers and cleanup

AddToContainer places nodes that already exist. global::RoomCamera.MoveObjectToContainer(global::IDrawable,FContainer) finds the first matching leaser and delegates placement to the drawable through that method. A common implementation chooses a default when the requested container is null, then adds each sprite to the selected container. Container names and ordering are runtime choices that need visual testing.

The drawable interface has no cleanup callback. global::RoomCamera.SpriteLeaser.CleanSpritesAndRemove() marks the leaser for removal, removes its sprites and stored containers from their parents, and deletes non-null mask sources. global::RoomCamera.ChangeDrawable(global::IDrawable,global::IDrawable) and camera cleanup use this operation.

Resources created outside the leaser’s tracked arrays need their own disposal path. Tie that path to the owning object’s real lifetime. Do not expect IDrawable to notify the object after the camera removes its nodes.

Static inspection did not run a game session. Verify room changes, camera changes, palette changes, drawable replacement, and multiple cameras. Confirm that all nodes disappear after cleanup and that no camera keeps stale object state.

Sources

  • Rain World v1.11.8, Steam app 312520, build 22785462. Assembly-CSharp.dll SHA-256 B6BE1D4E18CE219D21091B51564CB6A11C1E4106B41DE903EB8E58849CB16FDB.
  • Assembly-CSharp.dll: global::IDrawable.
  • Assembly-CSharp.dll: global::RoomCamera.SpriteLeaser..ctor(global::IDrawable,global::RoomCamera).
  • Assembly-CSharp.dll: global::RoomCamera.NewObjectInRoom(global::IDrawable) and global::RoomCamera.NewObjectInRoom(global::IDrawable,global::IDrawable).
  • Assembly-CSharp.dll: global::RoomCamera.DrawUpdate(System.Single,System.Single) and global::RoomCamera.SpriteLeaser.Update(System.Single,global::RoomCamera,UnityEngine.Vector2).
  • Assembly-CSharp.dll: global::RoomCamera.SpriteLeaser.CleanSpritesAndRemove() and global::RoomCamera.ChangeDrawable(global::IDrawable,global::IDrawable).
  • Assembly-CSharp.dll: global::RoomCamera.MoveObjectToContainer(global::IDrawable,FContainer) and global::RoomCamera.SpriteLeaser.AddSpritesToContainer(FContainer,global::RoomCamera).
  • Runtime reference records the same installed assembly baseline and its verification limits.