sschmid / Entitas

Entitas is a super fast Entity Component System (ECS) Framework specifically made for C# and Unity
MIT License
7.08k stars 1.11k forks source link

Is it ok that a View thing (Like animation‘s end trigger) calls game's core logic? #959

Closed atkdefender closed 3 years ago

atkdefender commented 3 years ago

Finishing reading example Match-One, I still don't know how the gameObject change trigger a component. The component change triggering the gameObject is great! By [Event(EventTarget.Self)]

Now I need something like, the gameObject's animation is end, tell the component to do the next job. Is it ok that a View thing calls game's core logic?

sschmid commented 3 years ago

I created a diagram on how I see things. https://github.com/sschmid/Entitas-CSharp/issues/610

In this case the gameobject / view can create an entity with components for things like "clicked" "animation complete" etc. I use this regularly in my projects where I have core systems that are not aware of views but support a kind of async completion of tasks like: "animation complete", "merging complete" etc

Other systems can pick up if sth is complete on continue with the next steps

sschmid commented 3 years ago
[GameState, Unique]
public sealed class PendingDisappearComponent : IComponent
{
    public HashSet<GameEntity> value;
}
// start disappearing
var pendingDisappear = _contexts.gameState.pendingDisappear.value;
pendingDisappear.Add(entity);
_contexts.gameState.ReplacePendingDisappear(pendingDisappear);

// later in code, on complete
tween.OnComplete(() =>
{
    pendingDisappear.Remove(entity);
    _contexts.gameState.ReplacePendingDisappear(pendingDisappear);
    Hide();
})

There might be multiple different things that disappear with different randomized durations. This system detects when all objects completed the animation

public sealed class PendingDisappearCompleteSystem : ReactiveSystem<GameStateEntity>
{
    readonly Contexts _contexts;

    public PendingDisappearCompleteSystem(Contexts contexts) : base(contexts.gameState)
    {
        _contexts = contexts;
    }

    protected override ICollector<GameStateEntity> GetTrigger(IContext<GameStateEntity> context)
        => context.CreateCollector(GameStateMatcher.PendingDisappear);

    protected override bool Filter(GameStateEntity entity) => entity.hasPendingDisappear;

    protected override void Execute(List<GameStateEntity> entities)
    {
        if (_contexts.gameState.pendingDisappear.value.Count == 0)
        {
            // do sth
        }
    }
}
sschmid commented 3 years ago

now that I pasted the code, I'm thinking about replacing the HashSet component with a simple flag component, that's probably even nicer

atkdefender commented 3 years ago

Thx for answer. Using a particular component to do this job, this can solve my problem.