Skip to content

Latest commit

 

History

History
200 lines (142 loc) · 6.01 KB

File metadata and controls

200 lines (142 loc) · 6.01 KB

Game objects

Summary

How to read and create game objects from a script. Covers the geode.cocos2d and geode.gd namespaces, table-shaped values like points and sizes, and per-object fields.

The two namespaces

geode.cocos2d holds engine classes, e.g.:

  • CCDirector
  • CCLabelBMFont
  • CCSprite
  • action classes such as CCMoveTo

geode.gd holds Geometry Dash classes, e.g.:

  • GameManager
  • GameStatsManager
  • MenuLayer

You usually begin from a singleton or a factory.

local cc2d = geode.cocos2d
local gd = geode.gd

local director = cc2d.CCDirector.sharedDirector()
local gm = gd.GameManager.get()

Creating objects

Many classes provide a create factory. Always check the result, because it can be nil.

local label = cc2d.CCLabelBMFont.create("Hello, World!", "bigFont.fnt")
if not label then return end
label:setScale(0.5)

Node IDs for mod compatibility

Geode adds string IDs to CCNode.

When creating a node, always call :setID() and pass an id string:

  • The id should start with your mod id as a prefix
  • Use lowercase kebab-case
  • Do not use spaces

Example: mod-id/my-node-id

String IDs let you find nodes with getChildByID and layouts instead of fragile child indexes, which is how Geode mods stay compatible.

local cc2d = geode.cocos2d
local modId = geode.Mod.getID()

local sprite = cc2d.CCSprite.createWithSpriteFrameName(modId .. "/coolSprite.png")
if not sprite then return end
sprite:setID(modId .. "/cool-sprite")

See the Geode nodetree tutorial.

Calling methods and reading properties

Call methods with a colon. Read and write simple properties with a dot.

local pos = self.m_obPosition

Some APIs return a CCArray. Use Cocos methods to read it. objectAtIndex starts at index 0.

local lists = localLevelManager:getCreatedLists(0)
if not lists or lists:count() == 0 then return nil end

local first = lists:objectAtIndex(0)

Table-shaped values

Some method arguments are small structs you pass as plain Lua tables with named keys.

  • Point: { x = number, y = number }
  • Size: { width = number, height = number }
  • Color: { r = number, g = number, b = number }
  • Rect: { origin = point, size = size }
label:setPosition({ x = 100, y = 200 })
bg:setContentSize({ width = 170, height = 84 })

Container-shaped values

Some generated APIs use Geode containers. Most by-value containers become plain table snapshots. Direct object and opaque-pointer vectors may use read-only sequence views.

  • Scalar-key map: { [K]: V }.
  • Vector or set: { T }.
  • Direct object vector view: { Object? }. Entries can be nil.
  • Pair record: { first = T1, second = T2 }. Always use first and second.
  • Pair-key map: entry list { { first = k1, second = k2, value = v } } because Lua cannot use tables as map keys.
  • Recursive containers apply these shapes at every level.

Sequence views use 1-based indexes and support #. They reject writes. Plain table snapshots follow Lua nil rules. A nil indexed leaf creates a hole. A nil value in a dictionary-shaped map removes that entry. Map table iteration order and unordered container order are unspecified.

Writing a container field updates its native value recursively without whole-container assignment. Some generated container fields are read-only. Check the type stubs for the exact surface. Read-only handler queues on input dispatchers use { Handler? } sequences and 1-based indexes.

Binding details for recursive containers, pairs, and ccCArray views:

Use type stubs for the exact stub type on each member.

Per-object fields

Store your own data on an object with geode.fields(node) or the m_fields property. Both return the same plain table for a live CCNode.

When the value is not a live CCNode, geode.fields returns an empty table. Missing keys read as nil, so flags default to false in normal checks.

local fields = geode.fields(self)
fields.count = (fields.count or 0) + 1

if geode.fields(layer).myFlag then
    -- only runs when the layer is live and the flag was set
end

Encrypted stat fields

Some GD stats on level and manager objects are encrypted ints in the game (m_attempts, m_stars, m_levelID, and others on GJGameLevel). In Luau they appear as number fields. Read and write them like any other number field. You can also use wrapper methods when the class has them (setAttempts, setLevelID, and similar).

print(level.m_levelID)
print(level.m_objectCount)
level:setAttempts(level.m_attempts + 1) -- level.m_attempts += 1

See Codegen for binding policy.

Enums

GD and Geode enums are integer constant tables on geode.gd and geode. See enums.

Ownership

The runtime tracks Lua-owned vs borrowed objects and handles C++ lifetime for you. Luau does not bind retain() or release(). Manual ref counting fights LuauAPI bookkeeping and can crash the game, leak nodes, or break callback and geode.fields cleanup.

Keep a normal Luau reference when you need an object to stay alive. Use a local, a module table, or the scene graph. See the Bindings framework.

Related

Source

  • src/framework/stack/Types.hpp
  • src/framework/stack/ContainerTables.hpp
  • src/framework/view/ReadOnlyVectorView.hpp
  • src/framework/usertype/Fields.cpp
  • tools/luau_codegen/emit/luau_types/
  • tools/luau_codegen/policy/fields.py