Improbable Icon

Getting started with Improbable for VR

v9-1-0
vr

#1

Hi Improbable!

I have been reading your tutorials and learning how to run SpatialOS over the past few days, and in that process I have arrived upon some questions.

Could you provide some elaborations as to why spawning & deleting players appears so complicated? (e.g. “PlayerManagementBehaviour”/“ClientPlayerSpawner” in HelloWorld demo)
Why the need for searching for a list of entity ids, where the first one is then stored as the “SimulationManagerEntityId” ? I thought the entity ID was accessible through the Unity GameObject? But you appear to be storing the WorkerId?

Somewhere in your docs, you suggest to keep “Improbable Components” (the ones generated through schema) private to their respective MonoBehaviours, which means that the same ‘Improbable Component’ will be ‘required’ by possibly many ‘Unity Components’. Doesn’t that introduce redundancy and more overhead in network message handling? I thought it would be simpler and/or more efficient to have a ‘centralized’ Unity MonoBehaviour component with more or les all reader/writers needed for that entity type.

I noticed in your “Hello World” demo the presence of at least 3 components with “TransformReceiver” in their names. I realize that two of them are only active on client and server respectively, but why not just combine them into one ‘TransformReceiver’ which internally checks whether it has authority or not, and uses that to execute the correct functionality? Your current approach seems to introduce more code repetition and redundancy, in my opinion.

Is there any way to use different prefabs for the same entity type? Specifically for VR players it can be desirable to have separate prefabs for local player (with VR components) and remote players (without VR). In the ‘Hello World’ demo, as well as in the ‘Pirates’ tutorial, you seem to suggest that adding an ‘Improbable Component’ with the appropriate ACL to the MonoBehaviour is the recommended approach (thus disabling that MonoBehaviour when not supposed to run).

As far as I can see, your approach means placing a more or less arbitrary ‘Improbable Component’ on the VR components with an ACL set for only allowing them on the specific local client. My issue with that approach is mainly that the component list on a particular game object can grow to almost unmaintanable proportions. I feel like this is the case in your ‘Hello World’ demo for the player entity prefab, even though that demo is supposed to be relatively simple and small, the list of components on that game object is quite long.

Having different prefabs for player entities depending on whether they are local or remote affords a more simplified component setup on the prefabs, as well as more specialization of components.

I read in your docs that using ‘FinishAndSend’ is a requirement to send data, but I cannot find a way to access the ‘Impl’ subclass within a generated Improbable Component (which seems to be the only one to expose that method). Also in my brief testing, synchronization appears to work without this ‘FinishAndSend’ method, just using ‘Send’. So are the docs outdated or how am I supposed to call FinishAndSend? (it is NOT exposed on the ‘Updater’ as suggested by the docs, as far as I can see).

If I understand the ‘Snapshot’ feature correctly, the idea is to write code to first define all “Snapshot Entities” (kind of like blueprints) and then (possibly) write code for ‘prepopulating’ the world. However, in Unity, most level designers are used to being able to drag and drop prefabs / game objects directly in to the scene. I realize that we could make some editor-script for generating a snapshot prepopulation based on the placed data, but I cannot help but wonder whether the use-case for hand-designed levels (without coding) was forgotten a little bit when developing this snapshot tool? The need for manual level design is especially relevant for highly-detailed or small worlds, which, for the moment, are quite common in VR.

I’m assuming that you have implemented networking optimization techniques, such as compression and message coalescing? I am only asking because Unity’s LLAPI does not provide these features.

Thanks in advance.
Best,
Rami
VRUnicorns / Apex Game Tools


#2

Hello there, @DeBock!

It is great to hear from you! These are all exellent questions; we’ll do our best to answer them!

  1. Could you provide some elaborations as to why spawning & deleting players appears so complicated?
    Why the need for searching for a list of entity ids, where the first one is then stored as the “SimulationManagerEntityId” ?
    I thought the entity ID was accessible through the Unity GameObject? But you appear to be storing the WorkerId?

The player spawning/deleting (lifecycle) management indeed appears rather complex! However, the reason is due to what we were trying to achieve with the game.

  • First, the simulation manager entity (SimulationManagerEntityId) can be viewed as the “manager” entity – being in charge of players logging in and out. Hence, this entity must thus first be located when a Unity client connects to the SpatialOS simulation so that it can send commands to it.

  • Second, the entity ID is accessible through the Unity GameObject – yes. However, at the start of the simulation, the Unity client does not yet have access to the Unity GameObject of the Simulation Manager entity yet. Hence, the need to query the simulation for it as we did first.

  • Third, we store both the WorkerId and EntityId to keep a mapping between Unity Clients (worker) and its playable character (entity) within the simulation. This way, we can handle players re-possessing characters they were controlling within the game prior to logging off.

  1. Somewhere in your docs, you suggest to keep “Improbable Components” (the ones generated through schema) private to their respective MonoBehaviours, which means that the same ‘Improbable Component’ will be ‘required’ by possibly many ‘Unity Components’.
    Doesn’t that introduce redundancy and more overhead in network message handling? I thought it would be simpler and/or more efficient to have a ‘centralized’ Unity MonoBehaviour component with more or les all reader/writers needed for that entity type.

While the docs suggest keeping “Improbable components” private to MonoBehaviours, it does not necessarily mean that you can not have a Unity MonoBehaviour that contains the readers/writers of different components. In fact, the nature of the Entity Component System design pattern does encourage the notion of a container entity (Unity GameObject) being made up of components (Unity MonoBehaviours/Components) – so it is perfectly valid for a Unity MonoBehaviour itself to be composed of several “Improbable components” themselves, especially in the interest of cutting down on redudnacy and to reduce the overhead of network messages.

However, two reasonw why you might want to keep MonoBehaviours associated with as few “Improbable components” as possible.

  • Modularity: You may then re-use MonoBehaviours between different GameObjects without having to worry about any implicit inter-dependencies between components. For example, if you have a “HealthComponent” only managed by a “HealthMonoBehaviour”, you can be sure that it used for any entity that has a “HealthComponent”. However, if you have both “HealthComponent” and “ManaComponent” handled by a single “HealthAndManaMonoBehaviour”, this means that you now have a coupling between health and mana. So you will not be able to have, an NPC that only has health and no mana, and vice versa. It’s a bit of a strawman example, but hopefully it becomes apparent why keeping Components <-> MonoBehaviours 1-to-1 becomes useful.

  • Safety: Do remember that only one worker can be authoritative over a component at one time. So if you have a “HealthComponent” and “ManaComponent”, you will have to be careful if you end up ever having to delegate different authorities to either component on the same entity. This is a bit related to modularity, but it has a subtleties.

  1. I noticed in your “Hello World” demo the presence of at least 3 components with “TransformReceiver” in their names. I realize that two of them are only active on client and server respectively, but why not just combine them into one ‘TransformReceiver’ which internally checks whether it has authority or not, and uses that to execute the correct functionality? Your current approach seems to introduce more code repetition and redundancy, in my opinion.

This is somewhat related to what we discussed above. In this case, however, you do mention having logic within the MonoBehaviour itself to switch between the behaviours when authority is delegated, or not. This definitely works! But in cases where logic between what the server and client does becomes complicated/vastly different, this may become unwieldy.

Consider movement: a client may be interested in interpolation and animation, but not on positional accuracy; a server would likely not have any animation logic but care about resolving collisions, hence a focus on accuracy. Both will require the same transform component, but will likely have different logic.

But there are definitely arguments to share components and not distinguish between Client / Server, of course.

I read in your docs that using ‘FinishAndSend’ is a requirement to send data, but I cannot find a way to access the ‘Impl’ subclass within a generated Improbable Component (which seems to be the only one to expose that method). Also in my brief testing, synchronization appears to work without this ‘FinishAndSend’ method, just using ‘Send’. So are the docs outdated or how am I supposed to call FinishAndSend? (it is NOT exposed on the ‘Updater’ as suggested by the docs, as far as I can see).

This is a quick one! So I’ll answer it. We must have missed this out: the FinishAndSend() command is not required when using the new writers in SpatialOS 9 (e.g., ShipControls.Writer.Send() is sufficient as seen in the Pirate tutorial..)


More to come…


Any chance you (spatial devs) could explain how Hello World works?
#3

For #4 you will unfortunately need to handle this yourself on your VR entity prefab. You can use the VR SDK’s to detect the user’s HMD and handle enabling and disabling entities in reaction to what you find. If you are using a interaction library you will most likely need to rewrite some of the code to work with spatial.


#4

Hey guys @cbdileo @chong-u ,

Thanks for the answers so far. Very useful!

We heard about an alleged VR demo/template for Unity/SpatialOS - is this nearing completion and will it be provided before the game jam this coming weekend? (10th-12th).

Additionally, we have an issue with SteamVR, which was posted here. Basically, we cannot build the project when SteamVR is added.


#5

Hey @DeBock, unforunately we’re unlikely to get it done before GDC which runs Feb 27th - March 3rd as we’re currently all hands on deck for that!

In meantime you’re welcome to experiment with the idea yourself - Chris just wrote up a cool intro on how to approach VR in SpatialOS and giving that a read would definitely be my first port of call!


#6

Hey @alastair

Thanks for the response.

Unfortunately that news is quite disappointing to us, since we were hoping to be able to use the VR demo for the upcoming Improbable Jam as well as for a demo to be displayed at GDC.

I’ve read the “VR in SpatialOS” a couple of times. While it does provide pointers as to basic network data management that is not the part that we had difficulties with anyway, unfortunately. I’m sure it’ll be helpful to some others though.

One issue that I’m facing with the suggested VR architecture in that article is that “EntityPosition” only exists for the VRPlayer component, but many other (non-VR) components, e.g. ‘EntitySimulationManager’, will also need an entity position. I realize that I can do this still, but SpatialOS is warning me that having EntityPosition on more than one Improbable component is deprecated and will not be supported in the future. Thus, I don’t want to go down that road.

Instead, I am looking at using at least two improbable components to represent VR players: A “generic” Transform which can be used anywhere, and only has the EntityPosition, and another one with resembling the suggested VRPlayer, although without EntityPosition.

Another challenge is that we would prefer to actually just have 1 player prefab, after internal discussions, and I am facing difficulties trying to achieve that.

I’ve tried with one “master/parent” Unity MonoBehaviour which simply checks authority and disables or destroys the correct child game object. The two children are one to represent remote players (for FSim & remotes on client) and another to represent the local player (with VR tracking). I could not get it to disable the correct game object, and destroying seemed to trigger ‘null reference exceptions’ on the server subsequently. Could you perhaps advise as to the simplest approach to achieve such a setup? Can there even be Improbable components on child game objects?


#7

Hey DeBock

Your approach of using two components seems sensible, and should be fine.

As for putting everything in one prefab and deleting the child objects, that could work, as long as you’re not using entity pooling.

You can have improbable components on child objects, yes. That should all work as you’d expect.

What problems did you have with disabling the child object? You can’t enable/disable game objects as they are being created in unity, so you might need to start with them both being disabled, and then enable one of them on the next frame.


#8

Hey @chris

Thanks for your response.

I suppose the entity pooling is why deleting child objects did not work then.

I thought that the automatic enable/disable only applied at the MonoBehaviour level, but you are saying that entire game objects can be activated/deactivated by SpatialOS automatically, depending on the state of requirements?


#9

No, you were right the first time, we only enable and disable monobehaviours - but you are free to enable and disable game objects in your own script.

On a [Require]'d component, there is an event called OnAuthorityChanged that you can hook into to enable and disable child objects.


#11

Thanks @chris

OnAuthorityChanged worked for toggling between the game objects. So that’s great.

I’ve been wondering about the in-unity menu options for “Build Development Players” and “Build Deployment Players”. Is running one of them exactly the same as running spatial build in the console? If so, why are they faster in Unity (at least on my machine)?

Also, I cannot confirm with certainty yet, but I get the feeling that the Unity ‘build players’ not always actually makes a new build - or misses some changes? Are there any circumstances where that may happen?


#12

I’m trying to get SteamVR to work together with SpatialOS, as also discussed in this thread.

This time I’ve made a completely controlled test. A small demo project that works completely, then I add SteamVR and DO NOTHING ELSE; suddenly SpatialOS can no longer spawn FSim workers. I get the following output in console:

C:\Users\Rami\Documents\utopia_spatialos>spatial local start
SpatialOS starting
Full file logs available at: C:\Users\Rami\AppData\Local\Temp\improbable\logs-255903543
SpatialOS runtime version: 9-20170207T134758Z-1c78c8b
SpatialOS ready. Access the inspector at http://localhost:21000/inspector
15:30:03.862 WARN [RemoteWorkerRunner] Failed to start UnityFSim worker UnityFSim0: Timed out waiting for worker connection.
15:30:03.885 ERROR[CommandLineExecutor] (UnityFSim0) Command exited with code 1: C:\Users\Rami\AppData\Local\Temp\improbable2774497786821349989\UnityFSim@Windows.exe +appName alpha_ohio_march_784 +receptionistIp 192.168.1.85 +receptionistPort 7777 +engineType UnityFSim +engineId UnityFSim0 +useInternalIpForBridge true +infraServicesUrl http://127.0.0.1:21000 +assemblyName local_assembly +linkProtocol RakNet -logfile C:\Users\Rami\AppData\Local\Temp\improbable\logs-255903543\UnityFSim0-5583407106247528391.log -batchmode -nographics

The log file referred is available here:
UnityFSim0-5583407106247528391.log (8.2 KB)

The log file indicates that there are some types that cannot be loaded, specifically the following part may be interesting:

ReflectionTypeLoadException: The classes in the module cannot be loaded.
  at (wrapper managed-to-native) System.Reflection.Assembly:GetTypes (bool)
  at System.Reflection.Assembly.GetTypes () [0x00000] in <filename unknown>:0 
  at Improbable.Worker.Internal.ComponentDatabase..ctor () [0x00000] in <filename unknown>:0 
  at Improbable.Worker.Internal.ComponentDatabase..cctor () [0x00000] in <filename unknown>:0 
Rethrow as TypeInitializationException: An exception was thrown by the type initializer for Improbable.Worker.Internal.ComponentDatabase
  at Improbable.Worker.Internal.ParameterConversion.CreateComponentVtableArray () [0x00000] in <filename unknown>:0 
  at Improbable.Worker.Internal.ParameterConversion.ConvertConnectionParameters (Improbable.Worker.ConnectionParameters connectionParams, Improbable.Worker.Internal.ConnectionParametersCallback callback) [0x00000] in <filename unknown>:0 
  at Improbable.Worker.Connection.ConnectAsync (System.String hostname, UInt16 port, Improbable.Worker.ConnectionParameters connectionParams) [0x00000] in <filename unknown>:0 
  at Improbable.Unity.Core.WorkerConnection+<ConnectToReceptionistAsync>c__Iterator3.MoveNext () [0x00000] in <filename unknown>:0 
  at UnityEngine.SetupCoroutine.InvokeMoveNext (IEnumerator enumerator, IntPtr returnValueAddress) [0x00000] in <filename unknown>:0 
UnityEngine.MonoBehaviour:StartCoroutine_Auto_Internal(IEnumerator)
UnityEngine.MonoBehaviour:StartCoroutine(IEnumerator)
Improbable.Unity.Core.ConnectionLifecycle:Start()

However, since .NET does not give more information as to which classes that cannot be loaded, I cannot say with complete certainty that the issue is with SteamVR. I can only conclude that this particular issue occurred in a 100% working project after the addition of SteamVR, with no other changes.

The main challenge is that I have close to no ideas as to what to even attempt to fix this issue. Hopefully you can provide some support?


#14

So I thought I might be on to a solution when I figured out how to create my own “Custom Packager”, which would be invoked as part of the build process. Simply disable virtual reality support for FSims and allow them for clients, right?

Well, unfortunately Unity does not allow setting two static booleans outside of the main thread, and apparently the build process for Improbable happens off-thread.

The errors are quite clear, but here is a screen shot anyway:

I am using the following code:

[InitializeOnLoad]
public static class CustomSceneProcessor
{
    static CustomSceneProcessor()
    {
        UnityPlayerBuilder.GetPackager = (EnginePlatform engineType, BuildTarget buildTarget, Config config) =>
        {
            Debug.Log("Invoking the custom packager exporter, for engine == " + engineType.ToString() + ", build target == " + buildTarget.ToString());
            return new CustomPackager(engineType);
        };
    }

    private class CustomPackager : IPackager
    {
        private EnginePlatform _platform;

        internal CustomPackager(EnginePlatform platform)
        {
            _platform = platform;
        }

        public void Prepare(string packagePath)
        {
            var vrEnabled = _platform == EnginePlatform.Client;
            PlayerSettings.virtualRealitySupported = vrEnabled;
            VRSettings.enabled = vrEnabled;
            Debug.Log("CustomPackager setting VR Enabled to == " + vrEnabled.ToString() + ", for platform == " + _platform.ToString());
        }
    }
}

#15

The build players menu item is essentially the same as running spatial build UnityClient UnityFSim --target=development
So will be slightly faster than running a full spatial build

As for the ReflectionTypeLoadException Can you check to see if autopatching is turned on under the improbable menu? There are some known issues with it, so try turning it off.


#16

Thanks for the response @chris. Elaborations on building makes sense.

So far I have not been using the autopatching feature at all. So it is not related to that. Also, it doesn’t matter if I build through Unity build menus or spatial build, the FSims cannot be run when SteamVR is in the project.


#17

Any chance of the remaining of my original post’s questions being answered? #6 and #7


#18

Currently, in SpatialOS 9, we have support for asset contexts. These allow allows prefabs to be associated with “contexts”, which should aid you with what you’ve described. The asset context discriptor mechanism is changing slightly in 10.0, but functionality should still be maintained – but just a heads up for now!

Your suggested approach is also valid, i.e., a component like ClientAuthorityCheck in Hello, World! that enables a script only for the controlling player.
You could also actually just have two child GameObjects on your prefab that effectively represent the local client and server versions that are enabled or disabled as needed.

It’s agreed the list of components is quite long in Hello, World! although this is mainly due to our preference for modularizing components where possible. We could just merge the Health and Flammable component properties into the PlayerInfo component however when created separately they can be reused across other entities - almost all entities in the case of Health and Flammable. Similarly, although Player entities are the only ones using the Chat component, keeping that as a separate component means the chatting functionality is a modular feature we can easily extract and reuse in a different game! This would be harder if the properties were all also bundled in the PlayerInfo component.

We definitely see the value in hand-designed levels! Previously we’ve had a script which took all the GameObjects in a static Unity scene and created a snapshot with the relevant entities in the same positions. The issue with this is that it’s not scalable and a single Unity scene couldn’t hold a static scene the size of a full scale Hello, World! demo so switching to a programmatic snapshot generation as the primary tool was natural. The logical midpoint of this is finding a system whereby a designer can create many separate parts of a world that are programmatically stitched together and used to generate a snapshot.

A great case study would be island creation in Worlds Adrift. The Island Creator tool (see below) allows players to hand craft islands which are turned into prefabs and loaded into the game at runtime. Something similar enabling a hand-designed level to be exported and programmatically placed within a much larger world is something we’re working towards in future! In the meantime you could check out @morrison’s tool for generating snapshots from Unity scenes in his own Pirates Tutorial repo and be sure to shoot any questions you have his way!

The short answer is: no, we do not currently do any networking optimization like compression or message coalescing.
The data types that are available via our SchemaLang language (and which then get converted to Protobuf types) are pretty much as low-level as it gets in terms of what gets sent down the wire.
We do know of users who have looked at message coalescing, etc., but nothing yet in terms of what our SDK provides.
This might be an interesting discussion point with others around here, actually, so thanks for bring this up!

Hope that answers some of your questions! :slight_smile:
Ally


#19

Hey @alastair

Thanks a lot for your elaborate responses! They are much appreciated and certainly helps bring clarity to my mind.

I found out about asset contexts after posting my initial post, but thanks for elaborating on the explanation. I’m sure they can be quite handy in certain circumstances.

I certainly understand the modularized approach, it lends itself very well to Unity’s component system. I just thought it was a bit in the extreme end in the Hello World demo.

Worlds Adrift’s solution sounds awesome, sharing something like that with SpatialOS developers would be beneficial for many game projects, I feel. Morrison’s tool is exactly what I was asking for, and I think you should make it more clearly available somewhere on your site or docs.

Finally, I am absolutely shocked that you do not do any network optimizations! I very much assume that you will make this a priority before going for a full public release. Otherwise, you are basically “wasting” your users’ money needlessly. Honestly I cannot even imagine how you can support such large worlds without a super efficient network architecture. Your server needs must be massive. Absolutely flabergasted!


#20

you do not do any network optimizations

The best people to make these optimisations will be the end user. You know far more about your network traffic than we do (in fact, the less we know the better!) and your decision to optimise is completely yours to make.

Any optimisation we make at an API level will of course affect every client and therefore every worker. If we did roll something out it would have to be an opt-in solution with full source code shipped : and that would make it more of a suggestion of how to do things more than anything else.


#21

He is an absolute hero! If we could only clone him :smiley: :support:


#22

Thanks for chiming in @callumb

However, I must say that I disagree. There is a standard set of networking optimization features that you would expect from any professional network library. Some of which, true enough, will need to be tailored to some extent to the specific game, while others are so general that I think all users will appreciate.

I am not an expert in these matters myself, which is why I ask of network libraries to provide some of these features. Basic stuff like message coalescing and compression, delta changes, pooling of messages and so on are basic, general-level features that 99,999% of all game developers will benefit from in their games.

I don’t agree that:

If we did roll something out it would have to be an opt-in solution with full source code shipped

Basic features like the ones mentioned above will be desirable to have for anyone, and not providing them to your users at all seems - to me - like you don’t really care if your users are overcharged for their network bandwidth usage.

Sorry for being a bit harsh, but I am truly surprised at meeting this response from you guys. Did NOT expect that!!