**Problem**: When I create gameobjects, I usually want to specify their attribut…es upon initialization, e.g. to have a common behavior but different properties (speed, health, firepower, you name it). Today, this is possible by providing a table with properties to factory.create().
Properties, however, for good reasons, allow only certain variable types. Strings, for example, are not permitted. Neither are more complex data structures like tables. This is because properties of game objects are exposed and can be referenced by other game objects, so performance is an issue.
However, there is no reason why one should have to expose all initialization values publicly for the entire lifetime of a game object. Here, Defold mixes up two aspects that have no logical connection.
There are many scenarios, where I need to initialize the game object with data without wanting to expose it to other game objects. An obvious example would be a name to be displayed in dialogs. Or, for a complex type of spaceship, it would be useful to provide data on many parts of the vessel in a single table for its engines, weapons, etc. There is no need, however, for other game objects to access these - actually, by default, it would be wise to prevent that.
**Suggestion**: factory.create() already provides the possibility to pass a table with names/values upon creation of a game object which must be defined as properties. I suggest to:
(a) remove the restriction that only the values of properties can be passed. Any name/value pairs should be allowed. It should be possible to have both properties and non-properties mixed in the same table. For non-properties, complex data types (like string and table) should be permitted.
(b) Extend the signature of the game objects' init method and include the table with names/values from factory.create() as a method parameter:
```
-- outside the game object ---------
local id = factory.create( "#myfactory", nil, nil, { speed = 100, caption = "Bob" } )
-- inside the game object --------------
go.property( "speed", 0 )
function init( self, attrs )
self.displayname = attrs[ caption ]
pprint( self.displayname ) -- "Bob"
pprint( self.speed ) -- 100
end
```
That way, in the init method, the game object has full access to anything it gets passed in, no matter if property or not. Init can receive strings, tables, as well as simpler properties.
Outside of the init method, nothing changes: properties getting passed in upon factory.create() remain as they are today and can be referenced from the outside. Names/values not declared as properties are local to init() only, they are not accessible from the outside and so there is no danger of performance issues.
**Alternatives**: There are workarounds for this problem, of course:
**(a) Global variable**: It is possible to define a table, make it globally available to all game objects and pass in the right key as a property to perform a lookup in the game object's init() method:
```
-- outside the game object ---------
GLOBALS = {}
GLOBALS[ 1 ] = { caption = "Bob" }
local id = factory.create("#myfactory", nil, nil, { key = 1 } )
-- game object --------------
go.property( "key", 0 )
go.property( "speed", 0 )
function init( self, attrs )
self.displayname = GLOBALS[ self.key ].caption
pprint( self.displayname ) -- "Bob"
pprint( self.speed ) -- 100
end
```
However, there are several downsides to this approach:
1. Global variables should be avoided if possible. To need them usually is an indicator of bad code.
2. Details of the game object must be exposed (read and write) via the global data structure although they may only be needed by a single game object.
3. The game object must declare and expose an otherwise useless property "key" just to look up the values required upon initialization
4. The developer must do more housekeeping: after factory.create(), the data in the global variable is no longer needed, but still it is available to all(!) other parts of the code
**(b) Message passing**: Right after the game object has been created, one can send a message with an arbitrary table as payload and the game object can wait for and evaluate that message. However, this is an even more cumbersome workaround:
1. Upon initialization, the data is not available yet. Instead of in the init() method, where logically, the game object should be setup, initialization code is dispersed.
2. Instead, the update method must perform a check on every single frame for the rest of the game object's lifetime and must perform code which, by design, should not be in this method at all:
```
function init( self )
-- required data is not available
-- can only do other initialization code
end
function on_message( self, message_id, message )
-- wait for the right message to come
-- then parse and setup data as required
end
function update( self, dt )
-- worst part: check on every(!) frame if init data has been sent and is available
-- only act if object has been initialized properly, do not act before
end
```
Both workarounds are cumbersome and ugly.
The change I suggest would provide elegance and flexibility for a large number of usecases while remaining downward compatible with existing code.
As for the effort to implement this behavior, I assume it must be limited as no new concepts get introduced and the only requirement would be to remove a seemingly arbitrary restriction concerning data types and to extend the init() method signature with an optional parameter that is available already.