Skip to content

Instantly share code, notes, and snippets.

@sebas77
Last active February 15, 2018 10:32
Show Gist options
  • Save sebas77/752f06693d1fc026af4223fcae4d70e6 to your computer and use it in GitHub Desktop.
Save sebas77/752f06693d1fc026af4223fcae4d70e6 to your computer and use it in GitHub Desktop.
void SetupEnginesAndEntities()
{
//The Engines Root is the core of Svelto.ECS. You must NEVER inject the EngineRoot
//as it is, therefore the composition root must hold a reference or it will be
//GCed.
//the UnitySumbmissionEntityViewScheduler is the scheduler that is used by the EnginesRoot to know
//when to inject the EntityViews. You shouldn't use a custom one unless you know what you
//are doing or you are not working with Unity.
_enginesRoot = new EnginesRoot(new UnitySumbmissionEntityViewScheduler());
//Engines root can never be held by anything else than the context itself to avoid leaks
//That's why the EntityFactory and EntityFunctions are generated.
//The EntityFactory can be injected inside factories (or engine acting as factories)
//to build new entities dynamically
_entityFactory = _enginesRoot.GenerateEntityFactory();
//The entity functions is a set of utility operations on Entities, including
//removing an entity. I couldn't find a better name so far.
var entityFunctions = _enginesRoot.GenerateEntityFunctions();
//GameObjectFactory allows to create GameObjects without using the Static
//method GameObject.Instantiate. While it seems a complication
//it's important to keep the engines testable and not
//coupled with hard dependencies references (read my articles to understand
//how dependency injection works and why solving dependencies
//with static classes and singletons is a terrible mistake)
GameObjectFactory factory = new GameObjectFactory();
//The observer pattern is one of the 3 official ways available
//in Svelto.ECS to communicate. Specifically, Observers should
//be used to communicate between engines in very specific cases
//as it's not the preferred solution and to communicate beteween
//engines and legacy code/third party code.
//Use them carefully and sparsely.
//Observers and Observables should be named according where they are
//used. Observer and Observable are decoupled to allow each object
//to be used in the relative context which promote separation of concerns.
//The preferred way to communicate between engines is through
//the entity components themselves. DispatchOnSet and DispatchOnChange
//should be able to cover most of the communication problems
//between engines.
var enemyKilledObservable = new EnemyKilledObservable();
var scoreOnEnemyKilledObserver = new ScoreOnEnemyKilledObserver(enemyKilledObservable);
//the ISequencer is one of the 3 official ways available in Svelto.ECS
//to communicate. They are mainly used for two specific cases:
//1) specify a strict execution order between engines (engine logic
//is executed horizontally instead than vertically, I will talk about this
//in my articles). 2) filter a data token passed as parameter through
//engines. The ISequencer is also not the common way to communicate
//between engines
Sequencer playerDamageSequence = new Sequencer();
Sequencer enemyDamageSequence = new Sequencer();
//wrap non testable unity static classes, so that
//can be mocked if needed.
IRayCaster rayCaster = new RayCaster();
ITime time = new Others.Time();
//Player related engines. ALL the dependecies must be solved at this point
//through constructor injection.
var playerHealthEngine = new HealthEngine(entityFunctions, playerDamageSequence);
var playerShootingEngine = new PlayerGunShootingEngine(enemyKilledObservable, enemyDamageSequence, rayCaster, time);
var playerMovementEngine = new PlayerMovementEngine(rayCaster, time);
var playerAnimationEngine = new PlayerAnimationEngine();
//Enemy related engines
var enemyAnimationEngine = new EnemyAnimationEngine();
var enemyHealthEngine = new HealthEngine(entityFunctions, enemyDamageSequence);
var enemyAttackEngine = new EnemyAttackEngine(playerDamageSequence, time);
var enemyMovementEngine = new EnemyMovementEngine();
var enemySpawnerEngine = new EnemySpawnerEngine(factory, _entityFactory);
//hud and sound engines
var hudEngine = new HUDEngine(time);
var damageSoundEngine = new DamageSoundEngine();
//The ISequencer implementaton is very simple, but allows to perform
//complex concatenation including loops and conditional branching.
playerDamageSequence.SetSequence(
new Steps //sequence of steps, this is a dictionary!
{
{ //first step
enemyAttackEngine, //this step can be triggered only by this engine through the Next function
new To //this step can lead only to one branch
{
playerHealthEngine, //this is the only engine that will be called when enemyAttackEngine triggers Next()
}
},
{ //second step
playerHealthEngine, //this step can be triggered only by this engine through the Next function
new To //this step can branch in two paths
{
{ DamageCondition.damage, new IStep[] { hudEngine, damageSoundEngine } }, //these engines will be called when the Next function is called with the DamageCondition.damage set
{ DamageCondition.dead, new IStep[] { hudEngine, damageSoundEngine,
playerMovementEngine, playerAnimationEngine, enemyAnimationEngine } }, //these engines will be called when the Next function is called with the DamageCondition.dead set
}
}
}
);
enemyDamageSequence.SetSequence(
new Steps
{
{
playerShootingEngine,
new To
{
enemyHealthEngine,
}
},
{
enemyHealthEngine,
new To
{
{ DamageCondition.damage, new IStep[] { enemyAnimationEngine, damageSoundEngine } },
{ DamageCondition.dead, new IStep[] { enemyMovementEngine,
enemyAnimationEngine, playerShootingEngine, enemySpawnerEngine, damageSoundEngine } },
}
}
}
);
//Mandatory step to make engines work
//Player engines
_enginesRoot.AddEngine(playerMovementEngine);
_enginesRoot.AddEngine(playerAnimationEngine);
_enginesRoot.AddEngine(playerShootingEngine);
_enginesRoot.AddEngine(playerHealthEngine);
_enginesRoot.AddEngine(new PlayerInputEngine());
_enginesRoot.AddEngine(new PlayerGunShootingFXsEngine());
//enemy engines
_enginesRoot.AddEngine(enemySpawnerEngine);
_enginesRoot.AddEngine(enemyAttackEngine);
_enginesRoot.AddEngine(enemyMovementEngine);
_enginesRoot.AddEngine(enemyAnimationEngine);
_enginesRoot.AddEngine(enemyHealthEngine);
//other engines
_enginesRoot.AddEngine(new CameraFollowTargetEngine(time));
_enginesRoot.AddEngine(damageSoundEngine);
_enginesRoot.AddEngine(hudEngine);
_enginesRoot.AddEngine(new ScoreEngine(scoreOnEnemyKilledObserver));
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment