stuartsierra / component

Managed lifecycle of stateful objects in Clojure
MIT License
2.09k stars 95 forks source link

Using ECS lib for getting rid of deftypes/defrecords/defprotocols #75

Closed damn closed 1 year ago

damn commented 1 year ago

I have recently developed an entity component system for a game I am developing in clojure. (https://github.com/damn/x.x)

I found out that component could be simplified with my library so that all the components could be plain maps and not have to be wrapped in deftypes/defrecords.

I started creating a diff of those parts of your component library but then thought to ask your opinion first before creating a PR.

The difference is to see components as [k v] and not a special type. Systems are just multimethods dispatching on k and by default returning v.

I believe this makes a more functional way of doing things, without special objects.

damn commented 1 year ago

System would be defined like this:

; ns com.stuartsierra.component

(defsystem start
  "Begins operation of this component. Synchronous, does not return
  until the component is started. Returns an updated version of this
  component."
  [component])

(defsystem stop
  "Ceases operation of this component. Synchronous, does not return
  until the component is stopped. Returns an updated version of this
  component."
  [component])

And components like this: (from /test examples)


; ns com.stuartsierra.component-test

(defcomponent :d {:keys [my-c b] :as this}
  (start [_]
    (log 'd.start this)
    (assert (started? b))
    (assert (started? my-c))
    (assoc this ::started? true))
  (stop [_]
    (log 'd.stop this)
    (assert (started? b))
    (assert (started? my-c))
    (assoc this ::started? false)))

(defcomponent :e {:keys [] :as this}
  (start [_]
    (log 'e.start this)
    (assoc this ::started? true))
  (stop [_]
    (log 'e.stop this)
    (assoc this ::started? false)))

(defn system-1 []
  ; deliberately scrambled order
  {:e {}
   :c (component/using {} [:a :b])
   :b (component/using {} [:a])
   :d (component/using {}
                       {:b :b
                        :my-c :c})
   :a {}})

I have omitted giving the components a random :state integer like in the tests to make it more clear that we can just work with plain maps.

lambdasierra commented 1 year ago

Hello, Michael, and thanks for your interest! Congrats on the release of your entity-component system, and I'm glad you found interesting connections to Component. However, I consider com.stuartsierra.component to be in its maintenance phase. It has been stable and largely unchanged for many years, so any changes to the design or public interfaces would be unexpectedly disruptive to the many projects that depend on it.

If you are interested in exploring that design space, I would recommend doing it in a separate project under a different name. There is plenty left to explore, as the many extensions and variations show!

You may also be interested in the addition of :extend-via-metadata to the Lifecycle protocol in Component 0.4.0, which allows plain maps to implement Lifecycle.

Thanks again and have fun exploring!