Skip to main content

Instantiate / Destroy

Prefabs

Instantiated objects are basically the same as synchronized objects placed on the scene during development. They are, as the name suggests, spawned at runtime. Thus they have one extra requirement: they have to be placed in prefabs with an ElympicsBehaviour as the root GameObject.

info

Prefabs that are to be instantiated at runtime need to be placed inside the Resources directory.

Prefab Example

Instantiate

ElympicsInstantiate

The only way to spawn synchronized prefab in runtime is to use ElympicsMonoBehaviour.ElympicsInstantiate.

This method has 2 parameters:

  • pathInResources: path to prefab in Resources directory e.g. "BlueBall"
  • player: predictable for player or ownership
    • ElympicsPlayer.World - only server can spawn with this option and it means it won't be predictable to any player - useful for random server events
    • ElympicsPlayer.All - object predictable for all players, can be spawned by any player - useful for fully predictable physics objects
    • ElympicsPlayer.FromIndex(i) - object predictable for specific player, can be spawned by this specific player or server - useful for projectiles created by player

Output of ElympicsInstantiate is of type GameObject and there is currently no generic override of the method available. To get desired component from prefab, simply use Unity GetComponent method, like in the example below.

// spawns "BlueBall" every 100 ticks
private readonly ElympicsInt _tick = new ElympicsInt(0);

public void ElympicsUpdate()
{
_tick.Value++;
var tickPeriod = _tick.Value % 100;
if (tickPeriod == 99)
{
GameObject prefabInstance = ElympicsInstantiate("BlueBall", ElympicsPlayer.All);
CustomScript custom = prefabInstance.GetComponent<CustomScript>();
custom.CustomMethod();
}
}

NetworkId

Objects created using ElympicsInstantiate have their NetworkIds assigned using formula:

var networkId = (playerIndex + 4) * 10 000 000 + i;

where i means index of instantiated object increased with every new object.

This means that all instantiated objects have these NetworkId ranges:

  • for All players - [10 000 000; 19 999 999]
  • for World player - [20 000 000; 29 999 999]
  • for Player 0 - [40 000 000; 49 999 999]
  • etc.
Player limit

Number of players is limited to 196, which means the maximal possible network ID for an instantiated object is 1 999 999 999. This can be changed in future releases.

tip

Remember! ElympicsUpdate, OnInput*, etc. are called in a deterministic order of increasing NetworkId. Scripts attached to objects instantiated at runtime will be executed at the end of the game loop. Learn more.

Destroy

Self destroy

The easiest way to destroy an object instantiated at runtime is to do this from a script within that object. So this object should have a script attached to it similar to:

private readonly ElympicsInt _ticksAlive = new ElympicsInt(0);

public void ElympicsUpdate()
{
_ticksAlive.Value++;
if (_ticksAlive.Value > 60)
ElympicsDestroy(gameObject);
}

From another object

Another way to destroy an instantiated object is to save its reference and destroy it from the outside of this object.

private readonly ElympicsInt        _tick = new ElympicsInt(0);
private readonly ElympicsGameObject _cube = new ElympicsGameObject(null);

public void ElympicsUpdate()
{
_tick.Value++;
var tickPeriod = _tick.Value % 200;
if (tickPeriod == 80) // or (tickPeriod >= 80 && tickPeriod <= 199 && ReferenceEquals(_cube.Value, null))
_cube.Value = ElympicsInstantiate("Cube", ElympicsPlayer.All).GetComponent<ElympicsBehaviour>();

if (tickPeriod == 199 && !ReferenceEquals(_cube.Value, null))
{
ElympicsDestroy(_cube.Value.gameObject);
_cube.Value = null;
}
}
Why check null reference?

The check is there to handle a very rare case when a client could reconnect at tick 198 and not have an up-to-date server snapshot with a valid reference to the object. Thus, the object would not be present because of not being predicted at line if (tickPeriod == 80), and not having a valid snapshot. However, this can be redesigned to instantiate an object if it's not already instantiated with condition tickPeriod >= 80 && tickPeriod <= 199 && ReferenceEquals(_cube.Value, null). This depends on whether you actually want to spawn an object that's going to be destroyed in the next tick anyway.

ElympicsFactory

How do objects instantiated at runtime on the server appear on the clients?

There is an alternative to ElympicsBehaviour designed for this: ElympicsFactory.

It's used internally by Elympics when you call ElympicsInstantiate or ElympicsDestroy methods.

ElympicsFactory is split into parts, where each part contains information about instantiated objects and has its predictability set to corresponding player ID (World, All, 0, 1 etc.) Thus each player can predict the state of their factory part and check if their prediction was successful.

Other parts that are not predictable are just simply deserialized and proper objects are instantiated.

Coding principles

info

These are the most important rules for instantiation programming and should always be followed!

Deal only with your objects

As mentioned earlier, clients should only instantiate objects they desire to use or interact with and destroy the ones created by themselves.

This applies also to the server. There are some special cases when server should also instantiate or destroy objects intended to be used by players. For example a special bonus weapon for a specific player (with PredictableFor or ownership set for this player), intended to be used for some specific time duration.