Вы можете сделать это по-разному, но, например, общий подход c к этому может быть:
abstract class Chain<T> {
/// returns if its time to return
public abstract bool Update ( double deltaTime );
public abstract T GetValue ();
}
class ButtonChain<T> : Chain<T> {
double time;
double maxTime;
T output;
public Key key;
List<ButtonChain<T>> next = new List<ButtonChain<T>();
ButtonChain<T> chained;
public ButtonChain ( double maxTime, Key key, T output ) {
this.output = output;
this.maxTime = maxTime;
this.key = key;
}
public override bool Update ( double deltaTime ) {
if ( chained != null ) return chained.Update( deltaTime );
foreach ( var i in chained ) {
if ( Input.Pressed( i.key ) ) {
chained = i;
return false;
}
}
time += deltaTime;
if ( time >= maxTime ) {
return true;
}
retrun false;
}
public override T GetValue () => ( chained is null ) ? output : chained.GetValue();
public ButtonChain Then ( ButtonChain chain ) {
next.Add( chain );
return this;
}
}
class Player {
...
List<ButtonChain<Attack>> attacks = new List<ButtonChain<Attack>>();
ButtonChain<Attack> active;
public void Update ( double deltaTime ) {
...
if ( active != null ) {
if ( active.Update( deltaTime ) ) {
UseAttack( active.GetValue() );
active = null;
}
} else {
foreach ( var i in attacks ) {
if ( Input.Pressed( i.key ) ) {
active = i;
}
}
}
...
}
public void Start () {
...
attacks.Add(
new ButtonChain<Attack>( 0.2, Key.Q, Attack.StandardAttack )
.Then(
new ButtonChain<Attack>( 0.2, Key.W, Attack.SpinAttack )
.Then(
new ButtonChain<Attack>( 0.2, Key.Q, Attack.UltraAttack )
)
)
);
...
}
...
}