oakmound / oak

A pure Go game engine
Apache License 2.0
1.53k stars 83 forks source link

Provide function to get next available CID. #124

Closed lolbinarycat closed 3 years ago

lolbinarycat commented 3 years ago

the implementation would be fairly simple, keep track of the last cid generated, and call HasEntity to make sure it hasn't been manually bound to.

200sc commented 3 years ago

What would this function be used for that event.NextID does not provide?

lolbinarycat commented 3 years ago

Well, clarity for one, as I'm not entirely sure what event.NextID does, or why it takes an argument, or why it's the only thing called in Entity.Init.

I was going to add a real example, but the more I think about it, the less I understand it.

Implausiblyfun commented 3 years ago

if I remember correctly/ reread correctly event.NextID takes in an entity purely so that it can be added to the callers list which will be used to track all entities. For an example of callers being used please see ScanForEntity. https://github.com/oakmound/oak/blob/master/event/entity.go#L47

Admittedly the signature of NextID can be confusing but at the end of the day it has two responsibilites: 1) find the id to assign to the entity calling it (https://github.com/oakmound/oak/blob/master/event/entity.go#L30) 2) track the entity https://github.com/oakmound/oak/blob/master/event/entity.go#L29

lolbinarycat commented 3 years ago

How would one go about using it then, when all of the implementations of event.Entity (e.g. entities.Moving) take a CID as an argument? It would seem you would need the CID before you have the entity.

200sc commented 3 years ago

All entities creation helpers call cid.Parse: https://github.com/oakmound/oak/blob/34e40dfb70b77e735f2865d712140ccf1d420877/event/cid.go#L18

Is the comment for Parse a clear explanation as to why they do this?

lolbinarycat commented 3 years ago

I have no Idea what that comment is talking about. Many things about CIDs are not well explained. For example, the player in platformer-tutorial is constructed using CID 0, suggesting that is a valid CID, but the doc for GlobalBind states that cid 0 is "a non entity". It is also not clear whether Init() should be called after creating an entity. Should CIDs be adjacent, or can you have 1 and 100 as the only CIDs without problems?

It feels like CIDs work best when hand picked with constants attached (you could even use iota), but if you are creating TONS of entities by loading, say, a TMX file, it isn't clear what to do. Can I just give them all CID 0? Is there a way to disable bindings to prevent having hundreds of goroutines?

200sc commented 3 years ago

I'll try to answer your questions with as much information as possible.

1. Explaining the comment

When using the entities package, constructors in that package can optionally be passed a 0 CID or an already existing CID. If they are given an already existing, valid CID, they will not register themselves as an entity in the event package and will not create a new CID. This is done when you have an existing entity that builds on top of an entities type, as you can see all of the types in entities do already-- Doodad builds on Point, Solid builds on Doodad-- to avoid needing to change logic in multiple places when basic logic is updated.

If they are given 0 instead, they will Init themselves and register themselves as a new entity in the event package so event bindings can be bound to them. This is what CID.Parse is shorthand for.

The above should explain why the tutorials provide a 0 CID. Because they do not create their own struct type that has new fields, but just use the fields of the entities they are creating, they bind their event bindings to to the entities struct they are using for their player character or object in game.

Another source of this information: in the platformer tutorial wiki explaining the line you are asking about we state : We don't use friction, so we set it to zero, and both collision.Tree and event.CID are arguments that will default to sensible values if we pass in zero values for them.

TL;DR: 0 means 'make a new CID'.

2. CID (Caller ID) iteration

CIDs must only be obtained from event.NextID. Any CID not obtained this way is useless, as they can't be retrieved at event start. This is because NextID registers the entity it accepts as an argument with the event package's entity list, which is what each binding must call as it starts to ensure it is applying its changes to the appropriate data.

Essentially, GetEntity: https://github.com/oakmound/oak/blob/34e40dfb70b77e735f2865d712140ccf1d420877/event/entity.go#L37 will fail if the CID is not real, and all real CIDs come from event.NextID. (They are also all cleared out on scene changes.)

TL;DR: CIDs must only be obtained from event.NextID

3. Entities and TMX files

If you don't want your TMX file entities spinning up goroutines for events, perhaps they shouldn't be entities? There are three distinct systems in oak / game engines that are relevant here: entities responding to logic (event), renderables being drawn to screen (render), and collision spaces reacting to collisions (collision). If your TMX components don't need to respond to logic, they don't need Caller IDs and they don't need bindings, and they can just be a collision space + renderable (or just the latter if they are non-colliding terrain).

So to answer the question: yes, you can partially disable bindings by not adding them for data in the program that does not need to respond to bindings. There is no way with oak to disable events being triggered, but you have no requirement to react to or bind response functions to any of these triggers, and you can reduce the load of these triggers by replacing the event handler with a NOP event handler if you do not want to interact with the event system at all.

TL;DR: Yes, you can disable bindings

4. Hundreds of goroutines

A small note on this: this is how Go is designed to work, with lots and lots of goroutines (ref https://golang.org/doc/faq#goroutines). Oak embraces this, but we have planned a mode of operation that minimizes goroutines for web browsers, where they cannot be efficiently simulated.

lolbinarycat commented 3 years ago

Wow, thanks! This clears up basically all the confusion I had regarding CIDs.

My remark about the number of goroutines stemmed mostly from an annoyance when debugging (delve has no way to list a subset of the active goroutines, if you want goroutines listed, you have to list all of them).

I'll try a custom Tile type, instead of just using something from the entities package.