Is it possible to spawn in objects without using a unique factory per object

I’m currently making a small roguelike game, where each room is loaded onto a single collection, the way I do this is that I convert a table like this

image
Into a room like this

the problem is that I need a unique factory for each object I’m using
image

This is an issue as It congests the collection and isn’t dev friendly, especially as I want this to be modding friendly
any ideas?

One option is to change the factory’s prototype at runtime:

Luckily I’ve found that as well, the issue is I’m running into an issue of factories only being able to copy ‘goc’ files, while the objects are ‘go’ files, Idk what this file type is and its only mentioned in addressing twice in the whole documentation as refrences

image

Interesting. I looked into it and found the following thread that explains the issue:

1 Like

I’ve realised I’ve gotten an undocumented error around this mechanic.
I seem to be able to implement this correctly, it is set to load dynamically and it has the correct file path
image
However I have been unable to spawn other than what was the original prototype,
the print says
“/!Main/EnemyObjects/Horf.goc”
and the filepath is
“/!Main/EnemyObjects/Horf.go” (its uncompiled so it should work)

the error I’m getting is

ERROR:GAMESYS: Failed to get factory prototype resource: /!Main/EnemyObjects/Horf.goc
ERROR:GAMEOBJECT: No prototype to spawn from.

which reads to me as a failure to reach the file, but the file path is correct.

I’m on Defold 1.6.4 for reference

It will not affect performance to have many factories so from that perspective it really isn’t a problem. But it may become very unwieldy to have hundreds of factories. I would take a more data driven approach.

I would have one enemy.go and one enemy factory. When I spawn a monster I associate the spawned enemy.go with what type of monster it is. You can do this in two ways, one is to use a script property:

-- enemy.script
go.property("type", hash(""))

function init(self)
    if self.type == hash("Fly") then
        print("I'm a fly")
        sprite.play_flipbook("#sprite", "Fly")
    elseif self.type == hash("Gaper") then
        print("I'm a gaper")
        sprite.play_flipbook("#sprite", "Gaper")
    end
end
local function spawn_enemy(enemy_type, pos)
    local properties = {
        type = enemy_type
    }
    local id = factory.create("#enemyfactory", pos, nil, properties)
    return id
end

function init(self)
    local fly_id = spawn_enemy(hash("Fly"), vmath.vector3(1,2,3))
end

The other would be to have an enemy manager of some kind which lets you look up what type an enemy is:

-- enemy_manager.lua
local M = {}

local enemies = {}

function M.add(id, enemy_type)
    enemies[id] = enemy_type
end

function M.type(id)
    return enemies[id]
end

return M
local enemy_manager = require("enemy_manager")

local function spawn_enemy(enemy_type, pos)
    local id = factory.create("#enemyfactory", pos, nil, properties)
    enemy_manager.add(id, enemy_type)
    return id
end

function init(self)
    local fly_id = spawn_enemy(hash("Fly"), vmath.vector3(1,2,3))
end
-- enemy.script
local enemy_manager = require("enemy_manager")

function init(self)
    local enemy_type = enemy_manager.type(go.get_id())
    if enemy_type == hash("Fly") then
        print("I'm a fly")
        sprite.play_flipbook("#sprite", "Fly")
    elseif enemy_type == hash("Gaper") then
        print("I'm a gaper")
        sprite.play_flipbook("#sprite", "Gaper")
    end
end
2 Likes

It seems having such api unload(), set_prototype() makes us confused. Why don’t we just remove them from the documentation?

I don’t think that is a very good suggestion. The functionality was added for a purpose. The set_prototype() and unload() function is an advanced feature which should be used together with Live Update (our DLC solution). It is very convenient when you for instance download new skins, holiday themes or the like and would like to spawn them using the same code path as in your core game.

The functionality is not really intended for any other use, but you can use it in any project to change prototype on a factory, but it is not a very convenient way of working, and you need to have an understanding of how the resource system works in Defold.

For the regular use-case of spawning “objects without using a unique factory per object” I recommend a data driven approach similar to what I showed above. The other solution is simply to have many factories (which in itself is not a performance issue).

1 Like

@WhiteBoxDev yeah bro ! issue born when i thinking: if i have a game or app, it need change other content on every day. if what way we can do with only one factory, we can safe so much of time to design new factory and update or delete builed file.

True, but @britzl’s data-driven solution is also a sound and reasonable choice.

Are you referring to how factories manage resources in the engine code?

1 Like

@WhiteBoxDev yes, data-driven is a solution. But sometime we have an other struct or we can say it not same of graphic design(ex: older content is 2d and newest content is 3d). You know, if have a solution to resolve all same exception. we have one factory to spawn anything in game, prototype you can download from your server

I am referring to the dependency graph. We only include what is in the resource graph starting from the bootstrap collection down to every leaf node. Anything not referenced will not be included in the game archive.

The problem some people have with factory.set_prototype() is that they try to set the prototype to a game object they have in their project that is not part of the resource graph when the project is bundled. If a game object is not part of the bundle you’ll get an error like the OP got:

ERROR:GAMESYS: Failed to get factory prototype resource: /!Main/EnemyObjects/Horf.goc
ERROR:GAMEOBJECT: No prototype to spawn from.

The solution? Make sure the game object is referenced somewhere in your project so that it gets included in the bundle. One way of doing this is to create a collection and with all dynamic game objects and reference it using a collection proxy. This will include all game objects but not load their resources.

2 Likes

I just realised that we have an example doing exactly this.

1 Like