示例#1
0
        public PlayerGunShootingEngine(EnemyKilledObservable enemyKilledObservable, Sequencer damageSequence)
        {
            _enemyKilledObservable = enemyKilledObservable;
            _enemyDamageSequence   = damageSequence;

            TaskRunner.Instance.Run(new Tasks.TimedLoopActionEnumerator(Tick));
        }
示例#2
0
 public PlayerGunShootingEngine(EnemyKilledObservable enemyKilledObservable, ISequencer damageSequence, IRayCaster rayCaster, ITime time)
 {
     _enemyKilledObservable = enemyKilledObservable;
     _enemyDamageSequence   = damageSequence;
     _rayCaster             = rayCaster;
     _time        = time;
     _taskRoutine = TaskRunner.Instance.AllocateNewTaskRoutine().SetEnumerator(Tick())
                    .SetScheduler(StandardSchedulers.physicScheduler);
 }
 public PlayerGunShootingEngine(EnemyKilledObservable enemyKilledObservable, ISequencer damageSequence,
                                IRayCaster rayCaster, ITime time, Factories.IGameObjectFactory gameobjectFactory,
                                IEntityFactory entityFactory)
 {
     _enemyKilledObservable = enemyKilledObservable;
     _enemyDamageSequence   = damageSequence;
     _rayCaster             = rayCaster;
     _time        = time;
     _taskRoutine = TaskRunner.Instance.AllocateNewTaskRoutine().SetEnumerator(Tick())
                    .SetScheduler(StandardSchedulers.physicScheduler);
     _gameObjectFactory = gameobjectFactory;
     _entityFactory     = entityFactory;
 }
/// <summary>
/// Before to start a review of Svelto.ECS terminologies:
/// - Entity:
///     it must be a real and concrete entity that you can explain
///     in terms of game design. The name of each entity should reflect
///     a specific concept from the game design domain
/// - IComponents:
///     Components must be seen as data holders. There are implementing
///     exceptions, but logically it must be still see as a group
///     of readable or writeable data.
///     In Svelto.ECS components are always interfaces declaring
///     Setters and Getters of ValueTypes. DispatchOnSet
///     and DispatchOnChange must not be seen as events, but
///     as pushing of data instead of data polling, similar
///     to the concept of DataBinding.
/// - Implementors:
///     Being components interfaces, they must be implemented through
///     Implementors. The relation Implementors to Components
///     is not 1:1 so that you can, if logic, group several
///     components in one implementor. This allows to easily
///     share data between components. Implementors also act
///     as bridge between the platform and Svelto.ECS.
///     Since Components can hold only value types, Implementors
///     are the objects that can interact directly with the platform
///     objects, I.E.: RigidBody, Transform and so on.
///     Note: IComponents must hold only valuetypes for
///     code design purposes and not optmization purposes.
///     The reason is that all the logic must lie in the engines
///     so Components cannot hold references to instances that can
///     expose functions with logic.
/// - Engines:
///     Where all the logic lies. Engines operates on EntityViews
/// - EntityViews:
///     EntityViews maps EntityComponents. The Engines can't
///     access directly to each entity (as a single set of components), but
///     through a component sets defined by EntityView.
///     They act as a component filters and expose only the entity components
///     that the Engine is interested in.
///     EntityViews are actually defined by the need of the Engine so they
///     come together with the engine and in the same namespace of the engine.
/// - EntityStructs:
///     In order to write Data Oriented Cache Friendly code, Svelto.ECS
///     also support EntityStructs. Please check other examples to
///     understand how to use them. However know that this kind of
///     optimizations is very limited to special circumstances
///     so the flexibility of EntityViews is most of the times what you need.
/// - EntityDescriptors:
///     Gives a way to formalize your Entity in svelto.ECS, it also
///     groups the EntityViews that must be generated once the
///     Entity is built
/// </summary>
        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));
        }
示例#5
0
        void SetupEnginesAndComponents()
        {
            _entityFactory = _enginesRoot = new EnginesRoot(new UnitySumbmissionNodeScheduler());

            GameObjectFactory factory = new GameObjectFactory();

            var enemyKilledObservable      = new EnemyKilledObservable();
            var scoreOnEnemyKilledObserver = new ScoreOnEnemyKilledObserver(enemyKilledObservable);

            Sequencer playerDamageSequence  = new Sequencer();
            Sequencer enemyDamageSequence   = new Sequencer();
            Sequencer playerHealSequence    = new Sequencer();
            Sequencer ammoRechargeSequence  = new Sequencer();
            Sequencer ammoDepletionSequence = new Sequencer();
            Sequencer specialAtkFXSequence  = new Sequencer();

            var enemyAnimationEngine     = new EnemyAnimationEngine();
            var playerHealthEngine       = new HealthEngine(playerDamageSequence, playerHealSequence);
            var enemyHealthEngine        = new HealthEngine(enemyDamageSequence);
            var hudEngine                = new HUDEngine();
            var damageSoundEngine        = new DamageSoundEngine();
            var playerShootingEngine     = new PlayerGunShootingEngine(enemyKilledObservable, enemyDamageSequence, ammoDepletionSequence);
            var playerMovementEngine     = new PlayerMovementEngine();
            var playerAnimationEngine    = new PlayerAnimationEngine();
            var enemyAttackEngine        = new EnemyAttackEngine(playerDamageSequence);
            var enemyMovementEngine      = new EnemyMovementEngine();
            var enemySpawnerEngine       = new EnemySpawnerEngine(factory, _entityFactory);
            var healingPickupEngine      = new HealthPickupEngine(playerHealSequence);
            var ammoPickupEngine         = new AmmoPickupEngine(ammoRechargeSequence);
            var pickupSpawnerEngine      = new PickupSpawnerEngine(factory, _entityFactory);
            var ammoGUIEngine            = new AmmoGUIEngine();
            var specialAttackEngine      = new SpecialAttackEngine(specialAtkFXSequence);
            var specialAttackGUIEngine   = new SpecialAttackGUIEngine();
            var specialAttackSoundEngine = new PlayerSpecialAtkSoundEngine();
            var ammoPickupSoundEngine    = new AmmoPickupSoundEngine();
            var healthPickupSoundEngine  = new HealthPickupSoundEngine();
            var rotatingPickupsEngine    = new RotatingIdlePickupsEngine();

            playerDamageSequence.SetSequence(
                new Steps()                                                  //sequence of steps
            {
                {                                                            //first step
                    enemyAttackEngine,                                       //this step can be triggered only by this engine through the Next function
                    new Dictionary <System.Enum, IStep[]>()                  //this step can lead only to one branch
                    {
                        { Condition.always, new [] { playerHealthEngine } }, //these engines will be called when the Next function is called with the Condition.always set
                    }
                },
                {                                                                                                                                                  //second step
                    playerHealthEngine,                                                                                                                            //this step can be triggered only by this engine through the Next function
                    new Dictionary <System.Enum, IStep[]>()                                                                                                        //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 Dictionary <System.Enum, IStep[]>()
                    {
                        { Condition.always, new [] { enemyHealthEngine } },
                    }
                },
                {
                    enemyHealthEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { DamageCondition.damage, new IStep[] { enemyAnimationEngine } },
                        { DamageCondition.dead, new IStep[] { enemyMovementEngine, enemyAnimationEngine, playerShootingEngine, enemySpawnerEngine } },
                    }
                }
            }
                );

            //healing sequence
            playerHealSequence.SetSequence(
                new Steps()
            {
                {
                    healingPickupEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { DamageCondition.heal, new IStep[] { playerHealthEngine } },
                    }
                },

                {      //second step
                    playerHealthEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { DamageCondition.heal, new IStep[] { hudEngine, healthPickupSoundEngine } },
                    }
                }
            }
                );

            ////Sequence for ammo recharge
            ammoRechargeSequence.SetSequence(
                new Steps()
            {
                {
                    ammoPickupEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { Condition.always, new IStep[] { ammoGUIEngine, ammoPickupSoundEngine } },
                    }
                },
            }
                );

            ////Sequence for ammo usage
            ammoDepletionSequence.SetSequence(
                new Steps()
            {
                {
                    playerShootingEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { Condition.always, new IStep[] { ammoGUIEngine } },
                    }
                },
            }
                );

            //Sequence for special attack FX
            specialAtkFXSequence.SetSequence(
                new Steps()
            {
                {
                    specialAttackEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { SpecialAttackCondition.perform, new IStep[] { specialAttackSoundEngine } },
                        { SpecialAttackCondition.fail, new IStep[] { specialAttackSoundEngine } },
                    }
                },
            }
                );

            AddEngine(playerMovementEngine);
            AddEngine(playerAnimationEngine);
            AddEngine(playerShootingEngine);
            AddEngine(playerHealthEngine);
            AddEngine(new PlayerGunShootingFXsEngine());

            AddEngine(enemySpawnerEngine);
            AddEngine(enemyAttackEngine);
            AddEngine(enemyMovementEngine);
            AddEngine(enemyAnimationEngine);
            AddEngine(enemyHealthEngine);

            AddEngine(damageSoundEngine);
            AddEngine(hudEngine);
            AddEngine(new ScoreEngine(scoreOnEnemyKilledObserver));
            AddEngine(healingPickupEngine);
            AddEngine(ammoPickupEngine);
            AddEngine(pickupSpawnerEngine);
            AddEngine(ammoGUIEngine);
            AddEngine(specialAttackEngine);
            AddEngine(specialAttackGUIEngine);
            AddEngine(specialAttackSoundEngine);
            AddEngine(ammoPickupSoundEngine);
            AddEngine(healthPickupSoundEngine);
            AddEngine(rotatingPickupsEngine);
        }
 public ScoreOnEnemyKilledObserver(EnemyKilledObservable observable) : base(observable)
 {
 }
 public PlayerGunShootingEngine(EnemyKilledObservable enemyKilledObservable)
 {
     _enemyKilledObservable = enemyKilledObservable;
 }
        void SetupEnginesAndComponents()
        {
            _entityFactory = _enginesRoot = new EnginesRoot();

            GameObjectFactory factory = new GameObjectFactory();

            var enemyKilledObservable      = new EnemyKilledObservable();
            var scoreOnEnemyKilledObserver = new ScoreOnEnemyKilledObserver(enemyKilledObservable);

            Sequencer playerDamageSequence = new Sequencer();
            Sequencer enemyDamageSequence  = new Sequencer();

            var enemyAnimationEngine  = new EnemyAnimationEngine();
            var playerHealthEngine    = new HealthEngine(playerDamageSequence);
            var enemyHealthEngine     = new HealthEngine(enemyDamageSequence);
            var hudEngine             = new HUDEngine();
            var damageSoundEngine     = new DamageSoundEngine();
            var playerShootingEngine  = new PlayerGunShootingEngine(enemyKilledObservable, enemyDamageSequence);
            var playerMovementEngine  = new PlayerMovementEngine();
            var playerAnimationEngine = new PlayerAnimationEngine();
            var enemyAttackEngine     = new EnemyAttackEngine(playerDamageSequence);
            var enemyMovementEngine   = new EnemyMovementEngine();

            playerDamageSequence.SetSequence(
                new Steps()                                                  //sequence of steps
            {
                {                                                            //first step
                    enemyAttackEngine,                                       //this step can be triggered only by this engine through the Next function
                    new Dictionary <System.Enum, IStep[]>()                  //this step can lead only to one branch
                    {
                        { Condition.always, new [] { playerHealthEngine } }, //these engines will be called when the Next function is called with the Condition.always set
                    }
                },
                {                                                                                                                                                  //second step
                    playerHealthEngine,                                                                                                                            //this step can be triggered only by this engine through the Next function
                    new Dictionary <System.Enum, IStep[]>()                                                                                                        //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 Dictionary <System.Enum, IStep[]>()
                    {
                        { Condition.always, new [] { enemyHealthEngine } },
                    }
                },
                {
                    enemyHealthEngine,
                    new Dictionary <System.Enum, IStep[]>()
                    {
                        { DamageCondition.damage, new IStep[] { enemyAnimationEngine } },
                        { DamageCondition.dead, new IStep[] { enemyMovementEngine, enemyAnimationEngine, playerShootingEngine } },
                    }
                }
            }
                );

            AddEngine(playerMovementEngine);
            AddEngine(playerAnimationEngine);
            AddEngine(playerShootingEngine);
            AddEngine(playerHealthEngine);
            AddEngine(new PlayerGunShootingFXsEngine());

            AddEngine(new EnemySpawnerEngine(factory, _entityFactory));
            AddEngine(enemyAttackEngine);
            AddEngine(enemyMovementEngine);
            AddEngine(enemyAnimationEngine);
            AddEngine(enemyHealthEngine);

            AddEngine(damageSoundEngine);
            AddEngine(hudEngine);
            AddEngine(new ScoreEngine(scoreOnEnemyKilledObserver));
        }