rlipsc / polymorph

A fast and frugal entity-component-system library with a focus on code generation and compile time optimisation.

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

How to split systems in multiple files

Gladius1 opened this issue · comments

Hello

First of all. Great work. I really like to code with polymorph.

I'm working on a small project. Right now it's about 300 lines long. I'd like to split it into multiple files because it's clear that it will grow some more. Unfortunately polymorph is giving me some issues.

Consider the following example.

main.nim

import polymorph as pl
import system_a
import system_b

pl.commitSystems "runSystems"

generateA("Hello")
generateB("World")

for i in 0 ..< 4:
  runSystems()

system_a.nim

import polymorph as pl

pl.register defaultCompOpts:
  type
    A = object
        value: string


pl.makeSystem "A", [A]:
    all:
        echo item.a.value

pl.makeEcs()

proc generateA* (value: string) =
    let
        a = newEntityWith(
            A(value: value)
        )

system_b.nim

import polymorph as pl

pl.register defaultCompOpts:
  type
    B = object
        value: string


pl.makeSystem "B", [B]:
    all:
        echo item.b.value

pl.makeEcs()

proc generateB* (value: string) =
    let
        b = newEntityWith(
            B(value: value)
        )

Is there another place where I can call makeEcs() so that the newEntityWith() makro is available for both generateA() and generateB()?

I'm very new to Nim btw. Maybe this is an issue with the Nim language?

Hi @Gladius1,

Each makeEcs in your example will generate an independent, sealed ECS that can't interact with the others. To seal them into a single ECS, remove the other makeEcs and place one in main.nim before commitSystems.

import polymorph, system_a, system_b

makeEcs() # Can see both a and b components/systems
commitSystems "runSystems"

generateA("Hello")
generateB("World")

for i in 0 ..< 4:
  runSystems()

Is there another place where I can call makeEcs() so that the newEntityWith() makro is available for both generateA() and generateB()?

The missing key is the onEcsBuilt macro. This queues code to be inserted after the next makeEcs has finished, so you can write procs, init stuff, and other code that uses the ECS there.

I'd like to split it into multiple files because it's clear that it will grow some more.

For larger projects, it's worth creating the ECS in a separate module (e.g., ecs.nim) which imports the component and system definitions, then runs makeEcs. This module can then be imported wherever you want to use or run the ECS.

As we're working across modules, the component types and fields also need to be annotated with * so they can be seen outside the module.

The following example shows how this might look:

## system_a
import polymorph as pl

pl.register defaultCompOpts:
  type
    A* = object
      value*: string

pl.makeSystem "A", [A]:
  all:
    echo item.a.value

onEcsBuilt:
  proc generateA* (value: string) =
    let
      a = newEntityWith(
        A(value: value)
      )
## system_b
import polymorph as pl

pl.register defaultCompOpts:
  type
    B* = object
      value*: string

pl.makeSystem "B", [B]:
  all:
    echo item.b.value

onEcsBuilt:
  proc generateB* (value: string) =
    let
      b = newEntityWith(
        B(value: value)
      )
## ecs
import polymorph, system_a, system_b
export system_a, system_b  # Export component types

makeEcsCommit "runSystems" # makeEcs() + commitSystems("runSystems")
## main
import polymorph, ecs

generateA("Hello")
generateB("World")

for i in 0 ..< 4:
  runSystems()
Hello
World
Hello
World
Hello
World
Hello
World

Alternatively, if you don't want to use onEcsBuilt for your procs, you can put them in another module that imports ecs:

## utils
import polymorph, ecs

proc generateB* (value: string) =
  let
    b = newEntityWith(
      B(value: value)
    )

proc generateA* (value: string) =
  let
    a = newEntityWith(
      A(value: value)
    )
## main
import polymorph, ecs, utils

generateA("Hello")
generateB("World")

for i in 0 ..< 4:
  runSystems()

I'm very sorry, I totally forgot about this.

Your answer was really helpful. Many thanks.