PLAYER ACTIONS
SCALABLES
L'architecture joueur separe l'intention, la simulation et la presentation. Le brain produit des commandes, le joueur predit applique les regles, les couches de states se combinent sans explosion de classes, et la vue reste libre pour animation, camera et feedbacks.
CE QUI DOIT RESTER SIMPLE
La maintenabilite vient surtout de frontieres nettes : une classe ne doit pas lire l'input, appliquer les regles, bouger le joueur et declencher les VFX en meme temps.
VUE GLOBALE NAVIGABLE
Zoom molette ou boutons, deplacement au drag. Le diagramme se concentre sur les frontieres : input, commandes, slots joueurs, prediction, simulation et presentation.
PurrDiction orchestre prediction, rollback et reconciliation. Le gameplay football reste dans un core deterministe partage par le local, l'online, l'IA et les replays.
ORDRE DES ETAPES
Le mode de jeu est choisi avant la composition. En local, les joueurs peuvent choisir leurs cotes. En online, le joueur choisit son equipe/roster, mais l'adversaire est fourni par le matchmaking ou le serveur.
Cette vue protege la logique UX : on ne manipule pas les memes choix de team en local et en online. Le verrouillage des slots arrive seulement quand toutes les contraintes du mode sont satisfaites.
COMBINER SANS EXPLOSER
Les etats combinables sont ranges par responsabilite. Ajouter une action ou un statut ne multiplie pas toutes les classes existantes.
FLUX DE PREDICTION
Le joueur reseau doit rester une simulation reproductible. Les entrees passent par PurrDiction, le state est snapshot/reconcile, la vue se met a jour ensuite.
CYCLE DE VIE EN COULEUR
Cas volontairement simple : cliquer un scenario et suivre comment une intention traverse le brain, l'input, les state machines, la simulation et la vue.
INTEGRATION RETRACTABLE
Ouvrir seulement ce qui est utile. Les exemples montrent la structure, pas une API finale verrouillee.
Contrat brain interchangeable
Le brain est remplace rapidement selon le contexte. En solo il lit la manette, en online local il fournit aussi les inputs a PurrDiction, en remote il rejoue les commandes recues.
public interface IPlayerBrain
{
PlayerInput GetInput(int tick);
}
public sealed class LocalInputBrain : IPlayerBrain
{
public PlayerInput GetInput(int tick)
{
return new PlayerInput
{
tick = tick,
move = ReadMoveStick(),
aim = ReadAimStick(),
sprint = ReadSprintButton(),
action = ReadRequestedAction()
};
}
}
Input et state compatibles prediction
Les donnees simulees restent compactes. Les references Unity, les VFX, les sons et les objets non deterministes restent hors du state predit.
public struct PlayerInput : IPredictedData
{
public int tick;
public Vector2 move;
public Vector2 aim;
public bool sprint;
public PlayerActionType action;
}
public struct PlayerState : IPredictedData<PlayerState>
{
public Vector3 position;
public Vector3 velocity;
public Quaternion rotation;
public LocomotionState locomotion;
public PossessionState possession;
public ActionState action;
public float actionTimer;
}
PredictedPlayer avec couches de gameplay
Le PredictedPlayer orchestre les modules. Chaque couche est testable separement, mais la simulation finale reste centralisee pour la prediction et la reconciliation.
public sealed class PredictedPlayer
: PredictedIdentity<PlayerInput, PlayerState>
{
[SerializeField] private PlayerBrainProvider brainProvider;
[SerializeField] private PlayerRulesResolver rules;
[SerializeField] private PlayerMovementMotor motor;
[SerializeField] private PlayerAnimationView animationView;
protected override PlayerInput GetFinalInput()
{
return brainProvider.Current.GetInput(CurrentTick);
}
protected override void SanitizeInput(ref PlayerInput input)
{
input.move = Vector2.ClampMagnitude(input.move, 1f);
input.aim = Vector2.ClampMagnitude(input.aim, 1f);
rules.SanitizeAction(ref input);
}
protected override void Simulate(
PlayerInput input,
ref PlayerState state,
float delta)
{
rules.Resolve(input, ref state);
motor.Simulate(input, ref state, delta);
}
protected override void UpdateView(PlayerState state, float delta)
{
animationView.Apply(state, delta);
}
}
Ajout d'une action scalable
Une nouvelle action ne doit pas modifier toutes les classes. Elle declare ses conditions, ses locks et ses effets, puis le resolver decide si elle peut demarrer.
[CreateAssetMenu(menuName = "ASU/Player Action")]
public sealed class PlayerActionDefinition : ScriptableObject
{
public PlayerActionType type;
public bool requiresBall;
public bool locksDirection;
public float duration;
public float agilityMultiplier = 1f;
}
public bool CanStart(
PlayerActionDefinition action,
in PlayerState state)
{
if (action.requiresBall && state.possession != PossessionState.WithBall)
return false;
if (state.action != ActionState.None)
return false;
return true;
}
REGLES DE MAINTENANCE
Ces garde-fous evitent que le prototype online devienne un bloc fragile au moment d'ajouter pouvoirs, coequipiers, IA et variantes de run.