1
 
 

I have implemented Observers for moecs.

Observers are a mechanism that allows to subscribe on events of structural and data changes in the world. By default observers are disable for performance reasons, so you need to pass true for observable argument of new_world procedure when you create the world. You also can change observable property of the world to turn off/on observers globally.

There are different event types that can be handled for entities, components, and tags.

Event Description
SPAWNED Entity has been spawned.
DESPAWNED Entity has been despawned.
ADDED Component has been added to an entity.
REMOVED Component has been removed from an entity.
SET Component value has been set (changed).
TAGGED Tag has been added to an entity.
UNTAGGED Tag has been removed from an entity.

Keep in mind that when you add/remove a component repeatedly or set/unset a tag repeatedly, the events will also be fired repeatedly for each operation even if you already made it before. It is safe for the data to call add_component (for example) procedure several times and pass the same component type to it, but your observers logic can be broken, so you need care about it yourself.

You can turn on/off observers for a specific component/tag type or globally for an event type. You can also check whether an observer is set or turned on. Events are not supported for resources.

When you set an observer using observe you must provide callback procedure that should follow ObserverCallback procedure type. SPAWNED/DESPAWNED events are being thrown for all entities and there are nothing to pass for type and component parameters, so the will be nil for these events in callback. Pointer to event target entity will be passed as entity parameter to callback. For TAGGED/UNTAGGED events tag type will be passed as type parameter, but component will be equals to nil. And finally for ADDED/REMOVED/SET events all callback parameters will be set, and component parameter is a pointer to event target component value, you can safety change it's value in place or read it, previously cast rawptr to expecting component type pointer.

When you provide several types in observe/unobserve procedures the same callback will be assigned as specific event handler for each of these types. This is done for convenience, there are no group observers, they are set separately for a specific event and element (component/tag) type.

You must set observers only when the world is already running, because of necessary indexes sorting made in run procedure of the world. Subsequent setting observers for some configuration will replace previous ones.

import ecs "moecs/src"

/* Observer callback procedure declaration. */
added :: proc(world: ^ecs.World, entity: ^ecs.Entity, event: ecs.Event, type: typeid, component: rawptr) {
  switch type {
    case Position:
      pos := cast(^Position)component
      /* Do not use observers for such purposes, it's just example. */
      pos.x += 50
      pos.y += 50

    case Center:
      center := cast(^Center)component
      /* Component values will be safety changed in place. */
      center.cx += 50
      center.cy += 50
  }
}

main :: proc() {
  ecs.init()
  /* Enable observers when create the world. */
  world := ecs.new_world(observable = true)
  /* ...register tags and components types here. */
  ecs.run(world)

  /* Set observers for entity spawning/despawning, you need to provide only callbacks. */
  ecs.observe(world, event = .SPAWNED, callback = spawned)
  ecs.observe(world, event = .DESPAWNED, callback = despawned)
  /* You can set observers for one or several types, subsequent assignments replace previous ones. */
  ecs.observe(world, event = .ADDED, types = { Rotation }, callback = added_rot)
  ecs.observe(world, event = .ADDED, types = { Position, Center, Health, Velocity }, callback = added)
  ecs.observe(world, event = .REMOVED, types = { Center, Position }, callback = removed)
  ecs.observe(world, event = .SET, types = { Position }, callback = set_pos)
  ecs.observe(world, event = .SET, types = { Center, Rotation, Health, Velocity }, callback = set)
  ecs.observe(world, event = .TAGGED, types = { Ship, Asteroid }, callback = tagged)
  ecs.observe(world, event = .UNTAGGED, types = { Ship, Asteroid }, callback = untagged)

  /* Turn off all added events for all component types. */
  ecs.turn_off(world, .ADDED)
  /* Turn off added events for Velocity component type. */
  ecs.turn_off(world, .ADDED, Velocity)

  if ecs.observable(world, .SET, Position) {
    /* Remove observer for set event of Position component type. */
    ecs.unobserve(world, .SET, { Position })
  }

  /* Turn on all added events for all component types.
     It is still turned off for Velocity component type. */
  if !ecs.turned_on(world, .ADDED) do ecs.turn_on(world, .ADDED)

  ecs.destroy()
}
Procedure Description
observe Sets observer for specified event and type(s).
unobserve Unsets observer for specified event and type(s).
observable Checks if observer is set for specific event and type.
turn_on Turn on observer for specific event and type.
turn_off Turn off observer for specific event and type.
turned_on Checks if the observer for specific event and type is turned on.

Do not enable and use observers unless absolutely necessary. Only do so if something can't be done using systems, as observers are very inefficient and reduce the speed of the ECS. For example, if you're developing a library that utilizes the ECS and initializes and runs the game's physics under the hood using specific components. You need to track the addition and modification of these components to make the appropriate changes to the physics engine. In this case observers are really necessary, for game/app logic use systems, it's much more efficient.

2
3
4
August 2024 Newsletter (odin-lang.org)
submitted 2 years ago by to c/odin@programming.dev
5
 
 

Why did you decide to work with Odin?

What did you build with Odin?

Where does Odin shine? What could use some polish?

6
 
 

Karl Zylinski writes:

This article is an introduction to the Odin Programming Language. It is aimed at people who know a bit of programming, but have never touched Odin. It is not a reference guide, rather I try to keep things informal and talk about what I think are important aspects of the language. There will be some notes on differences to C/C++, as Odin in many ways tries to be better C. If you enjoy this article and want to support me, then you can do so by becoming a patron.

In the recent years most of my programming has been done using the Odin Programming Language, sometimes referred to as Odinlang. Since a year back I create my own video games using Odin. You can find my game CAT & ONION on Steam, it is completely created in Odin.

...

While Odin is a great language, it is currently quite lacking in documentation. There is lots of documentation, but it is scattered and incomplete. I tried to address that by writing this article, which I hope gives a holistic introduction to the language.

Read Introduction to the Odin Programming Language

7
July 2024 Newsletter (odin-lang.org)
submitted 2 years ago by to c/odin@programming.dev
8
 
 

I'm sharing this Discord Message from someone that was doing their best to fill in their gaps in understanding Odin after reading the Overview documentation on odin-lang.org.

This was somewhat confusing, I could change the package foo at the top of the file and it makes no difference and everything works.

From reading this Server, I learned a lot, like this:

  • 1 - The package is only linked to the directory name (folder) and nothing else, except;
  • 2 - The files in the package directory must all have the same package foo header with the same name, but it does not have to be the same as the package directory name, just unique (see below); and
  • 3 - No two packages can have the same directory/folder name (globaly unique); and
  • 4 - No two packages can have the same package foo header names (i.e., must be globally unique but not tied to any package (directory) name needed - but would be a good idea, I mean why not...);
  • 5 - You cannot have cyclical imports - ever;
  • 6 - The package foo header seems powerless for the most part as far as programmer use, but I read it is used by the ABI as a prefix for symbols in the final binary;
  • 7 - Sub-packages do not exist, so even nested directories are standalone packages and also do not infer dependence;
  • 8 - In Odin you should put as much in one package as possible and do not use packages for namespaces since it will not work (see using) and do not use packages to organize your files, see 9 for proper reason to use packages;
  • 9 - Use separate packages only as reusable (stand alone) libraries;
  • 10 - Organize files within a package directory by their filenames (ala Java) and not subdirectors for all the reasons above; which is what I will do using snake case for the file names.

If I blew it above let me know.

imho the Overview would do better to include a little more detail like some of the stuff above. When I searched this server I found a huge amount of the above and just cobbled it together from 6-7 posts. I think what was not clear from the overview was: 1, 5, 6, 7 (its there but not clear), 9. It is clear that many people are confused by this, but on the bright side searching the Discord was very useful.

gingerBill indicated this is a common source of confusion for newcomers to Odin:

package name has nothing to do with the thing you import.

This is probably one of the most confusing things about Odin's packages. The exist for stable ABI and consistent linking names.

Hopefully, I can create a minimum working example that can be illustrative for others and have a PR merged into the Overview documentation. Until then I just want something that can be found outside of Discord for other learners.

9
submitted 2 years ago by to c/odin@programming.dev
10
submitted 2 years ago by to c/odin@programming.dev