(Feat) Add Animation

This commit is contained in:
2026-08-06 13:27:21 +02:00
parent fca1b271f1
commit 72a34f6e40
64 changed files with 1943 additions and 83 deletions
@@ -10,6 +10,7 @@
#include "Engine/LocalPlayer.h"
#include "GameMapsSettings.h"
#include "InputMappingContext.h"
#include "KeyboardLayout.h"
#include "Kismet/GameplayStatics.h"
#include "SessionSubsystem.h"
#include "SurvivalUserSettings.h"
@@ -233,13 +234,131 @@ void USurvivalGameInstance::RegisterMappableContexts()
return;
}
bool bRegisteredAnyContext = false;
for (const TObjectPtr<UInputMappingContext>& Context : MappableContexts)
{
if (Context)
{
bRegisteredAnyContext = true;
// Idempotent : le moteur ignore un contexte deja connu, on peut donc
// rappeler cette fonction sans precaution.
InputSettings->RegisterInputMappingContext(Context);
}
}
// APRES l'enregistrement et jamais avant : le profil de touches ne connait un
// mapping -- donc sa touche par defaut -- qu'une fois son IMC declare. Appeler
// la conversion plus tot la ferait tourner sur un profil vide, sans erreur ni
// effet, ce qui est le pire des cas a diagnostiquer.
//
// UNE SEULE FOIS par session, en revanche. Cette fonction est aussi le point
// d'entree du rafraichissement de l'onglet Touches, appele apres CHAQUE
// remappage : y laisser la conversion reprendrait sa touche au joueur qui a
// choisi celle de l'IMC -- la lettre W sur un AZERTY est identique au defaut,
// donc indiscernable d'un mapping auquel personne n'a touche.
if (bRegisteredAnyContext && !bKeyboardLayoutApplied)
{
bKeyboardLayoutApplied = true;
ApplyKeyboardLayoutToDefaultKeys();
}
}
void USurvivalGameInstance::ApplyKeyboardLayoutToDefaultKeys()
{
const ULocalPlayer* LocalPlayer = GetFirstGamePlayer();
if (!LocalPlayer)
{
return;
}
UEnhancedInputLocalPlayerSubsystem* Subsystem = LocalPlayer->GetSubsystem<UEnhancedInputLocalPlayerSubsystem>();
UEnhancedInputUserSettings* InputSettings = Subsystem ? Subsystem->GetUserSettings() : nullptr;
const UEnhancedPlayerMappableKeyProfile* Profile = InputSettings ? InputSettings->GetActiveKeyProfile() : nullptr;
if (!Profile)
{
return;
}
// On COLLECTE avant d'appliquer, au lieu de mapper au fil de la lecture.
// MapPlayerKey ecrit dans les memes lignes que celles qu'on parcourt : muter un
// conteneur pendant son iteration invalide les iterateurs des que le stockage
// est reorganise. Le bug qui en sort est intermittent et ne ressemble a rien.
struct FPendingRemap
{
FName MappingName;
EPlayerMappableKeySlot Slot;
FKey LayoutKey;
};
TArray<FPendingRemap> PendingRemaps;
for (const TPair<FName, FKeyMappingRow>& Row : Profile->GetPlayerMappingRows())
{
for (const FPlayerKeyMapping& Mapping : Row.Value.Mappings)
{
// Le joueur a deja choisi cette touche : son choix fait autorite, meme
// s'il est incoherent avec sa disposition. C'est aussi ce qui rend la
// fonction idempotente -- notre propre conversion compte comme un choix
// des qu'elle est posee, donc on ne repasse jamais dessus.
if (Mapping.IsCustomized())
{
continue;
}
const FKey DefaultKey = Mapping.GetDefaultKey();
const FKey LayoutKey = KeyboardLayout::RemapKeyToActiveLayout(DefaultKey);
// Clavier QWERTY, touche insensible a la disposition, ou manette : la
// conversion rend la touche d'origine et il n'y a rien a ecrire. Sans ce
// test on poserait une surcharge identique au defaut sur chaque mapping,
// et "Reinitialiser" n'aurait plus rien a reinitialiser.
if (LayoutKey == DefaultKey)
{
continue;
}
PendingRemaps.Add({ Row.Key, Mapping.GetSlot(), LayoutKey });
}
}
// Journalise AVANT de sortir, et donc meme quand il n'y a rien a faire. C'est
// la seule trace lisible quand un testeur signale des touches inattendues, et
// le cas "0 touche" est justement celui qu'il faut pouvoir distinguer du
// silence : sans cette ligne, un clavier QWERTY et un systeme qui n'a pas
// tourne du tout produisent exactement le meme log, c'est-a-dire aucun.
//
// Un KLID se lit en deux temps : "0000040C" est un AZERTY francais, "00000409"
// un QWERTY US. Un utilisateur francais sur clavier QWERTY rend bien 00000409,
// parce que c'est la DISPOSITION qui est journalisee, pas la langue -- et c'est
// exactement la distinction sur laquelle une detection par pays se tromperait.
UE_LOG(LogTemp, Log, TEXT("USurvivalGameInstance : disposition clavier [%s], %d touche(s) a traduire depuis le QWERTY de reference."),
*KeyboardLayout::GetActiveLayoutId(), PendingRemaps.Num());
if (PendingRemaps.IsEmpty())
{
return;
}
for (const FPendingRemap& Remap : PendingRemaps)
{
FMapPlayerKeyArgs Args;
Args.MappingName = Remap.MappingName;
Args.Slot = Remap.Slot;
Args.NewKey = Remap.LayoutKey;
FGameplayTagContainer FailureReason;
InputSettings->MapPlayerKey(Args, FailureReason);
if (!FailureReason.IsEmpty())
{
UE_LOG(LogTemp, Warning, TEXT("USurvivalGameInstance : conversion de '%s' vers '%s' refusee (%s)."),
*Remap.MappingName.ToString(), *Remap.LayoutKey.ToString(), *FailureReason.ToString());
}
}
// Pousse les surcharges dans les contextes deja actifs : le pawn n'a rien a
// rebrancher, les touches sont bonnes des la premiere image.
InputSettings->ApplySettings();
}