Projectile
Set up a ranged projectile prefab and fire it from a player instruction or an enemy using ShootSuscriber. Both paths share the same prefab format.
1. Create the projectile prefab
- Create a new GameObject with a
SpriteRendererand assign a sprite. - Add a
Collider2D(typically a smallCircleCollider2D) set to Is Trigger. - Add an
AudioSourcecomponent. - Add the
Projectilecomponent and configure it:- Speed — world units per second the projectile travels.
- Damage — hit points removed on impact.
- Shoot Sound — clip played at launch.
- Hit Sound — clip played on impact.
- Save as a prefab.
2a. Use in a player instruction
Both PlayerController and EnemyController use the same execution pattern: the instruction's Execute() runs first, then OnExecute fires all IExecutionSuscriber components. The projectile is fired by ShootSuscriber via that event — the instruction itself does not call Shoot().
Create a PlayerInstruction subclass with an empty Execute() and the shoot animation in Tick():
[System.Serializable] [TypeName("Shoot")] public class Instruction_Shoot : PlayerInstruction { public override string displayName => "Shoot"; public override void Execute() { } protected override IEnumerator Tick(ExecutionContext context) { context.player.SetDirection(direction); context.player.PlayAnimation("Shoot"); yield return new WaitForSeconds(1.5f); } }
Then add ShootSuscriber to the player root (or any child) and assign the prefab. PlayerController.Initialize() auto-discovers it via GetComponentsInChildren<IExecutionSuscriber>() and wires it to OnExecute — no manual wiring needed.
2b. Use on an enemy with ShootSuscriber
The setup for enemies is identical: create an EnemyInstruction with an empty Execute() and the animation in Tick(), then add ShootSuscriber to the enemy root (or any child) and assign the prefab.
EnemyController.Initialize() auto-discovers it the same way and wires it to OnExecute automatically.