Improbable Icon

World Building Approach

unity
virtualworld
v11-1

#1

Greetings all - First, a little about me;

I’ve been a software developer, architect, and research professional for nearly 15 years; and I’ve been an avid PC gamer since before PC gaming was actually a thing. A long, long time ago - a friend and I designed an entire RPG system for a tabletop game that I’ve always wanted to implement as something MMO-Like. The interesting parts of the system centered around things like evolving politics between player and non-player factions, economy, and story-telling, while components of the system like combat, skills, spells, and movement were fairly simple compared to some systems. All that being said, I’ve started laying the groundwork for this system (in all my spare-time) using a combination of PlayerFab (authentication), SpatialOS (World), Firebase (Data Storage), and Unity (Mechanics/Logic/etc).

All that being said, what I’m finding most challenging to wrap my head around with regards to SpatialOS is the world-building aspects of it; without over-architecting, I’ve a tendency to spend a lot of time thinking about scalable design that won’t paint components into a corner if they need to be scaled to accomodate success. Also, since worlds can be really big; I have been thinking about how to design my world(s) in small chunks, allowing me to “build in” those small details that make the world immersive without having to “load in” the entire world anytime I want to do something of the sort. I’ve begun modeling out the design of my world (through PoCs and mockups, primarily) envisioned through the lens of “zones” where a zone is generally made up of a chunk of terrain which is statically generated, a bunch of entities which are scripted (npc’s, buildings, vehicles, etc.), and various procedural components (trees, garbage on the streets, weather, etc.). Obviously, I don’t want all of these things “baked in” to the snapshot, there’s no reason that the terrain itself needs to exist on the server side and be streamed to the client when simply storing the mesh for the terrain will suffice for physics and collisions; likewise, procedural content should be generated on the client, and will serve as an immersive environmental component that has generally little-to-no effect on things that need to be decided authoratatively (with some exceptions, like weather - for instance, it’s a great deal harder to snipe someone with a bow in a torrential downpour with heavy winds than it is to do so on a bright sunny windless afternoon, but more on that in a future post).

So now, on to my actual set of questions:

  1. What would be the best way to generate this game world?

I’ve begun creation of some custom inspectors and editor windows that provide some world-building functionality already - but I’m curious how others have addressed this particular part of the puzzle as to a) not overthink it, and b) make world-design fun and simple (something you could do by simply opening the scene for that zone, do your thing, then click a button and have all the grunt work “done for you”

  1. How to hand positioning within the world(s) and universe?

In previous lives, things I’ve built in Unity I’ve relied greatly on containerizing things and using hierarchy’s to make relative positioning simple to calculate and keep in mind; but it’s unclear to me if this approach is tenable or even right for building potentially massive worlds in something like a snapshot.

  1. Zoning, generally?

Although it seems to be a relatively straight-forward approach (and not new, by any stretch of the imagination) – I’m curious what the community at large feels about the zone approach to world-building, particularly in terms of the way that SpatialOS “handles” scaling the world. Zoning feels like a great approach for “legacy” sharding architectures; but feels a little more unnatural in the component-based micro-things architecture that I see SpatialOS as.


#3

While I am by no means an expert when it comes to these question, I do have some thoughts that may help you think of what you want to do.

  1. When it comes to generate a world in the fashion you’d like I think would be best to design your world in segments and record the x and y values of each one. However, when it comes to a sort of “easy button” that may take a bit of effort, but it sounds like you’d benefit from making procedurally generated level script that would be the size of one of these segments. I’d have it just instantiate normal prefabs, not any that have something to do with SpatialOS for this, but also have SpatialOS made prefabs that you’ll use for when you create the snapshot. You could move around the instantiated unity prefabs around in the scene until everything for that newly procedurally made segment is exactly how you want it. Decide which segment you’d like that one to be located at. You could then have that script save all the values to an .xml, .txt, ect and read in those values into when making the snapshot, having them offset by the the segment’s x and y which brings me to number 2).

  2. When building the snapshot you can indeed do relative positioning which is great. What I do for that is after spawning my island I pass that newly instantiated SpatialOS prefab and pass it further along my script that builds the snapshot and use it for relative positions. What you could do is use a couple of for loops to go from each segment to segment x and y value and code in to read that segments data from the document saved and using that x and y as an offset.

  3. Zoning is something you can do. You can have players go from one zone to another, but do note that SpatialOS is designed with an “Area of Interest” aspect. Meaning that things on the server will only be loaded in to the client if the client is within range of the object. At other times the client won’t have it there at all, which sounds like what you were primarily worried about.

Hopefully something in here gets your gears rolling and we get to see an awesome game and world you make soon :smiley:


#4

Thanks +barret.keyes - some follow-up on #3

things on the server will only be loaded in to the client if the client is within range of the object

So how does that work with something like a Terrain, say for example I have an island that I’ve built within a scene. In my world editor, I save all the entities that I’ve created on the island as instantiable prefabs that get served up from the server-side (presumably these things are streamed to the client, but I’m getting ahead of myself here). My initial plan was that I wouldn’t save off the island itself as an entity that SpatialOS cared about, rather I would create an entity for the mesh of the island (so the server could handle collisions and movement constraints, obv the client can’t be trusted with those physics) and the Unity client itself would additively load the scene containing the island (I was planning to use an event from the server to tell the client which scenes to load in) then once the scene was loaded to render the entities from the snapshot within that scene. Judging by what you’ve written here, this wouldn’t necessarily be necessary, and the terrain should just be another entity in the snapshot and stored as a prefab, then presumably I’d need to come up with some means of tiling that terrain so that the entire asset wouldn’t have to be streamed to the client when they entered a new zone. Am I misunderstanding?


#5

Indeed. As for breaking up the terrain into more manageable tiles, this can easily be done by having smaller terrains and importing height maps and materials that are broken up into tiles from a larger height map and texture. There are some assets on the asset store that do this for you as well if I recall correctly.


#6

World building and scaling (regarding to player numbers) is one of the great questions regarding large scale for me.

I use Terrain Composer 2 (TC2) for terrain creation and object positioning in Unity. TC2 can procedurally generate really large worlds (up to 25x25 terrain tiles in editor, more in runtime).

My current workflow is:

  • Generate terrain and position objects in TC2
  • Export objects to a json file (objects have a export component on them to define export type and parameters)
  • Delete objects (not terrains), because they are childs of terrains and i have no forther use after writing them to json.
  • Make prefabs from terrains
  • Add terrains to client and server scenes
  • Generate snapshot from json file. I wrote an editor window for this to be able to filter objects. So i can create small snapshots for testing purposes.

For the future i need some more features around this. Let’s say player numbers get really high and the world becomes too small. The required step would be to add more terrains around the existing ones with exact match (regarding heightmap) at the borders. Currently i’m thinking about scripts to generate more terrains around the existing ones in runtime mode of TC2 which automatically builds terrain prefabs and object json file. Additionally i need an enhanced json to snapshot converter which only adds the objects in the new areas and doesn’t touch the existing ones. In my oppinion this should be doable, but i have to test this approach.

Within SpatialOS i use streaming queries to control which objects are spawned at which distance on the clients. I like distant views so i spawn trees up to 500m, but loot containers and small rocks are only spawned up to 200m. NPCs and players are also spawned in at 200m. Currently i’m experimenting with these values.


#7

This is great info - TC2 looks extremely useful. I’ve gone back and forth a lot on procedural terrain generation and I think that ultimately I’ve decided that I want to stick with static terrains for main game areas but enabling procedural terrain generation in “in-between” areas. To handle bordering tiles and lining up height maps I’ve settled on ensuring that borders are “flat” across the x and z axis and storing the y for each axis. In this way I can always be sure that my tiles will line up. I haven’t yet gotten into the implementation details of this, but that’s the general design that I’ve come up with.

For now, I’m attempting to build out a single “zone” as a static area that will act as the “noobie” area so I can get started on some of the other mechanics of the game world.

So the implementation approach I’m working with now looks like this:

  1. Create a “Zone” entity (non-rendered) that contains meta-data about the zone such as the zone’s name and any restrictions (i.e. no one above level 10, admin-only area, etc),
  2. Created a “base” component that get’s added to all entities called which simply contains the name of the zone.This will allow me to “query” the snapshot for all the entities in a zone for the world editor.
  3. Create the terrain for the zone, store it as a prefab - for now, this will be small terrain (since this is the noob zone). Store the terrain as an entity containing the component (future design thing) that will contain the y values for the borders of the terrain, and any other components that I need to add.
  4. Create all the entities in my zone (buildings, npcs, etc.)
  5. Profit

At least that’s what it looks like at a high level. I’m excited to get started on some of the core components of the game and am primarily interested in getting a small world started to start building the political systems, economy, and the various AI components within the game.


#8

Here are a few more tips when it comes to positioning stuff in the world:

  1. Be incremental - don’t try to design too far ahead, only add features when you need them. I have tried in my game to look too far ahead and take things into account for larger scale; only to discover that these things didn’t pan out as expected. SpatialOS is a very powerful system and some solutions only become obvious once you have been working with it for some time.

  2. Make mistakes - this is related to being incremental. There is nothing wrong with making an implementation and only to replace it later once you add more use-cases. This will also help you keep motivated because you will see results really early, instead of building a huge system without seeing the in-game rewards.

  3. Try to use as little hierarchies as possible for ‘Entities’ - because of the Interest Radius it could happen that a parent Entity may not be present for a child Entity and this may influence your local positioning. A single Entity may have game objects as a hierarchy under it; only linking Entities in a hierarchy are afflicted

  4. Remember that SpatialOS divides the world among workers but that it doesn’t do any repositioning or adjusting coordinates; everything is relative to your world’s (0, 0, 0). This is useful for a lot of calculations, but not when you cross the magic floating point precision boundaries. When numbers get too high you will need to do extra tricks (such as Floating Origin). Keep your world within the Floating Point precision boundaries or prepare to juggle your coordinates when translating to and from clients, the world and workers.


#9

Here’s another question with regards to entity ids - how static do they need to be? For instance, if I’m iterating on a zone in my game world snapshot (which I’m building as a scene in unity, with a script that “exports” entities into the snapshot with a button click; does it matter if the entity id’s change from one build to the next? Should I track dirtiness of my zone within my world editor script and only add new entities, remove existing ones (when removed from the scene), and update existing ones (only if something has changed with them) – from a “performance” perspective that’s definitely the right approach, however since it’s only the editor performance (at least right now) really doesn’t make that much difference. Here’s a snippet of my existing world builder code for an example:

    private void SaveZone()
    {
        EntityTemplate[] sceneEntities = FindObjectsOfType<EntityTemplate>();
        foreach (EntityTemplate entity in sceneEntities)
        {
            Entity e = entity.EntityBuilder().Build();

            // This is a new entity in the scene
            if (entity.EntityID < 1)
            {
                builder.Add(e);
            }
            else
            {
                builder.Update(new EntityId(entity.EntityID), e);
            }
        }
    }

The add and update methods simple either call .Add() method on the snapshot dictionary or if the entity already has an entity id associated with it, then it calls snapshot[EntityID] = entity – and within the Builder is a GenerateID() method that simply increments a counter. Is this uneccessary? Or to be more clear, how important (generally) is Entity ID management within the game world across snapshots?


#10

They do need to have a static id which is why GenerateID () is used in the snapshot builder. It ensures that the same number is never used twice for that snapshot.

When it comes to iterating on an area, it doesn’t matter if the id of the new object is much higher or lower of the surrounding pieces, so long as it has a different id then every other entity. You can do hardcore values if you so wish or reference the place of an object in a list for each zone; offset by say multiplying the x and y together of said zone to give a unique value, but if doing that you must be careful to ensure each entity has a unique value.


#11

Right - but across iterations, say in the snapshot I created yesterday, I have an entity “Basic Building” that has entityID 10, I make some changes and re-write the snapshot and now the same “Basic Building” instance becomes entityID 14. Does this break things in a live gameworld, or does it not make any difference. I’d imagine for things like character equipment and inventory this presents problems?


#12

Ah I see. Sorry for missing that. As far as I know the world made builds from the specifics snapshot and shouldn’t break. It’ll only break if you have code referencing objects by their specific id, which you should try to avoid anyways and use a query to find specific objects.
For character equipment and inventory I’m personally using an external database as the snapshot doesn’t constantly save from my understanding. That way my player data is always saved for sure.


#13

Hey guys - now that I’ve had more of an opportunity to explore and experiment with SpatialOS (including tying into PlayerFab before the tutorial existed; ironically my solution looked almost identical); I wanted to revisit this topic and explore the best way to accomplish what I’ve set out to do in my sim. First, a little context is probably necessary:

My game, “Moongate” is composed of several “worlds” all connected through a series of (you guessed it) Moongates. If you’re familiar with Stargate (I actually designed the concept of this game long before I saw the movie or shows) the dynamics are similar. Each of the worlds are connected through gates that open and close based on the cycle of the moon on both the origin and target worlds. These “Moongates” are the only way to travel between the various worlds and new worlds are discovered every day. These new worlds may or may not be aware of the existence of the moongates, and various civilizations have different ways of understanding them (when they’re aware of them). It’s entirely possible for a player to become “marooned” in a world until they’ve determined how to negotiate the moongate on the world where they’ve landed; no scenario is unescapable. There’s a lot more to this, and it’s a lofty undertaking to be sure; so I’m starting relatively small to build the platform and “test the waters” as it were. Also, the number of worlds should be something that scales with the size of the playerbase; otherwise it will simply feel like a single-player game where on rare occasion you run across other players (though, this could also be an incredibly interesting dynamic to explore in gameplay)

All that being said - I think Spatial is the “perfect” platform for this network of worlds to exist in; it’s highly scalable, persistent, and hits most of the other various checkboxes in my list (something that literally no other platform I’ve looked at comes close to doing). While I’ve experimented with various means of “world building” I’ve yet to come up with something that seems like the ideal solution for the mechanics that I’ve described. I have several questions here, and am looking for any input that you guys may be able to offer.

  1. Physically separating the “worlds” from each other – presumably, as long as there is enough “void” (empty space) between each of my worlds so that “the next world” in the snapshot won’t be streamed to the client as I approach the edge of the current world I’m on; this should provide the illusion of the type of separation that I’m going for. For instance, if my entity_interest is set to 200, then as long as there are at least 200 units between the edge of one world and the next, then the expected behavior I’ve described should result. Is this correct?
  2. To procgen, or not to procgen; that is the question – I’ve been experimenting with a seed-based procedural terrain generation engine (I actually got about 75% through building the thing before I stopped and asked myself how this would function on a spatial-based game). The initial state of a world could be procedurally generated, but the question to you guys is whether that “feels” like the right approach? To really do it that way, I’d need to not only procgen the terrain, I’d also need to determine where cities, settlements, etc. would be based; the various aspects of the world (language, religion, physical attributes of the peoples and architecture, etc.) and to me, that feels like a whole new classification of engine that I could spend years building. While this is incredibly interesting to me, it “feels like” this may be a great-deal more work than it’s actually worth. Have any of you worked on anything along these lines/have any feedback or thoughts on the matter?
  3. Building on the previous question; I’ve explored (unsuccessfully, thus far) building a custom entity pipeline for environment/terrain/general world rendering where rather than storing everything as “prefabs” (indicating that the procedural generation happens offline and is written to the snapshot as entities). I envision something stored within a component that describes the various parameters that are fed into the procgen engine in order to produce the (deterministic) desired resulting world at runtime. While this sounds incredibly scalable; it also seems like it could potentially come with a whole slew of problems.
  4. The floating-point precision problem – This problem seems like it will almost certainly manifest itself in my game world; I’ve seen a lot of references to it indicating that there are various things that can be implemented to resolve the problem but I’ve not really seen too many details about how our community is addressing this in a platform designed to support massive (potentially infinite) persistent worlds.

I’m sure that I’ll have a great deal more questions around these topics as we dive in; but this seems like a lot to start – so let us discuss! :slight_smile:


#14

Hello,

Newbie here trying to wrap my head around SpatialOS and what I would like my game worlds to do. I have had a similar idea that revolves around multiple versions of a world, sort of like a multiverse.

My question is, have you made any more progress on solving this issues? It seems like the many ideas of using origin shifting that I have seen are the way to go but I have been unable to find any concrete examples of making this work with SpatialOS. If there has been any more news on this subject I would love to hear about it as it would put me one step closer to finalizing the tech design process for my game idea. Thank you.

  • Josh

#15

Hey Josh,

You can find an example of a Floating Origin solution in the Friends of Spatial repository: https://github.com/FriendsOfSpatial/FloatingOrigin


#16

Nice, I think that will help me in the right direction. It looks like I need to do more research on origin shifting in multiplayer games as that seems to be where my disconnect is coming from. I appreciate the example as that will help me to implement my idea in the future.