From d2fc5a8fbbc6161b5fca013d6b3e46a5f6031979 Mon Sep 17 00:00:00 2001 From: "j.foucher" Date: Wed, 22 Jul 2026 13:22:14 +0200 Subject: [PATCH] Projet de test UE 5.7 pour le plugin PS_StrategicVision Projet C++ vide servant de banc de test au plugin (reference en depot imbrique dans Plugins/, a convertir en submodule quand il aura un remote). Co-Authored-By: Claude Fable 5 --- Unreal/PS_StratVision/Content/NewMap.umap | Bin 0 -> 55682 bytes .../PS_StratVision/Content/testStatVis.uasset | Bin 0 -> 50655 bytes .../PS_StratVision/Plugins/PS_StrategicVision | 1 + .../Plugins/PS_StrategicVision/.gitignore | 3 - .../PS_StrategicVision.uplugin | 30 -- .../Plugins/PS_StrategicVision/README.md | 101 ------- .../PS_StrategicVision.Build.cs | 18 -- .../Private/PS_StrategicDisplayComponent.cpp | 97 ------- .../Private/PS_StrategicVisionModule.cpp | 5 - .../Private/PS_StrategicVisionSettings.cpp | 26 -- .../Private/PS_StrategicVisionSubsystem.cpp | 58 ---- .../Private/PS_StrategicVisionTypes.cpp | 5 - .../Private/PS_VisionConeComponent.cpp | 270 ------------------ .../Public/PS_StrategicDisplayComponent.h | 75 ----- .../Public/PS_StrategicVisionSettings.h | 42 --- .../Public/PS_StrategicVisionSubsystem.h | 72 ----- .../Public/PS_StrategicVisionTypes.h | 25 -- .../Public/PS_VisionConeComponent.h | 106 ------- 18 files changed, 1 insertion(+), 933 deletions(-) create mode 100644 Unreal/PS_StratVision/Content/NewMap.umap create mode 100644 Unreal/PS_StratVision/Content/testStatVis.uasset create mode 160000 Unreal/PS_StratVision/Plugins/PS_StrategicVision delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/.gitignore delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/PS_StrategicVision.uplugin delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/README.md delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/PS_StrategicVision.Build.cs delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicDisplayComponent.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionModule.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSettings.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSubsystem.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionTypes.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_VisionConeComponent.cpp delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicDisplayComponent.h delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSettings.h delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSubsystem.h delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionTypes.h delete mode 100644 Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_VisionConeComponent.h diff --git a/Unreal/PS_StratVision/Content/NewMap.umap b/Unreal/PS_StratVision/Content/NewMap.umap new file mode 100644 index 0000000000000000000000000000000000000000..ba39b8a9ae73b7561b94fa8cf452594f06be1fa5 GIT binary patch literal 55682 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 literal 0 HcmV?d00001 diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision b/Unreal/PS_StratVision/Plugins/PS_StrategicVision new file mode 160000 index 0000000..199c140 --- /dev/null +++ b/Unreal/PS_StratVision/Plugins/PS_StrategicVision @@ -0,0 +1 @@ +Subproject commit 199c14097e451995c57b4cc777c269f69ac01d02 diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/.gitignore b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/.gitignore deleted file mode 100644 index 156d316..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/.gitignore +++ /dev/null @@ -1,3 +0,0 @@ -# Artefacts de build du plugin (regeneres a la compilation) -Binaries/ -Intermediate/ diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/PS_StrategicVision.uplugin b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/PS_StrategicVision.uplugin deleted file mode 100644 index 3c1052a..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/PS_StrategicVision.uplugin +++ /dev/null @@ -1,30 +0,0 @@ -{ - "FileVersion": 3, - "Version": 1, - "VersionName": "0.1.0", - "FriendlyName": "PS Strategic Vision", - "Description": "Affichage strategique pour vues spectateur et replays : cones de vision, et autres visualisations a venir.", - "Category": "Visualization", - "CreatedBy": "j.foucher", - "CreatedByURL": "", - "DocsURL": "", - "MarketplaceURL": "", - "SupportURL": "", - "CanContainContent": true, - "IsBetaVersion": false, - "IsExperimentalVersion": false, - "Installed": false, - "Modules": [ - { - "Name": "PS_StrategicVision", - "Type": "Runtime", - "LoadingPhase": "Default" - } - ], - "Plugins": [ - { - "Name": "ProceduralMeshComponent", - "Enabled": true - } - ] -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/README.md b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/README.md deleted file mode 100644 index 61768f0..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/README.md +++ /dev/null @@ -1,101 +0,0 @@ -# PS Strategic Vision - -Plugin Unreal Engine 5.7 d'affichage stratégique pour vues spectateur et replays : -cônes de vision des personnages, et d'autres visualisations à venir. - -## Installation dans un projet - -Copier (ou cloner) ce dossier dans `Plugins/PS_StrategicVision/` du projet, puis -activer le plugin dans le `.uproject` ou via *Edit > Plugins*. - -En submodule git : - -```bash -git submodule add Plugins/PS_StrategicVision -``` - -Le plugin est en C++ : le projet hôte doit être un projet C++ (il sera compilé -avec le projet). - -## Contenu - -| Classe | Rôle | -|---|---| -| `UPS_VisionConeComponent` | Component à poser sur un personnage : affiche son cône de vision (mesh procédural, bloqué par les murs). | -| `UPS_StrategicDisplayComponent` | Classe de base commune à tous les affichages stratégiques (camp, activation, couleur). Les futurs components en hériteront. | -| `UPS_StrategicVisionSubsystem` | Subsystem monde : interrupteurs globaux (tout, par catégorie, par camp). | -| `UPS_StrategicVisionSettings` | Réglages projet : couleurs des camps, matériau par défaut. | - -## Utilisation - -### 1. Poser le cône de vision - -Dans le Blueprint du personnage (ou en C++), ajouter le component **PS Vision Cone** -(idéalement attaché au root / à la capsule). Régler : - -- **Team** : `Player`, `Ally`, `Enemy` ou `Neutral` — détermine la couleur - (configurable dans *Project Settings > Plugins > PS Strategic Vision*). -- **View Distance** / **View Angle Degrees** : portée et ouverture du cône. -- **Trace Channel** : canal de collision utilisé pour bloquer le cône sur les murs - (`Visibility` par défaut). -- **Flatten On Ground** : le cône reste à plat et se projette au sol (recommandé - pour une vue spectateur top-down). - -Le cône suit automatiquement la position et le yaw du component. Pour qu'il suive -le regard de la tête plutôt que le corps, attacher le component au bone/socket voulu. - -### 2. Activer / désactiver selon l'état de jeu - -Par component : - -- `SetDisplayEnabled(bool)` — interrupteur local du component. - -Globalement, via le subsystem (Blueprint : nœud `Get PS_StrategicVisionSubsystem`, -ou `UPS_StrategicVisionSubsystem::Get(WorldContext)` en C++) : - -- `SetGlobalDisplayEnabled(bool)` — tout l'affichage stratégique (ex : entrée/sortie - du mode spectateur ou replay). -- `SetVisionConesEnabled(bool)` — uniquement les cônes de vision. -- `SetCategoryEnabled(FName, bool)` — par catégorie (pour les futurs components). -- `SetTeamEnabled(EPS_StrategicTeam, bool)` — par camp (ex : ne montrer que les ennemis). - -Un component n'est visible que si son interrupteur local ET les interrupteurs -globaux le permettent. Quand un cône est masqué, il ne fait plus aucune trace -(coût nul). - -### 3. Matériau (optionnel) - -Sans configuration, le cône utilise un matériau translucide du moteur -(`M_SimpleUnlitTranslucent`) teinté à la couleur du camp — fonctionnel -immédiatement, suffisant en développement. - -Pour un rendu personnalisé (dégradé, scanlines, bord lumineux...), créer un -matériau dans le Content du plugin à `/PS_StrategicVision/Materials/M_VisionCone` -(chemin par défaut des settings) ou l'assigner via **Material Override** sur le -component. Le component fournit au matériau : - -- paramètre vecteur `Color` : couleur du camp (alpha = opacité) ; -- paramètre scalaire `Opacity` : opacité ; -- vertex colors : couleur du camp ; -- UV : U = position angulaire (0..1), V = distance normalisée (0..1) — pratique - pour un fondu vers le bord du cône. - -Recette minimale : matériau *Translucent* / *Unlit*, node **VertexColor** → Emissive -Color, **VertexColor.A** → Opacity. Laisser *Two Sided* décoché (la géométrie est -déjà double face). - -> Note packaging : si le matériau n'est référencé que par les settings (soft -> reference), l'ajouter à la liste des assets toujours cookés, ou le référencer en -> dur via Material Override. - -### Performance - -- `ArcSegments` (48 par défaut) = nombre de line traces par mise à jour. -- `UpdateInterval` : 0 = chaque frame ; augmenter (ex : 0.05) si beaucoup de - personnages affichent leur cône simultanément. - -## Roadmap - -- [x] Cône de vision bloqué par les obstacles -- [ ] Autres affichages stratégiques (trajectoires, zones d'alerte, ...) sous forme - de components héritant de `UPS_StrategicDisplayComponent` diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/PS_StrategicVision.Build.cs b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/PS_StrategicVision.Build.cs deleted file mode 100644 index 0924c3e..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/PS_StrategicVision.Build.cs +++ /dev/null @@ -1,18 +0,0 @@ -using UnrealBuildTool; - -public class PS_StrategicVision : ModuleRules -{ - public PS_StrategicVision(ReadOnlyTargetRules Target) : base(Target) - { - PCHUsage = ModuleRules.PCHUsageMode.UseExplicitOrSharedPCHs; - - PublicDependencyModuleNames.AddRange(new string[] - { - "Core", - "CoreUObject", - "Engine", - "DeveloperSettings", - "ProceduralMeshComponent", - }); - } -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicDisplayComponent.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicDisplayComponent.cpp deleted file mode 100644 index 55acae4..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicDisplayComponent.cpp +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "PS_StrategicDisplayComponent.h" - -#include "Engine/World.h" -#include "PS_StrategicVisionSettings.h" -#include "PS_StrategicVisionSubsystem.h" - -UPS_StrategicDisplayComponent::UPS_StrategicDisplayComponent() -{ - PrimaryComponentTick.bCanEverTick = false; -} - -void UPS_StrategicDisplayComponent::SetTeam(EPS_StrategicTeam NewTeam) -{ - if (Team != NewTeam) - { - Team = NewTeam; - RefreshDisplayState(); - } -} - -void UPS_StrategicDisplayComponent::SetDisplayEnabled(bool bEnabled) -{ - if (bDisplayEnabled != bEnabled) - { - bDisplayEnabled = bEnabled; - RefreshDisplayState(); - } -} - -bool UPS_StrategicDisplayComponent::IsDisplayVisible() const -{ - if (!bDisplayEnabled) - { - return false; - } - - if (const UWorld* World = GetWorld()) - { - if (World->IsGameWorld()) - { - if (const UPS_StrategicVisionSubsystem* Subsystem = World->GetSubsystem()) - { - return Subsystem->ShouldDisplay(GetDisplayCategory(), Team); - } - } - } - - // Monde editeur (preview) : on suit uniquement l'activation locale. - return true; -} - -FLinearColor UPS_StrategicDisplayComponent::GetTeamColor() const -{ - return GetDefault()->GetTeamColor(Team); -} - -void UPS_StrategicDisplayComponent::BeginPlay() -{ - Super::BeginPlay(); - - if (UPS_StrategicVisionSubsystem* Subsystem = UPS_StrategicVisionSubsystem::Get(this)) - { - Subsystem->OnDisplayStateChanged.AddUniqueDynamic(this, &UPS_StrategicDisplayComponent::HandleGlobalDisplayStateChanged); - } - - RefreshDisplayState(); -} - -void UPS_StrategicDisplayComponent::EndPlay(const EEndPlayReason::Type EndPlayReason) -{ - if (UPS_StrategicVisionSubsystem* Subsystem = UPS_StrategicVisionSubsystem::Get(this)) - { - Subsystem->OnDisplayStateChanged.RemoveDynamic(this, &UPS_StrategicDisplayComponent::HandleGlobalDisplayStateChanged); - } - - Super::EndPlay(EndPlayReason); -} - -#if WITH_EDITOR -void UPS_StrategicDisplayComponent::PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) -{ - Super::PostEditChangeProperty(PropertyChangedEvent); - RefreshDisplayState(); -} -#endif - -void UPS_StrategicDisplayComponent::RefreshDisplayState() -{ - OnDisplayStateChanged(); -} - -void UPS_StrategicDisplayComponent::HandleGlobalDisplayStateChanged() -{ - RefreshDisplayState(); -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionModule.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionModule.cpp deleted file mode 100644 index 2587551..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionModule.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "Modules/ModuleManager.h" - -IMPLEMENT_MODULE(FDefaultModuleImpl, PS_StrategicVision) diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSettings.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSettings.cpp deleted file mode 100644 index ac75bcd..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSettings.cpp +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "PS_StrategicVisionSettings.h" - -#include "Materials/MaterialInterface.h" - -UPS_StrategicVisionSettings::UPS_StrategicVisionSettings() -{ - TeamColors.Add(EPS_StrategicTeam::Player, FLinearColor(0.05f, 0.45f, 1.0f)); - TeamColors.Add(EPS_StrategicTeam::Ally, FLinearColor(0.1f, 0.9f, 0.5f)); - TeamColors.Add(EPS_StrategicTeam::Enemy, FLinearColor(1.0f, 0.12f, 0.08f)); - TeamColors.Add(EPS_StrategicTeam::Neutral, FLinearColor(0.75f, 0.75f, 0.75f)); - - // Materiau optionnel a creer dans le Content du plugin (voir README). - DefaultConeMaterial = TSoftObjectPtr( - FSoftObjectPath(TEXT("/PS_StrategicVision/Materials/M_VisionCone.M_VisionCone"))); -} - -FLinearColor UPS_StrategicVisionSettings::GetTeamColor(EPS_StrategicTeam Team) const -{ - if (const FLinearColor* Color = TeamColors.Find(Team)) - { - return *Color; - } - return FLinearColor::White; -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSubsystem.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSubsystem.cpp deleted file mode 100644 index 575781c..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionSubsystem.cpp +++ /dev/null @@ -1,58 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "PS_StrategicVisionSubsystem.h" - -#include "Engine/Engine.h" -#include "Engine/World.h" - -UPS_StrategicVisionSubsystem* UPS_StrategicVisionSubsystem::Get(const UObject* WorldContextObject) -{ - if (GEngine) - { - if (const UWorld* World = GEngine->GetWorldFromContextObject(WorldContextObject, EGetWorldErrorMode::ReturnNull)) - { - return World->GetSubsystem(); - } - } - return nullptr; -} - -void UPS_StrategicVisionSubsystem::SetGlobalDisplayEnabled(bool bEnabled) -{ - if (bGlobalEnabled != bEnabled) - { - bGlobalEnabled = bEnabled; - NotifyStateChanged(); - } -} - -void UPS_StrategicVisionSubsystem::SetCategoryEnabled(FName Category, bool bEnabled) -{ - const bool bChanged = bEnabled ? (DisabledCategories.Remove(Category) > 0) : !DisabledCategories.Contains(Category); - if (!bEnabled) - { - DisabledCategories.Add(Category); - } - if (bChanged) - { - NotifyStateChanged(); - } -} - -void UPS_StrategicVisionSubsystem::SetTeamEnabled(EPS_StrategicTeam Team, bool bEnabled) -{ - const bool bChanged = bEnabled ? (DisabledTeams.Remove(Team) > 0) : !DisabledTeams.Contains(Team); - if (!bEnabled) - { - DisabledTeams.Add(Team); - } - if (bChanged) - { - NotifyStateChanged(); - } -} - -void UPS_StrategicVisionSubsystem::NotifyStateChanged() -{ - OnDisplayStateChanged.Broadcast(); -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionTypes.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionTypes.cpp deleted file mode 100644 index 1dd1cfb..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_StrategicVisionTypes.cpp +++ /dev/null @@ -1,5 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "PS_StrategicVisionTypes.h" - -const FName FPS_StrategicDisplayCategories::VisionCone(TEXT("VisionCone")); diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_VisionConeComponent.cpp b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_VisionConeComponent.cpp deleted file mode 100644 index 5408364..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Private/PS_VisionConeComponent.cpp +++ /dev/null @@ -1,270 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#include "PS_VisionConeComponent.h" - -#include "CollisionQueryParams.h" -#include "Engine/HitResult.h" -#include "Engine/World.h" -#include "GameFramework/Actor.h" -#include "Materials/Material.h" -#include "Materials/MaterialInstanceDynamic.h" -#include "Materials/MaterialInterface.h" -#include "ProceduralMeshComponent.h" -#include "PS_StrategicVisionSettings.h" - -namespace -{ - // Materiaux moteur utilises en dernier recours si aucun materiau n'est configure. - const TCHAR* GFallbackTranslucentMaterialPath = TEXT("/Engine/EngineDebugMaterials/M_SimpleUnlitTranslucent.M_SimpleUnlitTranslucent"); - const TCHAR* GFallbackOpaqueMaterialPath = TEXT("/Engine/BasicShapes/BasicShapeMaterial.BasicShapeMaterial"); - - const FName GColorParamName(TEXT("Color")); - const FName GOpacityParamName(TEXT("Opacity")); -} - -UPS_VisionConeComponent::UPS_VisionConeComponent() -{ - PrimaryComponentTick.bCanEverTick = true; - PrimaryComponentTick.bStartWithTickEnabled = true; - // Apres le mouvement des acteurs, pour que le cone suive sans retard. - PrimaryComponentTick.TickGroup = TG_PostUpdateWork; - bTickInEditor = true; -} - -void UPS_VisionConeComponent::OnRegister() -{ - Super::OnRegister(); - - EnsureConeMesh(); - EnsureMaterial(); - ApplyTeamColor(); -} - -void UPS_VisionConeComponent::OnComponentDestroyed(bool bDestroyingHierarchy) -{ - if (ConeMesh) - { - ConeMesh->DestroyComponent(); - ConeMesh = nullptr; - } - - Super::OnComponentDestroyed(bDestroyingHierarchy); -} - -void UPS_VisionConeComponent::BeginPlay() -{ - Super::BeginPlay(); - SetComponentTickInterval(UpdateInterval); -} - -void UPS_VisionConeComponent::TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) -{ - Super::TickComponent(DeltaTime, TickType, ThisTickFunction); - - if (IsDisplayVisible()) - { - RebuildCone(); - } -} - -void UPS_VisionConeComponent::OnDisplayStateChanged() -{ - const bool bShouldDisplay = IsDisplayVisible(); - - if (ConeMesh) - { - ConeMesh->SetVisibility(bShouldDisplay); - } - - // Pas de traces ni de rebuild quand le cone est cache. - SetComponentTickEnabled(bShouldDisplay); - SetComponentTickInterval(UpdateInterval); - - if (bShouldDisplay) - { - EnsureMaterial(); - ApplyTeamColor(); - RebuildCone(); - } -} - -void UPS_VisionConeComponent::EnsureConeMesh() -{ - if (ConeMesh || !GetWorld()) - { - return; - } - - ConeMesh = NewObject(this, NAME_None, RF_Transient); - ConeMesh->SetupAttachment(this); - // Rotation absolue : le fan est construit en espace monde, seul le point - // d'ancrage suit le personnage entre deux mises a jour. - ConeMesh->SetUsingAbsoluteRotation(true); - ConeMesh->SetCollisionEnabled(ECollisionEnabled::NoCollision); - ConeMesh->SetGenerateOverlapEvents(false); - ConeMesh->SetCastShadow(false); - ConeMesh->bReceivesDecals = false; - ConeMesh->SetTranslucentSortPriority(100); - ConeMesh->RegisterComponent(); - ConeMesh->SetWorldRotation(FQuat::Identity); -} - -void UPS_VisionConeComponent::EnsureMaterial() -{ - if (!ConeMesh) - { - return; - } - - UMaterialInterface* BaseMaterial = MaterialOverride; - - if (!BaseMaterial) - { - BaseMaterial = GetDefault()->DefaultConeMaterial.LoadSynchronous(); - } - if (!BaseMaterial) - { - BaseMaterial = LoadObject(nullptr, GFallbackTranslucentMaterialPath); - } - if (!BaseMaterial) - { - BaseMaterial = LoadObject(nullptr, GFallbackOpaqueMaterialPath); - } - if (!BaseMaterial) - { - return; - } - - if (!ConeMID || ConeMID->Parent != BaseMaterial) - { - ConeMID = UMaterialInstanceDynamic::Create(BaseMaterial, this); - } - ConeMesh->SetMaterial(0, ConeMID); -} - -void UPS_VisionConeComponent::ApplyTeamColor() -{ - if (!ConeMID) - { - return; - } - - FLinearColor Color = GetTeamColor(); - Color.A = Opacity; - ConeMID->SetVectorParameterValue(GColorParamName, Color); - ConeMID->SetScalarParameterValue(GOpacityParamName, Opacity); -} - -void UPS_VisionConeComponent::RebuildCone() -{ - UWorld* World = GetWorld(); - if (!World || !ConeMesh) - { - return; - } - - const FVector CompLocation = GetComponentLocation(); - const FRotator CompRotation = GetComponentRotation(); - - const FVector Forward = bFlattenOnGround - ? FRotator(0.f, CompRotation.Yaw, 0.f).Vector() - : GetForwardVector(); - const FVector Up = bFlattenOnGround ? FVector::UpVector : GetUpVector(); - - FCollisionQueryParams QueryParams(TEXT("PS_VisionCone"), bTraceComplex); - if (const AActor* Owner = GetOwner()) - { - QueryParams.AddIgnoredActor(Owner); - } - - // Hauteur de base du fan : projete au sol si demande, sinon a la hauteur du component. - float BaseZ = GroundOffset; - if (bFlattenOnGround) - { - FHitResult GroundHit; - const FVector GroundStart = CompLocation + FVector(0.f, 0.f, TraceHeight); - const FVector GroundEnd = CompLocation - FVector(0.f, 0.f, 500.f); - if (World->LineTraceSingleByChannel(GroundHit, GroundStart, GroundEnd, TraceChannel, QueryParams)) - { - BaseZ = (GroundHit.ImpactPoint.Z - CompLocation.Z) + GroundOffset; - } - } - - const int32 RayCount = FMath::Clamp(ArcSegments, 4, 256) + 1; - const float HalfAngle = FMath::Clamp(ViewAngleDegrees, 1.f, 360.f) * 0.5f; - const FVector TraceOrigin = CompLocation + FVector(0.f, 0.f, TraceHeight); - - FLinearColor VertexColor = GetTeamColor(); - VertexColor.A = Opacity; - - TArray Vertices; - TArray Normals; - TArray UVs; - TArray Colors; - Vertices.Reserve(RayCount + 1); - Normals.Reserve(RayCount + 1); - UVs.Reserve(RayCount + 1); - Colors.Reserve(RayCount + 1); - - // Sommet central (origine du cone). - Vertices.Add(bFlattenOnGround ? FVector(0.f, 0.f, BaseZ) : Up * GroundOffset); - Normals.Add(Up); - UVs.Add(FVector2D(0.5f, 0.f)); - Colors.Add(VertexColor); - - for (int32 RayIndex = 0; RayIndex < RayCount; ++RayIndex) - { - const float Alpha = static_cast(RayIndex) / static_cast(RayCount - 1); - const float AngleDeg = FMath::Lerp(-HalfAngle, HalfAngle, Alpha); - const FVector Direction = Forward.RotateAngleAxis(AngleDeg, Up); - - float HitDistance = ViewDistance; - FHitResult Hit; - if (World->LineTraceSingleByChannel(Hit, TraceOrigin, TraceOrigin + Direction * ViewDistance, TraceChannel, QueryParams)) - { - HitDistance = Hit.Distance; - } - - FVector LocalPoint = Direction * HitDistance; - if (bFlattenOnGround) - { - LocalPoint.Z = BaseZ; - } - else - { - LocalPoint += Up * GroundOffset; - } - - Vertices.Add(LocalPoint); - Normals.Add(Up); - UVs.Add(FVector2D(Alpha, HitDistance / ViewDistance)); - Colors.Add(VertexColor); - } - - const TArray NoTangents; - - if (Vertices.Num() == LastVertexCount) - { - // Meme topologie : mise a jour des sommets uniquement (moins couteux). - ConeMesh->UpdateMeshSection_LinearColor(0, Vertices, Normals, UVs, Colors, NoTangents); - } - else - { - // Triangles dans les deux sens pour rester visible sous tous les angles de camera. - TArray Triangles; - Triangles.Reserve((RayCount - 1) * 6); - for (int32 SegmentIndex = 0; SegmentIndex < RayCount - 1; ++SegmentIndex) - { - Triangles.Add(0); - Triangles.Add(1 + SegmentIndex); - Triangles.Add(2 + SegmentIndex); - - Triangles.Add(0); - Triangles.Add(2 + SegmentIndex); - Triangles.Add(1 + SegmentIndex); - } - - ConeMesh->CreateMeshSection_LinearColor(0, Vertices, Triangles, Normals, UVs, Colors, NoTangents, false); - LastVertexCount = Vertices.Num(); - } -} diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicDisplayComponent.h b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicDisplayComponent.h deleted file mode 100644 index 812ad99..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicDisplayComponent.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Components/SceneComponent.h" -#include "PS_StrategicVisionTypes.h" -#include "PS_StrategicDisplayComponent.generated.h" - -/** - * Classe de base des affichages strategiques poses sur les personnages - * (cone de vision, et autres visualisations a venir). - * - * Gere le camp (couleur), l'activation locale du component et la - * synchronisation avec l'etat global du UPS_StrategicVisionSubsystem. - */ -UCLASS(Abstract, ClassGroup = (PS_StrategicVision), HideCategories = (Physics, Collision, Mobility)) -class PS_STRATEGICVISION_API UPS_StrategicDisplayComponent : public USceneComponent -{ - GENERATED_BODY() - -public: - UPS_StrategicDisplayComponent(); - - /** Change le camp (met a jour la couleur de l'affichage). */ - UFUNCTION(BlueprintCallable, Category = "Strategic Display") - void SetTeam(EPS_StrategicTeam NewTeam); - - UFUNCTION(BlueprintPure, Category = "Strategic Display") - EPS_StrategicTeam GetTeam() const { return Team; } - - /** Active / desactive cet affichage precis (independamment de l'etat global). */ - UFUNCTION(BlueprintCallable, Category = "Strategic Display") - void SetDisplayEnabled(bool bEnabled); - - UFUNCTION(BlueprintPure, Category = "Strategic Display") - bool IsDisplayEnabled() const { return bDisplayEnabled; } - - /** Visibilite effective : activation locale ET etat global du subsystem. */ - UFUNCTION(BlueprintPure, Category = "Strategic Display") - bool IsDisplayVisible() const; - - /** Couleur du camp courant, lue dans les settings du plugin. */ - UFUNCTION(BlueprintPure, Category = "Strategic Display") - FLinearColor GetTeamColor() const; - - /** Categorie declaree par chaque type d'affichage (cf. FPS_StrategicDisplayCategories). */ - virtual FName GetDisplayCategory() const { return NAME_None; } - -protected: - virtual void BeginPlay() override; - virtual void EndPlay(const EEndPlayReason::Type EndPlayReason) override; - -#if WITH_EDITOR - virtual void PostEditChangeProperty(FPropertyChangedEvent& PropertyChangedEvent) override; -#endif - - /** Appele quand la visibilite effective ou le camp change. */ - virtual void OnDisplayStateChanged() {} - - /** Reevalue la visibilite effective et previent la classe fille. */ - void RefreshDisplayState(); - - /** Camp du personnage porteur, determine la couleur de l'affichage. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Strategic Display") - EPS_StrategicTeam Team = EPS_StrategicTeam::Enemy; - - /** Etat d'activation local de cet affichage. */ - UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "Strategic Display") - bool bDisplayEnabled = true; - -private: - UFUNCTION() - void HandleGlobalDisplayStateChanged(); -}; diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSettings.h b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSettings.h deleted file mode 100644 index 67dc7bc..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSettings.h +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Engine/DeveloperSettings.h" -#include "PS_StrategicVisionTypes.h" -#include "PS_StrategicVisionSettings.generated.h" - -class UMaterialInterface; - -/** - * Reglages du plugin PS Strategic Vision. - * Accessibles dans Project Settings > Plugins > PS Strategic Vision. - */ -UCLASS(Config = Game, DefaultConfig, meta = (DisplayName = "PS Strategic Vision")) -class PS_STRATEGICVISION_API UPS_StrategicVisionSettings : public UDeveloperSettings -{ - GENERATED_BODY() - -public: - UPS_StrategicVisionSettings(); - - virtual FName GetCategoryName() const override { return TEXT("Plugins"); } - - /** Couleur associee a chaque camp. */ - UPROPERTY(EditAnywhere, Config, BlueprintReadOnly, Category = "Teams") - TMap TeamColors; - - /** - * Materiau par defaut des cones de vision. La couleur est appliquee via le - * parametre vecteur "Color" (et le parametre scalaire "Opacity" s'il existe), - * ainsi que via les vertex colors du mesh. - * Si vide ou introuvable, un materiau translucide du moteur est utilise. - */ - UPROPERTY(EditAnywhere, Config, BlueprintReadOnly, Category = "Rendering") - TSoftObjectPtr DefaultConeMaterial; - - /** Couleur du camp demande (blanc si non configuree). */ - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision") - FLinearColor GetTeamColor(EPS_StrategicTeam Team) const; -}; diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSubsystem.h b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSubsystem.h deleted file mode 100644 index b59c383..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionSubsystem.h +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Subsystems/WorldSubsystem.h" -#include "PS_StrategicVisionTypes.h" -#include "PS_StrategicVisionSubsystem.generated.h" - -DECLARE_DYNAMIC_MULTICAST_DELEGATE(FPS_StrategicDisplayStateChanged); - -/** - * Subsystem monde pilotant l'affichage strategique global. - * Permet d'activer / desactiver tous les affichages, une categorie (ex : cones - * de vision) ou un camp entier selon l'etat de jeu (vue spectateur, replay...). - * Tous les UPS_StrategicDisplayComponent s'y abonnent automatiquement. - */ -UCLASS() -class PS_STRATEGICVISION_API UPS_StrategicVisionSubsystem : public UWorldSubsystem -{ - GENERATED_BODY() - -public: - /** Raccourci d'acces au subsystem depuis n'importe quel contexte monde. */ - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision", meta = (WorldContext = "WorldContextObject")) - static UPS_StrategicVisionSubsystem* Get(const UObject* WorldContextObject); - - /** Interrupteur general de tous les affichages strategiques. */ - UFUNCTION(BlueprintCallable, Category = "PS Strategic Vision") - void SetGlobalDisplayEnabled(bool bEnabled); - - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision") - bool IsGlobalDisplayEnabled() const { return bGlobalEnabled; } - - /** Active / desactive une categorie d'affichage (ex : "VisionCone"). */ - UFUNCTION(BlueprintCallable, Category = "PS Strategic Vision") - void SetCategoryEnabled(FName Category, bool bEnabled); - - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision") - bool IsCategoryEnabled(FName Category) const { return !DisabledCategories.Contains(Category); } - - /** Active / desactive les affichages d'un camp (ex : ne montrer que les ennemis). */ - UFUNCTION(BlueprintCallable, Category = "PS Strategic Vision") - void SetTeamEnabled(EPS_StrategicTeam Team, bool bEnabled); - - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision") - bool IsTeamEnabled(EPS_StrategicTeam Team) const { return !DisabledTeams.Contains(Team); } - - /** Raccourci : active / desactive tous les cones de vision. */ - UFUNCTION(BlueprintCallable, Category = "PS Strategic Vision|Vision Cone") - void SetVisionConesEnabled(bool bEnabled) { SetCategoryEnabled(FPS_StrategicDisplayCategories::VisionCone, bEnabled); } - - UFUNCTION(BlueprintPure, Category = "PS Strategic Vision|Vision Cone") - bool AreVisionConesEnabled() const { return IsCategoryEnabled(FPS_StrategicDisplayCategories::VisionCone); } - - /** Etat combine utilise par les components pour savoir s'ils doivent s'afficher. */ - bool ShouldDisplay(FName Category, EPS_StrategicTeam Team) const - { - return bGlobalEnabled && IsCategoryEnabled(Category) && IsTeamEnabled(Team); - } - - /** Notifie a chaque changement d'etat global (les components s'y abonnent). */ - UPROPERTY(BlueprintAssignable, Category = "PS Strategic Vision") - FPS_StrategicDisplayStateChanged OnDisplayStateChanged; - -private: - void NotifyStateChanged(); - - bool bGlobalEnabled = true; - TSet DisabledCategories; - TSet DisabledTeams; -}; diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionTypes.h b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionTypes.h deleted file mode 100644 index 21c0d3f..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_StrategicVisionTypes.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "PS_StrategicVisionTypes.generated.h" - -/** Camp d'un personnage, utilise pour colorer / filtrer les affichages strategiques. */ -UENUM(BlueprintType) -enum class EPS_StrategicTeam : uint8 -{ - Player, - Ally, - Enemy, - Neutral -}; - -/** - * Categories d'affichage strategique. Chaque type de component declare sa categorie, - * ce qui permet de l'activer / desactiver globalement via le subsystem. - */ -struct PS_STRATEGICVISION_API FPS_StrategicDisplayCategories -{ - static const FName VisionCone; -}; diff --git a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_VisionConeComponent.h b/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_VisionConeComponent.h deleted file mode 100644 index 9191d11..0000000 --- a/Unreal/PS_StratVision/Plugins/PS_StrategicVision/Source/PS_StrategicVision/Public/PS_VisionConeComponent.h +++ /dev/null @@ -1,106 +0,0 @@ -// Copyright (c) 2026 j.foucher. All rights reserved. - -#pragma once - -#include "CoreMinimal.h" -#include "Engine/EngineTypes.h" -#include "PS_StrategicDisplayComponent.h" -#include "PS_VisionConeComponent.generated.h" - -class UMaterialInstanceDynamic; -class UMaterialInterface; -class UProceduralMeshComponent; - -/** - * Affiche le cone de vision d'un personnage sous forme de mesh procedural. - * Le cone est decoupe par les obstacles (murs) via des line traces, colore - * selon le camp, et activable / desactivable localement ou globalement - * (vue spectateur, replay) via le UPS_StrategicVisionSubsystem. - * - * A attacher sur le personnage : le cone suit la position du component et - * s'oriente selon son yaw (rotation horizontale). - */ -UCLASS(ClassGroup = (PS_StrategicVision), meta = (BlueprintSpawnableComponent, DisplayName = "PS Vision Cone")) -class PS_STRATEGICVISION_API UPS_VisionConeComponent : public UPS_StrategicDisplayComponent -{ - GENERATED_BODY() - -public: - UPS_VisionConeComponent(); - - virtual FName GetDisplayCategory() const override { return FPS_StrategicDisplayCategories::VisionCone; } - - /** Portee de vision, en cm. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone", meta = (ClampMin = "10.0", Units = "cm")) - float ViewDistance = 1500.f; - - /** Angle total du cone, en degres (ex : 90 = 45 de chaque cote). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone", meta = (ClampMin = "1.0", ClampMax = "360.0", Units = "deg")) - float ViewAngleDegrees = 90.f; - - /** Opacite du cone (0 = invisible, 1 = opaque). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone", meta = (ClampMin = "0.0", ClampMax = "1.0")) - float Opacity = 0.35f; - - /** - * Si vrai, le cone reste a plat au sol : seul le yaw du component est pris - * en compte et le mesh est projete a la hauteur du sol detecte. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone") - bool bFlattenOnGround = true; - - /** Decalage vertical du mesh au-dessus du sol, pour eviter le z-fighting. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone", meta = (Units = "cm")) - float GroundOffset = 3.f; - - /** Nombre de segments de l'arc : plus eleve = plus precis mais plus de traces. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Quality", meta = (ClampMin = "4", ClampMax = "256")) - int32 ArcSegments = 48; - - /** Intervalle de mise a jour en secondes (0 = chaque frame). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Quality", meta = (ClampMin = "0.0", Units = "s")) - float UpdateInterval = 0.f; - - /** Canal de collision utilise pour bloquer le cone sur les obstacles. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Collision") - TEnumAsByte TraceChannel = ECC_Visibility; - - /** Trace contre la geometrie complexe (plus precis, plus couteux). */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Collision") - bool bTraceComplex = false; - - /** Hauteur (relative au component) a laquelle les traces de vision sont lancees. */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Collision", meta = (Units = "cm")) - float TraceHeight = 50.f; - - /** - * Materiau du cone pour ce component (prioritaire sur celui des settings). - * La couleur du camp est envoyee dans le parametre vecteur "Color", l'opacite - * dans le parametre scalaire "Opacity", et dans les vertex colors du mesh. - */ - UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "Vision Cone|Rendering") - TObjectPtr MaterialOverride; - - virtual void TickComponent(float DeltaTime, ELevelTick TickType, FActorComponentTickFunction* ThisTickFunction) override; - -protected: - virtual void OnRegister() override; - virtual void OnComponentDestroyed(bool bDestroyingHierarchy) override; - virtual void BeginPlay() override; - virtual void OnDisplayStateChanged() override; - -private: - /** Mesh procedural interne portant le fan du cone. */ - UPROPERTY(Transient) - TObjectPtr ConeMesh; - - UPROPERTY(Transient) - TObjectPtr ConeMID; - - int32 LastVertexCount = 0; - - void EnsureConeMesh(); - void EnsureMaterial(); - void ApplyTeamColor(); - void RebuildCone(); -};