Improbable Icon

Environment Creation


#1

Hi, new to SpatialOS here, but after having played with the tutorials and getting a cursory understanding of how SpatialOS works, my question is about content creation. It appears that every prefab/object in the game is spawned at run time with the locations of each prefab being set in the schema/settings script. Is there a way to visual this as the world is being constructed or are people creating/placing the prefabs and then manually editing the position/rotations via scheme/scripts? Seems like this is a workflow issue and I’m sure there’s something I don’t understand here…

Thanks!


#3

Hey @Sharkbite_Rick
There’s a few ways to populate your world

  1. You can do what you’ve suggested and write scripts to spawn in entities at runtime (or right after you start your instance)
  2. You can open your Worker scene in Unity straight up and just add stuff right in there like you would anything else. This would work for static terrain and such. This would allow you to visually see what you’re doing.
    or
  3. Any combination of the above, you run your instance and maybe populate it by script until you reach a world state you want, then you can save the state of the world to a snapshot. This stores only the SpatialOS component/entity information, so when you do it make sure you have all the information you need to reconstruct your entities from the SpatialOS components :smile:

Hopefully that made sense, ask away if not. Also come say hi in the community Discord!


#4

Great question!

Internally we’re working on a few projects - a workflow I have developed looks like this:

a) in maya / your DCC you build ‘tiles’ of content in 50m squared blocks
b) you ‘export’ those blocks as a list of names mapped to a transform matrix. we call it a 'tileset’
c) in unity - you write an importer that takes a tileset, and maps the exported names to prefabs.
d) this gives you a set of prefabs that represent one 50m chunk of the world - you can flatten the meshes at this point into one chunk, or leave each individual prefab parented.
d) you create a scene called snapshot scene - place your tiles however you like them, and export them to a snapshot using the Improbable apis - one entity per tile
e) when you run the game, your client checks out the tile entities in a grid around the player.

Dynamic entites like NPCs can be spawned at runtime from ‘Spawner’ prefabs that are exported - this way in Maya you can place a node called ‘BadGuySpawner’ or something, which will correspond to a prefab called the same - containing a script that periodically spawns bad guys :slight_smile:

It’s important to note - although you are building the world in tiles, it doesn’t have to look tiled. It’s simply a nice way of splitting the world up into entities so that you don’t have the entire world baked into a single scene or terrain - if you have a large set piece (say a bridge that extends over several tiles ) you simply have to split it into several mesh chunks, and make sure in the snapshot scene you align the right tiles in the correct order.

This method also allows you to have some fun with procedural techniques - maybe don’t place trees in the DCC scene, but allow the snapshot code to place them according to some heuristic / algorithm you’ve written.

If you want more details, leme know!

Cal


Worker startup sequence
#5

Great thanks for the tips. @callumb are you guys working on this tool now or should I start having devs build something like this?

Thanks!
Rick


#6

It’s actually a whole host of tools!

The exporter in MAYA is really simple - something like this, if it helps:

type_to_find='transform'
float_precision=3
char_to_split_name_on='_'
directory_to_output_to="C:\proj"

selection = cmds.ls(exactType=type_to_find)
content = ""
for curr_obj in selection:
    position = cmds.xform(curr_obj, query=True, translation=True, worldSpace=True)
    rotation = cmds.xform(curr_obj, query=True, ro=True, worldSpace=True)
    scale = cmds.xform(curr_obj, query=True, s=True, worldSpace=True)
    name = str(curr_obj).split(char_to_split_name_on)
        content += name[0] + ',' + str( -1 * round(position[0], float_precision)) + ',' + str(round(position[1], float_precision)) + ',' + str(round(position[2], float_precision))\
             + ',' + str(round(rotation[0], float_precision)) + ',' + str(-1 * round(rotation[1], float_precision)) + ',' + str(round(rotation[2], float_precision))\
             + ',' + str(round(scale[0], float_precision)) + ',' + str(round(scale[1], float_precision)) + ',' + str(round(scale[2], float_precision))\
             + "\n"

fileWrite.write(content)
fileWrite.close()

And then the import in Unity looks a little like this:


enum EXPORT_FILE_INDEX
        {
            PREFAB_NAME = 0,
            POS_X = 1,
            POS_Y = 2,
            POS_Z = 3,
            ROT_X = 4,
            ROT_Y = 5,
            ROT_Z = 6,
            SCALE_X = 7,
            SCALE_Y = 8,
            SCALE_Z = 9,
        }

static void ProcessTileMetaFile(string filepath)
        {
            string fileContents = File.ReadAllText(filepath);
            string[] records = fileContents.Split('\n');
            foreach (string record in records)
            {
                //Load the prefab first to confirm the name is correct
                    GameObject go = Resources.Load("Prefabs/"+fields[(int)EXPORT_FILE_INDEX.PREFAB_NAME]) as GameObject;

                    GameObject rootNode = new GameObject(string.Format("{0}{1}", prefabName, lineIndex));
                    rootNode.transform.position = new Vector3(float.Parse(fields[(int)EXPORT_FILE_INDEX.POS_X]), float.Parse(fields[(int)EXPORT_FILE_INDEX.POS_Y]),
                        float.Parse(fields[(int)EXPORT_FILE_INDEX.POS_Z]));
                    rootNode.transform.rotation = Quaternion.Euler(float.Parse(fields[(int)EXPORT_FILE_INDEX.ROT_X]), float.Parse(fields[(int)EXPORT_FILE_INDEX.ROT_Y]),
                        float.Parse(fields[(int)EXPORT_FILE_INDEX.ROT_Z]));
                    rootNode.transform.localScale = new Vector3(float.Parse(fields[(int)EXPORT_FILE_INDEX.SCALE_X]), float.Parse(fields[(int)EXPORT_FILE_INDEX.SCALE_Y]),
                        float.Parse(fields[(int)EXPORT_FILE_INDEX.SCALE_Z]));
                    AssetInstantiator inst = rootNode.AddComponent(typeof(AssetInstantiator)) as AssetInstantiator;
                    inst.asset_prefab_name = prefabName;
            }
        }

It simply adds a script to an object that takes a prefab name - and when you create an instance of that object, it instantiates that prefab and sets the transform to that of the export :slight_smile:

We won’t be shipping any tools for a while - I’d definitely have your devs look into a pipeline suitable for your project. Let me know if they need a hand!
Cal