├── .gitattributes ├── SenseSystem ├── Source │ ├── SenseSystem │ │ ├── Private │ │ │ ├── SenseSysHelpers.cpp │ │ │ ├── SenseSysSettings.cpp │ │ │ ├── SenseObstacleInterface.cpp │ │ │ ├── SenseStimulusInterface.cpp │ │ │ ├── Sensors │ │ │ │ ├── PassiveSensor.cpp │ │ │ │ ├── Tests │ │ │ │ │ ├── SensorBoolTest.cpp │ │ │ │ │ ├── SensorLocationTestBase.cpp │ │ │ │ │ ├── SensorTestBlueprintBase.cpp │ │ │ │ │ ├── SensorTestBase.cpp │ │ │ │ │ ├── SensorDistanceTest.cpp │ │ │ │ │ ├── SensorTraceTest.cpp │ │ │ │ │ ├── SensorBoxTest.cpp │ │ │ │ │ ├── SensorAngleTest.cpp │ │ │ │ │ ├── SensorTraceTestBase.cpp │ │ │ │ │ └── FrustumTest.cpp │ │ │ │ ├── ActiveSensorBlueprintBase.cpp │ │ │ │ ├── SensorHearing.cpp │ │ │ │ ├── ActiveSensor.cpp │ │ │ │ ├── SensorSight.cpp │ │ │ │ └── SensorTouch.cpp │ │ │ ├── SenseSystem.cpp │ │ │ ├── SensedStimulStruct.cpp │ │ │ ├── BaseSensorTask.cpp │ │ │ ├── SenseStimulusComponent.cpp │ │ │ ├── BaseSensorTask.h │ │ │ ├── SenseDetectPool.h │ │ │ └── SenseSystemBPLibrary.cpp │ │ ├── Public │ │ │ ├── SenseSystem.h │ │ │ ├── SenseObstacleInterface.h │ │ │ ├── Sensors │ │ │ │ ├── Tests │ │ │ │ │ ├── SensorBoolTest.h │ │ │ │ │ ├── SensorLocationTestBase.h │ │ │ │ │ ├── SensorTraceTest.h │ │ │ │ │ ├── SensorTraceTestBase.h │ │ │ │ │ ├── SensorAngleTest.h │ │ │ │ │ ├── SensorBoxTest.h │ │ │ │ │ ├── SensorDistanceTest.h │ │ │ │ │ ├── SensorDistanceAndAngleTest.h │ │ │ │ │ ├── FrustumTest.h │ │ │ │ │ ├── SensorTestBlueprintBase.h │ │ │ │ │ └── SensorTestBase.h │ │ │ │ ├── PassiveSensor.h │ │ │ │ ├── ActiveSensorBlueprintBase.h │ │ │ │ ├── SensorHearing.h │ │ │ │ ├── ActiveSensor.h │ │ │ │ ├── SensorSight.h │ │ │ │ └── SensorTouch.h │ │ │ ├── SenseStimulusInterface.h │ │ │ ├── SenseStimulusComponent.h │ │ │ ├── SenseSysSettings.h │ │ │ ├── SenseSystemBPLibrary.h │ │ │ ├── SensedStimulStruct.h │ │ │ ├── SenseSysHelpers.h │ │ │ └── SenseManager.h │ │ └── SenseSystem.Build.cs │ └── SenseSystemEditor │ │ ├── Public │ │ ├── SenseSystemEditor.h │ │ └── SenseSysComponentVisualizer.h │ │ ├── SenseSystemEditor.Build.cs │ │ └── Private │ │ ├── SenseSysComponentVisualizer.cpp │ │ ├── SenseSystemEditor.cpp │ │ ├── BitFlag64_Customization.h │ │ └── BitFlag64_Customization.cpp ├── Resources │ └── Icon128.png ├── Content │ └── Curves │ │ ├── AngleTest_Curve.uasset │ │ └── Distance_Curve.uasset ├── Config │ └── FilterPlugin.ini └── SenseSystem.uplugin ├── docs ├── images │ ├── sensor.png │ ├── blueprint-api.png │ ├── sensor-test.png │ ├── quick-start-1.1.png │ ├── quick-start-1.2.png │ ├── quick-start-1.3.png │ ├── quick-start-2.1.png │ ├── quick-start-2.2.png │ ├── quick-start-2.3.png │ ├── quick-start-3.1.png │ ├── quick-start-3.2.png │ ├── quick-start-3.3.png │ ├── quick-start-3.4.png │ ├── quick-start-5.1.png │ ├── project-settings.png │ └── sense-receiver-component.png └── en │ ├── project-settings.md │ └── quick-start.md ├── .gitignore ├── README.md ├── .clang-format └── CHANGELOG.md /.gitattributes: -------------------------------------------------------------------------------- 1 | Content/** filter=lfs diff=lfs merge=lfs -text 2 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseSysHelpers.cpp: -------------------------------------------------------------------------------- 1 | #include "SenseSysHelpers.h" -------------------------------------------------------------------------------- /docs/images/sensor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/sensor.png -------------------------------------------------------------------------------- /docs/images/blueprint-api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/blueprint-api.png -------------------------------------------------------------------------------- /docs/images/sensor-test.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/sensor-test.png -------------------------------------------------------------------------------- /docs/images/quick-start-1.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-1.1.png -------------------------------------------------------------------------------- /docs/images/quick-start-1.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-1.2.png -------------------------------------------------------------------------------- /docs/images/quick-start-1.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-1.3.png -------------------------------------------------------------------------------- /docs/images/quick-start-2.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-2.1.png -------------------------------------------------------------------------------- /docs/images/quick-start-2.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-2.2.png -------------------------------------------------------------------------------- /docs/images/quick-start-2.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-2.3.png -------------------------------------------------------------------------------- /docs/images/quick-start-3.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-3.1.png -------------------------------------------------------------------------------- /docs/images/quick-start-3.2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-3.2.png -------------------------------------------------------------------------------- /docs/images/quick-start-3.3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-3.3.png -------------------------------------------------------------------------------- /docs/images/quick-start-3.4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-3.4.png -------------------------------------------------------------------------------- /docs/images/quick-start-5.1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/quick-start-5.1.png -------------------------------------------------------------------------------- /SenseSystem/Resources/Icon128.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/SenseSystem/Resources/Icon128.png -------------------------------------------------------------------------------- /docs/images/project-settings.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/project-settings.png -------------------------------------------------------------------------------- /docs/images/sense-receiver-component.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/docs/images/sense-receiver-component.png -------------------------------------------------------------------------------- /SenseSystem/Content/Curves/AngleTest_Curve.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/SenseSystem/Content/Curves/AngleTest_Curve.uasset -------------------------------------------------------------------------------- /SenseSystem/Content/Curves/Distance_Curve.uasset: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Sn-a-ke/SenseSystem/HEAD/SenseSystem/Content/Curves/Distance_Curve.uasset -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseSysSettings.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseSysSettings.h" 4 | -------------------------------------------------------------------------------- /docs/en/project-settings.md: -------------------------------------------------------------------------------- 1 | # Project Settings 2 | 3 | ## 1. Summary 4 | 5 | ## 2. settings pictures 6 | 7 | ![Settings Pictures](../images/project-settings.png) 8 | 9 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseObstacleInterface.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2019 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseObstacleInterface.h" 4 | 5 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseStimulusInterface.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2019 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseStimulusInterface.h" 4 | 5 | -------------------------------------------------------------------------------- /SenseSystem/Config/FilterPlugin.ini: -------------------------------------------------------------------------------- 1 | [FilterPlugin] 2 | ; This section lists additional files which will be packaged along with your plugin. Paths should be listed relative to the root plugin directory, and 3 | ; may include "...", "*", and "?" wildcards to match directories, files, and individual characters respectively. 4 | ; 5 | ; Examples: 6 | ; /README.txt 7 | ; /Extras/... 8 | ; /Binaries/ThirdParty/*.dll 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Ignore everything 2 | * 3 | 4 | # NotIgnore 5 | !/SenseSystem/Source/** 6 | !/SenseSystem/Resources/** 7 | !/SenseSystem/Content/** 8 | !/docs/ 9 | !/docs/** 10 | !*.uplugin 11 | !clang-format 12 | !*.md 13 | 14 | # Windows 15 | ehthumbs.db 16 | Thumbs.db 17 | 18 | # Visual Studio 19 | .vs 20 | .vscode 21 | .vs 22 | *.VC.db 23 | *.opensdf 24 | *.opendb 25 | *.sdf 26 | *.sln 27 | *.suo 28 | *.xcodeproj 29 | *.xcworkspace -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/PassiveSensor.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/PassiveSensor.h" 4 | 5 | 6 | UPassiveSensor::UPassiveSensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 7 | { 8 | } 9 | 10 | UPassiveSensor::~UPassiveSensor() 11 | { 12 | } 13 | 14 | void UPassiveSensor::BeginDestroy() 15 | { 16 | Super::BeginDestroy(); 17 | } 18 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseSystem.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseSystem.h" 4 | 5 | #define LOCTEXT_NAMESPACE "FSenseSystemModule" 6 | 7 | void FSenseSystemModule::StartupModule() 8 | { 9 | } 10 | 11 | void FSenseSystemModule::ShutdownModule() 12 | { 13 | } 14 | 15 | #undef LOCTEXT_NAMESPACE 16 | 17 | IMPLEMENT_MODULE(FSenseSystemModule, SenseSystem) 18 | DEFINE_LOG_CATEGORY(LogSenseSys); -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorBoolTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorBoolTest.h" 4 | 5 | 6 | EUpdateReady USensorBoolTest::GetReadyToTest() 7 | { 8 | return EUpdateReady::Ready; 9 | } 10 | 11 | bool USensorBoolTest::PreTest() 12 | { 13 | Super::PreTest(); 14 | return true; 15 | } 16 | 17 | ESenseTestResult USensorBoolTest::RunTest(FSensedStimulus& SensedStimulus) const 18 | { 19 | return TestResult; 20 | } 21 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseSystem.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Modules/ModuleManager.h" 6 | 7 | class FSenseSystemModule : public IModuleInterface 8 | { 9 | public: 10 | /** IModuleInterface implementation */ 11 | virtual void StartupModule() override; 12 | virtual void ShutdownModule() override; 13 | 14 | using ElementIndexType = uint16; //todo options 15 | }; 16 | 17 | DECLARE_LOG_CATEGORY_EXTERN(LogSenseSys, Log, All); //Fatal, Error, Warning, Display, Log, Verbose, VeryVerbose -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Public/SenseSystemEditor.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Modules/ModuleInterface.h" 7 | #include "Modules/ModuleManager.h" 8 | #include "Templates/SharedPointer.h" 9 | 10 | class FSenseSystemEditorModule final : public IModuleInterface 11 | { 12 | public: 13 | /** IModuleInterface implementation */ 14 | virtual void StartupModule() override; 15 | virtual void ShutdownModule() override; 16 | 17 | private: 18 | void RegisterComponentVisualizer(FName ComponentClassName, TSharedPtr Visualizer); 19 | TArray RegisteredComponentClassNames; 20 | }; 21 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/ActiveSensorBlueprintBase.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/ActiveSensorBlueprintBase.h" 4 | 5 | 6 | UActiveSensorBlueprintBase::UActiveSensorBlueprintBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 7 | { 8 | } 9 | 10 | UActiveSensorBlueprintBase::~UActiveSensorBlueprintBase() 11 | { 12 | } 13 | 14 | #if WITH_EDITOR 15 | void UActiveSensorBlueprintBase::PostEditChangeProperty(struct FPropertyChangedEvent& e) 16 | { 17 | Super::PostEditChangeProperty(e); 18 | } 19 | #endif 20 | 21 | void UActiveSensorBlueprintBase::InitializeFromReceiver(USenseReceiverComponent* FromReceiver) 22 | { 23 | UActiveSensor::InitializeFromReceiver(FromReceiver); 24 | InitializeFromReceiver_BP(FromReceiver); 25 | } 26 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseObstacleInterface.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "UObject/Interface.h" 6 | 7 | #include "SenseObstacleInterface.generated.h" 8 | 9 | 10 | /** 11 | * SenseObstacleInterface 12 | */ 13 | UINTERFACE(BlueprintType) 14 | class SENSESYSTEM_API USenseObstacleInterface : public UInterface 15 | { 16 | GENERATED_BODY() 17 | }; 18 | 19 | /** 20 | * SenseObstacleInterface 21 | */ 22 | class SENSESYSTEM_API ISenseObstacleInterface 23 | { 24 | GENERATED_BODY() 25 | public: 26 | /**Not Blueprint Thread Safe*/ 27 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SenseObstacleInterface") 28 | float GetTransparency(FName SensorTag, class USceneComponent* HitComponent, FVector HitLocation) const; 29 | }; 30 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Public/SenseSysComponentVisualizer.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "ComponentVisualizer.h" 7 | 8 | class FPrimitiveDrawInterface; 9 | class FSceneView; 10 | 11 | /** 12 | * 13 | */ 14 | class SENSESYSTEMEDITOR_API FSenseSysComponentVisualizer final : public FComponentVisualizer 15 | { 16 | public: 17 | //~ Begin FComponentVisualizer Interface 18 | 19 | virtual void DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) override; 20 | /** Draw HUD on viewport for the supplied component */ 21 | virtual void DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) override; 22 | //~ End FComponentVisualizer Interface 23 | }; 24 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorBoolTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorTestBase.h" 7 | 8 | #include "SensorBoolTest.generated.h" 9 | 10 | 11 | /** 12 | * BoolTest 13 | */ 14 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 15 | class SENSESYSTEM_API USensorBoolTest : public USensorTestBase 16 | { 17 | GENERATED_BODY() 18 | public: 19 | 20 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorTest") 21 | ESenseTestResult TestResult = ESenseTestResult::Sensed; 22 | 23 | /** Prepare for the test, called before RunTest */ 24 | virtual EUpdateReady GetReadyToTest() override; 25 | 26 | /** PreTest */ 27 | virtual bool PreTest() override; 28 | 29 | /** Test Body , if return true - remove elem */ 30 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const override; 31 | }; 32 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/PassiveSensor.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Sensors/SensorBase.h" 6 | 7 | #include "PassiveSensor.generated.h" 8 | 9 | 10 | /** 11 | * Base Class for Passive Sensor 12 | * Event Or Delegate Update Sensor 13 | */ 14 | UCLASS(abstract, Blueprintable, BlueprintType, EditInlineNew, HideDropdown) 15 | class SENSESYSTEM_API UPassiveSensor : public USensorBase 16 | { 17 | GENERATED_BODY() 18 | public: 19 | 20 | UPassiveSensor(const FObjectInitializer& ObjectInitializer); 21 | virtual ~UPassiveSensor() override; 22 | 23 | virtual void BeginDestroy() override; 24 | virtual void ReportPassiveEvent(class USenseStimulusBase* StimulusComponent) { ReportSenseStimulusEvent(StimulusComponent); } 25 | virtual void ReportPassiveEvent(const FSenseSystemModule::ElementIndexType StimulusID) { ReportSenseStimulusEvent(StimulusID); } 26 | virtual bool IsOverrideSenseState() const override { return false; } 27 | }; 28 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SensedStimulStruct.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SensedStimulStruct.h" 4 | #include "SenseStimulusBase.h" 5 | 6 | FBox FSensedStimulus::Init( 7 | const FName& SensorTag, // 8 | USenseStimulusBase* Component, 9 | const float InScore, 10 | const float InAge, 11 | const float CurrentTime, 12 | const uint64 InBitChannels) 13 | { 14 | if (IsValid(Component)) 15 | { 16 | StimulusComponent = Component; 17 | TmpHash = GetTypeHash(Component); 18 | Score = InScore; 19 | Age = InAge; 20 | SensedTime = CurrentTime; 21 | BitChannels = InBitChannels; 22 | 23 | const FSensedPoint P0 = FSensedPoint(StimulusComponent->GetSingleSensePoint(SensorTag), Score); 24 | FBox NewBox(P0.SensedPoint, P0.SensedPoint); 25 | 26 | const TArray Points = StimulusComponent->GetSensePoints(SensorTag); 27 | SensedPoints.Reserve(Points.Num() + 1); 28 | SensedPoints.Add(P0); 29 | for (const FVector& V : Points) 30 | { 31 | NewBox += V; 32 | SensedPoints.Add(FSensedPoint(V, Score)); 33 | } 34 | return NewBox; 35 | } 36 | return FBox(); 37 | } 38 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/ActiveSensorBlueprintBase.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Sensors/ActiveSensor.h" 6 | 7 | #include "ActiveSensorBlueprintBase.generated.h" 8 | 9 | 10 | /** 11 | * Base Class for Active Sensor Blueprint 12 | */ 13 | UCLASS(abstract, Blueprintable, BlueprintType, EditInlineNew, HideDropdown) 14 | class SENSESYSTEM_API UActiveSensorBlueprintBase : public UActiveSensor 15 | { 16 | GENERATED_BODY() 17 | 18 | public: 19 | UActiveSensorBlueprintBase(const FObjectInitializer& ObjectInitializer); 20 | virtual ~UActiveSensorBlueprintBase() override; 21 | 22 | #if WITH_EDITOR 23 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 24 | #endif 25 | 26 | /**Initialize Sensor Blueprint Implementation*/ 27 | UFUNCTION(BlueprintImplementableEvent, Category = "SenseSystem|Sensor", meta = (DisplayName = "InitializeFromReceiver")) 28 | void InitializeFromReceiver_BP(USenseReceiverComponent* InSenseReceiver); 29 | 30 | virtual void InitializeFromReceiver(USenseReceiverComponent* FromReceiver) override; 31 | }; 32 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseStimulusInterface.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/Interface.h" 7 | 8 | #include "SenseStimulusInterface.generated.h" 9 | 10 | 11 | class USenseStimulusComponent; 12 | 13 | 14 | /** 15 | * SenseStimulusInterface 16 | */ 17 | UINTERFACE(BlueprintType) 18 | class SENSESYSTEM_API USenseStimulusInterface : public UInterface 19 | { 20 | GENERATED_BODY() 21 | }; 22 | 23 | /** 24 | * SenseStimulusInterface 25 | */ 26 | class SENSESYSTEM_API ISenseStimulusInterface 27 | { 28 | GENERATED_BODY() 29 | public: 30 | /** (!) Not Blueprint Thread Safe!!!*/ 31 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SenseStimulusInterface") 32 | class USenseStimulusBase* IGetActorStimulus() const; 33 | 34 | /** (!) Not Blueprint Thread Safe!!!*/ 35 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SenseStimulusInterface") 36 | FVector IGetSingleSensePoint(FName SensorTag) const; 37 | 38 | /** (!) Not Blueprint Thread Safe!!!*/ 39 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SenseStimulusInterface") 40 | TArray IGetSensePoints(FName SensorTag) const; 41 | }; -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorLocationTestBase.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorTestBase.h" 7 | 8 | #include "SensorLocationTestBase.generated.h" 9 | 10 | 11 | /** 12 | * SensorTestBy 13 | */ 14 | UENUM(BlueprintType) 15 | enum class ESensorTestBy : uint8 16 | { 17 | ActorLocation = 0 UMETA(DisplayName = "ActorLocation"), 18 | SensePoints UMETA(DisplayName = "SensePoints"), 19 | }; 20 | 21 | 22 | /** 23 | * SensorLocationTestBase 24 | */ 25 | UCLASS(abstract, BlueprintType, EditInlineNew, HideDropdown) 26 | class SENSESYSTEM_API USensorLocationTestBase : public USensorTestBase 27 | { 28 | GENERATED_BODY() 29 | 30 | public: 31 | /** Test By Single Location */ 32 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 33 | bool bTestBySingleLocation = true; 34 | 35 | /** full test implementation */ 36 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const override; 37 | 38 | protected: 39 | /** test for one sensed point */ 40 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const; 41 | 42 | FIntPoint GetBoundFotSensePoints(TArray& SensedPoints) const; 43 | }; 44 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorTraceTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorTestBase.h" 7 | #include "SensorTraceTestBase.h" 8 | 9 | #include "SensorTraceTest.generated.h" 10 | 11 | 12 | /** 13 | * Sensor Trace Test 14 | */ 15 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 16 | class SENSESYSTEM_API USensorTraceTest : public USensorTraceTestBase 17 | { 18 | GENERATED_BODY() 19 | public: 20 | USensorTraceTest(const FObjectInitializer& ObjectInitializer); 21 | 22 | #if WITH_EDITOR 23 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 24 | #endif 25 | 26 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 27 | void SetTraceParam(ETraceTestParam TraceParam, ECollisionChannel Collision, bool bInTestBySingleLocation, bool TraceComplex); 28 | 29 | virtual EUpdateReady GetReadyToTest() override; 30 | virtual bool PreTest() override; 31 | 32 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const override; 33 | 34 | protected: 35 | virtual void InitializeCacheTest() override; 36 | 37 | bool LineTraceTest(const UWorld* World, const FCollisionQueryParams& CollisionParams, const FVector& StartTrace, FSensedStimulus& SensedStimulus) const; 38 | }; 39 | -------------------------------------------------------------------------------- /SenseSystem/SenseSystem.uplugin: -------------------------------------------------------------------------------- 1 | { 2 | "FileVersion": 3, 3 | "Version": 36, 4 | "VersionName": "SenseSystem 1.31", 5 | "EngineVersion": "5.3.0", 6 | "FriendlyName": "SenseSystem", 7 | "Description": "SenseSystem", 8 | "Category": "Other", 9 | "CreatedBy": "Alexandr Marchenko", 10 | "CreatedByURL": "https://www.unrealengine.com/marketplace/en-US/profile/Alexander+UE4", 11 | "DocsURL": "https://drive.google.com/drive/folders/1atJzhBuEcQyUeMrZbVL-5p9DVAVhif4Z", 12 | "MarketplaceURL": "com.epicgames.launcher: //ue/marketplace/content/28a0c4ba00bf4eee8c17e52b7b2c8d52", 13 | "SupportURL": "https://www.unrealengine.com/marketplace/en-US/profile/Alexander+UE4", 14 | "EnabledByDefault": true, 15 | "CanContainContent": true, 16 | "IsBetaVersion": false, 17 | "Installed": false, 18 | "Modules": 19 | [ 20 | { 21 | "Name": "SenseSystem", 22 | "Type": "Runtime", 23 | "LoadingPhase": "PreDefault", 24 | "PlatformAllowList": 25 | [ 26 | "Win64", 27 | "Mac", 28 | "Linux", 29 | "Android", 30 | "LinuxArm64" 31 | ] 32 | }, 33 | { 34 | "Name": "SenseSystemEditor", 35 | "Type": "Editor", 36 | "LoadingPhase": "PostEngineInit", 37 | "PlatformAllowList": 38 | [ 39 | "Win64", 40 | "Mac", 41 | "Linux" 42 | ] 43 | } 44 | ] 45 | } -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/SenseSystemEditor.Build.cs: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SenseSystemEditor : ModuleRules 6 | { 7 | public SenseSystemEditor(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | PrecompileForTargets = PrecompileTargetsType.Any; 11 | 12 | PublicIncludePaths.AddRange(new string[] 13 | { 14 | ModuleDirectory + "/Public" 15 | }); 16 | 17 | 18 | PrivateIncludePaths.AddRange(new string[] 19 | { 20 | "SenseSystemEditor/Private", 21 | }); 22 | 23 | PublicDependencyModuleNames.AddRange(new string[] 24 | { 25 | "Core", 26 | "CoreUObject", 27 | "Engine", 28 | }); 29 | 30 | PrivateDependencyModuleNames.AddRange(new string[] 31 | { 32 | "Core", 33 | "CoreUObject", 34 | "Engine", 35 | "UnrealEd", 36 | "EditorStyle", 37 | "Slate", 38 | "SlateCore", 39 | "PropertyEditor", 40 | "InputCore", 41 | "ComponentVisualizers", 42 | "SenseSystem", 43 | }); 44 | 45 | //PrivateIncludePathModuleNames.AddRange(new string[]{}); 46 | //DynamicallyLoadedModuleNames.AddRange(new string[]{}); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Private/SenseSysComponentVisualizer.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseSysComponentVisualizer.h" 4 | #include "Components/ActorComponent.h" 5 | #include "SceneView.h" 6 | #include "SceneManagement.h" 7 | #include "SenseReceiverComponent.h" 8 | #include "SenseStimulusBase.h" 9 | #include "CanvasTypes.h" 10 | #include "UnrealClient.h" 11 | 12 | void FSenseSysComponentVisualizer::DrawVisualization(const UActorComponent* Component, const FSceneView* View, FPrimitiveDrawInterface* PDI) 13 | { 14 | #if WITH_EDITORONLY_DATA 15 | if (View->Family->EngineShowFlags.VisualizeSenses) 16 | { 17 | 18 | if (const auto Senses = Cast(Component)) 19 | { 20 | Senses->DrawComponent(View, PDI); 21 | } 22 | else if (const auto Stimulus = Cast(Component)) 23 | { 24 | Stimulus->DrawComponent(View, PDI); 25 | } 26 | } 27 | #endif 28 | } 29 | 30 | void FSenseSysComponentVisualizer::DrawVisualizationHUD(const UActorComponent* Component, const FViewport* Viewport, const FSceneView* View, FCanvas* Canvas) 31 | { 32 | #if WITH_EDITORONLY_DATA 33 | if (View->Family->EngineShowFlags.VisualizeSenses) 34 | { 35 | 36 | if (const auto Senses = Cast(Component)) 37 | { 38 | Senses->DrawComponentHUD(Viewport, View, Canvas); 39 | } 40 | else if (const auto Stimulus = Cast(Component)) 41 | { 42 | Stimulus->DrawComponentHUD(Viewport, View, Canvas); 43 | } 44 | } 45 | #endif 46 | } 47 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/SenseSystem.Build.cs: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | using UnrealBuildTool; 4 | 5 | public class SenseSystem : ModuleRules 6 | { 7 | public SenseSystem(ReadOnlyTargetRules Target) : base(Target) 8 | { 9 | PCHUsage = PCHUsageMode.UseExplicitOrSharedPCHs; 10 | PrecompileForTargets = PrecompileTargetsType.Any; 11 | 12 | PublicIncludePaths.AddRange(new string[] 13 | { 14 | ModuleDirectory + "/Public", 15 | ModuleDirectory + "/Public/Sensors", 16 | ModuleDirectory + "/Public/Sensors/Tests", 17 | }); 18 | 19 | 20 | PrivateIncludePaths.AddRange(new string[] 21 | { 22 | "SenseSystem/Private", 23 | "SenseSystem/Private/Sensors", 24 | "SenseSystem/Private/Sensors/Tests", 25 | }); 26 | 27 | PublicDependencyModuleNames.AddRange(new string[] 28 | { 29 | "Core", 30 | "CoreUObject", 31 | "Engine", 32 | }); 33 | 34 | 35 | PrivateDependencyModuleNames.AddRange(new string[] 36 | { 37 | "Core", 38 | "CoreUObject", 39 | "Engine", 40 | }); 41 | 42 | 43 | DynamicallyLoadedModuleNames.AddRange(new string[] 44 | { 45 | }); 46 | 47 | } 48 | } 49 | 50 | /* 51 | "WhitelistPlatforms": 52 | [ 53 | "Win32", 54 | "Win64", 55 | "HoloLens", 56 | "Mac", 57 | "XboxOne", 58 | "PS4", 59 | "IOS", 60 | "Android", 61 | "Linux", 62 | "LinuxAArch64", 63 | "AllDesktop", 64 | "TVOS", 65 | "Switch", 66 | "Quail", 67 | "Lumin" 68 | ] 69 | */ -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseStimulusComponent.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/ObjectMacros.h" 7 | #include "GameFramework/Actor.h" 8 | #include "SenseStimulusBase.h" 9 | 10 | #include "SenseStimulusComponent.generated.h" 11 | 12 | 13 | /** 14 | * SensorArrayByType 15 | */ 16 | UENUM(Meta = (Bitflags, UseEnumValuesAsMaskValuesInEditor = "true")) 17 | enum class ESSInterfaceFlag : uint8 18 | { 19 | ESS_None = 0, 20 | ESS_SensePointsInterface = 0x04, 21 | ESS_SinglePointInterface = 0x08 22 | }; 23 | 24 | /** 25 | * SenseStimulusComponent for Actor Owner SenseStimulusInterface 26 | */ 27 | UCLASS(BlueprintType, Blueprintable, ClassGroup = (SenseSystem), meta = (BlueprintSpawnableComponent)) 28 | class SENSESYSTEM_API USenseStimulusComponent : public USenseStimulusBase 29 | { 30 | GENERATED_BODY() 31 | 32 | public: 33 | USenseStimulusComponent(const FObjectInitializer& ObjectInitializer); 34 | virtual ~USenseStimulusComponent() override; 35 | 36 | #if WITH_EDITORONLY_DATA 37 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 38 | #endif 39 | 40 | virtual void OnRegister() override; 41 | 42 | UPROPERTY(EditDefaultsOnly, BlueprintReadOnly, Category = "SenseStimulus", meta = (Bitmask, BitmaskEnum = "/Script/SenseSystem.ESSInterfaceFlag")) 43 | uint8 InterfaceOwnerBitFlags = 0; 44 | 45 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SenseStimulus", meta = (Keywords = "Set Interface Flag Stimulus")) 46 | void SetInterfaceFlag(ESSInterfaceFlag Flag, bool bValue); 47 | 48 | protected: 49 | /**Interface*/ 50 | //UPROPERTY()//VisibleAnywhere, BlueprintReadOnly, Category = "SenseStimulus" 51 | bool bOwnerSenseStimulusInterface; 52 | 53 | void InitOwnerInterface(); 54 | 55 | /**Get Main Point for Sensing*/ 56 | virtual FVector GetSingleSensePoint(FName SensorTag) const override; 57 | 58 | /**Get Points for Sensing*/ 59 | virtual TArray GetSensePoints(FName SensorTag) const override; 60 | }; 61 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorLocationTestBase.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorLocationTestBase.h" 4 | #include "SensedStimulStruct.h" 5 | 6 | 7 | ESenseTestResult USensorLocationTestBase::RunTest(FSensedStimulus& SensedStimulus) const 8 | { 9 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_LocationTestBase_RunTest); 10 | 11 | ESenseTestResult OutResult = ESenseTestResult::Lost; 12 | if (MinScore < SensedStimulus.Score && SensedStimulus.SensedPoints.Num()) 13 | { 14 | TArray& Points = SensedStimulus.SensedPoints; 15 | const FIntPoint TestBound = GetBoundFotSensePoints(Points); 16 | float TotalScore = 0.f; 17 | 18 | for (int32 i = TestBound.X; i < TestBound.Y; i++) 19 | { 20 | float Score = 1.0f; 21 | Points[i].PointTestResult = RunTestForLocation(SensedStimulus, Points[i].SensedPoint, Score); 22 | if (Points[i].PointTestResult != ESenseTestResult::Lost) 23 | { 24 | Points[i].PointScore *= Score; 25 | OutResult = FMath::Min(OutResult, Points[i].PointTestResult); 26 | TotalScore += Score; 27 | } 28 | } 29 | 30 | if (OutResult != ESenseTestResult::Lost) 31 | { 32 | SensedStimulus.Score *= TotalScore / static_cast(TestBound.Y - TestBound.X); 33 | if (OutResult == ESenseTestResult::Sensed && MinScore > SensedStimulus.Score) 34 | { 35 | OutResult = ESenseTestResult::NotLost; 36 | } 37 | } 38 | } 39 | return OutResult; 40 | } 41 | 42 | ESenseTestResult USensorLocationTestBase::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 43 | { 44 | return ESenseTestResult::Lost; 45 | } 46 | 47 | FIntPoint USensorLocationTestBase::GetBoundFotSensePoints(TArray& SensedPoints) const 48 | { 49 | if (!bTestBySingleLocation && SensedPoints.Num() > 1) 50 | { 51 | SensedPoints[0].PointTestResult = ESenseTestResult::None; 52 | return FIntPoint(1, SensedPoints.Num()); // the location of the actor is not taken into account 53 | } 54 | return FIntPoint(0, 1); 55 | } 56 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseSysSettings.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SenseSysHelpers.h" 7 | #include "UObject/NoExportTypes.h" 8 | 9 | #include "SenseSysSettings.generated.h" 10 | 11 | 12 | /** 13 | * SuccessState 14 | */ 15 | UENUM(BlueprintType) 16 | enum class ESenseSys_QtOtSwitch : uint8 17 | { 18 | // OcTree32 max nodes MAX_uint32 - 1 = 4294967294 19 | OcTree = 0 UMETA(DisplayName = "OcTree"), 20 | 21 | // QuadTree32 max nodes MAX_uint32 - 1= 4294967294 22 | QuadTree UMETA(DisplayName = "QuadTree"), 23 | 24 | // OcTree16 max nodes MAX_uint16 - 1 = 65534 25 | //OcTree16 UMETA(DisplayName = "OcTree16"), 26 | 27 | // QuadTree16 max nodes MAX_uint16 - 1 = 65534 28 | //QuadTree16 UMETA(DisplayName = "QuadTree16") 29 | }; 30 | 31 | /** 32 | * SensedPoint 33 | */ 34 | USTRUCT(BlueprintType) 35 | struct SENSESYSTEM_API FSensorTagSettings 36 | { 37 | GENERATED_USTRUCT_BODY() 38 | 39 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem", meta = (ClampMin = "10.0", ClampMax = "100000.0", UIMin = "10.0", UIMax = "100000.0")) 40 | float MinimumQuadTreeSize = 100.f; 41 | 42 | //QuadTree - 4. Octree - 8 43 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem", meta = (ClampMin = "4", ClampMax = "128", UIMin = "4", UIMax = "128")) 44 | int32 NodeCantSplit = 4; 45 | 46 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem") 47 | ESenseSys_QtOtSwitch QtOtSwitch = ESenseSys_QtOtSwitch::QuadTree; 48 | 49 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem") 50 | FSenseSysDebugDraw SenseSysDebugDraw; 51 | 52 | //todo draw color 53 | }; 54 | 55 | 56 | /** 57 | * SenseSysSettings 58 | */ 59 | //UCLASS(Config = EditorPerProjectUserSettings) 60 | UCLASS(config = Game, defaultconfig) 61 | class SENSESYSTEM_API USenseSysSettings : public UObject 62 | { 63 | GENERATED_BODY() 64 | public: 65 | 66 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem") 67 | TMap SensorTagSettings; 68 | 69 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem") 70 | int32 CountPerOneCyclesUpdate = 10; 71 | 72 | UPROPERTY(Config, EditAnywhere, Category = "SenseSystem") 73 | float WaitTimeBetweenCyclesUpdate = 0.0001f; 74 | }; 75 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/SensorHearing.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/PassiveSensor.h" 7 | #include "Curves/CurveFloat.h" 8 | #include "Sensors/Tests/SensorTraceTestBase.h" 9 | 10 | #include "SensorHearing.generated.h" 11 | 12 | 13 | /** 14 | * SensorHearing 15 | */ 16 | UCLASS(Blueprintable, BlueprintType, EditInlineNew, HideCategories = (SensorTests)) 17 | class SENSESYSTEM_API USensorHearing : public UPassiveSensor 18 | { 19 | GENERATED_BODY() 20 | public: 21 | USensorHearing(const FObjectInitializer& ObjectInitializer); 22 | virtual ~USensorHearing() override; 23 | 24 | #if WITH_EDITOR 25 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 26 | #endif 27 | 28 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorHearing") 29 | float MinScore = 0.f; 30 | 31 | /**Max Hearing Distance*/ 32 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorHearing") 33 | float MaxHearingDistance = 10000; 34 | 35 | /**Score Modify by Curve*/ 36 | UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "SensorHearing") 37 | FRuntimeFloatCurve DistanceCurve; 38 | 39 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor|SensorHearing") 40 | void SetDistanceParam(float InMaxHearingDistance, float InMinScore); 41 | 42 | 43 | //virtual void Serialize(FArchive& Ar) override; 44 | 45 | //todo: noise trace ScoreFromTransparency, need an overridden method of scoring points for test tests when Overlapping without interfaces. 46 | 47 | // /** whether Noise will propagate through objects */ 48 | //UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorHearing") 49 | // ETraceTestParam NoiseCollisionCheck = ETraceTestParam::MultiTraceTest; 50 | 51 | // /** 52 | // * NoiseOverlapChannel - add NoiseChannel in project settings trace channels 53 | // * and set overlap if need reduce noise score 54 | // * NoiseCollisionCheck set to MultiTraceTest 55 | // */ 56 | //UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorHearing") 57 | // TEnumAsByte NoiseOverlapChannel = ECollisionChannel::ECC_Visibility; 58 | 59 | 60 | protected: 61 | void SetValuesToSensorTests(); 62 | }; 63 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorTraceTestBase.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorLocationTestBase.h" 7 | #include "CollisionQueryParams.h" 8 | #include "Components/PrimitiveComponent.h" 9 | 10 | #include "SensorTraceTestBase.generated.h" 11 | 12 | 13 | /** 14 | * TraceTestParam 15 | */ 16 | UENUM(BlueprintType) 17 | enum class ETraceTestParam : uint8 18 | { 19 | BoolTraceTest = 0 UMETA(DisplayName = "BoolTraceTest"), 20 | MultiTraceTest UMETA(DisplayName = "MultiTraceTest"), 21 | }; 22 | 23 | class UWorld; 24 | 25 | /** 26 | * SensorTraceTestBase 27 | */ 28 | UCLASS(abstract, BlueprintType, EditInlineNew, HideDropdown) 29 | class SENSESYSTEM_API USensorTraceTestBase : public USensorLocationTestBase 30 | { 31 | GENERATED_BODY() 32 | public: 33 | USensorTraceTestBase(const FObjectInitializer& ObjectInitializer); 34 | 35 | virtual void BeginDestroy() override; 36 | 37 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 38 | ETraceTestParam TraceTestParam = ETraceTestParam::BoolTraceTest; 39 | 40 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 41 | TEnumAsByte CollisionChannel = ECollisionChannel::ECC_Visibility; 42 | 43 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 44 | bool bTraceComplex = false; 45 | 46 | virtual void InitializeFromSensor() override; 47 | //virtual void OnSensorChanged() override; 48 | 49 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 50 | void SetBaseTraceParam(ETraceTestParam TraceParam, ECollisionChannel Collision, bool TraceComplex); 51 | 52 | protected: 53 | FCollisionQueryParams Collision_Params; 54 | 55 | bool ScoreFromTransparency(const TArray& OutHits, float& InScore) const; 56 | 57 | virtual void InitCollisionParams(); 58 | 59 | virtual bool LineMultiTraceTest( 60 | const UWorld* World, const FCollisionQueryParams& CollisionParams, const FVector& StartTrace, FSensedStimulus& SensedStimulus) const; 61 | 62 | virtual bool LineBoolTraceTest( 63 | const UWorld* World, const FCollisionQueryParams& CollisionParams, const FVector& StartTrace, FSensedStimulus& SensedStimulus) const; 64 | 65 | }; -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorAngleTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Curves/CurveFloat.h" 7 | #include "Sensors/Tests/SensorLocationTestBase.h" 8 | 9 | #include "SensorAngleTest.generated.h" 10 | 11 | /** 12 | * AngleTest 13 | */ 14 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 15 | class SENSESYSTEM_API USensorAngleTest : public USensorLocationTestBase 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | USensorAngleTest(const FObjectInitializer& ObjectInitializer); 21 | 22 | #if WITH_EDITOR 23 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 24 | #endif 25 | 26 | #if WITH_EDITORONLY_DATA 27 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const override; 28 | virtual void DrawDebug(float Duration) const override; 29 | #endif 30 | 31 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 32 | float MaxAngle = 62; 33 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 34 | float MaxAngleLost = 120; 35 | 36 | #if WITH_EDITORONLY_DATA 37 | /** EDITORONLY */ 38 | UPROPERTY(EditAnywhere, Category = "SensorTest") 39 | float DrawDistance = 1000.f; 40 | #endif 41 | 42 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 43 | void SetAngleParam(float Max_Angle, float Max_AngleLost); 44 | 45 | UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "SensorTest") 46 | FRuntimeFloatCurve ScoreModifyCurve; 47 | 48 | float ModifyScoreByCurve(float Value) const; 49 | 50 | virtual EUpdateReady GetReadyToTest() override; 51 | virtual bool PreTest() override; 52 | 53 | protected: 54 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 55 | 56 | virtual void InitializeCacheTest() override; 57 | 58 | FVector TmpSelfForward; 59 | 60 | float MaxAngleCos; 61 | float MaxAngleLostCos; 62 | }; 63 | 64 | FORCEINLINE float USensorAngleTest::ModifyScoreByCurve(const float Value) const 65 | { 66 | return ScoreModifyCurve.EditorCurveData.Eval(FMath::Clamp(Value, 0.f, 1.f)); 67 | } -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorBoxTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorLocationTestBase.h" 7 | #include "Curves/CurveFloat.h" 8 | 9 | #include "SensorBoxTest.generated.h" 10 | 11 | 12 | /** 13 | * BoolTest 14 | */ 15 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 16 | class SENSESYSTEM_API USensorBoxTest : public USensorLocationTestBase 17 | { 18 | GENERATED_BODY() 19 | public: 20 | USensorBoxTest(); 21 | 22 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorTest") 23 | FVector BoxExtent; 24 | 25 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorTest") 26 | bool bOrientedBox = true; 27 | 28 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorTest", meta = (EditCondition = "bOrientedBox")) 29 | bool bIgnoreVerticalRotation = true; 30 | 31 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SensorTest") 32 | bool bScore = false; 33 | 34 | //UPROPERTY(EditAnywhere, BlueprintReadWrite, AdvancedDisplay, Category = "SensorTest") 35 | //FRuntimeFloatCurve ScoreModifyCurve; 36 | 37 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 38 | void SetBoxParam(FVector Extent, bool OrientedBox, bool IgnoreVerticalRotation, bool Score); 39 | 40 | virtual FBox GetSensorTestBoundBox() const override { return AABB_Box; } 41 | //virtual float GetSensorTestRadius() const override { return 0.f; } 42 | 43 | virtual EUpdateReady GetReadyToTest() override; 44 | virtual void InitializeCacheTest() override; 45 | virtual void InitializeFromSensor() override; 46 | virtual bool PreTest() override; 47 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 48 | 49 | #if WITH_EDITORONLY_DATA 50 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const override; 51 | virtual void DrawDebug(const float Duration) const override; 52 | #endif 53 | 54 | private: 55 | FBox AABB_Box; 56 | FVector::FReal MaxManhattanDistance = 0.f; 57 | void InitAABBBoxAndSensorTransform(); 58 | //float ModifyScoreByCurve(float Value) const; 59 | }; 60 | 61 | //FORCEINLINE float USensorBoxTest::ModifyScoreByCurve(const float Value) const 62 | //{ 63 | // return ScoreModifyCurve.EditorCurveData.Eval(FMath::Clamp(Value, 0.f, 1.f)); 64 | //} -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # SenseSystem 2 | 3 | SenseSystem is a flexible, extensible, and multi-functional system for detecting and responding to the detection of game objects in UnrealEngine. The ease of implementation and fine-tuning makes it suitable for projects of the most diverse genres from shooters, strategies, and to arcade ones. Allow the player or AI to interact with the entire game world. 4 | 5 | Its purpose is an alternative to existing systems in the engine such as AIPerception, PawnSensing, and EQS. SenseSystem has better performance, a wider range of tasks, more features, is simpler, and it's universal to use. 6 | 7 | MyOtherMarketplaceProducts 8 | 9 | Technical Details 10 | Features: 11 | 12 | SenseSystem has all the features of AIPerception, PawnSensing and some functions of EQS, a system that currently detects targets with increased performance. The physical engine is not used to detect overlapping detection zones. The number of targets for detection can be very large, thousands of simultaneously detected targets. There are all standard sensor sets, such as vision, touch, noise, distance. 13 | 14 | The system has a built-in reaction function for detecting both the detector and the target, which is implemented through delegates. 15 | 16 | The scope is not limited only to AI-perception, the system is also easily integrated into any system of interaction between an arbitrary object and the environment, interaction with objects without direct guidance, selecting objects with the cursor and interaction with groups of objects. 17 | 18 | The system is designed for customization in both c++ and blueprints 19 | 20 | (The example project build clearly demonstrate the features, and it is available at the link below.) 21 | 22 | Number of Blueprints: 0, but have blueprintable c++ classes 23 | 24 | Number of c++ Classes: >50 25 | 26 | Network Replicated: No 27 | 28 | Supported Development Platforms: Win64, Win32, Mac, Linux 29 | 30 | Supported Target Build Platforms: Win64, Win32, Mac, Linux, PS4, XboxOne 31 | 32 | - marketplace : https://www.unrealengine.com/marketplace/en-US/product/28a0c4ba00bf4eee8c17e52b7b2c8d52 33 | - LinkToVideo : https://www.youtube.com/watch?v=BRw0GmMvG_g 34 | - Doc and Example : https://drive.google.com/drive/folders/1YznJOVTO32tNC0-Zcv9XrGiVkhYsfNcv?usp=sharing 35 | - Project Discord : https://discord.gg/2m5DCzC 36 | 37 | TODO: 38 | - [ ] Documentation of DebugDraw in [Quick Start](./doc/en/quick-start.md) 39 | - [ ] Documentation of Profiling in [Quick Start](./doc/en/quick-start.md) 40 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Private/SenseSystemEditor.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseSystemEditor.h" 4 | #include "BitFlag64_Customization.h" 5 | #include "SenseSysComponentVisualizer.h" 6 | #include "SenseReceiverComponent.h" 7 | #include "SenseStimulusComponent.h" 8 | #include "SenseSysSettings.h" 9 | 10 | #include "Templates/SharedPointer.h" 11 | #include "ISettingsModule.h" 12 | #include "PropertyEditorModule.h" 13 | #include "PropertyEditorDelegates.h" 14 | #include "UnrealEdGlobals.h" 15 | #include "Editor/UnrealEdEngine.h" 16 | #include "EditorModes.h" 17 | 18 | 19 | #define LOCTEXT_NAMESPACE "FSenseSystemEditorModule" 20 | 21 | void FSenseSystemEditorModule::StartupModule() 22 | { 23 | FPropertyEditorModule& PropertyModule = FModuleManager::LoadModuleChecked("PropertyEditor"); 24 | PropertyModule.RegisterCustomPropertyTypeLayout( 25 | "BitFlag64_SenseSys", 26 | FOnGetPropertyTypeCustomizationInstance::CreateStatic(&FBitFlag64_Customization::MakeInstance)); 27 | 28 | RegisterComponentVisualizer(USenseReceiverComponent::StaticClass()->GetFName(), MakeShareable(new FSenseSysComponentVisualizer())); 29 | RegisterComponentVisualizer(USenseStimulusComponent::StaticClass()->GetFName(), MakeShareable(new FSenseSysComponentVisualizer())); 30 | 31 | if (ISettingsModule* SettingsModule = FModuleManager::GetModulePtr("Settings")) 32 | { 33 | SettingsModule->RegisterSettings( 34 | "Project", 35 | "Plugins", 36 | "SenseSystem", 37 | LOCTEXT("RuntimeSettingsName", "SenseSystem"), 38 | LOCTEXT("RuntimeSettingsDescription", "Configure the SenseSystem plugin"), 39 | GetMutableDefault()); 40 | } 41 | } 42 | 43 | void FSenseSystemEditorModule::ShutdownModule() 44 | { 45 | if (GUnrealEd) 46 | { 47 | for (const FName ClassName : RegisteredComponentClassNames) 48 | { 49 | GUnrealEd->UnregisterComponentVisualizer(ClassName); 50 | } 51 | } 52 | } 53 | 54 | void FSenseSystemEditorModule::RegisterComponentVisualizer(const FName ComponentClassName, TSharedPtr Visualizer) 55 | { 56 | if (GUnrealEd) 57 | { 58 | GUnrealEd->RegisterComponentVisualizer(ComponentClassName, Visualizer); 59 | } 60 | 61 | RegisteredComponentClassNames.Add(ComponentClassName); 62 | 63 | if (Visualizer.IsValid()) 64 | { 65 | Visualizer->OnRegister(); 66 | } 67 | } 68 | 69 | #undef LOCTEXT_NAMESPACE 70 | 71 | IMPLEMENT_MODULE(FSenseSystemEditorModule, SenseSystemEditor); 72 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorDistanceTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorLocationTestBase.h" 7 | #include "Curves/CurveFloat.h" 8 | 9 | #include "SensorDistanceTest.generated.h" 10 | 11 | /** 12 | * DistanceTest 13 | */ 14 | UCLASS(BlueprintType, EditInlineNew) 15 | class SENSESYSTEM_API USensorDistanceTest : public USensorLocationTestBase 16 | { 17 | GENERATED_BODY() 18 | 19 | public: 20 | USensorDistanceTest(const FObjectInitializer& ObjectInitializer); 21 | 22 | #if WITH_EDITOR 23 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 24 | #endif 25 | 26 | #if WITH_EDITORONLY_DATA 27 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const override; 28 | virtual void DrawDebug(float Duration) const override; 29 | #endif 30 | 31 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 32 | float MaxDistance = 10000; 33 | 34 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 35 | float MaxDistanceLost = 15000; 36 | 37 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 38 | void SetDistanceParam(const float InMaxDistance, const float InMaxDistanceLost); 39 | 40 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorTest") 41 | FRuntimeFloatCurve ScoreModifyCurve; 42 | 43 | float ModifyScoreByCurve(float Value) const; 44 | 45 | virtual EUpdateReady GetReadyToTest() override; 46 | virtual bool PreTest() override; 47 | 48 | virtual FBox GetSensorTestBoundBox() const override { return AABB_Box; } 49 | virtual float GetSensorTestRadius() const override { return AABB_Box.GetExtent().X; } 50 | 51 | protected: 52 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 53 | 54 | virtual void InitializeCacheTest() override; 55 | 56 | FBox AABB_Box = FBox(FVector::ZeroVector, FVector::ZeroVector); 57 | FVector::FReal MaxDistanceSquared; 58 | FVector::FReal MaxDistanceLostSquared; 59 | }; 60 | 61 | FORCEINLINE float USensorDistanceTest::ModifyScoreByCurve(const float Value) const 62 | { 63 | return ScoreModifyCurve.EditorCurveData.Eval(FMath::Clamp(Value, 0.f, 1.f)); 64 | } 65 | 66 | FORCEINLINE void USensorDistanceTest::SetDistanceParam(const float InMaxDistance, const float InMaxDistanceLost) 67 | { 68 | MaxDistance = InMaxDistance; 69 | MaxDistanceLost = InMaxDistanceLost; 70 | InitializeCacheTest(); 71 | } 72 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Private/BitFlag64_Customization.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Templates/SharedPointer.h" 7 | #include "Widgets/DeclarativeSyntaxSupport.h" 8 | #include "Widgets/SCompoundWidget.h" 9 | #include "Types/SlateEnums.h" 10 | #include "IPropertyTypeCustomization.h" 11 | #include "SenseSysHelpers.h" 12 | 13 | /* 14 | #include "Misc/Optional.h" 15 | #include "Misc/Attribute.h" 16 | #include "Fonts/SlateFontInfo.h" 17 | */ 18 | 19 | class SENSESYSTEMEDITOR_API SBitMask64Widget : public SCompoundWidget 20 | { 21 | public: 22 | DECLARE_DELEGATE_OneParam(FOnValueChanged, uint64); 23 | DECLARE_DELEGATE_TwoParams(FOnValueCommitted, uint64, ETextCommit::Type); 24 | 25 | SLATE_BEGIN_ARGS(SBitMask64Widget) {} 26 | SLATE_ARGUMENT(uint64, InBitMask) 27 | //SLATE_ATTRIBUTE(FSlateFontInfo, Font) 28 | SLATE_EVENT(FOnValueChanged, OnValueChanged) 29 | SLATE_EVENT(FOnValueCommitted, OnValueCommitted) 30 | SLATE_END_ARGS() 31 | 32 | SBitMask64Widget() {} 33 | ~SBitMask64Widget() {} 34 | 35 | void Construct(const FArguments& InArgs); 36 | 37 | uint64 BitMask = 0; 38 | FOnValueChanged ValueChanged; 39 | FOnValueCommitted ValueCommitted; 40 | 41 | uint64 OnGetValue() const { return BitMask; } 42 | void OnValueCommitted(uint64 NewValue, ETextCommit::Type CommitInfo); 43 | void OnValueChanged(uint64 NewValue); 44 | 45 | private: 46 | TSharedPtr PrimaryWidget; 47 | 48 | struct FBitmaskFlagInfo 49 | { 50 | uint64 Value; 51 | FText DisplayName; 52 | FText ToolTipText; 53 | }; 54 | }; 55 | 56 | class IPropertyHandle; 57 | 58 | /** 59 | * 60 | */ 61 | class SENSESYSTEMEDITOR_API FBitFlag64_Customization : public IPropertyTypeCustomization //, public FEditorUndoClient 62 | { 63 | public: 64 | FBitFlag64_Customization(); 65 | 66 | static TSharedRef MakeInstance() { return MakeShareable(new FBitFlag64_Customization()); } 67 | 68 | virtual void CustomizeHeader( 69 | TSharedRef StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 70 | virtual void CustomizeChildren( 71 | TSharedRef StructPropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) override; 72 | 73 | void OnValueChanged(uint64 Val); 74 | void OnValueCommitted(uint64 Val, ETextCommit::Type CommitInfo); 75 | 76 | FBitFlag64_SenseSys GT; 77 | FBitFlag64_SenseSys FromProperty() const; 78 | 79 | private: 80 | TSharedPtr PropertyHandle; 81 | }; 82 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/SensorHearing.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/SensorHearing.h" 4 | #include "Sensors/Tests/SensorDistanceTest.h" 5 | #include "Sensors/Tests/SensorTraceTest.h" 6 | 7 | 8 | USensorHearing::USensorHearing(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 9 | { 10 | DistanceCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 11 | DistanceCurve.GetRichCurve()->AddKey(1.f, 0.f, false); 12 | SensorTests.SetNum(1); 13 | SensorTests[0] = CreateDefaultSubobject(TEXT("DistanceTest")); 14 | SetValuesToSensorTests(); 15 | } 16 | 17 | USensorHearing::~USensorHearing() 18 | { 19 | } 20 | 21 | #if WITH_EDITOR 22 | void USensorHearing::PostEditChangeProperty(struct FPropertyChangedEvent& e) 23 | { 24 | Super::PostEditChangeProperty(e); 25 | SetValuesToSensorTests(); 26 | } 27 | #endif 28 | 29 | void USensorHearing::SetDistanceParam(const float InMaxHearingDistance, const float InMinScore) 30 | { 31 | MinScore = InMinScore; 32 | MaxHearingDistance = InMaxHearingDistance; 33 | FScopeLock Lock_CriticalSection(&SensorCriticalSection); 34 | SetValuesToSensorTests(); 35 | } 36 | 37 | void USensorHearing::SetValuesToSensorTests() 38 | { 39 | if (SensorTests.IsValidIndex(0)) 40 | { 41 | if (const auto D = Cast(SensorTests[0])) 42 | { 43 | D->ScoreModifyCurve.EditorCurveData = *DistanceCurve.GetRichCurveConst(); 44 | D->ScoreModifyCurve.ExternalCurve = nullptr; 45 | 46 | D->SetDistanceParam(MaxHearingDistance, MaxHearingDistance); 47 | D->bTestBySingleLocation = true; 48 | D->MinScore = MinScore; 49 | return; 50 | } 51 | } 52 | 53 | UE_LOG(LogSenseSys, Error, TEXT("DistanceTEst invalid cast from - SensorTests[0] ")); 54 | } 55 | 56 | /* 57 | void USensorHearing::Serialize(FArchive& Ar) 58 | { 59 | Super::Serialize(Ar); 60 | if (SensorTests.Num() > 0) 61 | { 62 | // This API returns the "correct" subobject by name, instanced or non-instanced. 63 | const auto Dso = Cast(GetDefaultSubobjectByName(TEXT("DistanceTest"))); 64 | if (SensorTests[0]) 65 | { 66 | if (SensorTests[0] != Dso) // ...if they don't match, the saved state needs to be fixed up. 67 | { 68 | // We have to dump this guy so he doesn't get serialized out with us anymore. 69 | SensorTests[0]->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_ForceNoResetLoaders); 70 | // Redirect to the "correct" subobject instance. 71 | SensorTests[0] = Dso; 72 | } 73 | } 74 | else 75 | { 76 | SensorTests[0] = Dso; 77 | } 78 | } 79 | } 80 | */ 81 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorTestBlueprintBase.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorTestBlueprintBase.h" 4 | #include "Sensors/SensorBase.h" 5 | #include "SenseStimulusBase.h" 6 | #include "UObject/GarbageCollection.h" 7 | 8 | 9 | #if WITH_EDITOR 10 | void USensorTestBlueprintBase::PostEditChangeProperty(struct FPropertyChangedEvent& e) 11 | { 12 | Super::PostEditChangeProperty(e); 13 | } 14 | #endif 15 | 16 | /********************************/ 17 | 18 | FTransform USensorTestBlueprintBase::GetSensorTransformBP() const 19 | { 20 | return GetSensorTransform(); 21 | } 22 | 23 | ESenseTestResult USensorTestBlueprintBase::RunTest(FSensedStimulus& SensedStimulus) const 24 | { 25 | if (SensedStimulus.TmpHash != MAX_uint32 && SensedStimulus.StimulusComponent.IsValid() && IsValid(GetSensorOwner())) 26 | { 27 | FGCScopeGuard GCScope; 28 | return RunTestBP(SensedStimulus); 29 | } 30 | return ESenseTestResult::Lost; 31 | } 32 | 33 | ESenseTestResult USensorTestBlueprintBase::RunTestBP_Implementation(FSensedStimulus& SensedStimulus) const 34 | { 35 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTestBlueprintBase_RunTestBPImplementation); 36 | 37 | return USensorLocationTestBase::RunTest(SensedStimulus); 38 | } 39 | 40 | /********************************/ 41 | 42 | ESenseTestResult USensorTestBlueprintBase::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 43 | { 44 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTestBlueprintBase_RunTestForLocation); 45 | 46 | if (SensedStimulus.TmpHash != MAX_uint32 && SensedStimulus.StimulusComponent.IsValid() && IsValid(GetSensorOwner())) 47 | { 48 | FGCScopeGuard GCScope; 49 | return RunLocationTestBP(SensedStimulus, TestLocation, ScoreResult); 50 | } 51 | return ESenseTestResult::Lost; 52 | } 53 | 54 | EUpdateReady USensorTestBlueprintBase::GetReadyToTest() 55 | { 56 | USensorTestBase::GetReadyToTest(); 57 | if (IsValid(GetSensorOwner())) 58 | { 59 | return GetReadyToTestBP(); 60 | } 61 | return EUpdateReady::Fail; 62 | } 63 | 64 | bool USensorTestBlueprintBase::PreTest() 65 | { 66 | if (IsValid(GetSensorOwner())) 67 | { 68 | Super::PreTest(); 69 | return PreTest_BP(); 70 | } 71 | return false; 72 | } 73 | 74 | FBox USensorTestBlueprintBase::GetSensorTestBoundBox() const 75 | { 76 | if (IsValid(GetSensorOwner())) 77 | { 78 | return GetSensorTestBoundBoxBP(); 79 | } 80 | return Super::GetSensorTestBoundBox(); 81 | } 82 | 83 | float USensorTestBlueprintBase::GetSensorTestRadius() const 84 | { 85 | if (IsValid(GetSensorOwner())) 86 | { 87 | return GetSensorTestRadiusBP(); 88 | } 89 | return Super::GetSensorTestRadius(); 90 | } 91 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/ActiveSensor.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SenseSystem.h" 7 | #include "Sensors/SensorBase.h" 8 | 9 | #include "ActiveSensor.generated.h" 10 | 11 | class AActor; 12 | 13 | //delegates for manual sensor update 14 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSenseTestActor, class AActor*, Actor); 15 | DECLARE_DYNAMIC_DELEGATE_OneParam(FOnSensorAsyncComplete, class UActiveSensor*, ActiveSensor); 16 | 17 | /** 18 | * Base Class for Active Sensor 19 | * Realtime Update Sensor 20 | */ 21 | UCLASS(BlueprintType, EditInlineNew) /*abstract, Blueprintable, HideDropdown,*/ 22 | class SENSESYSTEM_API UActiveSensor : public USensorBase 23 | { 24 | GENERATED_BODY() 25 | 26 | public: 27 | UActiveSensor(const FObjectInitializer& ObjectInitializer); 28 | virtual ~UActiveSensor() override; 29 | 30 | #if WITH_EDITOR 31 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 32 | #endif 33 | 34 | virtual void BeginDestroy() override; 35 | virtual void InitializeFromReceiver(USenseReceiverComponent* FromReceiver) override; 36 | 37 | virtual void TickSensor(const float DeltaTime) override { Super::TickSensor(DeltaTime); } 38 | 39 | virtual void OnSensorUpdated() override; 40 | virtual void OnAgeUpdated() override; 41 | 42 | /************************************/ 43 | 44 | /**Run Manual Sensor Detect*/ 45 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor", meta = (Keywords = "Run Async Manual Sensor")) 46 | void RunManualSensor(bool bOverride_SenseState = true); 47 | 48 | /**Async Manual Run Sensor Detect*/ 49 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor", meta = (Keywords = "Run Async Manual Sensor")) 50 | void AsyncManualSensor(UPARAM(DisplayName = "Event") FOnSensorAsyncComplete Delegate, bool bOverride_SenseState = true); 51 | 52 | /**Async Manual Run Sensor Detect*/ 53 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor", meta = (Keywords = "Run Async Manual Sensor")) 54 | void AsyncManualSensorDetect_BestActor(UPARAM(DisplayName = "Event") FOnSenseTestActor Delegate, bool bOverride_SenseState = true); 55 | 56 | /************************************/ 57 | 58 | virtual bool IsOverrideSenseState() const override 59 | { 60 | if (SensorType == ESensorType::Manual && bRequestManualUpdate) 61 | { 62 | return bOverrideSenseState; 63 | } 64 | return Super::IsOverrideSenseState(); 65 | } 66 | 67 | protected: 68 | /************************************/ 69 | 70 | TArray CallbackTargetsSingleResult; 71 | TArray Callback; 72 | bool bRequestManualUpdate = false; 73 | bool bOverrideSenseState = true; 74 | }; 75 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | --- 2 | Language : Cpp 3 | Standard : Latest 4 | BasedOnStyle : LLVM 5 | AccessModifierOffset : -4 6 | 7 | IndentWidth : 4 8 | TabWidth : 4 9 | UseTab : Always 10 | ColumnLimit : 160 11 | PointerAlignment : Left 12 | 13 | AlignAfterOpenBracket : AlwaysBreak 14 | AllowAllArgumentsOnNextLine : false 15 | AlignTrailingComments : true 16 | AlignOperands : false 17 | 18 | AlwaysBreakAfterDefinitionReturnType: None 19 | AlwaysBreakAfterReturnType: None 20 | AlwaysBreakTemplateDeclarations : Yes 21 | PenaltyReturnTypeOnItsOwnLine: 1000000 22 | 23 | BreakBeforeBraces : Custom #Allman 24 | BreakBeforeTernaryOperators : true 25 | BreakConstructorInitializers : BeforeComma 26 | BreakInheritanceList : BeforeComma 27 | 28 | AllowShortEnumsOnASingleLine : false 29 | 30 | AllowAllConstructorInitializersOnNextLine : false 31 | AllowAllParametersOfDeclarationOnNextLine : false 32 | 33 | 34 | AllowShortCaseLabelsOnASingleLine : true 35 | AllowShortIfStatementsOnASingleLine : true 36 | AllowShortFunctionsOnASingleLine : InlineOnly 37 | AllowShortLambdasOnASingleLine : All 38 | 39 | 40 | BinPackArguments : false 41 | BinPackParameters : false 42 | 43 | BraceWrapping : 44 | AfterCaseLabel : true 45 | AfterClass : true 46 | AfterControlStatement : Always 47 | AfterEnum : true 48 | 49 | AfterFunction : true 50 | AfterNamespace : true 51 | AfterStruct : true 52 | AfterUnion : true 53 | AfterExternBlock : true 54 | 55 | BeforeCatch : true 56 | BeforeElse : true 57 | BeforeLambdaBody : true 58 | 59 | IndentBraces : false 60 | 61 | SplitEmptyFunction : false 62 | SplitEmptyRecord : false 63 | SplitEmptyNamespace : false 64 | 65 | 66 | ConstructorInitializerAllOnOneLineOrOnePerLine : true 67 | 68 | Cpp11BracedListStyle : true 69 | FixNamespaceComments : true 70 | 71 | 72 | SortIncludes : false 73 | SortUsingDeclarations : false 74 | IncludeCategories : 75 | - Regex : '^<.*' 76 | Priority : 1 77 | - Regex : '^".*' 78 | Priority : 2 79 | - Regex : '.*' 80 | Priority : 3 81 | IncludeIsMainRegex : '([-_](test|unittest))?$' 82 | 83 | IndentCaseLabels : true 84 | 85 | IndentWrappedFunctionNames : false 86 | IndentPPDirectives : BeforeHash 87 | 88 | MaxEmptyLinesToKeep : 2 89 | NamespaceIndentation : All 90 | ReflowComments : false 91 | 92 | SpaceAfterLogicalNot : false 93 | SpaceAfterTemplateKeyword : false 94 | 95 | SpacesBeforeTrailingComments : 1 96 | #SpaceBeforeCaseColon : false 97 | SpaceBeforeParens : ControlStatements 98 | 99 | SpaceInEmptyBlock : false 100 | SpacesInCStyleCastParentheses : false 101 | SpaceAfterCStyleCast : false 102 | SpacesInSquareBrackets : false 103 | 104 | #AlignConsecutiveAssignments : Consecutive 105 | #AlignConsecutiveBitFields : Consecutive 106 | #AlignConsecutiveDeclarations : None 107 | #AlignConsecutiveMacros : None 108 | #EmptyLineAfterAccessModifier : Always 109 | #AlignEscapedNewlines : DontAlign 110 | ... 111 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/BaseSensorTask.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "BaseSensorTask.h" 4 | #include "SenseManager.h" 5 | #include "Sensors/SensorBase.h" 6 | #include "HAL/Platform.h" 7 | #include "UObject/UObjectGlobals.h" 8 | 9 | 10 | FSenseRunnable::FSenseRunnable(const double InWaitTime, const int32 InCounter) : WaitTime(InWaitTime), CounterLimit(InCounter) 11 | { 12 | m_Kill = false; 13 | Thread = FRunnableThread::Create( // 14 | this, // 15 | TEXT("FSenseRunnable"), // 16 | 0, // 17 | EThreadPriority::TPri_Lowest, // 18 | FPlatformAffinity::GetNoAffinityMask()); // 19 | 20 | WorkEvent = FPlatformProcess::GetSynchEventFromPool(); 21 | } 22 | 23 | FSenseRunnable::~FSenseRunnable() 24 | { 25 | FPlatformProcess::ReturnSynchEventToPool(WorkEvent); 26 | if (Thread) 27 | { 28 | //Cleanup the worker thread 29 | delete Thread; 30 | Thread = nullptr; 31 | } 32 | } 33 | 34 | uint32 FSenseRunnable::Run() 35 | { 36 | while (!m_Kill) 37 | { 38 | if (m_Kill) 39 | { 40 | return 0; 41 | } 42 | if (!m_Kill) 43 | { 44 | WorkEvent->Wait(1); 45 | if (!UpdateQueue() || CounterLimit <= Counter) 46 | { 47 | Counter = 0; 48 | } 49 | } 50 | else 51 | { 52 | Thread->SetThreadPriority(EThreadPriority::TPri_Lowest); 53 | } 54 | } 55 | return 0; 56 | } 57 | 58 | bool FSenseRunnable::UpdateQueue() 59 | { 60 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_FSenseRunnableTick); 61 | 62 | if (m_Kill) 63 | { 64 | return false; 65 | } 66 | 67 | FSensorQueue& Queue = HighSensorQueue.IsEmpty() ? SensorQueue : HighSensorQueue; 68 | 69 | if (Queue.IsEmpty()) 70 | { 71 | Thread->SetThreadPriority(EThreadPriority::TPri_Lowest); 72 | } 73 | if (Thread->GetThreadPriority() == EThreadPriority::TPri_Lowest /*IsThreadPaused()*/) 74 | { 75 | return true; 76 | } 77 | 78 | bool bPop = true; 79 | USensorBase* const Sensor = Queue.Peek(); 80 | if (LIKELY(IsValid(Sensor) && Sensor->IsValidForTest_Short())) 81 | { 82 | if (LIKELY(Sensor->UpdateState.Get() == ESensorState::ReadyToUpdate)) 83 | { 84 | bPop = Sensor->UpdateSensor(); 85 | } 86 | else 87 | { 88 | Sensor->UpdateState = ESensorState::NotUpdate; //skip 89 | bPop = true; 90 | } 91 | } 92 | 93 | if (bPop) 94 | { 95 | Queue.Pop(); 96 | Counter++; 97 | if (SensorQueue.IsEmpty() && HighSensorQueue.IsEmpty()) 98 | { 99 | Thread->SetThreadPriority(EThreadPriority::TPri_Lowest); 100 | } 101 | } 102 | return bPop; 103 | } 104 | 105 | bool FSenseRunnable::AddQueueSensors(USensorBase* Sensor, const bool bHighPriority) 106 | { 107 | if (Sensor) 108 | { 109 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_AddQueueSensors); 110 | if (bHighPriority) 111 | { 112 | HighSensorQueue.Enqueue(Sensor); 113 | } 114 | else 115 | { 116 | SensorQueue.Enqueue(Sensor); 117 | } 118 | Thread->SetThreadPriority(EThreadPriority::TPri_Normal); 119 | return true; 120 | } 121 | return false; 122 | } 123 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/SensorSight.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Curves/CurveFloat.h" 7 | #include "Sensors/ActiveSensor.h" 8 | #include "Sensors/Tests/SensorTraceTest.h" 9 | 10 | #include "SensorSight.generated.h" 11 | 12 | 13 | /** 14 | * Sensor Sight 15 | */ 16 | UCLASS(Blueprintable, BlueprintType, EditInlineNew, HideCategories = (SensorTests)) 17 | class SENSESYSTEM_API USensorSight : public UActiveSensor 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | USensorSight(const FObjectInitializer& ObjectInitializer); 23 | virtual ~USensorSight() override; 24 | 25 | #if WITH_EDITOR 26 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 27 | #endif 28 | 29 | /**Skip test if input score < MinScore*/ 30 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight") 31 | double MinScore = 0; 32 | 33 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight", meta = (ClampMin = "0.0", UIMin = "0.0")) 34 | double MinDistance = 0; 35 | 36 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight", meta = (ClampMin = "0.0", UIMin = "0.0")) 37 | double MaxDistance = 30000; 38 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight", meta = (ClampMin = "0.0", UIMin = "0.0")) 39 | double MaxDistanceLost = 200000;/*TNumericLimits::Max()*/ 40 | 41 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 42 | double MaxAngle = 60; 43 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 44 | double MaxAngleLost = 360; 45 | 46 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight") 47 | ETraceTestParam TraceTestParam = ETraceTestParam::BoolTraceTest; 48 | 49 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight") 50 | TEnumAsByte CollisionChannel = ECollisionChannel::ECC_Visibility; 51 | 52 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorSight") 53 | bool bTraceComplex = false; 54 | 55 | 56 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorSight") 57 | FRuntimeFloatCurve DistanceCurve; 58 | 59 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorSight") 60 | FRuntimeFloatCurve AngleCurve; 61 | 62 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorSight") 63 | bool bDistanceAngleBySingleLocation = true; 64 | 65 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorSight") 66 | bool bTraceBySingleLocation = false; 67 | 68 | 69 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor|SensorSight") 70 | void SetTraceParam(ETraceTestParam InTraceParam, ECollisionChannel InCollision, bool TraceComplex, bool InTestBySingleLocation, float InMinScore); 71 | 72 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor|SensorSight") 73 | void SetDistanceAngleParam( 74 | float InMaxDistance, 75 | float InMaxDistanceLost, 76 | float InMaxAngle, 77 | float InMaxAngleLost, 78 | bool InTestBySingleLocation, 79 | float InMinScore, 80 | float InMinDistance = 0.f); 81 | 82 | //virtual void Serialize(FArchive& Ar) override; 83 | 84 | protected: 85 | void SetValuesToSensorTests(); 86 | }; 87 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorDistanceAndAngleTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorLocationTestBase.h" 7 | #include "Curves/CurveFloat.h" 8 | #include "Containers/StaticArray.h" 9 | 10 | #include "SensorDistanceAndAngleTest.generated.h" 11 | 12 | 13 | /** 14 | * DistanceAndAngleTest 15 | */ 16 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 17 | class SENSESYSTEM_API USensorDistanceAndAngleTest : public USensorLocationTestBase 18 | { 19 | GENERATED_BODY() 20 | 21 | public: 22 | USensorDistanceAndAngleTest(const FObjectInitializer& ObjectInitializer); 23 | 24 | #if WITH_EDITOR 25 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 26 | #endif 27 | 28 | #if WITH_EDITORONLY_DATA 29 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const override; 30 | virtual void DrawDebug(float Duration) const override; 31 | #endif 32 | 33 | 34 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", UIMin = "0.0")) 35 | float MinDistance = 0.f; 36 | 37 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", UIMin = "0.0")) 38 | float MaxDistance = 1000; 39 | 40 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", UIMin = "0.0")) 41 | float MaxDistanceLost = 1500; 42 | 43 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 44 | float MaxAngle = 62.f; 45 | 46 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.0", ClampMax = "180.0", UIMin = "0.0", UIMax = "180.0")) 47 | float MaxAngleLost = 120.f; 48 | 49 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 50 | void SetDistanceAndAngleParam(float Max_Angle, float Max_AngleLost, float Max_Distance, float Max_DistanceLost, float Min_Distance = 0.f); 51 | 52 | 53 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorTest") 54 | FRuntimeFloatCurve DistanceCurve; 55 | 56 | UPROPERTY(EditAnywhere, BlueprintReadOnly, AdvancedDisplay, Category = "SensorTest") 57 | FRuntimeFloatCurve AngleCurve; 58 | 59 | float ModifyDistanceScore(float Value) const; 60 | float ModifyAngleScore(float Value) const; 61 | 62 | virtual bool PreTest() override; 63 | 64 | virtual FBox GetSensorTestBoundBox() const override { return AABB_Box; } 65 | virtual float GetSensorTestRadius() const override { return MaxDistanceLost; } 66 | 67 | protected: 68 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 69 | 70 | virtual void InitializeCacheTest() override; 71 | 72 | FBox AABB_Box; 73 | FVector::FReal MinDistanceSquared; 74 | FVector::FReal MaxDistanceSquared; 75 | FVector::FReal MaxDistanceLostSquared; 76 | float MaxAngleCos; 77 | float MaxAngleLostCos; 78 | 79 | 80 | FVector TmpSelfForward; 81 | TStaticArray AABB_Helper; 82 | 83 | }; 84 | 85 | FORCEINLINE float USensorDistanceAndAngleTest::ModifyDistanceScore(const float Value) const 86 | { 87 | return DistanceCurve.EditorCurveData.Eval(FMath::Clamp(Value, 0.f, 1.f)); 88 | } 89 | FORCEINLINE float USensorDistanceAndAngleTest::ModifyAngleScore(const float Value) const 90 | { 91 | return AngleCurve.EditorCurveData.Eval(FMath::Clamp(Value, 0.f, 1.f)); 92 | } 93 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseStimulusComponent.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseStimulusComponent.h" 4 | #include "SenseManager.h" 5 | #include "Sensors/SensorBase.h" 6 | #include "SenseStimulusInterface.h" 7 | #include "SenseReceiverComponent.h" 8 | #include "HashSorted.h" 9 | #include "Algo/MinElement.h" 10 | 11 | #include "GameFramework/Actor.h" 12 | 13 | USenseStimulusComponent::USenseStimulusComponent(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 14 | { 15 | bOwnerSenseStimulusInterface = false; 16 | 17 | // manual forceinline InitOwnerInterface in constructor 18 | const AActor* Owner = GetOwner(); 19 | if (IsValid(Owner)) 20 | { 21 | bOwnerSenseStimulusInterface = // 22 | Cast(Owner) != nullptr // 23 | || Owner->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass()); 24 | 25 | //if (!bOwnerSenseStimulusInterface) 26 | //{ 27 | // InterfaceOwnerBitFlags = 0; 28 | //} 29 | } 30 | } 31 | 32 | USenseStimulusComponent::~USenseStimulusComponent() 33 | {} 34 | 35 | 36 | #if WITH_EDITORONLY_DATA 37 | void USenseStimulusComponent::PostEditChangeProperty(struct FPropertyChangedEvent& e) 38 | { 39 | Super::PostEditChangeProperty(e); 40 | } 41 | #endif 42 | 43 | 44 | void USenseStimulusComponent::OnRegister() 45 | { 46 | Super::OnRegister(); 47 | InitOwnerInterface(); 48 | } 49 | 50 | void USenseStimulusComponent::InitOwnerInterface() 51 | { 52 | const AActor* Owner = GetOwner(); 53 | if (IsValid(Owner)) 54 | { 55 | bOwnerSenseStimulusInterface = 56 | Cast(Owner) != nullptr || Owner->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass()); 57 | } 58 | 59 | if (bOwnerSenseStimulusInterface) 60 | { 61 | check(GetOwner() && GetOwner()->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass())); 62 | } 63 | } 64 | 65 | void USenseStimulusComponent::SetInterfaceFlag(ESSInterfaceFlag Flag, const bool bValue) 66 | { 67 | if (bValue) 68 | { 69 | InterfaceOwnerBitFlags |= static_cast(Flag); 70 | } 71 | else 72 | { 73 | InterfaceOwnerBitFlags &= ~static_cast(Flag); 74 | } 75 | } 76 | 77 | FVector USenseStimulusComponent::GetSingleSensePoint(const FName SensorTag) const 78 | { 79 | 80 | #if WITH_EDITOR 81 | check(IsInGameThread()); 82 | #endif 83 | 84 | if (bOwnerSenseStimulusInterface && (InterfaceOwnerBitFlags & static_cast(ESSInterfaceFlag::ESS_SinglePointInterface))) 85 | { 86 | 87 | #if WITH_EDITOR 88 | check(GetOwner() && GetOwner()->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass())); 89 | #endif 90 | 91 | return ISenseStimulusInterface::Execute_IGetSingleSensePoint(GetOwner(), SensorTag); 92 | } 93 | return USenseStimulusBase::GetSingleSensePoint(SensorTag); 94 | } 95 | 96 | TArray USenseStimulusComponent::GetSensePoints(const FName SensorTag) const 97 | { 98 | 99 | #if WITH_EDITOR 100 | check(IsInGameThread()); 101 | #endif 102 | 103 | if (bOwnerSenseStimulusInterface && (InterfaceOwnerBitFlags & static_cast(ESSInterfaceFlag::ESS_SensePointsInterface))) 104 | { 105 | #if WITH_EDITOR 106 | check(GetOwner() && GetOwner()->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass())); 107 | #endif 108 | return ISenseStimulusInterface::Execute_IGetSensePoints(GetOwner(), SensorTag); 109 | } 110 | return USenseStimulusBase::GetSensePoints(SensorTag); 111 | } 112 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/ActiveSensor.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/ActiveSensor.h" 4 | #include "SenseManager.h" 5 | #include "SenseStimulusBase.h" 6 | #include "GameFramework/Actor.h" 7 | 8 | 9 | UActiveSensor::UActiveSensor(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 10 | { 11 | } 12 | 13 | UActiveSensor::~UActiveSensor() 14 | { 15 | } 16 | 17 | void UActiveSensor::BeginDestroy() 18 | { 19 | Super::BeginDestroy(); 20 | CallbackTargetsSingleResult.Empty(); 21 | Callback.Empty(); 22 | } 23 | 24 | #if WITH_EDITOR 25 | void UActiveSensor::PostEditChangeProperty(struct FPropertyChangedEvent& e) 26 | { 27 | Super::PostEditChangeProperty(e); 28 | } 29 | #endif 30 | 31 | void UActiveSensor::InitializeFromReceiver(USenseReceiverComponent* FromReceiver) 32 | { 33 | Super::InitializeFromReceiver(FromReceiver); 34 | bOverrideSenseState = SensorType == ESensorType::Active; 35 | } 36 | 37 | void UActiveSensor::OnSensorUpdated() 38 | { 39 | 40 | Super::OnSensorUpdated(); 41 | 42 | if (SensorType == ESensorType::Manual) 43 | { 44 | bOverrideSenseState = IsOverrideSenseState(); 45 | } 46 | 47 | if (Callback.Num() > 0) 48 | { 49 | for (int32 i = 0; i < Callback.Num(); i++) 50 | { 51 | Callback[i].ExecuteIfBound(this); 52 | } 53 | Callback.Empty(); 54 | bRequestManualUpdate = false; 55 | } 56 | 57 | if (CallbackTargetsSingleResult.Num() > 0) 58 | { 59 | for (FChannelSetup& CS : ChannelSetup) //todo ActiveSensor CallbackTargetsSingleResult by channel 60 | { 61 | const FSensedStimulus* Tmp = CS.GetBestSenseStimulus(); 62 | if (Tmp && Tmp->StimulusComponent.IsValid()) 63 | { 64 | const auto Actor = Tmp->StimulusComponent.Get()->GetOwner(); 65 | if (Actor) 66 | { 67 | for (int32 i = 0; i < CallbackTargetsSingleResult.Num(); i++) 68 | { 69 | CallbackTargetsSingleResult[i].ExecuteIfBound(Actor); 70 | } 71 | } 72 | } 73 | } 74 | CallbackTargetsSingleResult.Empty(); 75 | bRequestManualUpdate = false; 76 | } 77 | //if (bRequestManualUpdate) 78 | //{ 79 | // bOverrideSenseState = false; 80 | //} 81 | } 82 | 83 | void UActiveSensor::OnAgeUpdated() 84 | { 85 | bRequestManualUpdate = false; 86 | Super::OnAgeUpdated(); 87 | //if (bRequestManualUpdate) 88 | //{ 89 | // bOverrideSenseState = false; 90 | //} 91 | } 92 | 93 | 94 | void UActiveSensor::RunManualSensor(const bool bOverride_SenseState /*= true*/) 95 | { 96 | if (bEnable && IsInitialized() && SensorType == ESensorType::Manual && UpdateState == ESensorState::NotUpdate) 97 | { 98 | bRequestManualUpdate = true; 99 | bOverrideSenseState = bOverride_SenseState; 100 | TrySensorUpdate(); 101 | } 102 | } 103 | 104 | void UActiveSensor::AsyncManualSensor(UPARAM(DisplayName = "Event") FOnSensorAsyncComplete Delegate, const bool bOverride_SenseState) 105 | { 106 | if (bEnable && IsInitialized()) 107 | { 108 | bRequestManualUpdate = true; 109 | bOverrideSenseState = bOverride_SenseState; 110 | 111 | Callback.Add(Delegate); 112 | if (UpdateState == ESensorState::NotUpdate) 113 | { 114 | TrySensorUpdate(); 115 | } 116 | } 117 | } 118 | 119 | void UActiveSensor::AsyncManualSensorDetect_BestActor(UPARAM(DisplayName = "Event") FOnSenseTestActor Delegate, const bool bOverride_SenseState) 120 | { 121 | if (bEnable && IsInitialized()) 122 | { 123 | bRequestManualUpdate = true; 124 | bOverrideSenseState = bOverride_SenseState; 125 | CallbackTargetsSingleResult.Add(Delegate); 126 | if (UpdateState == ESensorState::NotUpdate) 127 | { 128 | TrySensorUpdate(); 129 | } 130 | } 131 | } 132 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/SensorTouch.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/PassiveSensor.h" 7 | #include "Components/PrimitiveComponent.h" 8 | 9 | #include "SensorTouch.generated.h" 10 | 11 | /** 12 | * SensorTouch 13 | */ 14 | UCLASS(Blueprintable, BlueprintType, EditInlineNew, HideCategories = (SensorTests)) 15 | class SENSESYSTEM_API USensorTouch : public UPassiveSensor 16 | { 17 | GENERATED_BODY() 18 | public: 19 | USensorTouch(const FObjectInitializer& ObjectInitializer); 20 | 21 | virtual ~USensorTouch() override; 22 | 23 | virtual void BeginDestroy() override; 24 | virtual void Cleanup() override; 25 | 26 | #if WITH_EDITOR 27 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 28 | #endif 29 | 30 | #if WITH_EDITORONLY_DATA 31 | virtual void DrawSensor(const class FSceneView* View, class FPrimitiveDrawInterface* PDI) const override; 32 | virtual void DrawSensorHUD(const class FViewport* Viewport, const class FSceneView* View, class FCanvas* Canvas) const override; 33 | #endif 34 | 35 | /************************************/ 36 | 37 | UPROPERTY(BlueprintReadOnly, Category = "Sensor") 38 | TArray> TouchCollisions; 39 | 40 | /************************************/ 41 | 42 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTouch") 43 | bool bActorRootCollision = true; 44 | 45 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTouch") 46 | bool bOnComponentHit = true; 47 | 48 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTouch") 49 | bool bOnComponentBeginOverlap = true; 50 | 51 | /************************************/ 52 | 53 | 54 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor|SensorTouch") 55 | void SetTouchCollision(UPrimitiveComponent* Prim); 56 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Sensor|SensorTouch") 57 | void SetTouchCollisions(TArray InTouchCollisions); 58 | 59 | /************************************/ 60 | 61 | UFUNCTION() 62 | void AddTouchCollision(UPrimitiveComponent* InTouchCollision); 63 | UFUNCTION() 64 | void AddTouchCollisions(TArray InTouchCollisions); 65 | UFUNCTION() 66 | void RemoveTouchCollision(UPrimitiveComponent* InTouchCollision); 67 | UFUNCTION() 68 | void RemoveTouchCollisions(TArray InTouchCollisions); 69 | 70 | /************************************/ 71 | 72 | UFUNCTION() 73 | void OnTouchHit(UPrimitiveComponent* HitComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, FVector NormalImpulse, const FHitResult& Hit); 74 | UFUNCTION() 75 | void OnBeginOverlap( 76 | UPrimitiveComponent* OverlappedComponent, 77 | AActor* OtherActor, 78 | UPrimitiveComponent* OtherComp, 79 | int32 OtherBodyIndex, 80 | bool bFromSweep, 81 | const FHitResult& SweepResult); 82 | UFUNCTION() 83 | void OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex); 84 | 85 | /************************************/ 86 | 87 | void BindHitEvent(UPrimitiveComponent* InTouchCollision); 88 | void UnBindHitEvent(UPrimitiveComponent* InTouchCollision); 89 | 90 | void BindHitEvent(TArray InTouchCollisions); 91 | void UnBindHitEvent(TArray InTouchCollisions); 92 | 93 | /************************************/ 94 | 95 | virtual void InitializeFromReceiver(USenseReceiverComponent* InSenseReceiver) override; 96 | 97 | TSet NeedLost; 98 | void LostCurrentSensed(); 99 | void UpdateNeedLost(); 100 | }; 101 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseSystemBPLibrary.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Kismet/BlueprintFunctionLibrary.h" 7 | #include "SensedStimulStruct.h" 8 | #include "SenseSysHelpers.h" 9 | 10 | #include "SenseSystemBPLibrary.generated.h" 11 | 12 | 13 | class USenseStimulusBase; 14 | class USenseReceiverComponent; 15 | class USkinnedAsset; 16 | 17 | 18 | /** 19 | * StimulusTagResponse struct 20 | */ 21 | USTRUCT(BlueprintType) 22 | struct SENSESYSTEM_API FSkeletalBoneIDCache //96 byte 23 | { 24 | GENERATED_USTRUCT_BODY() 25 | 26 | FSkeletalBoneIDCache() {} 27 | FSkeletalBoneIDCache(const class USkeletalMeshComponent* SkeletalMeshComponent, const TArray& BoneNames); 28 | 29 | UPROPERTY(BlueprintReadOnly, Category = "SkeletalBoneIDCache") 30 | TObjectPtr SkelMesh = nullptr; 31 | 32 | UPROPERTY(BlueprintReadOnly, Category = "SkeletalBoneIDCache") 33 | TArray BoneIDs; 34 | 35 | bool Init(const class USkeletalMeshComponent* SkeletalMeshComponent, const TArray& BoneNames); 36 | bool GetBoneLocations(const class USkeletalMeshComponent* SkeletalMeshComponent, TArray& Out) const; 37 | }; 38 | 39 | 40 | /* 41 | * Function library class. 42 | */ 43 | UCLASS() 44 | class USenseSystemBPLibrary : public UBlueprintFunctionLibrary 45 | { 46 | GENERATED_UCLASS_BODY() 47 | public: 48 | static USenseStimulusBase* GetStimulusFromActor(const AActor* Actor, bool bInterfaceOnly = false); 49 | 50 | static USenseReceiverComponent* GetReceiverFromActor(const AActor* Actor); 51 | 52 | UFUNCTION(BlueprintCallable, Category = "SenseSystem", meta = (DisplayName = "GetStimulusFromActor", Keywords = "SenseSystem Get Stimulus From Actor")) 53 | static USenseStimulusBase* GetStimulusFromActor_BP(const AActor* Actor); 54 | UFUNCTION(BlueprintCallable, Category = "SenseSystem", meta = (DisplayName = "GetReceiverFromActor", Keywords = "SenseSystem Get Receiver From Actor")) 55 | static USenseReceiverComponent* GetReceiverFromActor_BP(const AActor* Actor); 56 | 57 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SkeletalBoneIDCache", meta = (Keywords = "SenseSystem Create Bone ID Position Location Cache")) 58 | static FSkeletalBoneIDCache CreateBoneIDCache(const class USkeletalMeshComponent* SkeletalMeshComponent, const TArray& BoneNames, bool& bSuccess) 59 | { 60 | FSkeletalBoneIDCache Out(SkeletalMeshComponent, BoneNames); 61 | bSuccess = Out.BoneIDs.Num() > 0; //Out.Init(SkeletalMeshComponent, BoneNames); 62 | return Out; 63 | } 64 | 65 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SkeletalBoneIDCache", meta = (Keywords = "SenseSystem Get Bone Locations Bone ID Cache")) 66 | static TArray GetBoneLocations_BoneIDCache( 67 | const class USkeletalMeshComponent* SkeletalMeshComponent, 68 | const FSkeletalBoneIDCache& SkeletalBoneIDCache, 69 | bool& bSuccess) 70 | { 71 | TArray Out; 72 | bSuccess = SkeletalBoneIDCache.GetBoneLocations(SkeletalMeshComponent, Out); 73 | return Out; 74 | } 75 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SkeletalBoneIDCache", meta = (Keywords = "SenseSystem Convert BitFlag 64 to Array")) 76 | static TArray BitFlag64_ToArray(const FBitFlag64_SenseSys& In) { return FBitFlag64_SenseSys::To_ArrayBitFlagChannel(In.Value); } 77 | 78 | #if WITH_EDITOR 79 | static class UBlueprintGeneratedClass* GetOwnerBlueprintClass(UObject* Obj); 80 | static class USenseReceiverComponent* GetSenseReceiverComponent_FromDefaultActorClass(class UClass* InClass, const FName& Name); 81 | static EOwnerBlueprintClassType GetOwnerBlueprintClassType(UClass* BaseClass); 82 | static UClass* GetOwnerBlueprintClassType(EOwnerBlueprintClassType In); 83 | #endif 84 | 85 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|Utils") 86 | static void ForceGarbageCollection(); 87 | }; 88 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorTestBase.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorTestBase.h" 4 | #include "Sensors/SensorBase.h" 5 | #include "SenseStimulusComponent.h" 6 | #include "SenseReceiverComponent.h" 7 | #include "GameFramework/Actor.h" 8 | #include "SenseObstacleInterface.h" 9 | #include "SenseStimulusInterface.h" 10 | #include "SenseSystemBPLibrary.h" 11 | 12 | 13 | USensorTestBase::USensorTestBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 14 | { 15 | PrivateSensorOwner = GetTypedOuter(); 16 | } 17 | 18 | USensorTestBase::~USensorTestBase() 19 | { 20 | /* */ 21 | } 22 | 23 | #if WITH_EDITOR 24 | void USensorTestBase::PostEditChangeProperty(struct FPropertyChangedEvent& e) 25 | { 26 | Super::PostEditChangeProperty(e); 27 | } 28 | #endif 29 | 30 | FName USensorTestBase::GetSensorTag() const 31 | { 32 | check(GetSensorOwner() != nullptr); 33 | return GetSensorOwner()->SensorTag; 34 | } 35 | 36 | void USensorTestBase::BeginDestroy() 37 | { 38 | Super::BeginDestroy(); 39 | } 40 | 41 | bool USensorTestBase::IsObstacleInterface(const AActor* Actor) 42 | { 43 | if (IsValid(Actor) && Actor->GetClass()->ImplementsInterface(USenseObstacleInterface::StaticClass()) && !Actor->IsUnreachable()) 44 | { 45 | return true; 46 | } 47 | return false; 48 | } 49 | 50 | bool USensorTestBase::IsStimulusInterface(const AActor* Actor) 51 | { 52 | if (IsValid(Actor) && Actor->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass()) && !Actor->IsUnreachable()) 53 | { 54 | return true; 55 | } 56 | return false; 57 | } 58 | 59 | void USensorTestBase::InitializeFromSensor() 60 | { 61 | check(GetOuter() == GetSensorOwner()); 62 | //Sensor->OnSensorChanged.AddUObject(this, &USensorTestBase::OnSensorChanged); 63 | SensorTransform = GetSensorOwner()->GetSensorTransform(); 64 | InitializeCacheTest(); 65 | 66 | if (IsValid(GetSensorOwner())) 67 | { 68 | InitializeFromSensorBP(GetSensorOwner()); 69 | } 70 | } 71 | 72 | bool USensorTestBase::PreTest() 73 | { 74 | checkSlow(GetSensorOwner() && IsValid(GetSensorOwner())); 75 | 76 | if (const USensorBase* Sensor = GetSensorOwner()) 77 | { 78 | SensorTransform = Sensor->GetSensorTransform(); 79 | } 80 | return true; 81 | } 82 | 83 | ESenseTestResult USensorTestBase::RunTest(FSensedStimulus& SensedStimulus) const 84 | { 85 | return ESenseTestResult::Lost; 86 | } 87 | 88 | class UWorld* USensorTestBase::GetWorld() const 89 | { 90 | const USensorBase* SensorOwner = GetSensorOwner(); 91 | if (IsValid(SensorOwner)) 92 | { 93 | return GetSensorOwner()->GetWorld(); 94 | } 95 | return nullptr; 96 | } 97 | 98 | const FTransform& USensorTestBase::GetReceiverTransform() const 99 | { 100 | const USenseReceiverComponent* Comp = GetSenseReceiverComponent(); 101 | if (IsValid(Comp)) 102 | { 103 | return Comp->GetComponentTransform(); 104 | } 105 | return FTransform::Identity; 106 | } 107 | 108 | USenseReceiverComponent* USensorTestBase::GetSenseReceiverComponent() const 109 | { 110 | check(GetSensorOwner() != nullptr); 111 | const USensorBase* SensorOwner = GetSensorOwner(); 112 | if (IsValid(SensorOwner)) 113 | { 114 | return GetSensorOwner()->GetSenseReceiverComponent(); 115 | } 116 | return nullptr; 117 | } 118 | 119 | AActor* USensorTestBase::GetReceiverActor() const 120 | { 121 | check(GetSensorOwner() != nullptr); 122 | const USenseReceiverComponent* ReceiverComp = GetSenseReceiverComponent(); 123 | if (IsValid(ReceiverComp)) 124 | { 125 | AActor* Owner = ReceiverComp->GetOwner(); 126 | if (IsValid(Owner)) 127 | { 128 | return Owner; 129 | } 130 | } 131 | return nullptr; 132 | } 133 | 134 | //USenseStimulusBase* USensorTestBase::GetActorStimulus(const AActor* Actor) 135 | //{ 136 | // return USenseSystemBPLibrary::GetStimulusFromActor(Actor); 137 | //} -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorDistanceTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorDistanceTest.h" 4 | #include "Sensors/SensorBase.h" 5 | #include "Math/UnrealMathUtility.h" 6 | 7 | #if WITH_EDITORONLY_DATA 8 | #include "DrawDebugHelpers.h" 9 | #include "SceneManagement.h" 10 | #endif 11 | 12 | USensorDistanceTest::USensorDistanceTest(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 13 | { 14 | bTestBySingleLocation = true; 15 | 16 | ScoreModifyCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 17 | ScoreModifyCurve.GetRichCurve()->AddKey(1.f, 0.f, false); 18 | const float _key = MaxDistance / MaxDistanceLost; 19 | ScoreModifyCurve.GetRichCurve()->AddKey(_key, 0.1f, false); 20 | } 21 | 22 | #if WITH_EDITOR 23 | void USensorDistanceTest::PostEditChangeProperty(struct FPropertyChangedEvent& e) 24 | { 25 | Super::PostEditChangeProperty(e); 26 | InitializeCacheTest(); 27 | } 28 | #endif 29 | 30 | #if WITH_EDITORONLY_DATA 31 | void USensorDistanceTest::DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const 32 | { 33 | if (MaxDistance > KINDA_SMALL_NUMBER) 34 | { 35 | DrawWireSphere(PDI, GetSensorTransform(), FColor::Yellow, MaxDistance, 16, SDPG_Foreground); 36 | if (FMath::Abs(MaxDistanceLost - MaxDistance) > KINDA_SMALL_NUMBER) 37 | { 38 | DrawWireSphere(PDI, GetSensorTransform(), FColor::Cyan, MaxDistanceLost, 16, SDPG_Foreground); 39 | } 40 | } 41 | } 42 | 43 | void USensorDistanceTest::DrawDebug(const float Duration) const 44 | { 45 | #if ENABLE_DRAW_DEBUG 46 | if (MaxDistance > KINDA_SMALL_NUMBER && GetSensorOwner()) 47 | { 48 | if (const UWorld* World = GetSensorOwner()->GetWorld()) 49 | { 50 | DrawDebugSphere(World, GetSensorTransform().GetLocation(), MaxDistance, 16, FColor::Yellow, false, Duration, SDPG_Foreground, 1.5f); 51 | if (FMath::Abs(MaxDistanceLost - MaxDistance) > KINDA_SMALL_NUMBER) 52 | { 53 | DrawDebugSphere(World, GetSensorTransform().GetLocation(), MaxDistanceLost, 16, FColor::Cyan, false, Duration, SDPG_Foreground, 1.5f); 54 | } 55 | } 56 | } 57 | #endif 58 | } 59 | #endif 60 | 61 | 62 | EUpdateReady USensorDistanceTest::GetReadyToTest() 63 | { 64 | return Super::GetReadyToTest(); 65 | } 66 | 67 | bool USensorDistanceTest::PreTest() 68 | { 69 | Super::PreTest(); 70 | 71 | if (ScoreModifyCurve.ExternalCurve) 72 | { 73 | ScoreModifyCurve.EditorCurveData = *(ScoreModifyCurve.GetRichCurve()); 74 | ScoreModifyCurve.ExternalCurve = nullptr; 75 | } 76 | 77 | const FVector V = GetSensorTransform().GetLocation(); 78 | AABB_Box = FBox(V - MaxDistanceLost, V + MaxDistanceLost); //AABB 79 | return true; 80 | } 81 | 82 | ESenseTestResult USensorDistanceTest::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 83 | { 84 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_DistanceTest); 85 | 86 | ESenseTestResult OutResult = ESenseTestResult::Lost; 87 | if (AABB_Box.IsInsideOrOn(TestLocation)) //AABB 88 | { 89 | const FVector::FReal DistSquared = (GetSensorTransform().GetLocation() - TestLocation).SizeSquared(); //Distance Squared 90 | if (DistSquared <= MaxDistanceLostSquared) 91 | { 92 | const FVector::FReal Dist = FMath::Sqrt(DistSquared); 93 | ScoreResult *= ModifyScoreByCurve(Dist / MaxDistanceLost); 94 | OutResult = // 95 | (MinScore > ScoreResult || Dist > MaxDistance) // 96 | ? ESenseTestResult::NotLost // 97 | : ESenseTestResult::Sensed; // 98 | } 99 | else 100 | { 101 | ScoreResult = 0; 102 | } 103 | } 104 | return OutResult; 105 | } 106 | 107 | void USensorDistanceTest::InitializeCacheTest() 108 | { 109 | if (MaxDistanceLost < MaxDistance) 110 | { 111 | MaxDistanceLost = MaxDistance; 112 | } 113 | MaxDistanceSquared = MaxDistance * MaxDistance; 114 | MaxDistanceLostSquared = MaxDistanceLost * MaxDistanceLost; 115 | } 116 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/BaseSensorTask.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "SenseSystem.h" 6 | 7 | #include "CoreMinimal.h" 8 | 9 | #include "Async/AsyncWork.h" 10 | #include "Async/Async.h" 11 | 12 | #include "HAL/Runnable.h" 13 | #include "HAL/RunnableThread.h" 14 | #include "HAL/ThreadSafeBool.h" 15 | #include "HAL/ThreadingBase.h" 16 | #include "Containers/Queue.h" 17 | 18 | 19 | class USensorBase; 20 | 21 | 22 | /** 23 | * SensorQueue 24 | */ 25 | class FSensorQueue : private TQueue 26 | { 27 | public: 28 | FSensorQueue() {} 29 | ~FSensorQueue() {} 30 | 31 | bool Enqueue(USensorBase* Item); 32 | USensorBase* Dequeue(); 33 | USensorBase* Peek() const; 34 | bool Pop(); 35 | void Empty(); 36 | bool IsEmpty() const; 37 | int32 Num() const; 38 | 39 | private: 40 | int32 QueueNum = 0; 41 | }; 42 | 43 | 44 | /** 45 | * SenseRunnable Thread 46 | */ 47 | class FSenseRunnable final 48 | : public FRunnable 49 | //, FSingleThreadRunnable 50 | { 51 | public: 52 | FSenseRunnable(double InWaitTime = 0.0001, int32 InCounter = 10); 53 | virtual ~FSenseRunnable() override; 54 | 55 | #if WITH_EDITOR 56 | bool bSenseThreadPauseLog = false; 57 | bool bSenseThreadStateLog = true; 58 | #endif 59 | 60 | void EnsureCompletion(); 61 | 62 | //FRunnable interface. 63 | virtual bool Init() override; 64 | virtual void Stop() override; 65 | virtual void Exit() override; 66 | virtual uint32 Run() override; 67 | 68 | //virtual class FSingleThreadRunnable* GetSingleThreadInterface() { return nullptr; } 69 | //virtual void Tick() override {}; 70 | 71 | //FSenseRunnable AddQueueSensors 72 | bool AddQueueSensors(USensorBase* Sensor, bool bHighPriority = false); 73 | 74 | private: 75 | const double WaitTime; 76 | const int32 CounterLimit; 77 | int32 Counter = 0; 78 | uint32 SenseThreadId = 0; 79 | 80 | bool UpdateQueue(); 81 | 82 | //Thread to run the worker FRunnable on 83 | FRunnableThread* Thread; 84 | FEvent* WorkEvent; 85 | 86 | //As the name states those members are Thread safe 87 | FThreadSafeBool m_Kill; 88 | FThreadSafeBool m_Pause; 89 | 90 | FSensorQueue SensorQueue; 91 | FSensorQueue HighSensorQueue; 92 | }; 93 | 94 | 95 | FORCEINLINE bool FSensorQueue::Enqueue(USensorBase* Item) 96 | { 97 | QueueNum++; 98 | return TQueue::Enqueue(Item); 99 | } 100 | 101 | FORCEINLINE USensorBase* FSensorQueue::Dequeue() 102 | { 103 | USensorBase* Ptr = nullptr; 104 | if (TQueue::Dequeue(Ptr)) 105 | { 106 | QueueNum--; 107 | } 108 | return Ptr; 109 | } 110 | 111 | FORCEINLINE USensorBase* FSensorQueue::Peek() const 112 | { 113 | USensorBase* Ptr = nullptr; 114 | TQueue::Peek(Ptr); 115 | return Ptr; 116 | } 117 | 118 | FORCEINLINE bool FSensorQueue::Pop() 119 | { 120 | QueueNum--; 121 | return TQueue::Pop(); 122 | } 123 | 124 | FORCEINLINE void FSensorQueue::Empty() 125 | { 126 | TQueue::Empty(); 127 | } 128 | 129 | FORCEINLINE bool FSensorQueue::IsEmpty() const 130 | { 131 | checkSlow(!TQueue::IsEmpty() || QueueNum > 0); 132 | return TQueue::IsEmpty(); 133 | } 134 | 135 | FORCEINLINE int32 FSensorQueue::Num() const 136 | { 137 | return QueueNum; 138 | } 139 | 140 | 141 | FORCEINLINE void FSenseRunnable::EnsureCompletion() 142 | { 143 | Stop(); 144 | if (Thread) 145 | { 146 | Thread->WaitForCompletion(); 147 | } 148 | } 149 | 150 | 151 | FORCEINLINE bool FSenseRunnable::Init() 152 | { 153 | SenseThreadId = Thread->GetThreadID(); 154 | #if WITH_EDITOR 155 | if (bSenseThreadStateLog) 156 | { 157 | UE_LOG(LogSenseSys, Log, TEXT("SenseThread Initialized")); 158 | } 159 | #endif 160 | return true; 161 | } 162 | 163 | FORCEINLINE void FSenseRunnable::Stop() 164 | { 165 | m_Kill = true; 166 | WorkEvent->Trigger(); 167 | } 168 | 169 | FORCEINLINE void FSenseRunnable::Exit() 170 | { 171 | Stop(); 172 | Thread->Kill(); 173 | #if WITH_EDITOR 174 | if (bSenseThreadStateLog) 175 | { 176 | UE_LOG(LogSenseSys, Log, TEXT("SenseThread Exit")); 177 | } 178 | #endif 179 | SenseThreadId = 0; 180 | } 181 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/FrustumTest.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "UObject/ObjectMacros.h" 7 | #include "Sensors/Tests/SensorLocationTestBase.h" 8 | 9 | #include "FrustumTest.generated.h" 10 | 11 | 12 | struct FFrustumTestData final 13 | { 14 | FFrustumTestData() {} 15 | FFrustumTestData(FVector2D Point1, FVector2D Point2, FVector2D ViewportSize, float FOVAngle, float FarPlaneDistance); 16 | FFrustumTestData(float FOVAngle, float AspectRatio, float FarPlaneDistance); 17 | 18 | float MaxRadius = 0.f; 19 | float MaxCosAngle = -1.f; 20 | FVector::FReal MaxManhattanDistance = 0.f; 21 | 22 | FVector2D Center = FVector2D::ZeroVector; 23 | FBox2D Bound = FBox2D(FVector2D::ZeroVector, FVector2D::ZeroVector); 24 | 25 | FORCEINLINE bool IsInsideBound(const FVector& In) const { return IsInsideBound(FVector2D(In.Y, In.Z)); } 26 | FORCEINLINE bool IsInsideBound(const FVector2D In) const { return Bound.IsInside(In); } 27 | 28 | private: 29 | void InitDefault(float FarDistance); 30 | }; 31 | 32 | 33 | /** 34 | * Camera Frustum Test 35 | */ 36 | UCLASS(Blueprintable, BlueprintType, EditInlineNew) 37 | class SENSESYSTEM_API UFrustumTest : public USensorLocationTestBase 38 | { 39 | GENERATED_BODY() 40 | public: 41 | UFrustumTest(const FObjectInitializer& ObjectInitializer); 42 | virtual ~UFrustumTest() override; 43 | virtual void BeginDestroy() override; 44 | 45 | #if WITH_EDITOR 46 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 47 | #endif 48 | 49 | #if WITH_EDITORONLY_DATA 50 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const override; 51 | virtual void DrawDebug(float Duration) const override; 52 | #endif 53 | 54 | 55 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.001", ClampMax = "360.0", UIMin = "0.001", UIMax = "360.0")) 56 | float FOVAngle = 90.f; 57 | 58 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "0.01", UIMin = "0.01")) 59 | float AspectRatio = 1.7777f; 60 | 61 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (ClampMin = "1.0", UIMin = "1.0")) 62 | float FarPlaneDistance = 1000.f; 63 | 64 | 65 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 66 | bool bScoreDistance = true; 67 | 68 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest", meta = (EditCondition = "bScore")) 69 | bool bScoreScreenDistance = true; 70 | 71 | 72 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 73 | void SetFrustumParam(float FieldOfView, float Aspect_Ratio, float FarDistancePlane); 74 | 75 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest") 76 | void SetFrustumSelectionRectangleParam(FVector2D Point1, FVector2D Point2, FVector2D ViewportSize, float FOV, float FarDistance); 77 | 78 | /*USensorTestBase*/ 79 | virtual void InitializeFromSensor() override; 80 | virtual EUpdateReady GetReadyToTest() override; 81 | virtual bool PreTest() override; 82 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const override; 83 | 84 | virtual FBox GetSensorTestBoundBox() const override { return AABB_Box; } 85 | virtual float GetSensorTestRadius() const override { return TmpData.MaxRadius; } 86 | 87 | protected: 88 | virtual void InitializeCacheTest() override; 89 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 90 | 91 | private: 92 | float ScoreByScreenSpaceManhattanDistance(const FVector& IntersectPoint) const; 93 | 94 | FFrustumTestData TmpData; 95 | FBox AABB_Box; 96 | 97 | FVector TmpSelfForward = FVector::ForwardVector; 98 | 99 | }; 100 | 101 | FORCEINLINE float UFrustumTest::ScoreByScreenSpaceManhattanDistance(const FVector& IntersectPoint) const 102 | { 103 | const FVector::FReal AbsX = FMath::Abs(IntersectPoint.Y - TmpData.Center.X); 104 | const FVector::FReal AbsY = FMath::Abs(IntersectPoint.Z - TmpData.Center.Y); 105 | return 1.f - ((AbsX + AbsY) / TmpData.MaxManhattanDistance); 106 | } -------------------------------------------------------------------------------- /docs/en/quick-start.md: -------------------------------------------------------------------------------- 1 | # Quick start 2 | 3 | - 1. [SenseManager for UE4.21 and lower](#1-sensemanager-for-ue421-and-lower) 4 | - 2. [SenseReceiverComponent](#2-sensereceivercomponent) 5 | - 3. [SenseStimulusComponent](#3-sensestimuluscomponent) 6 | - 4. [Implement Interface](#4-implement-interface) 7 | - 5. [Customization Sensor and SensorTest in Blueprint](#5-customization-sensor-and-sensortest-in-blueprint) 8 | - 6. [Customization SensorTest in Cpp](#6-customization-sensortest-in-cpp) 9 | - 7. [Customization Sensorin Cpp](#7-customization-sensor-and-sensortest-in-cpp) 10 | - 8. [DebugDraw](#8-debugdraw) # todo 11 | - 9. [Profiling](#9-profiling) # todo 12 | 13 | ## 1. SenseManager for UE4.21 and lower 14 | 15 | First, where to start: SenseManager implementation required. The current implementation prior to 16 | UE4.22 uses GameInstance as the owner of the SenseManager object. With UE4.22, SenseManager is 17 | implemented as a GameInstanceSubSystem, and no action is required to create it. 18 | 19 | Create an object from the SenseManager class and save it into a variable: 20 | 21 | ![Quick start 1.1](../images/quick-start-1.1.png) 22 | 23 | Next, you need to implement the SenseManagerInterface, 24 | getter interface 25 | 26 | ![Quick start 1.2](../images/quick-start-1.2.png) 27 | 28 | add a single function implementation using one of the described methods 29 | **Get Sense Manager** ( USenseManager* **GetSenseManager** ()) 30 | Which returns a pointer to a **SenseManager object** 31 | 32 | ![Quick start 1.3](../images/quick-start-1.3.png) 33 | 34 | This completes the operation with the GameInstance setup. 35 | 36 | ## 2. SenseReceiverComponent 37 | 38 | add to actor Sense Receiver Component 39 | 40 | ![Quick start 2.1](../images/quick-start-2.1.png) 41 | 42 | add sensors to component 43 | 44 | ![Quick start 2.2](../images/quick-start-2.2.png) 45 | 46 | adjust the parameters of the sensor, in particular the received channels on which corresponds to 47 | the stimulus 48 | 49 | ![Quick start 2.3](../images/quick-start-2.3.png) 50 | 51 | ## 3. SenseStimulusComponent 52 | 53 | Add to detectable actor Sense Stimulus Component 54 | 55 | ![Quick start 3.1](../images/quick-start-3.1.png) 56 | 57 | add the required sensor tag 58 | 59 | ![Quick start 3.2](../images/quick-start-3.2.png) 60 | 61 | Add any implementation in the receiver or stimulus to the necessary events. 62 | eg: 63 | 64 | ![Quick start 3.3](../images/quick-start-3.3.png) 65 | 66 | All that remains is to add or spawn actors on the level. 67 | 68 | ## 4. Implement Interface 69 | 70 | 71 | See [section 3](./sensesystem-pdf.md#3-sensesystem-architecture). 72 | 73 | ## 5. Customization Sensor and SensorTest in Blueprint 74 | 75 | ![Quick start 5.1](../images/quick-start-5.1.png) 76 | 77 | See an example in: 78 | **SenseSysExample\Content\ExampleCustomization\CustomActiveSensor.uasset 79 | SenseSysExample\Content\ExampleCustomization\CustomSensorTest_Location.uasset** 80 | 81 | ## 6. CustomSensorTest 82 | 83 | A special class has been allocated for blueprints: **SensorTestBlueprintBase** 84 | there are three functions available for redefinition.. 85 | - 1. **GetReadyToTest** - Preparing the sensor for the test 86 | - a. **Ready** - sensor ready for test 87 | - b. **Skip** - the sensor will skip the test 88 | - c. **Fail** - an error occurred while preparing the test,the results will be reset, and the test itself will be skipped, a message will be displayed in the log. 89 | - 2. **RunTest** - implementation of the test itself, if thesensor is configured to call not in the game stream, 90 | then it is forbidden to call thread-unsafe functions in the method 91 | - a. input parameters: 92 | - * **StimulusComponent** - SenseStimulusComponent 93 | - * **Location** - Object location 94 | - * **CurrentScore** - current score 95 | - b. output parameters: 96 | - * **Score** - score after test 97 | - * **ESenseTestResult** 98 | - 1. **Sensed** - detection was successful 99 | - 2. **NotLost** - detection failed but the object will notbe lost if it was previously detected 100 | - 3. **Lost** - detection failed 101 | - 3. Initialize - implementation of initialization 102 | 103 | 104 | ## 7. CustomSensor 105 | 106 | It makes sense to create a custom sensor when it has a unique set of tests, or unique actions. All 107 | classes are available for blueprint inheritance. 108 | 109 | ## 8. Customization Sensor and SensorTest in Cpp 110 | 111 | See an example in: 112 | 113 | ``` 114 | SenseSysExample\Source\SenseSysExample\Public\MyCustomSensorTest.h 115 | SenseSysExample\Source\SenseSysExample\Public\MyCustomActiveSensor.h 116 | ``` 117 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorTestBlueprintBase.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Sensors/Tests/SensorLocationTestBase.h" 7 | 8 | #include "SensorTestBlueprintBase.generated.h" 9 | 10 | class USensorBase; 11 | class USenseReceiverComponent; 12 | class AActor; 13 | 14 | 15 | /** 16 | * Sensor Test Blueprint Base 17 | */ 18 | UCLASS(abstract, Blueprintable, BlueprintType, EditInlineNew, HideDropdown, meta = (DontUseGenericSpawnObject, BlueprintThreadSafe)) 19 | class SENSESYSTEM_API USensorTestBlueprintBase : public USensorLocationTestBase 20 | { 21 | GENERATED_BODY() 22 | public: 23 | #if WITH_EDITOR 24 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 25 | #endif 26 | 27 | /**Get Sense Receiver Component*/ 28 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetSenseReceiver")) 29 | USenseReceiverComponent* GetSenseReceiverComponentBP() const; 30 | 31 | /**Sensor Owner*/ 32 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetSensor")) 33 | USensorBase* GetSensorOwnerBP() const; 34 | 35 | /**Get Receiver Owner Actor*/ 36 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetReceiverActor")) 37 | AActor* GetReceiverActorBP() const; 38 | 39 | /**Get Receiver Transform*/ 40 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetReceiverTransform")) 41 | FTransform GetReceiverTransformBP() const; 42 | 43 | /**Get Cached Sensor Transform, (!) only if sensor Ready*/ 44 | UFUNCTION(BlueprintCallable, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetSensorTransform")) 45 | FTransform GetSensorTransformBP() const; 46 | 47 | 48 | /********************************/ 49 | /* Blueprint Implementable */ 50 | /********************************/ 51 | 52 | /** 53 | * Blueprint Thread Safe 54 | * Prepare for the test 55 | */ 56 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "GetReadyToTest")) 57 | EUpdateReady GetReadyToTestBP(); 58 | 59 | /** 60 | * (!) Not Blueprint Thread Safe!!! 61 | * PreTest Implementation 62 | */ 63 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "PreTest")) 64 | bool PreTest_BP(); 65 | 66 | /** 67 | * (!) Not Blueprint Thread Safe!!! 68 | * TestBody Implementation 69 | */ 70 | UFUNCTION(BlueprintCallable, BlueprintNativeEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "RunTest")) 71 | ESenseTestResult RunTestBP(UPARAM(ref) FSensedStimulus& SensedStimulus) const; 72 | virtual ESenseTestResult RunTestBP_Implementation(UPARAM(ref) FSensedStimulus& SensedStimulus) const; 73 | 74 | /** 75 | * (!) Not Blueprint Thread Safe!!! 76 | * Single LocationTest Implementation 77 | */ 78 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "RunLocationTest")) 79 | ESenseTestResult RunLocationTestBP(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const; 80 | 81 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "Get Sensor Test Bound Box")) 82 | FBox GetSensorTestBoundBoxBP() const; 83 | 84 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "Get Sensor Test Radius")) 85 | float GetSensorTestRadiusBP() const; 86 | 87 | private: 88 | virtual EUpdateReady GetReadyToTest() override; 89 | virtual bool PreTest() override; 90 | virtual FBox GetSensorTestBoundBox() const override; 91 | virtual float GetSensorTestRadius() const override; 92 | 93 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const override; 94 | virtual ESenseTestResult RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const override; 95 | }; 96 | 97 | FORCEINLINE USenseReceiverComponent* USensorTestBlueprintBase::GetSenseReceiverComponentBP() const 98 | { 99 | return GetSenseReceiverComponent(); 100 | } 101 | 102 | FORCEINLINE USensorBase* USensorTestBlueprintBase::GetSensorOwnerBP() const 103 | { 104 | return GetSensorOwner(); 105 | } 106 | 107 | FORCEINLINE AActor* USensorTestBlueprintBase::GetReceiverActorBP() const 108 | { 109 | return GetReceiverActor(); 110 | } 111 | 112 | FORCEINLINE FTransform USensorTestBlueprintBase::GetReceiverTransformBP() const 113 | { 114 | return GetReceiverTransform(); 115 | } 116 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/SensorSight.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/SensorSight.h" 4 | #include "Sensors/Tests/SensorDistanceAndAngleTest.h" 5 | #include "Sensors/Tests/SensorTraceTest.h" 6 | 7 | USensorSight::USensorSight(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 8 | { 9 | DistanceCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 10 | DistanceCurve.GetRichCurve()->AddKey(1.f, 0.f, false); 11 | AngleCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 12 | AngleCurve.GetRichCurve()->AddKey(1.f, 0.f, false); 13 | 14 | SensorTests.SetNum(2); 15 | SensorTests[0] = CreateDefaultSubobject(TEXT("DistanceAndAngleTest")); 16 | SensorTests[1] = CreateDefaultSubobject(TEXT("TraceTest")); 17 | SetValuesToSensorTests(); 18 | } 19 | 20 | USensorSight::~USensorSight() 21 | {} 22 | 23 | #if WITH_EDITOR 24 | void USensorSight::PostEditChangeProperty(struct FPropertyChangedEvent& e) 25 | { 26 | Super::PostEditChangeProperty(e); 27 | if (MaxDistanceLost < MaxDistance) 28 | { 29 | MaxDistanceLost = MaxDistance; 30 | } 31 | if (MaxAngleLost < MaxAngle) 32 | { 33 | MaxAngleLost = MaxAngle; 34 | } 35 | if (MinDistance > MaxDistance) 36 | { 37 | MaxDistance = MinDistance; 38 | } 39 | SetValuesToSensorTests(); 40 | } 41 | #endif 42 | 43 | void USensorSight::SetTraceParam( 44 | const ETraceTestParam InTraceParam, 45 | const ECollisionChannel InCollision, 46 | const bool TraceComplex, 47 | const bool InTestBySingleLocation, 48 | const float InMinScore) 49 | { 50 | TraceTestParam = InTraceParam; 51 | CollisionChannel = InCollision; 52 | bTraceBySingleLocation = InTestBySingleLocation; 53 | MinScore = InMinScore; 54 | bTraceComplex = TraceComplex; 55 | FScopeLock Lock_CriticalSection(&SensorCriticalSection); 56 | SetValuesToSensorTests(); 57 | } 58 | 59 | void USensorSight::SetDistanceAngleParam( 60 | const float InMaxDistance, 61 | const float InMaxDistanceLost, 62 | const float InMaxAngle, 63 | const float InMaxAngleLost, 64 | const bool InTestBySingleLocation, 65 | const float InMinScore, 66 | const float InMinDistance) 67 | { 68 | MaxAngle = InMaxAngle; 69 | MaxAngleLost = InMaxAngleLost; 70 | MaxDistance = InMaxDistance; 71 | MaxDistanceLost = InMaxDistanceLost; 72 | bDistanceAngleBySingleLocation = InTestBySingleLocation; 73 | MinScore = InMinScore; 74 | MinDistance = InMinDistance; 75 | FScopeLock Lock_CriticalSection(&SensorCriticalSection); 76 | SetValuesToSensorTests(); 77 | } 78 | 79 | void USensorSight::SetValuesToSensorTests() 80 | { 81 | check(SensorTests.IsValidIndex(0) && SensorTests.IsValidIndex(1)); 82 | 83 | if (const auto D = Cast(SensorTests[0])) 84 | { 85 | D->DistanceCurve.EditorCurveData = *DistanceCurve.GetRichCurveConst(); 86 | D->DistanceCurve.ExternalCurve = nullptr; 87 | 88 | D->AngleCurve.EditorCurveData = *AngleCurve.GetRichCurveConst(); 89 | D->AngleCurve.ExternalCurve = nullptr; 90 | 91 | D->bTestBySingleLocation = bDistanceAngleBySingleLocation; 92 | D->SetDistanceAndAngleParam(MaxAngle, MaxAngleLost, MaxDistance, MaxDistanceLost, MinDistance); 93 | D->MinScore = MinScore; 94 | } 95 | else 96 | { 97 | UE_LOG(LogSenseSys, Error, TEXT("DistanceTEst invalid cast from - SensorTests[0] ")); 98 | } 99 | 100 | if (const auto SensorTraceTest = Cast(SensorTests[1])) 101 | { 102 | SensorTraceTest->SetTraceParam(TraceTestParam, CollisionChannel, bTraceBySingleLocation, bTraceComplex); 103 | SensorTraceTest->MinScore = MinScore; 104 | } 105 | else 106 | { 107 | UE_LOG(LogSenseSys, Error, TEXT("TraceTest invalid cast from - SensorTests[1] ")); 108 | } 109 | } 110 | 111 | 112 | /* 113 | void USensorSight::Serialize(FArchive& Ar) 114 | { 115 | Super::Serialize(Ar); 116 | if (SensorTests.Num() > 1) 117 | { 118 | const auto Dso_1 = Cast(GetDefaultSubobjectByName(TEXT("DistanceAndAngleTest"))); 119 | if (SensorTests[0]) 120 | { 121 | if (SensorTests[0] != Dso_1) // ...if they don't match, the saved state needs to be fixed up. 122 | { 123 | SensorTests[0]->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_ForceNoResetLoaders); 124 | SensorTests[0] = Dso_1; 125 | } 126 | } 127 | else 128 | { 129 | SensorTests[0] = Dso_1; 130 | } 131 | 132 | const auto Dso_2 = Cast(GetDefaultSubobjectByName(TEXT("TraceTest"))); 133 | if (SensorTests[1]) 134 | { 135 | if (SensorTests[1] != Dso_2) // ...if they don't match, the saved state needs to be fixed up. 136 | { 137 | SensorTests[1]->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_ForceNoResetLoaders); 138 | SensorTests[1] = Dso_2; 139 | } 140 | } 141 | else 142 | { 143 | SensorTests[1] = Dso_2; 144 | } 145 | } 146 | 147 | } 148 | */ -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorTraceTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorTraceTest.h" 4 | #include "Sensors/SensorBase.h" 5 | #include "SensedStimulStruct.h" 6 | #include "SenseStimulusBase.h" 7 | #include "CollisionQueryParams.h" 8 | #include "Engine/World.h" 9 | 10 | 11 | USensorTraceTest::USensorTraceTest(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 12 | {} 13 | 14 | #if WITH_EDITOR 15 | void USensorTraceTest::PostEditChangeProperty(struct FPropertyChangedEvent& e) 16 | { 17 | USensorTraceTestBase::PostEditChangeProperty(e); 18 | InitializeCacheTest(); 19 | } 20 | #endif 21 | 22 | void USensorTraceTest::SetTraceParam(const ETraceTestParam TraceParam, const ECollisionChannel Collision, const bool bInTestBySingleLocation, const bool TraceComplex) 23 | { 24 | SetBaseTraceParam(TraceParam, Collision, TraceComplex); 25 | bTestBySingleLocation = bInTestBySingleLocation; 26 | InitializeCacheTest(); 27 | } 28 | 29 | EUpdateReady USensorTraceTest::GetReadyToTest() 30 | { 31 | bool bNeedInitCollision; 32 | { 33 | const TArray& IgnActors = GetSensorOwner()->GetIgnoredActors(); 34 | const auto& IgnCollisions = Collision_Params.GetIgnoredActors(); 35 | 36 | bNeedInitCollision = IgnCollisions.Num() != IgnActors.Num(); 37 | if (!bNeedInitCollision) 38 | { 39 | for (int32 i = 0; i < IgnActors.Num(); i++) 40 | { 41 | if (IgnActors[i]) 42 | { 43 | if (IgnActors[i]->GetUniqueID() != IgnCollisions[i]) 44 | { 45 | bNeedInitCollision = true; 46 | break; 47 | } 48 | } 49 | } 50 | } 51 | } 52 | 53 | if (bNeedInitCollision) 54 | { 55 | InitCollisionParams(); 56 | } 57 | 58 | return USensorTraceTestBase::GetReadyToTest(); 59 | } 60 | 61 | bool USensorTraceTest::PreTest() 62 | { 63 | return Super::PreTest(); 64 | } 65 | 66 | void USensorTraceTest::InitializeCacheTest() 67 | { 68 | Super::InitializeCacheTest(); 69 | } 70 | 71 | ESenseTestResult USensorTraceTest::RunTest(FSensedStimulus& SensedStimulus) const 72 | { 73 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTraceTestRunTest); 74 | if (MinScore < SensedStimulus.Score) 75 | { 76 | if (const UWorld* World = GetWorld()) 77 | { 78 | if (SensedStimulus.StimulusComponent.IsValid()) 79 | { 80 | check(SensedStimulus.SensedPoints.Num()); 81 | FCollisionQueryParams CollisionParams = Collision_Params; 82 | CollisionParams.AddIgnoredActors(SensedStimulus.StimulusComponent.Get()->GetIgnoreTraceActors()); 83 | if (LineTraceTest(World, CollisionParams, GetSensorTransform().GetLocation(), SensedStimulus)) 84 | { 85 | return MinScore > SensedStimulus.Score ? ESenseTestResult::NotLost : ESenseTestResult::Sensed; 86 | } 87 | } 88 | } 89 | } 90 | return ESenseTestResult::Lost; 91 | } 92 | 93 | bool USensorTraceTest::LineTraceTest( 94 | const UWorld* World, 95 | const FCollisionQueryParams& CollisionParams, 96 | const FVector& StartTrace, 97 | FSensedStimulus& SensedStimulus) const 98 | { 99 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_TraceTest); 100 | 101 | TArray& Points = SensedStimulus.SensedPoints; 102 | const FIntPoint TestBound = GetBoundFotSensePoints(Points); 103 | const int32 CountLoop = TestBound.Y - TestBound.X; 104 | 105 | const float MaxScorePerLoop = 1.f / static_cast(CountLoop); 106 | const float ScaledMinScore = MinScore / SensedStimulus.Score; 107 | 108 | float TotalScore = 0.f; 109 | for (int32 i = TestBound.X; i < TestBound.Y; i++) 110 | { 111 | float LocScore = 1.f; 112 | bool bNotHit; 113 | { 114 | if (TraceTestParam == ETraceTestParam::BoolTraceTest) 115 | { 116 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_LineTraceTestByChannel); 117 | bNotHit = !World->LineTraceTestByChannel(StartTrace, Points[i].SensedPoint, CollisionChannel, CollisionParams); 118 | } 119 | else 120 | { 121 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_MultiLineTraceByChannel); 122 | TArray OutHits; 123 | bNotHit = !World->LineTraceMultiByChannel(OutHits, StartTrace, Points[i].SensedPoint, CollisionChannel, CollisionParams); 124 | bNotHit = bNotHit && ScoreFromTransparency(OutHits, LocScore); 125 | } 126 | } 127 | if (bNotHit) 128 | { 129 | TotalScore += LocScore; 130 | Points[i].PointScore *= LocScore; 131 | if (Points[i].PointTestResult == ESenseTestResult::None) 132 | { 133 | Points[i].PointTestResult = ESenseTestResult::Sensed; 134 | } 135 | } 136 | else 137 | { 138 | Points[i].PointTestResult = ESenseTestResult::Lost; 139 | Points[i].PointScore = 0; 140 | if (ScaledMinScore > (TotalScore + ((CountLoop - i - 1) * MaxScorePerLoop))) //max possible score 141 | { 142 | return false; 143 | } 144 | } 145 | } 146 | 147 | if (TotalScore > 0 && TotalScore >= MinScore) 148 | { 149 | SensedStimulus.Score *= TotalScore / static_cast(CountLoop); 150 | return true; 151 | } 152 | return false; 153 | } 154 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/Sensors/Tests/SensorTestBase.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "SensedStimulStruct.h" 7 | #include "UObject/Object.h" 8 | 9 | 10 | #if WITH_EDITORONLY_DATA 11 | #include "SceneManagement.h" 12 | #include "SceneView.h" 13 | #endif 14 | 15 | #include "SensorTestBase.generated.h" 16 | 17 | 18 | class USensorBase; 19 | class USenseReceiverComponent; 20 | class USenseStimulusBase; 21 | class AActor; 22 | 23 | 24 | /** UpdateReady */ 25 | UENUM(BlueprintType) 26 | enum class EUpdateReady : uint8 27 | { 28 | None = 0 UMETA(DisplayName = "None"), 29 | Ready UMETA(DisplayName = "Ready"), 30 | Skip UMETA(DisplayName = "Skip"), 31 | Fail UMETA(DisplayName = "Fail") 32 | }; 33 | 34 | 35 | /** 36 | * Base CLass for Sensor Test 37 | */ 38 | UCLASS(abstract, BlueprintType, EditInlineNew, HideDropdown, meta = (BlueprintThreadSafe = false)) //, Within = SensorBase 39 | class SENSESYSTEM_API USensorTestBase : public UObject 40 | { 41 | GENERATED_BODY() 42 | 43 | public: 44 | USensorTestBase(const FObjectInitializer& ObjectInitializer); 45 | virtual ~USensorTestBase() override; 46 | virtual void BeginDestroy() override; 47 | 48 | #if WITH_EDITOR 49 | virtual void PostEditChangeProperty(struct FPropertyChangedEvent& e) override; 50 | #endif 51 | 52 | #if WITH_EDITORONLY_DATA 53 | virtual void DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const {} 54 | virtual void DrawTestHUD(const class FViewport* Viewport, const class FSceneView* View, class FCanvas* Canvas) const {} 55 | virtual void DrawDebug(float Duration) const {} 56 | #endif 57 | 58 | /**Toggle enable Test*/ 59 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 60 | bool bEnableTest = true; 61 | 62 | /**Force Skip Test*/ 63 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 64 | bool bSkipTest = false; 65 | 66 | /**Skip test if input score < MinScore*/ 67 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Category = "SensorTest") 68 | float MinScore = 0.f; 69 | 70 | bool NeedTest() const; 71 | 72 | /** Initialize from Sensor for this test */ 73 | virtual void InitializeFromSensor(); 74 | 75 | /** Initialize from Sensor, called once on sensor initialized */ 76 | UFUNCTION(BlueprintCallable, BlueprintImplementableEvent, Category = "SenseSystem|SensorTest", meta = (DisplayName = "Initialize")) 77 | bool InitializeFromSensorBP(USensorBase* Sensor); 78 | 79 | virtual FBox GetSensorTestBoundBox() const { return FBox(FVector::ZeroVector, FVector::ZeroVector); } 80 | virtual float GetSensorTestRadius() const { return 0.f; } 81 | 82 | /** GetWorld */ 83 | virtual class UWorld* GetWorld() const override; 84 | 85 | const FTransform& GetSensorTransform() const; 86 | 87 | FName GetSensorTag() const; 88 | 89 | USensorBase* GetSensorOwner() const; 90 | 91 | 92 | /** Prepare for the test, called once per update, before RunTest, cache dynamic data for test */ 93 | virtual EUpdateReady GetReadyToTest() { return EUpdateReady::Ready; } 94 | 95 | /** PreTest */ 96 | virtual bool PreTest(); 97 | 98 | /** full test implementation */ 99 | virtual ESenseTestResult RunTest(FSensedStimulus& SensedStimulus) const; 100 | 101 | protected: 102 | /** Cache Static data for test , called once on test creation*/ 103 | virtual void InitializeCacheTest() {} 104 | 105 | class AActor* GetReceiverActor() const; 106 | class USenseReceiverComponent* GetSenseReceiverComponent() const; 107 | const FTransform& GetReceiverTransform() const; 108 | 109 | static FVector NormalWithSizeSquared(const FVector& Vec, float SizeSquared); 110 | 111 | static bool IsObstacleInterface(const AActor* Actor); 112 | static bool IsStimulusInterface(const AActor* Actor); 113 | 114 | FTransform SensorTransform = FTransform::Identity; 115 | 116 | // NotUproperty 117 | USensorBase* PrivateSensorOwner = nullptr; 118 | 119 | }; 120 | 121 | FORCEINLINE bool USensorTestBase::NeedTest() const 122 | { 123 | return bEnableTest && !bSkipTest; 124 | } 125 | 126 | FORCEINLINE USensorBase* USensorTestBase::GetSensorOwner() const 127 | { 128 | return PrivateSensorOwner; 129 | } 130 | 131 | FORCEINLINE FVector USensorTestBase::NormalWithSizeSquared(const FVector& Vec, const float SizeSquared) 132 | { 133 | if (SizeSquared == 1.f) 134 | { 135 | return Vec; 136 | } 137 | if (SizeSquared < KINDA_SMALL_NUMBER) 138 | { 139 | return FVector::ForwardVector; 140 | } 141 | return Vec * FMath::InvSqrt(SizeSquared); 142 | } 143 | 144 | FORCEINLINE const FTransform& USensorTestBase::GetSensorTransform() const 145 | { 146 | return SensorTransform; 147 | } 148 | 149 | //todo: FilterAndScoring settings for TestBase 150 | //UENUM(BlueprintType) 151 | //enum class ESTType : uint8 152 | //{ 153 | // FilterAndScoring = 0 UMETA(DisplayName = "FilterAndScoring"), //all 154 | // FilterOnly UMETA(DisplayName = "Filter"), //skipScoring 155 | // ScoringOnly UMETA(DisplayName = "Scoring"), //skip filter 156 | //}; -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # CHANGELOG 2 | 3 | CHANGELOG pattern, see here : https://keepachangelog.com/en/1.0.0/ 4 | 5 | ## 7.1 v1.04 - 2022-10-06 6 | 7 | https://youtu.be/oniC1fxWqFUand some bugfix. 8 | Individual per channel settings and updates 9 | 10 | ## 7.2 v1.10 - 2022-10-06 11 | 12 | ### Added 13 | 14 | - **QuadTree** added to quickly get participants in thesystem for the sensor from the current level, 15 | which significantly increased system performance. It is very suitable for relatively flat worlds, for non-planar 16 | ones a little worse, but it will still be effective (This will be in future updates.) 17 | https://i.imgur.com/Uj4jMmm.gifv 18 | - Added **system settings** in project settings 19 | minimum squares cell - the optimal cell size depends on how tight the targets for sensing are 20 | placed at, ideal - so that in one square there are less than 8-16 objects 21 | debug settings for sensors with matching default tag 22 | - Added **auxiliary structure** for faster obtaining the position of sockets or bones in the skeleton 23 | mesh 24 | - Added **SetScore** and **SetAge** 25 | 26 | ### Changed 27 | 28 | - **Default Age and Score** moved to **StimulusSensorNameResponse** 29 | - **Refactoring** the system made it less prone to multithreadingerrors, as well as more productive. 30 | SenseSys 1.04 :https://i.imgur.com/d9YAEzD.mp4 31 | SenseSys 1.10 :https://i.imgur.com/DAYLBNf.mp4 32 | 33 | ### Removed 34 | 35 | - **IGetSensedAge** and **IGetSensedScore** functions removed from the interface 36 | **SetScore** and **SetAge** added instead 37 | 38 | ### Fixed 39 | 40 | - **ParallelTestUpdate** - the experimental function disabled by default, enables parallel testing when 41 | updating. Higher load on all processor threads, which can reduce the maximum FPS, but the average FPS 42 | can be more stable. 43 | 44 | ### 7.3 v1.20 - 2022-10-06 45 | 46 | ### Added 47 | 48 | - Added Project settings for sensors by tag 49 | **1. SenseSystem Project Settings** 50 | 51 | ``` 52 | individual settings for sensors by tag. 53 | NodeCanSplit -the number of objects for the tree,exceeding which the tree will be split 54 | into subtrees, default values QuadTree - 4 , OcTree- 8 55 | QtOtSwitch - selection of tree type: 56 | ``` 57 | 58 | - **QuadTree** - for flat levels, when the sensing targetsare located on the map with a 59 | small variation in height, and the target's height does not greatly affect the result. 60 | - **OcTree** - for volumetric levels where sensing targetsare located on the map with a 61 | large variation in height (e.g. flying targets). 62 | - **CountPerOneCyclesUpdate** - the number of sensors thatthe "sensor thread" processes 63 | for breaking 64 | - **WaitTimeBetweenCyclesUpdate** - time in seconds, a shortpause between updates of the 65 | "sensor thread" 66 | 67 | **2.** Added default functions for debugging - **DrawDebugSensor** (...) 68 | 69 | **3.** Added ability to disable rendering of default sensors (when many sensors in one actor, rendering 70 | all - prevents configure them) 71 | 72 | ``` 73 | https://i.imgur.com/NTKk2Wo.gif 74 | ``` 75 | 76 | **4. OnSenseStateChanged -** for StimulusComponent addedSensor tag by which the state changes, 77 | now the state can be separated for each tag separately 78 | **5. ReceiveStimulusFlag** for **StimulusComponent** moved fromcomponent properties to 79 | **StimulusSensorNameResponse**. 80 | 81 | ``` 82 | which allows you to customize the receipt of calls from sensors by tag individually for each. 83 | the inactive ReceiveStimulusFlag in the componentis left for backward compatibility - recompile 84 | and save your blueprints to automatically transfer the flag values to the 85 | StimulusSensorNameResponse. 86 | ``` 87 | 88 | ``` 89 | 6. TestBySingleLocation - in the sensor test now doesnot affect the performance of the sensor 90 | update, with the advent of QuadTree, OcTree trees and test check Bounds. 91 | https://i.imgur.com/lDgAknN.png 92 | https://i.imgur.com/udPogI0.png 93 | ``` 94 | 95 | ``` 96 | 7. SensorTest now has virtual functions 97 | ``` 98 | 99 | - FBox GetSensorTestBoundBox () 100 | - float GetSensorTestRadius () 101 | for the test to work correctly, one or both of them must be overridden 102 | https://i.imgur.com/tH3GpjW.png 103 | **8.** Added **ExampleLight** to the sample project - an exampleof target visibility by its illumination. 104 | **9.** Refactoring the system with more loyal memory use when updating the sensor. which gives a 105 | performance boost. 106 | **10.** Bug fix. 107 | 108 | ## 7.4 v1.24 - 2022-10-06 109 | 110 | **1.** Replacing arrays with channels with 64-bit flag https://i.imgur.com/8rWPTO6.mp4 111 | **2.** Channel zero is now invalid - and means no value, it's more 112 | sequential channel logic. 113 | **3.** Updating the values ​​of your saved resources will be updated 114 | automatically when you open and save an asset (channels will be shifted by +1) 115 | **4.** Updated functions for setting channel values 116 | **5.** All old variables and functions associated with channels and arrays are marked 117 | as DEPRECATED 118 | 119 | **Although this update will cause some inconvenience - to re-save 120 | current assets, I tried to keep these inconveniences to a minimum so that it was 121 | automatic. However, if you are using channel values ​​somewhere in external code, 122 | it needs to be updated.** -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorBoxTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorBoxTest.h" 4 | #include "Sensors/SensorBase.h" 5 | 6 | #if WITH_EDITORONLY_DATA 7 | #include "DrawDebugHelpers.h" 8 | #include "SceneManagement.h" 9 | #endif 10 | 11 | USensorBoxTest::USensorBoxTest() 12 | { 13 | //ScoreModifyCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 14 | //ScoreModifyCurve.GetRichCurve()->AddKey(1.f, 1.f, false); 15 | } 16 | 17 | void USensorBoxTest::InitAABBBoxAndSensorTransform() 18 | { 19 | if (bOrientedBox) 20 | { 21 | if (bIgnoreVerticalRotation) 22 | { 23 | FVector Forward = SensorTransform.GetRotation().GetForwardVector(); 24 | Forward.Z = 0.f; 25 | const FQuat Q = FRotationMatrix::MakeFromX(Forward.GetSafeNormal()).ToQuat(); 26 | SensorTransform.SetRotation(Q); 27 | } 28 | 29 | const FTransform& T = GetSensorTransform(); 30 | const FVector Loc = T.GetLocation(); 31 | AABB_Box = FBox(Loc, Loc); 32 | 33 | AABB_Box += T.TransformPositionNoScale(BoxExtent); 34 | AABB_Box += T.TransformPositionNoScale(FVector(BoxExtent.X, -BoxExtent.Y, BoxExtent.Z)); 35 | AABB_Box += T.TransformPositionNoScale(FVector(BoxExtent.X, BoxExtent.Y, -BoxExtent.Z)); 36 | AABB_Box += T.TransformPositionNoScale(FVector(BoxExtent.X, -BoxExtent.Y, -BoxExtent.Z)); 37 | 38 | AABB_Box += T.TransformPositionNoScale(-BoxExtent); 39 | AABB_Box += T.TransformPositionNoScale(FVector(-BoxExtent.X, -BoxExtent.Y, BoxExtent.Z)); 40 | AABB_Box += T.TransformPositionNoScale(FVector(-BoxExtent.X, BoxExtent.Y, -BoxExtent.Z)); 41 | AABB_Box += T.TransformPositionNoScale(FVector(-BoxExtent.X, BoxExtent.Y, BoxExtent.Z)); 42 | } 43 | else 44 | { 45 | AABB_Box = FBox::BuildAABB(GetSensorTransform().GetLocation(), BoxExtent); 46 | } 47 | } 48 | 49 | void USensorBoxTest::SetBoxParam(const FVector Extent, const bool OrientedBox, const bool IgnoreVerticalRotation, const bool Score) 50 | { 51 | BoxExtent = Extent; 52 | bOrientedBox = OrientedBox; 53 | bIgnoreVerticalRotation = IgnoreVerticalRotation; 54 | bScore = Score; 55 | InitAABBBoxAndSensorTransform(); 56 | InitializeCacheTest(); 57 | } 58 | 59 | void USensorBoxTest::InitializeCacheTest() 60 | { 61 | Super::InitializeCacheTest(); 62 | if (bScore) 63 | { 64 | MaxManhattanDistance = BoxExtent.X + BoxExtent.Y + BoxExtent.Z; 65 | } 66 | } 67 | 68 | EUpdateReady USensorBoxTest::GetReadyToTest() 69 | { 70 | if (!AABB_Box.GetExtent().Equals(FVector::ZeroVector, KINDA_SMALL_NUMBER)) 71 | { 72 | return Super::GetReadyToTest(); 73 | } 74 | return EUpdateReady::Fail; 75 | } 76 | 77 | void USensorBoxTest::InitializeFromSensor() 78 | { 79 | check(GetOuter() == GetSensorOwner()); 80 | SensorTransform = GetSensorOwner()->GetSensorTransform(); 81 | InitAABBBoxAndSensorTransform(); 82 | InitializeCacheTest(); 83 | 84 | if (IsValid(GetSensorOwner())) 85 | { 86 | InitializeFromSensorBP(GetSensorOwner()); 87 | } 88 | } 89 | 90 | bool USensorBoxTest::PreTest() 91 | { 92 | Super::PreTest(); 93 | InitAABBBoxAndSensorTransform(); 94 | return true; 95 | } 96 | 97 | ESenseTestResult USensorBoxTest::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 98 | { 99 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_BoxTest); 100 | if (AABB_Box.IsInsideOrOn(TestLocation)) //AABB 101 | { 102 | FVector TestLoc; 103 | if (bOrientedBox) 104 | { 105 | TestLoc = GetSensorTransform().InverseTransformPositionNoScale(TestLocation); 106 | if (TestLoc.X > BoxExtent.X || TestLoc.X < -BoxExtent.X || 107 | TestLoc.Y > BoxExtent.Y || TestLoc.Y < -BoxExtent.Y || 108 | TestLoc.Z > BoxExtent.Z || TestLoc.Z < -BoxExtent.Z) 109 | { 110 | return ESenseTestResult::Lost; 111 | } 112 | } 113 | else if (bScore) 114 | { 115 | TestLoc = TestLocation - GetSensorTransform().GetLocation(); 116 | } 117 | 118 | if (bScore) 119 | { 120 | ScoreResult = 1.f - (FMath::Abs(TestLoc.X) + FMath::Abs(TestLoc.Y) + FMath::Abs(TestLoc.Z)) / MaxManhattanDistance; 121 | return (MinScore > ScoreResult) // 122 | ? ESenseTestResult::NotLost // 123 | : ESenseTestResult::Sensed; // 124 | } 125 | 126 | return ESenseTestResult::Sensed; 127 | } 128 | return ESenseTestResult::Lost; 129 | } 130 | 131 | 132 | #if WITH_EDITORONLY_DATA 133 | void USensorBoxTest::DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const 134 | { 135 | if (!BoxExtent.Equals(FVector::ZeroVector, KINDA_SMALL_NUMBER)) 136 | { 137 | const FTransform T = GetSensorTransform(); 138 | const FVector Loc = T.GetLocation(); 139 | const FQuat Q = bOrientedBox ? T.GetRotation() : FQuat::Identity; 140 | const auto BoxColor = FColor::Yellow; 141 | DrawOrientedWireBox(PDI, Loc, Q.GetForwardVector(), Q.GetRightVector(), Q.GetUpVector(), BoxExtent, BoxColor, SDPG_World); 142 | } 143 | } 144 | 145 | void USensorBoxTest::DrawDebug(const float Duration) const 146 | { 147 | #if ENABLE_DRAW_DEBUG 148 | if (!BoxExtent.Equals(FVector::ZeroVector, KINDA_SMALL_NUMBER)) 149 | { 150 | if (const UWorld* World = GetSensorOwner()->GetWorld()) 151 | { 152 | const FTransform T = GetSensorTransform(); 153 | const FVector Loc = T.GetLocation(); 154 | const FQuat Q = T.GetRotation(); 155 | const auto BoxColor = FColor::Yellow; 156 | const uint8 DepthPriorityGroup = SDPG_Foreground; 157 | 158 | DrawDebugBox(World, Loc, BoxExtent, Q, BoxColor, false, Duration, DepthPriorityGroup, 1.5f); 159 | } 160 | } 161 | #endif 162 | } 163 | #endif -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorAngleTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorAngleTest.h" 4 | #include "Sensors/SensorBase.h" 5 | #include "Math/UnrealMathUtility.h" 6 | 7 | #if WITH_EDITORONLY_DATA 8 | #include "DrawDebugHelpers.h" 9 | #include "SceneManagement.h" 10 | #endif 11 | 12 | 13 | USensorAngleTest::USensorAngleTest(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 14 | { 15 | bTestBySingleLocation = true; 16 | 17 | ScoreModifyCurve.GetRichCurve()->AddKey(0.f, 1.f, false); 18 | ScoreModifyCurve.GetRichCurve()->AddKey(1.f, 0.f, false); 19 | } 20 | 21 | #if WITH_EDITOR 22 | void USensorAngleTest::PostEditChangeProperty(struct FPropertyChangedEvent& e) 23 | { 24 | Super::PostEditChangeProperty(e); 25 | const FName PropertyName = (e.Property != nullptr) ? e.Property->GetFName() : NAME_None; 26 | if (PropertyName == GET_MEMBER_NAME_CHECKED(USensorAngleTest, MaxAngle) || PropertyName == GET_MEMBER_NAME_CHECKED(USensorAngleTest, MaxAngleLost)) 27 | { 28 | if (MaxAngleLost < MaxAngle) 29 | { 30 | MaxAngleLost = MaxAngle; 31 | } 32 | } 33 | } 34 | #endif 35 | 36 | void USensorAngleTest::SetAngleParam(const float Max_Angle, const float Max_AngleLost) 37 | { 38 | MaxAngle = FMath::Clamp(FMath::Abs(Max_Angle), 0.f, 180.f); 39 | MaxAngleLost = FMath::Clamp(FMath::Abs(Max_AngleLost), 0.f, 180.f); 40 | InitializeCacheTest(); 41 | } 42 | 43 | EUpdateReady USensorAngleTest::GetReadyToTest() 44 | { 45 | return Super::GetReadyToTest(); 46 | } 47 | 48 | 49 | bool USensorAngleTest::PreTest() 50 | { 51 | Super::PreTest(); 52 | 53 | if (ScoreModifyCurve.ExternalCurve) 54 | { 55 | ScoreModifyCurve.EditorCurveData = *(ScoreModifyCurve.GetRichCurve()); 56 | ScoreModifyCurve.ExternalCurve = nullptr; 57 | } 58 | 59 | const FTransform& T = GetSensorTransform(); 60 | TmpSelfForward = GetSensorTransform().GetRotation().GetForwardVector(); 61 | return true; 62 | } 63 | 64 | 65 | ESenseTestResult USensorAngleTest::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 66 | { 67 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_AngleTest); 68 | 69 | const FVector Delta = TestLocation - GetSensorTransform().GetLocation(); 70 | const float CosAngle = FVector::DotProduct(TmpSelfForward, Delta.GetSafeNormal()); 71 | 72 | if (CosAngle >= MaxAngleLostCos) 73 | { 74 | constexpr float Hpi = (180.0f) / PI; 75 | const float Angle = FMath::Acos(CosAngle) * Hpi; 76 | ScoreResult *= ModifyScoreByCurve(Angle / MaxAngleLost); 77 | 78 | return (MinScore > ScoreResult || Angle > MaxAngle) // 79 | ? ESenseTestResult::NotLost // 80 | : ESenseTestResult::Sensed; // 81 | } 82 | return ESenseTestResult::Lost; 83 | } 84 | 85 | void USensorAngleTest::InitializeCacheTest() 86 | { 87 | Super::InitializeCacheTest(); 88 | constexpr float DegToRad = PI / (180.f); 89 | MaxAngleCos = FMath::Cos(MaxAngle * DegToRad); 90 | MaxAngleLostCos = FMath::Cos(MaxAngleLost * DegToRad); 91 | } 92 | 93 | 94 | #if WITH_EDITORONLY_DATA 95 | void USensorAngleTest::DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const 96 | { 97 | if (MaxAngle > 0.0f) 98 | { 99 | const FTransform OriginalT = GetSensorTransform(); 100 | TArray Verts; 101 | 102 | { 103 | FTransform T = OriginalT; 104 | float DrawMaxAngle = MaxAngle; 105 | if (DrawMaxAngle > 90.f) 106 | { 107 | DrawMaxAngle = 180.f - DrawMaxAngle; 108 | T.ConcatenateRotation(FQuat(FVector::UpVector, PI)); 109 | } 110 | DrawWireCone(PDI, Verts, T, DrawDistance, DrawMaxAngle, 12, FColor::Yellow, SDPG_Foreground); 111 | } 112 | 113 | if (FMath::Abs(MaxAngleLost - MaxAngle) > KINDA_SMALL_NUMBER) 114 | { 115 | Verts.Reset(); 116 | FTransform T = OriginalT; 117 | float DrawMaxAngleLost = MaxAngleLost; 118 | if (DrawMaxAngleLost > 90.f) 119 | { 120 | DrawMaxAngleLost = 180.f - DrawMaxAngleLost; 121 | T.ConcatenateRotation(FQuat(FVector::UpVector, PI)); 122 | } 123 | DrawWireCone(PDI, Verts, T, DrawDistance, DrawMaxAngleLost, 12, FColor::Cyan, SDPG_Foreground); 124 | } 125 | } 126 | } 127 | 128 | void USensorAngleTest::DrawDebug(const float Duration) const 129 | { 130 | #if ENABLE_DRAW_DEBUG 131 | if (MaxAngle > KINDA_SMALL_NUMBER && GetSensorOwner()) 132 | { 133 | if (const UWorld* World = GetSensorOwner()->GetWorld()) 134 | { 135 | const float DrawDist = DrawDistance; 136 | constexpr uint8 Depth = SDPG_Foreground; 137 | 138 | FTransform OriginalT = GetSensorTransform(); 139 | const FVector X = OriginalT.GetUnitAxis(EAxis::X); 140 | const FQuat R = FRotationMatrix::MakeFromX(X).ToQuat(); 141 | OriginalT.SetRotation(R); 142 | 143 | { 144 | 145 | FVector Forward = TmpSelfForward; 146 | 147 | float DrawMaxAngle = MaxAngle; 148 | if (DrawMaxAngle > 90.f) 149 | { 150 | Forward = -Forward; 151 | DrawMaxAngle = 180.f - DrawMaxAngle; 152 | } 153 | const FColor Color = FColor::Yellow; 154 | DrawMaxAngle = FMath::DegreesToRadians(DrawMaxAngle); 155 | DrawDebugCone(World, OriginalT.GetLocation(), Forward, DrawDist, DrawMaxAngle, DrawMaxAngle, 12, Color, false, Duration, Depth, 1.5f); 156 | } 157 | 158 | if (FMath::Abs(MaxAngleLost - MaxAngle) > KINDA_SMALL_NUMBER) 159 | { 160 | FVector Forward = TmpSelfForward; 161 | 162 | float DrawMaxAngleLost = MaxAngleLost; 163 | if (DrawMaxAngleLost > 90.f) 164 | { 165 | Forward = -Forward; 166 | DrawMaxAngleLost = 180.f - DrawMaxAngleLost; 167 | } 168 | const FColor Color = FColor::Cyan; 169 | DrawMaxAngleLost = FMath::DegreesToRadians(DrawMaxAngleLost); 170 | DrawDebugCone(World, OriginalT.GetLocation(), Forward, DrawDist, DrawMaxAngleLost, DrawMaxAngleLost, 12, Color, false, Duration, Depth, 1.5f); 171 | } 172 | } 173 | } 174 | #endif 175 | } 176 | #endif 177 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/SensorTraceTestBase.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/SensorTraceTestBase.h" 4 | #include "SenseReceiverComponent.h" 5 | #include "Engine/EngineTypes.h" 6 | #include "GameFramework/Actor.h" 7 | #include "Sensors/SensorBase.h" 8 | #include "Sensors/Tests/SensorTestBase.h" 9 | #include "SensedStimulStruct.h" 10 | #include "SenseStimulusBase.h" 11 | #include "SenseObstacleInterface.h" 12 | #include "UObject/GarbageCollection.h" 13 | 14 | 15 | USensorTraceTestBase::USensorTraceTestBase(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 16 | { 17 | USensorTraceTestBase::InitCollisionParams(); 18 | } 19 | 20 | void USensorTraceTestBase::BeginDestroy() 21 | { 22 | Collision_Params = FCollisionQueryParams::DefaultQueryParam; 23 | Super::BeginDestroy(); 24 | } 25 | 26 | void USensorTraceTestBase::InitializeFromSensor() 27 | { 28 | InitCollisionParams(); 29 | USensorTestBase::InitializeFromSensor(); 30 | } 31 | 32 | void USensorTraceTestBase::SetBaseTraceParam(const ETraceTestParam TraceParam, const ECollisionChannel Collision, bool TraceComplex) 33 | { 34 | TraceTestParam = TraceParam; 35 | CollisionChannel = Collision; 36 | bTraceComplex = TraceComplex; 37 | } 38 | 39 | void USensorTraceTestBase::InitCollisionParams() 40 | { 41 | Collision_Params = FCollisionQueryParams(TEXT("SenseSysLineTrace"), SCENE_QUERY_STAT_ONLY(SenseTrace), true); 42 | Collision_Params.bReturnPhysicalMaterial = false; 43 | Collision_Params.bReturnFaceIndex = false; 44 | Collision_Params.bReturnPhysicalMaterial = false; 45 | Collision_Params.bTraceComplex = bTraceComplex; 46 | if (GetSensorOwner()) 47 | { 48 | Collision_Params.AddIgnoredActors(GetSensorOwner()->GetIgnoredActors()); 49 | } 50 | } 51 | 52 | bool USensorTraceTestBase::LineMultiTraceTest( 53 | const UWorld* World, const FCollisionQueryParams& CollisionParams, const FVector& StartTrace, FSensedStimulus& SensedStimulus) const 54 | { 55 | for (FSensedPoint& It : SensedStimulus.SensedPoints) 56 | { 57 | TArray OutHits; 58 | const bool bHit = World->LineTraceMultiByChannel(OutHits, StartTrace, It.SensedPoint, CollisionChannel, CollisionParams); 59 | if (!bHit) 60 | { 61 | return false; 62 | } 63 | It.PointTestResult = ESenseTestResult::Lost; 64 | } 65 | return true; 66 | } 67 | 68 | bool USensorTraceTestBase::LineBoolTraceTest( 69 | const UWorld* World, const FCollisionQueryParams& CollisionParams, const FVector& StartTrace, FSensedStimulus& SensedStimulus) const 70 | { 71 | for (FSensedPoint& It : SensedStimulus.SensedPoints) 72 | { 73 | const bool bHit = World->LineTraceTestByChannel(StartTrace, It.SensedPoint, CollisionChannel, CollisionParams); 74 | if (!bHit) 75 | { 76 | return false; 77 | } 78 | It.PointTestResult = ESenseTestResult::Lost; 79 | } 80 | return true; 81 | } 82 | 83 | 84 | bool USensorTraceTestBase::ScoreFromTransparency(const TArray& OutHits, float& InScore) const 85 | { 86 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTraceTestBase_ScoreFromTransparency); 87 | 88 | if (OutHits.Num() > 0) 89 | { 90 | const FName SenorTag = GetSensorTag(); 91 | FGCScopeGuard GCScope; 92 | for (const FHitResult& ItHit : OutHits) 93 | { 94 | UPrimitiveComponent* Component = ItHit.Component.Get(); 95 | const auto Owner = Component != nullptr ? Component->GetOwner() : nullptr; 96 | if (IsObstacleInterface(Owner)) 97 | { 98 | InScore *= ISenseObstacleInterface::Execute_GetTransparency(Owner, SenorTag, Component, ItHit.Location); 99 | } 100 | else 101 | { 102 | InScore = 0.f; 103 | return false; 104 | } 105 | } 106 | } 107 | return true; 108 | } 109 | 110 | // void USensorTraceTestBase::OnSensorChanged() 111 | // { 112 | // InitCollisionParams(); 113 | // } 114 | //FCollisionObjectQueryParams USensorTraceTestBase::GetCollisionObjectParams(const TArray>& ObjectTypes) const 115 | //{ 116 | // checkNoEntry(); 117 | // QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTraceTestBase_GetCollisionObjectParams); 118 | // 119 | // TArray> CollisionObjectTraces; 120 | // CollisionObjectTraces.AddUninitialized(ObjectTypes.Num()); 121 | // 122 | // for (auto Iterate = ObjectTypes.CreateConstIterator(); Iterate; Iterate++) 123 | // { 124 | // CollisionObjectTraces[Iterate.GetIndex()] = UEngineTypes::ConvertToCollisionChannel(*Iterate); 125 | // } 126 | // 127 | // FCollisionObjectQueryParams ObjectParams; 128 | // for (auto Iterate = CollisionObjectTraces.CreateConstIterator(); Iterate; Iterate++) 129 | // { 130 | // const ECollisionChannel& Channel = (*Iterate); 131 | // if (FCollisionObjectQueryParams::IsValidObjectQuery(Channel)) 132 | // { 133 | // ObjectParams.AddObjectTypesToQuery(Channel); 134 | // } 135 | // else 136 | // { 137 | // UE_LOG(LogBlueprintUserMessages, Warning, TEXT("%d isn't valid object type"), int32(Channel)); 138 | // } 139 | // } 140 | // 141 | // return ObjectParams; 142 | //} 143 | //FCollisionQueryParams USensorTraceTestBase::GetCollisionParams(const FSensedStimulus& SensedStimulus) const 144 | //{ 145 | // checkNoEntry(); 146 | // FCollisionQueryParams Params(TEXT("SenseSysTraceMulti"), SCENE_QUERY_STAT_ONLY(SenseTrace), true); 147 | // Params.AddIgnoredActors(GetIgnoreTraceActors(SensedStimulus)); 148 | // //Params.AddIgnoredComponent() 149 | // return Params; 150 | //} 151 | //TArray USensorTraceTestBase::GetIgnoreTraceActors(const FSensedStimulus& SensedStimulus) const 152 | //{ 153 | // checkNoEntry(); 154 | // QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_SensorTraceTestBase_GetIgnoreTraceActors); 155 | // if (SensedStimulus.StimulusComponent && GetSensorOwner() != nullptr && GetSensorOwner()->IsValidLowLevel()) 156 | // { 157 | // auto Ignore = GetSensorOwner()->GetIgnoredActors(); 158 | // if (AActor* IgnoredActor = SensedStimulus.StimulusComponent->GetOwner()) 159 | // { 160 | // Ignore.Add(IgnoredActor); 161 | // } 162 | // return Ignore; 163 | // } 164 | // return TArray{}; 165 | //} -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystemEditor/Private/BitFlag64_Customization.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "BitFlag64_Customization.h" 4 | 5 | #include "Widgets/DeclarativeSyntaxSupport.h" 6 | #include "Engine/GameViewportClient.h" 7 | #include "PropertyHandle.h" 8 | #include "IPropertyTypeCustomization.h" 9 | 10 | #include "Delegates/DelegateSignatureImpl.inl" 11 | #include "DetailWidgetRow.h" 12 | #include "Framework/Commands/UIAction.h" 13 | #include "Framework/MultiBox/MultiBoxBuilder.h" 14 | #include "Widgets/Input/SEditableTextBox.h" 15 | #include "Widgets/Input/SComboButton.h" 16 | #include "Widgets/Input/SSpinBox.h" 17 | #include "Widgets/Text/STextBlock.h" 18 | 19 | #include "Textures/SlateIcon.h" 20 | 21 | //#include "EditorStyleSet.h" 22 | //#include "Framework/Commands/UIAction.h" 23 | //#include "Widgets/Input/NumericTypeInterface.h" 24 | 25 | 26 | #define LOCTEXT_NAMESPACE "PropertyEditor" 27 | 28 | void SBitMask64Widget::Construct(const FArguments& InArgs) 29 | { 30 | BitMask = InArgs._InBitMask; 31 | ValueChanged = InArgs._OnValueChanged; 32 | ValueCommitted = InArgs._OnValueCommitted; 33 | 34 | auto CreateBitmaskFlagsArray = []() -> TStaticArray 35 | { 36 | TStaticArray Result; 37 | for (int32 Idx = 0; Idx < 64; Idx++) 38 | { 39 | Result[Idx].Value = 1llu << Idx; 40 | Result[Idx].DisplayName = FText::AsNumber(Idx + 1); 41 | Result[Idx].ToolTipText = FText::Format(LOCTEXT("BitmaskDefaultFlagToolTipText", "Channel {0} on/off"), Result[Idx].DisplayName); 42 | } 43 | return Result; 44 | }; 45 | 46 | const auto& GetComboButtonText = [this, CreateBitmaskFlagsArray]() -> FText 47 | { 48 | const int64 BitmaskValue = OnGetValue(); 49 | if (BitmaskValue != 0) 50 | { 51 | const TStaticArray BitmaskFlags = CreateBitmaskFlagsArray(); 52 | FString Out; 53 | 54 | for (int32 Idx = 0; Idx < 64; Idx++) 55 | { 56 | if (BitmaskValue & BitmaskFlags[Idx].Value) 57 | { 58 | const FString SText = Out.Len() == 0 ? FString::Printf(TEXT("%s"), *BitmaskFlags[Idx].DisplayName.ToString()) 59 | : FString::Printf(TEXT(", %s"), *BitmaskFlags[Idx].DisplayName.ToString()); 60 | Out.Append(SText); 61 | } 62 | } 63 | return FText::FromString(Out); 64 | } 65 | return LOCTEXT("BitmaskButtonContentNoFlagsSet", "None"); 66 | }; 67 | 68 | const FComboBoxStyle& ComboBoxStyle = FCoreStyle::Get().GetWidgetStyle("ComboBox"); 69 | 70 | SAssignNew(PrimaryWidget, SComboButton) 71 | .ComboButtonStyle(&ComboBoxStyle.ComboButtonStyle) 72 | .ContentPadding(FMargin(4.0, 2.0)) 73 | .ButtonContent()[SNew(STextBlock).Text_Lambda(GetComboButtonText)] 74 | .OnGetMenuContent_Lambda([this, CreateBitmaskFlagsArray]() 75 | { 76 | FMenuBuilder MenuBuilder(false, nullptr); 77 | TStaticArray BitmaskFlags = CreateBitmaskFlagsArray(); 78 | for (int32 i = 0; i < BitmaskFlags.Num(); i++) 79 | { 80 | MenuBuilder.AddMenuEntry( 81 | BitmaskFlags[i].DisplayName, 82 | BitmaskFlags[i].ToolTipText, 83 | FSlateIcon(), 84 | FUIAction( 85 | FExecuteAction::CreateLambda( 86 | [this, i, BitmaskFlags]() { OnValueCommitted((OnGetValue() ^ BitmaskFlags[i].Value), ETextCommit::Default); }), 87 | FCanExecuteAction(), 88 | FIsActionChecked::CreateLambda( 89 | [this, i, BitmaskFlags]() -> bool { return (OnGetValue() & BitmaskFlags[i].Value) != static_cast(0); })), 90 | NAME_None, 91 | EUserInterfaceActionType::Check); 92 | } 93 | return MenuBuilder.MakeWidget(); 94 | }); 95 | 96 | ChildSlot.AttachWidget(PrimaryWidget.ToSharedRef()); 97 | } 98 | 99 | 100 | void SBitMask64Widget::OnValueCommitted(const uint64 NewValue, const ETextCommit::Type CommitInfo) 101 | { 102 | BitMask = NewValue; 103 | ValueCommitted.ExecuteIfBound(BitMask, CommitInfo); 104 | } 105 | 106 | void SBitMask64Widget::OnValueChanged(const uint64 NewValue) 107 | { 108 | BitMask = NewValue; 109 | ValueChanged.ExecuteIfBound(BitMask); 110 | } 111 | 112 | #undef LOCTEXT_NAMESPACE 113 | 114 | void FBitFlag64_Customization::CustomizeHeader( 115 | TSharedRef StructPropertyHandle, FDetailWidgetRow& HeaderRow, IPropertyTypeCustomizationUtils& CustomizationUtils) 116 | { 117 | PropertyHandle = StructPropertyHandle; 118 | GT = FromProperty(); 119 | HeaderRow.NameContent()[StructPropertyHandle->CreatePropertyNameWidget()].ValueContent().MaxDesiredWidth(0.0f).MinDesiredWidth( 120 | 512.f)[SNew(SBitMask64Widget) 121 | .InBitMask(GT.Value) 122 | .OnValueChanged(this, &FBitFlag64_Customization::OnValueChanged) 123 | .OnValueCommitted(this, &FBitFlag64_Customization::OnValueCommitted)]; 124 | } 125 | 126 | void FBitFlag64_Customization::CustomizeChildren( 127 | TSharedRef StructPropertyHandle, IDetailChildrenBuilder& ChildBuilder, IPropertyTypeCustomizationUtils& CustomizationUtils) 128 | { 129 | } 130 | 131 | void FBitFlag64_Customization::OnValueChanged(const uint64 Val) 132 | { 133 | GT.Value = Val; 134 | } 135 | 136 | void FBitFlag64_Customization::OnValueCommitted(const uint64 Val, ETextCommit::Type CommitInfo) 137 | { 138 | GT.Value = Val; 139 | if (PropertyHandle.IsValid()) 140 | { 141 | TArray RawData; 142 | PropertyHandle->AccessRawData(RawData); 143 | for (void* RawDataInstance : RawData) 144 | { 145 | FBitFlag64_SenseSys& Ref = *(FBitFlag64_SenseSys*)(RawDataInstance) = GT; 146 | Ref = GT; 147 | } 148 | } 149 | else 150 | { 151 | UE_LOG(LogTemp, Error, TEXT("SenseSys FBitFlag64_Customization::OnValueCommitted PropertyHandle.IsValid() FAILED!")); 152 | } 153 | } 154 | 155 | FBitFlag64_SenseSys FBitFlag64_Customization::FromProperty() const 156 | { 157 | TArray RawData; 158 | PropertyHandle->AccessRawData(RawData); 159 | 160 | if (RawData.Num() != 1) 161 | { 162 | return FBitFlag64_SenseSys(); 163 | } 164 | FBitFlag64_SenseSys* DataPtr = (FBitFlag64_SenseSys*)(RawData[0]); 165 | if (DataPtr == nullptr) 166 | { 167 | return FBitFlag64_SenseSys(); 168 | } 169 | return *DataPtr; 170 | } 171 | 172 | FBitFlag64_Customization::FBitFlag64_Customization() 173 | { 174 | } 175 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SensedStimulStruct.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreTypes.h" 6 | #include "Containers/Array.h" 7 | #include "UObject/ObjectMacros.h" 8 | #include "UObject/UObjectBaseUtility.h" 9 | 10 | #include "SenseSystem.h" 11 | #include "SenseSysHelpers.h" 12 | 13 | #include "SensedStimulStruct.generated.h" 14 | 15 | 16 | class AActor; 17 | class USenseStimulusBase; 18 | 19 | 20 | /** SensedPoint */ 21 | USTRUCT(BlueprintType) 22 | struct SENSESYSTEM_API FSensedPoint 23 | { 24 | GENERATED_BODY() 25 | public: 26 | FSensedPoint() {} 27 | FSensedPoint(const FVector& V, const float Score = 0.f) : SensedPoint(V), PointScore(Score) {} 28 | ~FSensedPoint() {} 29 | 30 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 31 | FVector SensedPoint = FVector::ZeroVector; 32 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 33 | float PointScore = 0.f; 34 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 35 | ESenseTestResult PointTestResult = ESenseTestResult::None; 36 | 37 | friend FArchive& operator<<(FArchive& Ar, FSensedPoint& Sp) 38 | { 39 | Ar << Sp.SensedPoint; 40 | Ar << Sp.PointScore; 41 | Ar << Sp.PointTestResult; 42 | return Ar; 43 | } 44 | 45 | FORCEINLINE bool operator==(const FSensedPoint& Other) const 46 | { 47 | return SensedPoint == Other.SensedPoint && PointScore == Other.PointScore && PointTestResult == Other.PointTestResult; 48 | } 49 | FORCEINLINE bool operator!=(const FSensedPoint& Other) const { return !((*this) == Other); } 50 | }; 51 | 52 | 53 | /** SensedStimulus struct */ 54 | USTRUCT(BlueprintType) 55 | struct SENSESYSTEM_API FSensedStimulus 56 | { 57 | GENERATED_USTRUCT_BODY() 58 | public: 59 | FSensedStimulus() {} 60 | ~FSensedStimulus() {} 61 | 62 | FBox Init(const FName& SensorTag, USenseStimulusBase* Component, float InScore = 1.f, float InAge = 0.f, float CurrentTime = 0.f, uint64 InBitChannels = 0); 63 | 64 | 65 | /** StimulusComponent */ 66 | UPROPERTY(BlueprintReadOnly, SkipSerialization, Transient, Category = "SensedStimulus") 67 | TWeakObjectPtr StimulusComponent = nullptr; // 8 byte 68 | 69 | /** if StimulusComponent destroyed filtering will not crash result */ 70 | UPROPERTY(Transient) 71 | uint32 TmpHash = MAX_uint32; // 4 byte 72 | 73 | /** Sensed Score */ 74 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 75 | float Score = 1.f; // 4 byte 76 | 77 | /** Sensed Age */ 78 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 79 | float Age = 0.f; // 4 byte 80 | 81 | /** SensedTime World->GetTimeSeconds() */ 82 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 83 | float SensedTime = 0.f; // 4 byte 84 | 85 | /** First SensedTime World->GetTimeSeconds() */ 86 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Transient, Category = "SensedStimulus") 87 | float FirstSensedTime = -1.f; // 4 byte 88 | 89 | UPROPERTY() 90 | uint64 BitChannels = 0; // 8 byte //todo replace type to FBitFlag64_SenseSys 91 | 92 | UPROPERTY(EditAnywhere, BlueprintReadOnly, Transient, Category = "SensedStimulus") 93 | TWeakObjectPtr StimulusMeta; // 8 byte //todo: StimulusDataIdx 94 | 95 | /** SensedPoints */ 96 | UPROPERTY(BlueprintReadOnly, SkipSerialization, Transient, Category = "SensedStimulus") 97 | TArray SensedPoints; // 16 byte and dynamic memory allocation 98 | 99 | FORCEINLINE void InValidate() 100 | { 101 | StimulusComponent = nullptr; 102 | TmpHash = MAX_uint32; 103 | Score = -1.f; 104 | Age = 0.f; 105 | SensedPoints.Empty(); 106 | BitChannels = 0; 107 | } 108 | 109 | FORCEINLINE friend uint32 GetTypeHash(const FSensedStimulus& In) { return In.TmpHash; } 110 | FORCEINLINE bool operator==(const FSensedStimulus& Other) const { return TmpHash == Other.TmpHash; } 111 | FORCEINLINE bool operator==(const USenseStimulusBase* Ssc) const { return StimulusComponent.Get() == Ssc; } 112 | 113 | friend FArchive& operator<<(FArchive& Ar, FSensedStimulus& SS) 114 | { 115 | Ar << SS.Score; 116 | Ar << SS.Age; 117 | Ar << SS.TmpHash; 118 | Ar << SS.SensedTime; 119 | Ar << SS.BitChannels; 120 | Ar << SS.SensedPoints; 121 | return Ar; 122 | } 123 | 124 | FORCEINLINE FSensedStimulus& operator=(const FSensedStimulus& Other) 125 | { 126 | StimulusComponent = Other.StimulusComponent; 127 | TmpHash = Other.TmpHash; 128 | Score = Other.Score; 129 | Age = Other.Age; 130 | SensedTime = Other.SensedTime; 131 | FirstSensedTime = Other.FirstSensedTime; 132 | BitChannels = Other.BitChannels; 133 | SensedPoints = Other.SensedPoints; 134 | return *this; 135 | } 136 | }; 137 | 138 | /** TickingTimer struct */ 139 | struct SENSESYSTEM_API FTickingTimer 140 | { 141 | FTickingTimer() {} 142 | FTickingTimer(const float InUpdateTimeRate) : UpdateTimeRate(FMath::Max(InUpdateTimeRate, EFPSTime_SenseSys::fps120)) {} 143 | ~FTickingTimer() {} 144 | 145 | /** Zero value "0" - disable timer */ 146 | float UpdateTimeRate = EFPSTime_SenseSys::fps30; 147 | 148 | FORCEINLINE bool TickTimer(const float DeltaTime) 149 | { 150 | if (IsValidUpdateTimeRate()) 151 | { 152 | TimeToUpdate += DeltaTime; 153 | if (TimeToUpdate >= UpdateTimeRate) 154 | { 155 | TimeToUpdate = 0.f; 156 | return true; 157 | } 158 | } 159 | return false; 160 | } 161 | 162 | FORCEINLINE void SetUpdateTimeRate(const float NewValue) { UpdateTimeRate = FMath::Max(NewValue, EFPSTime_SenseSys::fps120); } 163 | FORCEINLINE void ContinueTimer() { bStopTimer = false; } 164 | FORCEINLINE void StopTimer() { bStopTimer = true; } 165 | FORCEINLINE bool IsStoppedTimer() const { return bStopTimer; } 166 | FORCEINLINE void ForceNextTickTimer() { TimeToUpdate = UpdateTimeRate; } 167 | 168 | private: 169 | FORCEINLINE bool IsValidUpdateTimeRate() const { return !bStopTimer; } 170 | 171 | float TimeToUpdate = 0.0f; 172 | bool bStopTimer = false; 173 | }; 174 | 175 | 176 | /** TrackBestSenseScore struct */ 177 | struct SENSESYSTEM_API FTrackBestSenseScore 178 | { 179 | FTrackBestSenseScore() 180 | { 181 | if (TrackBestScoreCount < 0) 182 | { 183 | TrackBestScoreCount = 0; 184 | } 185 | } 186 | FTrackBestSenseScore(const int32 InTrackBestScoreCount, const float InMinBestScore) 187 | : TrackBestScoreCount(InTrackBestScoreCount) 188 | , MinBestScore(InMinBestScore) 189 | { 190 | if (TrackBestScoreCount < 0) 191 | { 192 | TrackBestScoreCount = 0; 193 | } 194 | } 195 | FTrackBestSenseScore(const FTrackBestSenseScore& In) : TrackBestScoreCount(In.TrackBestScoreCount), MinBestScore(In.MinBestScore) 196 | { 197 | if (TrackBestScoreCount < 0) 198 | { 199 | TrackBestScoreCount = 0; 200 | } 201 | } 202 | ~FTrackBestSenseScore() {} 203 | 204 | int32 TrackBestScoreCount = 1; 205 | float MinBestScore = 0.f; 206 | TArray ScoreIDs; 207 | 208 | friend FArchive& operator<<(FArchive& Ar, FTrackBestSenseScore& Tbs) 209 | { 210 | Ar << Tbs.TrackBestScoreCount; 211 | Ar << Tbs.MinBestScore; 212 | Ar << Tbs.ScoreIDs; 213 | return Ar; 214 | } 215 | }; 216 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseSysHelpers.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "Engine/EngineTypes.h" 6 | #include "HAL/Platform.h" 7 | #include "UObject/ObjectMacros.h" 8 | 9 | #include "SenseSysHelpers.generated.h" 10 | 11 | 12 | /** 13 | * FPSTime_SenseSys SenseSys 14 | */ 15 | namespace EFPSTime_SenseSys 16 | { 17 | constexpr float fps120 = 1.0 / 120; 18 | constexpr float fps90 = 1.0 / 90; 19 | constexpr float fps60 = 1.0 / 60; 20 | constexpr float fps45 = 1.0 / 45; 21 | constexpr float fps30 = 1.0 / 30; 22 | constexpr float fps24 = 1.0 / 24; 23 | constexpr float fps15 = 1.0 / 15; 24 | constexpr float fps10 = 0.1; 25 | 26 | }; // namespace EFPSTime_SenseSys 27 | 28 | // clang-format off 29 | 30 | /** SensorType */ 31 | UENUM(BlueprintType) 32 | enum class ESensorType : uint8 33 | { 34 | None = 0, 35 | Active, 36 | Passive, 37 | Manual, 38 | }; 39 | 40 | /** SuccessState SenseSys */ 41 | UENUM(BlueprintType) 42 | enum class ESuccessState : uint8 43 | { 44 | Success = 0, 45 | Failed 46 | }; 47 | 48 | /** DrawDepth SenseSys */ 49 | UENUM(BlueprintType) 50 | enum class EDrawDepthSenseSys : uint8 51 | { 52 | /** World scene DPG. */ 53 | World, 54 | /** Foreground scene DPG. */ 55 | Foreground 56 | }; 57 | 58 | /** OnSenseEvent SenseSys */ 59 | UENUM(BlueprintType) 60 | enum class EOnSenseEvent : uint8 61 | { 62 | SenseCurrent = 0 UMETA(DisplayName = "OnSenseCurrent"), 63 | SenseNew UMETA(DisplayName = "OnSensedNew"), 64 | SenseLost UMETA(DisplayName = "OnSenseLost"), 65 | SenseForget UMETA(DisplayName = "OnSenseForget"), 66 | }; 67 | 68 | /** SenseTestResult SenseSys */ 69 | UENUM(BlueprintType) 70 | enum class ESenseTestResult : uint8 71 | { 72 | None = 0, 73 | Sensed = 1, 74 | NotLost = 2, 75 | Lost = 3 76 | }; 77 | 78 | /** SensorArrayByType SenseSys */ 79 | UENUM(BlueprintType) 80 | enum class ESensorArrayByType : uint8 81 | { 82 | SenseLost = 0, 83 | SensedNew = 1, 84 | SenseCurrent = 2, 85 | SenseCurrentLost = 3, 86 | SenseForget = 4 87 | }; 88 | 89 | /** SensorThreadType SenseSys */ 90 | UENUM(BlueprintType) 91 | enum class ESensorThreadType : uint8 92 | { 93 | Main_Thread = 0, 94 | Sense_Thread = 1, 95 | Sense_Thread_HighPriority = 2, 96 | Async_Task = 3 97 | }; 98 | 99 | // clang-format on 100 | 101 | /** DrawElementSetup SenseSys */ 102 | USTRUCT(BlueprintType) 103 | struct SENSESYSTEM_API FDrawElementSetup //40 bytes array pointer inside 104 | { 105 | GENERATED_USTRUCT_BODY() 106 | 107 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DrawElementSetup") 108 | FLinearColor Color = FLinearColor::White; 109 | 110 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DrawElementSetup") 111 | float Thickness = 1.f; 112 | 113 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "DrawElementSetup") 114 | EDrawDepthSenseSys DrawDepth = EDrawDepthSenseSys::World; 115 | 116 | static ESceneDepthPriorityGroup ToSceneDepthPriorityGroup(const EDrawDepthSenseSys DrawDepth) 117 | { 118 | switch (DrawDepth) 119 | { 120 | case EDrawDepthSenseSys::World: return ESceneDepthPriorityGroup::SDPG_World; 121 | case EDrawDepthSenseSys::Foreground: return ESceneDepthPriorityGroup::SDPG_Foreground; 122 | default: return ESceneDepthPriorityGroup::SDPG_World; ; 123 | } 124 | } 125 | }; 126 | 127 | 128 | /** DebugDrawFlag SenseSys */ 129 | USTRUCT(BlueprintType) 130 | struct SENSESYSTEM_API FSenseSysDebugDraw //40 bytes array pointer inside 131 | { 132 | GENERATED_USTRUCT_BODY() 133 | 134 | FSenseSysDebugDraw(const bool bAllVal) 135 | : Stimulus_DebugSensedPoints(bAllVal) 136 | , Stimulus_DebugCurrentSensed(bAllVal) 137 | , Stimulus_DebugLostSensed(bAllVal) 138 | , Sensor_DebugTest(bAllVal) 139 | , Sensor_DebugCurrentSensed(bAllVal) 140 | , Sensor_DebugLostSensed(bAllVal) 141 | , Sensor_DebugBestSensed(bAllVal) 142 | , SenseSys_DebugAge(bAllVal) 143 | , SenseSys_DebugScore(bAllVal) 144 | {} 145 | FSenseSysDebugDraw() : FSenseSysDebugDraw(true) {} 146 | 147 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 148 | bool Stimulus_DebugSensedPoints; 149 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 150 | bool Stimulus_DebugCurrentSensed; 151 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 152 | bool Stimulus_DebugLostSensed; 153 | 154 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 155 | bool Sensor_DebugTest; 156 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 157 | bool Sensor_DebugCurrentSensed; 158 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 159 | bool Sensor_DebugLostSensed; 160 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 161 | bool Sensor_DebugBestSensed; 162 | 163 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 164 | bool SenseSys_DebugAge; 165 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSysDebugDraw") 166 | bool SenseSys_DebugScore; 167 | }; 168 | 169 | 170 | /** BitFlag64 SenseSys */ 171 | USTRUCT(BlueprintType) 172 | struct SENSESYSTEM_API FBitFlag64_SenseSys 173 | { 174 | GENERATED_USTRUCT_BODY() 175 | 176 | FBitFlag64_SenseSys() {} 177 | FBitFlag64_SenseSys(const uint64 InValue) : Value(InValue) {} 178 | 179 | UPROPERTY() 180 | uint64 Value = 0; 181 | 182 | FORCEINLINE operator uint64&() { return Value; } 183 | FORCEINLINE operator const uint64&() const { return Value; } 184 | 185 | FBitFlag64_SenseSys& operator=(const uint64 Other) 186 | { 187 | Value = Other; 188 | return *this; 189 | } 190 | FBitFlag64_SenseSys& operator=(const FBitFlag64_SenseSys Other) 191 | { 192 | Value = Other.Value; 193 | return *this; 194 | } 195 | 196 | friend FArchive& operator<<(FArchive& Ar, FBitFlag64_SenseSys& Val) 197 | { 198 | Ar << Val.Value; 199 | return Ar; 200 | } 201 | 202 | static uint64 To_64BitFlagChannel(const TArray& InChan) 203 | { 204 | uint64 Out = 0; 205 | for (const uint8 Channel : InChan) 206 | { 207 | check(Channel < 64); 208 | Out |= 1llu << Channel; 209 | } 210 | return Out; 211 | } 212 | 213 | static TArray To_ArrayBitFlagChannel(uint64 InChan) 214 | { 215 | TArray Out; 216 | { 217 | int32 i = 0; 218 | while (InChan) 219 | { 220 | if (InChan & 1llu) Out.Add(static_cast(i)); 221 | i++; 222 | InChan = InChan >> 1; 223 | } 224 | } 225 | return Out; 226 | } 227 | }; 228 | 229 | 230 | /** DebugSenseSysHelpers SenseSys */ 231 | namespace EDebugSenseSysHelpers 232 | { 233 | constexpr float DebugSmallSize = 4.f; 234 | constexpr float DebugNormalSize = 8.f; 235 | constexpr float DebugBigSize = 16.f; 236 | constexpr float DebugLargeSize = 32.f; 237 | } // namespace EDebugSenseSysHelpers 238 | 239 | #if WITH_EDITOR | WITH_EDITORONLY_DATA 240 | 241 | enum class EOwnerBlueprintClassType : uint8 242 | { 243 | None = 0, 244 | ActorBP, 245 | SenseReceiverComponentBP, 246 | SensorBaseBP, 247 | }; 248 | 249 | struct FSenseSysRestoreObject 250 | { 251 | FSenseSysRestoreObject( // 252 | class UObject* InDefaultObject = nullptr, 253 | class UClass* InClass = nullptr, 254 | const FName& InName = NAME_None, 255 | const int32 InIdx = -1, 256 | const ESensorType InSensorType = ESensorType::None) 257 | : DefaultObject(InDefaultObject) 258 | , Class(InClass) 259 | , ObjectName(InName) 260 | , Idx(InIdx) 261 | , SensorType(InSensorType) 262 | {} 263 | 264 | UObject* DefaultObject /*= nullptr*/; 265 | UClass* Class /*= nullptr*/; 266 | FName ObjectName /*= NAME_None*/; 267 | int32 Idx /*= -1*/; 268 | ESensorType SensorType; 269 | }; 270 | 271 | #endif //WITH_EDITOR -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Public/SenseManager.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Engine/World.h" 7 | #include "Containers/Map.h" 8 | #include "Templates/UniquePtr.h" 9 | #include "Stats/Stats2.h" 10 | #include "Tickable.h" 11 | #include "UObject/NoExportTypes.h" 12 | #include "Subsystems/WorldSubsystem.h" 13 | 14 | #include "SenseSysHelpers.h" 15 | #include "SensedStimulStruct.h" 16 | #include "Sensors/SensorBase.h" 17 | #include "BaseSensorTask.h" 18 | 19 | #include "SenseManager.generated.h" 20 | 21 | 22 | struct FStimulusTagResponse; 23 | class USenseStimulusBase; 24 | class USenseReceiverComponent; 25 | class IContainerTree; 26 | class UObject; 27 | 28 | 29 | /** 30 | * RegisteredSensorTags struct 31 | */ 32 | struct SENSESYSTEM_API FRegisteredSensorTags final : FNoncopyable 33 | { 34 | FRegisteredSensorTags(); 35 | ~FRegisteredSensorTags(); 36 | 37 | friend class USenseManager; 38 | 39 | bool AddSenseStimulus(USenseStimulusBase* Ssc); 40 | bool RemoveSenseStimulus(USenseStimulusBase* Ssc); 41 | 42 | bool Add_SenseStimulus(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 43 | bool Remove_SenseStimulus(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 44 | 45 | const IContainerTree* GetContainerTree(const FName& Tag) const; 46 | IContainerTree* GetContainerTree(const FName& Tag); 47 | 48 | void Empty(); 49 | void Remove(const FName SensorTag); 50 | void CollapseAllTrees(); 51 | bool IsValidTag(const FName& SensorTag) const; 52 | const TMap>& GetMap() const { return SenseRegChannels; } 53 | 54 | private: 55 | TMap> SenseRegChannels; 56 | 57 | bool AddSenseStimulus_Internal(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 58 | bool RemoveSenseStimulus_Internal(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 59 | 60 | static TUniquePtr MakeTree(const FName Tag); 61 | 62 | //todo you need to make sure that a new element is not added when we take another 63 | mutable FCriticalSection CriticalSection; 64 | 65 | void Empty_Internal(); 66 | }; 67 | 68 | 69 | /** 70 | * Class for: 71 | * managing all sensing components 72 | * Communication between all sensing components 73 | * Managing Sense Thread 74 | */ 75 | UCLASS(BlueprintType, ClassGroup = (SenseSystem)) 76 | class SENSESYSTEM_API USenseManager final 77 | : public UWorldSubsystem 78 | , public FTickableGameObject 79 | { 80 | GENERATED_BODY() 81 | public: 82 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_TwoParams(FReport_StimulusEvent, int32, StimulusID, FName, SensorTag); 83 | DECLARE_DYNAMIC_MULTICAST_DELEGATE_OneParam(FOnObjStatusChanged, UObject*, Obj); 84 | 85 | USenseManager(); 86 | USenseManager(FVTableHelper& Helper); 87 | virtual ~USenseManager() override; 88 | 89 | virtual void Initialize(FSubsystemCollectionBase& Collection) override; 90 | virtual void Deinitialize() override; 91 | 92 | virtual void BeginDestroy() override; 93 | virtual void Cleanup(); 94 | 95 | void OnWorldCleanup(class UWorld* World, bool bSessionEnded, bool bCleanupResources); 96 | 97 | 98 | /**static SenseManager getter*/ 99 | static USenseManager* GetSenseManager(const UObject* WorldContext); 100 | 101 | public: 102 | FTickingTimer TickingTimer = FTickingTimer(0.5f); 103 | 104 | virtual UWorld* GetTickableGameObjectWorld() const override { return GetWorld(); } 105 | virtual bool IsTickableWhenPaused() const override { return false; } 106 | virtual bool IsTickable() const override { return true; /*GetWorld();*/ } 107 | virtual bool IsTickableInEditor() const override { return false; } 108 | virtual void Tick(float DeltaTime) override; 109 | virtual TStatId GetStatId() const override { RETURN_QUICK_DECLARE_CYCLE_STAT(USenseManager, STATGROUP_Tickables); }; 110 | 111 | public: 112 | bool RequestAsyncSenseUpdate(USensorBase* InSensor, bool bHighPriority) const; 113 | 114 | 115 | IContainerTree* const GetNamedContainerTree(const FName SensorTag) { return RegisteredSensorTags.GetContainerTree(SensorTag); } 116 | const IContainerTree* GetNamedContainerTree(const FName SensorTag) const { return RegisteredSensorTags.GetContainerTree(SensorTag); } 117 | 118 | UFUNCTION(BlueprintCallable, Category = "QuadTree") 119 | FBox GetIntersectTreeBox(FName SensorTag, FBox InBox); 120 | 121 | 122 | UFUNCTION(BlueprintCallable, Category = "QuadTree") 123 | void DrawTree(FName SensorTag, FDrawElementSetup TreeNode, FDrawElementSetup Link, FDrawElementSetup ElemNode, float LifeTime) const; 124 | 125 | /*********************************************/ 126 | 127 | /**Delegate for Communication passive sense events "Event channel"*/ 128 | UPROPERTY(/*BlueprintAssignable*/) 129 | FReport_StimulusEvent ReportStimulus_Event; 130 | 131 | /** Delegate UnregisterStimulus */ 132 | UPROPERTY(BlueprintAssignable) 133 | FOnObjStatusChanged On_UnregisterStimulus; 134 | 135 | /** Delegate UnregisterReceiver */ 136 | UPROPERTY(BlueprintAssignable) 137 | FOnObjStatusChanged On_UnregisterReceiver; 138 | 139 | UPROPERTY() 140 | TSet Receivers; 141 | UPROPERTY() 142 | TSet StimulsWorldOrigin; 143 | 144 | bool RegisterSenseReceiver(USenseReceiverComponent* Receiver); 145 | bool UnRegisterSenseReceiver(USenseReceiverComponent* Receiver); 146 | 147 | void Add_ReceiverSensor(USenseReceiverComponent* Receiver, USensorBase* Sensor, const bool bThreadCountUpdt); 148 | void Remove_ReceiverSensor(USenseReceiverComponent* Receiver, USensorBase* Sensor, const bool bThreadCountUpdt); 149 | 150 | 151 | bool RegisterSenseStimulus(USenseStimulusBase* Stimulus); 152 | bool UnRegisterSenseStimulus(USenseStimulusBase* Stimulus); 153 | 154 | void Add_SenseStimulus(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 155 | void Remove_SenseStimulus(USenseStimulusBase* Ssc, const FName& SensorTag, FStimulusTagResponse& Str); 156 | 157 | 158 | void ChangeSensorThreadType( 159 | const USenseReceiverComponent* Receiver, 160 | const ESensorThreadType NewSensorThreadType, 161 | const ESensorThreadType OldSensorThreadType); 162 | 163 | bool HaveReceivers() const; 164 | bool HaveSenseStimulus() const; 165 | 166 | bool IsHaveStimulusTag(const FName Tag) const; 167 | bool IsHaveReceiverTag(const FName Tag) const; 168 | 169 | void PreWorldOriginOffsetUpdt(UWorld* InWorld, FIntVector OriginLocation, FIntVector NewOriginLocation); 170 | void PostWorldOriginOffsetUpdt(UWorld* InWorld, FIntVector OriginLocation, FIntVector NewOriginLocation); 171 | 172 | #if WITH_EDITORONLY_DATA 173 | UPROPERTY(BlueprintReadWrite, Category = "SenseSystem|SenseManager") 174 | bool bSenseThreadPauseLog = false; 175 | UPROPERTY(BlueprintReadWrite, Category = "SenseManager|SenseSystem") 176 | bool bSenseThreadStateLog = true; 177 | #endif 178 | 179 | protected: 180 | /**StimulusContainer*/ 181 | FRegisteredSensorTags RegisteredSensorTags; 182 | 183 | bool SenseThread_CreateIfNeed(); 184 | bool SenseThread_DeleteIfNeed(); 185 | 186 | /**Destroy Sense Thread*/ 187 | void Close_SenseThread(); 188 | 189 | private: 190 | double WaitTime = 0.0001f; 191 | int32 CounterLimit = 10; 192 | 193 | /**Receivers with ContainsThread counter*/ 194 | uint32 ContainsThreadCount = 0; 195 | 196 | TMap TagReceiversCount; 197 | 198 | uint32 StimulusCount = 0; 199 | 200 | /**SenseThread ptr*/ 201 | TUniquePtr SenseThread = nullptr; 202 | 203 | /**Create Sense Thread*/ 204 | void Create_SenseThread(); 205 | 206 | #if WITH_EDITORONLY_DATA 207 | 208 | public: 209 | UPROPERTY(EditAnywhere, BlueprintReadWrite, Category = "SenseSystem|SenseManager") 210 | FSenseSysDebugDraw SenseSysDebugDraw; 211 | 212 | FSenseSysDebugDraw GetSenseSysDebugDraw(const FName& SensorTag = NAME_None) const; 213 | void SetSenseSysDebugDraw(FSenseSysDebugDraw DebugDrawParam); 214 | 215 | void DebugDrawSenseSys(const class FSceneView* View, class FPrimitiveDrawInterface* PDI) const; 216 | void DebugDrawSenseSysHUD(const class FViewport* Viewport, const class FSceneView* View, class FCanvas* Canvas) const; 217 | 218 | #endif 219 | }; -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseDetectPool.h: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #pragma once 4 | 5 | #include "CoreMinimal.h" 6 | #include "Math/NumericLimits.h" 7 | #include "HAL/Platform.h" 8 | #include "Containers/Array.h" 9 | #include "Containers/SparseArray.h" 10 | #include 11 | 12 | #include "SenseSystem.h" 13 | #include "SensedStimulStruct.h" 14 | using ElementIndexType = FSenseSystemModule::ElementIndexType; 15 | 16 | class FSenseDetectPool final 17 | { 18 | TSparseArray ObjPool; 19 | public: 20 | FSenseDetectPool() { ObjPool.Reserve(128); } 21 | ~FSenseDetectPool() {} 22 | 23 | const TSparseArray& GetPool() const; 24 | TSparseArray& GetPool(); 25 | 26 | TArray Current; 27 | TArray Lost; 28 | TArray Forget; 29 | 30 | TArray NewCurrent; 31 | TArray LostCurrent; 32 | 33 | FTrackBestSenseScore Best_Sense; 34 | 35 | TArray DetectNew; 36 | TArray DetectCurrent; 37 | 38 | 39 | public: 40 | void Add(const float CurrentTime, FSensedStimulus SS, const ElementIndexType ID) 41 | { 42 | if (SS.TmpHash != MAX_uint32) 43 | { 44 | if (ID == TNumericLimits::Max()) 45 | { 46 | SS.FirstSensedTime = CurrentTime; 47 | DetectNew.Add(AddToPool(MoveTemp(SS))); 48 | 49 | } 50 | else 51 | { 52 | check(ObjPool.IsValidIndex(ID)); 53 | DetectCurrent.Add(AddToPool(ID, MoveTemp(SS))); 54 | } 55 | } 56 | } 57 | 58 | ElementIndexType ContainsInCurrentSense(const FSensedStimulus& InElem) const; 59 | ElementIndexType ContainsInLostSense(const FSensedStimulus& InElem) const; 60 | 61 | ElementIndexType ContainsIn(const FSensedStimulus& InElem, const TArray& InArr) const; 62 | 63 | void NewSensedUpdate(EOnSenseEvent Ost, bool bOverrideSenseState, bool bNewSenseForcedByBestScore); 64 | void EmptyUpdate(EOnSenseEvent Ost, bool bOverrideSenseState = true); 65 | 66 | void NewSensed(EOnSenseEvent Ost, bool bNewSenseForcedByBestScore); 67 | void AddSensed(EOnSenseEvent Ost, bool bNewSenseForcedByBestScore); 68 | 69 | void NewAgeUpdate(float CurrentTime, EOnSenseEvent Ost); 70 | 71 | TArray GetArray_Copy(const TArray& Arr) const; 72 | TArray GetArrayCopy_SenseEvent(ESensorArrayByType SenseEvent) const; 73 | 74 | void ResetArr(ESensorArrayByType SenseEvent); 75 | void EmptyArr(ESensorArrayByType SenseEvent); 76 | 77 | void Empty(); 78 | 79 | 80 | template 81 | static void BestIdsByPredicate(const int32 Count, const TArray& InArr, SortTPredicate Predicate, TArray& Out); 82 | 83 | bool LostIndex(const ElementIndexType ID); 84 | 85 | private: 86 | 87 | FORCEINLINE ElementIndexType AddToPool(FSensedStimulus&& Elem) 88 | { 89 | return ObjPool.Add(MoveTemp(Elem)); 90 | } 91 | FORCEINLINE ElementIndexType AddToPool(const ElementIndexType PoolId, FSensedStimulus&& Elem) 92 | { 93 | FSensedStimulus& Obj = ObjPool[PoolId]; 94 | Elem.FirstSensedTime = Obj.FirstSensedTime; 95 | Obj = MoveTemp(Elem); 96 | return PoolId; 97 | } 98 | 99 | void BestScoreUpdt(TArray& InArr, bool bNewSenseForcedByBestScore); 100 | 101 | 102 | TArray& BySenseEvent_Ref(ESensorArrayByType SenseEvent); 103 | const TArray& BySenseEvent_Ref(ESensorArrayByType SenseEvent) const; 104 | 105 | void ResetArr(TArray& A, int32 Size); 106 | void ResetArr(TArray& A) { ResetArr(A, A.Num()); } 107 | void EmptyArr(TArray& A); 108 | void EmptyArr(TArray&& A); 109 | 110 | 111 | #if WITH_EDITOR 112 | public: 113 | void CheckSort() const; 114 | bool CheckPoolDup(); 115 | bool CheckPool(); 116 | #endif 117 | }; 118 | 119 | FORCEINLINE void FSenseDetectPool::Empty() 120 | { 121 | ObjPool.Empty(); 122 | NewCurrent.Empty(); 123 | Current.Empty(); 124 | LostCurrent.Empty(); 125 | Forget.Empty(); 126 | Lost.Empty(); 127 | Best_Sense.ScoreIDs.Empty(); 128 | } 129 | 130 | FORCEINLINE const TSparseArray& FSenseDetectPool::GetPool() const 131 | { 132 | return ObjPool; 133 | } 134 | FORCEINLINE TSparseArray& FSenseDetectPool::GetPool() 135 | { 136 | return ObjPool; 137 | } 138 | 139 | FORCEINLINE TArray& FSenseDetectPool::BySenseEvent_Ref(const ESensorArrayByType SenseEvent) 140 | { 141 | switch (SenseEvent) 142 | { 143 | case ESensorArrayByType::SensedNew: return NewCurrent; 144 | case ESensorArrayByType::SenseCurrent: return Current; 145 | case ESensorArrayByType::SenseCurrentLost: return LostCurrent; 146 | case ESensorArrayByType::SenseForget: return Forget; 147 | case ESensorArrayByType::SenseLost: return Lost; 148 | } 149 | checkNoEntry(); 150 | return Current; 151 | } 152 | FORCEINLINE const TArray& FSenseDetectPool::BySenseEvent_Ref(const ESensorArrayByType SenseEvent) const 153 | { 154 | switch (SenseEvent) 155 | { 156 | case ESensorArrayByType::SensedNew: return NewCurrent; 157 | case ESensorArrayByType::SenseCurrent: return Current; 158 | case ESensorArrayByType::SenseCurrentLost: return LostCurrent; 159 | case ESensorArrayByType::SenseForget: return Forget; 160 | case ESensorArrayByType::SenseLost: return Lost; 161 | } 162 | checkNoEntry(); 163 | return Current; 164 | } 165 | FORCEINLINE TArray FSenseDetectPool::GetArrayCopy_SenseEvent(const ESensorArrayByType SenseEvent) const 166 | { 167 | return GetArray_Copy(BySenseEvent_Ref(SenseEvent)); 168 | } 169 | 170 | FORCEINLINE void FSenseDetectPool::ResetArr(TArray& A, const int32 Size) 171 | { 172 | for (const auto i : A) 173 | { 174 | ObjPool.RemoveAt(i); 175 | } 176 | A.Reset(Size); 177 | } 178 | FORCEINLINE void FSenseDetectPool::ResetArr(const ESensorArrayByType SenseEvent) 179 | { 180 | check(SenseEvent != ESensorArrayByType::SensedNew && SenseEvent != ESensorArrayByType::SenseCurrentLost); 181 | ResetArr(BySenseEvent_Ref(SenseEvent)); 182 | } 183 | 184 | FORCEINLINE void FSenseDetectPool::EmptyArr(TArray& A) 185 | { 186 | if (A.Num()) 187 | { 188 | for (const auto i : A) 189 | { 190 | ObjPool.RemoveAt(i); 191 | } 192 | A.Empty(); 193 | } 194 | } 195 | FORCEINLINE void FSenseDetectPool::EmptyArr(TArray&& A) 196 | { 197 | if (A.Num()) 198 | { 199 | for (const auto i : A) 200 | { 201 | ObjPool.RemoveAt(i); 202 | } 203 | A.Empty(); 204 | } 205 | } 206 | 207 | FORCEINLINE void FSenseDetectPool::EmptyArr(const ESensorArrayByType SenseEvent) 208 | { 209 | check(SenseEvent != ESensorArrayByType::SensedNew && SenseEvent != ESensorArrayByType::SenseCurrentLost); 210 | EmptyArr(BySenseEvent_Ref(SenseEvent)); 211 | } 212 | 213 | FORCEINLINE ElementIndexType FSenseDetectPool::ContainsInCurrentSense(const FSensedStimulus& InElem) const 214 | { 215 | return ContainsIn(InElem, Current); 216 | } 217 | FORCEINLINE ElementIndexType FSenseDetectPool::ContainsInLostSense(const FSensedStimulus& InElem) const 218 | { 219 | return ContainsIn(InElem, Lost); 220 | } 221 | 222 | 223 | template 224 | inline void FSenseDetectPool::BestIdsByPredicate(const int32 Count, const TArray& InArr, SortTPredicate Predicate, TArray& Out) 225 | { 226 | const int32 InArrNum = InArr.Num(); 227 | if (InArrNum > 0 && Count > 0) 228 | { 229 | if (Count == 1) // O(N) 230 | { 231 | Out.Init(0, 1); 232 | for (int32 i = 1; i < InArrNum; i++) 233 | { 234 | if (Predicate(i, Out[0])) 235 | { 236 | Out[0] = i; 237 | } 238 | } 239 | } 240 | else 241 | { 242 | Out.Reset(InArrNum); 243 | for (int32 i = 0; i < InArrNum; i++) 244 | { 245 | Out.Add(i); 246 | } 247 | 248 | const int32 SortNum = FMath::Min(Count, Out.Num()); 249 | if (Count < 10 && Out.Num() != SortNum) // O(N log K) 250 | { 251 | std::partial_sort(Out.GetData(), Out.GetData() + SortNum, Out.GetData() + InArrNum, Predicate); 252 | } 253 | else 254 | { 255 | if (Out.Num() != SortNum) // O(N) 256 | { 257 | std::nth_element(Out.GetData(), Out.GetData() + SortNum, Out.GetData() + InArrNum, Predicate); 258 | } 259 | TArrayView ArrayView = TArrayView(Out.GetData(), SortNum); 260 | Algo::Sort(ArrayView, Predicate); // O(K log K) 261 | } 262 | 263 | if (Out.Num() > Count) 264 | { 265 | Out.RemoveAt(Count, Out.Num() - Count, true); 266 | } 267 | } 268 | } 269 | else 270 | { 271 | Out.Empty(); 272 | } 273 | } -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/SenseSystemBPLibrary.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "SenseSystemBPLibrary.h" 4 | 5 | #include "SenseStimulusBase.h" 6 | #include "SenseReceiverComponent.h" 7 | #include "SenseStimulusInterface.h" 8 | #include "Components/SkeletalMeshComponent.h" 9 | #include "Components/SkinnedMeshComponent.h" 10 | 11 | #include "Engine/Engine.h" 12 | #include "Engine/InheritableComponentHandler.h" 13 | #include "Engine/SCS_Node.h" 14 | #include "Engine/SimpleConstructionScript.h" 15 | 16 | //#include "Async/ParallelFor.h" 17 | 18 | 19 | USenseSystemBPLibrary::USenseSystemBPLibrary(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 20 | {} 21 | 22 | 23 | USenseStimulusBase* USenseSystemBPLibrary::GetStimulusFromActor(const AActor* Actor, const bool bInterfaceOnly /*= false*/) 24 | { 25 | USenseStimulusBase* Out = nullptr; 26 | if (IsValid(Actor)) 27 | { 28 | if (!Actor->IsUnreachable()) 29 | { 30 | /* 31 | // cpp interface implementation 32 | const ISenseStimulusInterface* ISenseStimulus = Cast(Actor); 33 | if (ISenseStimulus) 34 | { 35 | return ISenseStimulus->IGetActorStimulus(); 36 | } 37 | else 38 | */ 39 | if (Actor->GetClass()->ImplementsInterface(USenseStimulusInterface::StaticClass())) 40 | { 41 | Out = ISenseStimulusInterface::Execute_IGetActorStimulus(Actor); 42 | } 43 | } 44 | 45 | if (!Out && !bInterfaceOnly) 46 | { 47 | return Actor->FindComponentByClass(); 48 | } 49 | } 50 | return Out; 51 | } 52 | 53 | USenseReceiverComponent* USenseSystemBPLibrary::GetReceiverFromActor(const AActor* Actor) 54 | { 55 | if (IsValid(Actor)) 56 | { 57 | return Actor->FindComponentByClass(); 58 | } 59 | return nullptr; 60 | } 61 | 62 | USenseStimulusBase* USenseSystemBPLibrary::GetStimulusFromActor_BP(const AActor* Actor) 63 | { 64 | return GetStimulusFromActor(Actor, false); 65 | } 66 | 67 | USenseReceiverComponent* USenseSystemBPLibrary::GetReceiverFromActor_BP(const AActor* Actor) 68 | { 69 | return GetReceiverFromActor(Actor); 70 | } 71 | 72 | 73 | bool FSkeletalBoneIDCache::Init(const class USkeletalMeshComponent* SkeletalMeshComponent, const TArray& BoneNames) 74 | { 75 | bool bNoErrors = false; 76 | if (SkeletalMeshComponent) 77 | { 78 | SkelMesh = SkeletalMeshComponent->GetSkinnedAsset(); 79 | if (SkelMesh) 80 | { 81 | bNoErrors = true; 82 | BoneIDs.Reserve(BoneNames.Num()); 83 | for (const FName& BnName : BoneNames) 84 | { 85 | const int32 BoneIndex = SkeletalMeshComponent->GetBoneIndex(BnName); //SkeletalMeshComponent->GetSocketLocation(BnName); 86 | if (BoneIndex != INDEX_NONE) 87 | { 88 | BoneIDs.Add(BoneIndex); 89 | } 90 | else 91 | { 92 | bNoErrors = false; 93 | } 94 | } 95 | } 96 | } 97 | return bNoErrors; 98 | } 99 | 100 | bool FSkeletalBoneIDCache::GetBoneLocations(const class USkeletalMeshComponent* SkeletalMeshComponent, TArray& Out) const 101 | { 102 | if (IsValid(SkeletalMeshComponent) && SkelMesh == SkeletalMeshComponent->GetSkinnedAsset()) 103 | { 104 | Out.Reserve(BoneIDs.Num()); 105 | for (const int32 BnID : BoneIDs) 106 | { 107 | const FVector Loc = SkeletalMeshComponent->GetBoneTransform(BnID).GetLocation(); 108 | Out.Add(Loc); 109 | } 110 | return true; 111 | } 112 | return false; 113 | } 114 | 115 | FSkeletalBoneIDCache::FSkeletalBoneIDCache(const class USkeletalMeshComponent* SkeletalMeshComponent, const TArray& BoneNames) 116 | { 117 | if (SkeletalMeshComponent) 118 | { 119 | SkelMesh = SkeletalMeshComponent->GetSkinnedAsset(); 120 | if (SkelMesh) 121 | { 122 | BoneIDs.Reserve(BoneNames.Num()); 123 | for (const FName& BnName : BoneNames) 124 | { 125 | const int32 BoneIndex = SkeletalMeshComponent->GetBoneIndex(BnName); //SkeletalMeshComponent->GetSocketLocation(BnName); 126 | if (BoneIndex != INDEX_NONE) BoneIDs.Add(BoneIndex); 127 | } 128 | } 129 | } 130 | } 131 | 132 | 133 | #if WITH_EDITOR 134 | 135 | UBlueprintGeneratedClass* USenseSystemBPLibrary::GetOwnerBlueprintClass(UObject* Obj) 136 | { 137 | UBlueprintGeneratedClass* BaseClass = nullptr; 138 | if (IsValid(Obj)) 139 | { 140 | UObject* PackageObj = Obj; 141 | for (UObject* Top = PackageObj;;) 142 | { 143 | UObject* CurrentOuter = Top->GetOuter(); 144 | if (!CurrentOuter) 145 | { 146 | //check(Cast(PackageObj) || Cast(PackageObj->GetClass())); 147 | break; 148 | } 149 | PackageObj = Top; 150 | Top = CurrentOuter; 151 | } 152 | 153 | if (PackageObj) 154 | { 155 | if (Cast(PackageObj)) 156 | { 157 | BaseClass = Cast(PackageObj); //if Actor Package 158 | } 159 | if (!BaseClass) 160 | { 161 | BaseClass = Cast(PackageObj->GetClass()); 162 | } 163 | } 164 | } 165 | 166 | return BaseClass; 167 | } 168 | 169 | USenseReceiverComponent* USenseSystemBPLibrary::GetSenseReceiverComponent_FromDefaultActorClass(UClass* InClass, const FName& Name) 170 | { 171 | if (InClass) 172 | { 173 | TArray Components; 174 | 175 | { 176 | TArray Templates; 177 | if (const AActor* DefaultActor = Cast(InClass->GetDefaultObject(true))) 178 | { 179 | DefaultActor->GetComponents(USenseReceiverComponent::StaticClass(), Templates); 180 | Components.Append(Templates); 181 | } 182 | } 183 | 184 | if (UBlueprintGeneratedClass* BP_Class = Cast(InClass)) 185 | { 186 | if (BP_Class->GetInheritableComponentHandler(true)) 187 | { 188 | TArray Templates; 189 | 190 | BP_Class->GetInheritableComponentHandler()->GetAllTemplates(Templates); 191 | Components.Append(Templates); 192 | } 193 | 194 | if (BP_Class->SimpleConstructionScript) 195 | { 196 | TArray Templates; 197 | 198 | auto Nodes = BP_Class->SimpleConstructionScript->GetRootNodes(); 199 | for (const USCS_Node* Node : Nodes) 200 | { 201 | if (Node && Node->ComponentTemplate->GetFName() == Name && 202 | Node->ComponentTemplate->GetClass()->IsChildOf(USenseReceiverComponent::StaticClass())) 203 | { 204 | Components.Add(Node->ComponentTemplate); 205 | break; 206 | } 207 | } 208 | //AActor* DefaultActor = Class_1->SimpleConstructionScript->GetComponentEditorActorInstance(); 209 | //DefaultActor->GetComponents(USenseReceiverComponent::StaticClass(), Templates); 210 | //Components.Append(Templates); 211 | } 212 | 213 | UBlueprint* Blueprint = Cast(BP_Class->ClassGeneratedBy); 214 | for (UActorComponent* Component : Blueprint->ComponentTemplates) 215 | { 216 | if (Component && Component->GetFName() == Name && Component->GetClass()->IsChildOf(USenseReceiverComponent::StaticClass())) 217 | { 218 | Components.Add(Component); 219 | } 220 | } 221 | } 222 | 223 | if (Components.Num() > 0) 224 | { 225 | Components = Components.FilterByPredicate( 226 | // 227 | [&Name](const UActorComponent* Comp) // 228 | { 229 | // 230 | return Comp->GetFName() == Name && Comp->GetClass()->IsChildOf(USenseReceiverComponent::StaticClass()); 231 | }); 232 | } 233 | if (Components.Num() > 0) 234 | { 235 | return Cast(Components[0]); 236 | } 237 | } 238 | return nullptr; 239 | } 240 | 241 | EOwnerBlueprintClassType USenseSystemBPLibrary::GetOwnerBlueprintClassType(UClass* BaseClass) 242 | { 243 | if (BaseClass) 244 | { 245 | if (BaseClass->IsChildOf(AActor::StaticClass())) 246 | { 247 | return EOwnerBlueprintClassType::ActorBP; 248 | } 249 | if (BaseClass->IsChildOf(USenseReceiverComponent::StaticClass())) 250 | { 251 | return EOwnerBlueprintClassType::SenseReceiverComponentBP; 252 | } 253 | if (BaseClass->IsChildOf(USensorBase::StaticClass())) 254 | { 255 | return EOwnerBlueprintClassType::SensorBaseBP; 256 | } 257 | } 258 | return EOwnerBlueprintClassType::None; 259 | } 260 | 261 | UClass* USenseSystemBPLibrary::GetOwnerBlueprintClassType(const EOwnerBlueprintClassType In) 262 | { 263 | switch (In) 264 | { 265 | case EOwnerBlueprintClassType::None: return nullptr; 266 | case EOwnerBlueprintClassType::ActorBP: return AActor::StaticClass(); 267 | case EOwnerBlueprintClassType::SenseReceiverComponentBP: return USenseReceiverComponent::StaticClass(); 268 | case EOwnerBlueprintClassType::SensorBaseBP: return USensorBase::StaticClass(); 269 | default: 270 | { 271 | checkNoEntry(); 272 | UE_ASSUME(0); 273 | return nullptr; 274 | } 275 | } 276 | } 277 | 278 | 279 | #endif 280 | 281 | void USenseSystemBPLibrary::ForceGarbageCollection() 282 | { 283 | if (GEngine) 284 | { 285 | GEngine->ForceGarbageCollection(true); 286 | } 287 | } -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/SensorTouch.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/SensorTouch.h" 4 | #include "Sensors/Tests/SensorBoolTest.h" 5 | #include "SenseReceiverComponent.h" 6 | #include "SenseStimulusComponent.h" 7 | #include "SenseSystemBPLibrary.h" 8 | #include "SenseManager.h" 9 | 10 | #include "GameFramework/Actor.h" 11 | #include "Components/SceneComponent.h" 12 | #include "Engine/World.h" 13 | 14 | #if WITH_EDITORONLY_DATA 15 | #include "Components/CapsuleComponent.h" 16 | #include "Components/BoxComponent.h" 17 | #include "Components/SphereComponent.h" 18 | 19 | #include "Engine/Engine.h" 20 | #include "CanvasTypes.h" 21 | #include "SceneManagement.h" 22 | #include "SceneView.h" 23 | #include "UnrealClient.h" 24 | #endif 25 | 26 | 27 | USensorTouch::USensorTouch(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 28 | { 29 | SensorTests.SetNum(1); 30 | SensorTests[0] = CreateDefaultSubobject(TEXT("BoolTest")); 31 | auto BT = Cast(SensorTests[0]); 32 | if (BT) 33 | { 34 | BT->TestResult = ESenseTestResult::Sensed; 35 | } 36 | } 37 | 38 | USensorTouch::~USensorTouch() {} 39 | 40 | void USensorTouch::BeginDestroy() 41 | { 42 | UnBindHitEvent(TouchCollisions); 43 | TouchCollisions.Empty(); 44 | Super::BeginDestroy(); 45 | } 46 | 47 | void USensorTouch::Cleanup() 48 | { 49 | UnBindHitEvent(TouchCollisions); 50 | Super::Cleanup(); 51 | } 52 | 53 | #if WITH_EDITOR 54 | void USensorTouch::PostEditChangeProperty(struct FPropertyChangedEvent& e) 55 | { 56 | Super::PostEditChangeProperty(e); 57 | } 58 | #endif 59 | 60 | #if WITH_EDITORONLY_DATA 61 | void USensorTouch::DrawSensor(const class FSceneView* View, class FPrimitiveDrawInterface* PDI) const 62 | { 63 | const UWorld* World = GetWorld(); 64 | if (!bEnable && !World) return; 65 | 66 | FSenseSysDebugDraw DebugDraw; 67 | if (GetSenseManager() && World->IsGameWorld()) 68 | { 69 | DebugDraw = GetSenseManager()->GetSenseSysDebugDraw(SensorTag); 70 | } 71 | else 72 | { 73 | DebugDraw = FSenseSysDebugDraw(false); 74 | DebugDraw.Sensor_DebugTest = true; 75 | } 76 | 77 | if (DebugDraw.Sensor_DebugTest) 78 | { 79 | const FLinearColor Color = FLinearColor::Yellow; 80 | for (const auto Prim : TouchCollisions) 81 | { 82 | if (Prim != nullptr) 83 | { 84 | const FTransform T = Prim->GetComponentTransform(); 85 | const FVector Loc = T.GetLocation(); 86 | const FQuat Q = T.GetRotation(); 87 | 88 | const UCapsuleComponent* Capsule = Cast(Prim); 89 | if (Capsule) 90 | { 91 | DrawWireCapsule( 92 | PDI, 93 | Loc, 94 | Q.GetForwardVector(), 95 | Q.GetRightVector(), 96 | Q.GetUpVector(), 97 | Color, 98 | Capsule->GetScaledCapsuleRadius() + 1.f, 99 | Capsule->GetScaledCapsuleHalfHeight() + 1.f, 100 | 8, 101 | SDPG_World); 102 | continue; 103 | } 104 | const UBoxComponent* Box = Cast(Prim); 105 | if (Box) 106 | { 107 | DrawOrientedWireBox( 108 | PDI, 109 | Loc, 110 | Q.GetForwardVector(), 111 | Q.GetRightVector(), 112 | Q.GetUpVector(), 113 | Box->GetScaledBoxExtent() + 1.f, 114 | Color, 115 | SDPG_World); 116 | continue; 117 | } 118 | const USphereComponent* Sphere = Cast(Prim); 119 | if (Sphere) 120 | { 121 | DrawWireSphere(PDI, T, Color, Sphere->GetScaledSphereRadius() + 1.f, 8, SDPG_World); 122 | } 123 | } 124 | } 125 | } 126 | Super::DrawSensor(View, PDI); 127 | } 128 | 129 | void USensorTouch::DrawSensorHUD(const class FViewport* Viewport, const class FSceneView* View, class FCanvas* Canvas) const 130 | { 131 | Super::DrawSensorHUD(Viewport, View, Canvas); 132 | } 133 | 134 | #endif 135 | 136 | 137 | void USensorTouch::SetTouchCollision(UPrimitiveComponent* Prim) 138 | { 139 | if (IsValid(Prim)) 140 | { 141 | //UnBindHitEvent(Prim); 142 | FScopeLock Lock_CriticalSection(&SensorCriticalSection); 143 | RemoveTouchCollisions(TouchCollisions); 144 | TouchCollisions.Empty(); 145 | AddTouchCollision(Prim); 146 | TouchCollisions.Shrink(); 147 | } 148 | } 149 | 150 | void USensorTouch::SetTouchCollisions(TArray InTouchCollisions) 151 | { 152 | //UnBindHitEvent(TouchCollisions); 153 | FScopeLock Lock_CriticalSection(&SensorCriticalSection); 154 | RemoveTouchCollisions(TouchCollisions); 155 | TouchCollisions.Reset(); 156 | AddTouchCollisions(InTouchCollisions); 157 | TouchCollisions.Shrink(); 158 | } 159 | 160 | 161 | void USensorTouch::AddTouchCollision(UPrimitiveComponent* InTouchCollision) 162 | { 163 | if (IsValid(InTouchCollision)) 164 | { 165 | TouchCollisions.AddUnique(InTouchCollision); 166 | BindHitEvent(InTouchCollision); 167 | } 168 | } 169 | void USensorTouch::RemoveTouchCollision(UPrimitiveComponent* InTouchCollision) 170 | { 171 | if (InTouchCollision != nullptr) 172 | { 173 | UnBindHitEvent(InTouchCollision); 174 | TouchCollisions.Remove(InTouchCollision); 175 | } 176 | } 177 | 178 | void USensorTouch::AddTouchCollisions(TArray InTouchCollisions) 179 | { 180 | for (auto Prim : InTouchCollisions) 181 | { 182 | if (IsValid(Prim)) 183 | { 184 | AddTouchCollision(Prim); 185 | } 186 | } 187 | } 188 | void USensorTouch::RemoveTouchCollisions(TArray InTouchCollisions) 189 | { 190 | for (auto Prim : InTouchCollisions) 191 | { 192 | if (Prim != nullptr) 193 | { 194 | RemoveTouchCollision(Prim); 195 | } 196 | } 197 | } 198 | 199 | /************************************/ 200 | 201 | void USensorTouch::OnTouchHit( 202 | UPrimitiveComponent* HitComponent, // 203 | AActor* OtherActor, 204 | UPrimitiveComponent* OtherComp, 205 | FVector NormalImpulse, 206 | const FHitResult& Hit) 207 | { 208 | if (IsValidForTest_Short() && bEnable && GetSenseReceiverComponent()->bEnableSenseReceiver) 209 | { 210 | if (USenseStimulusBase* SenseStimulus = USenseSystemBPLibrary::GetStimulusFromActor(OtherActor)) 211 | { 212 | ReportPassiveEvent(SenseStimulus); 213 | if (const UWorld* World = GetWorld()) 214 | { 215 | NeedLost.Add(SenseStimulus); 216 | World->GetTimerManager().SetTimerForNextTick(this, &USensorTouch::UpdateNeedLost); 217 | } 218 | } 219 | } 220 | } 221 | 222 | void USensorTouch::OnBeginOverlap( 223 | UPrimitiveComponent* OverlappedComponent, 224 | AActor* OtherActor, 225 | UPrimitiveComponent* OtherComp, 226 | int32 OtherBodyIndex, 227 | bool bFromSweep, 228 | const FHitResult& SweepResult) 229 | { 230 | if (IsValidForTest_Short() && bEnable && GetSenseReceiverComponent()->bEnableSenseReceiver) 231 | { 232 | if (USenseStimulusBase* SenseStimulus = USenseSystemBPLibrary::GetStimulusFromActor(OtherActor)) 233 | { 234 | ReportPassiveEvent(SenseStimulus); 235 | } 236 | } 237 | } 238 | 239 | void USensorTouch::OnEndOverlap(UPrimitiveComponent* OverlappedComponent, AActor* OtherActor, UPrimitiveComponent* OtherComp, int32 OtherBodyIndex) 240 | { 241 | if (IsValidForTest_Short() && bEnable && GetSenseReceiverComponent()->bEnableSenseReceiver) 242 | { 243 | if (USenseStimulusBase* SenseStimulus = USenseSystemBPLibrary::GetStimulusFromActor(OtherActor)) 244 | { 245 | NeedLost.Add(SenseStimulus); 246 | UpdateNeedLost(); 247 | } 248 | } 249 | } 250 | 251 | 252 | void USensorTouch::BindHitEvent(UPrimitiveComponent* InTouchCollision) 253 | { 254 | if (IsValid(InTouchCollision)) 255 | { 256 | if (bOnComponentHit) 257 | { 258 | InTouchCollision->OnComponentHit.AddUniqueDynamic(this, &USensorTouch::OnTouchHit); 259 | } 260 | if (bOnComponentBeginOverlap) 261 | { 262 | InTouchCollision->OnComponentBeginOverlap.AddUniqueDynamic(this, &USensorTouch::OnBeginOverlap); 263 | InTouchCollision->OnComponentEndOverlap.AddUniqueDynamic(this, &USensorTouch::OnEndOverlap); 264 | } 265 | } 266 | } 267 | void USensorTouch::BindHitEvent(TArray InTouchCollisions) 268 | { 269 | for (auto Prim : InTouchCollisions) 270 | { 271 | if (IsValid(Prim)) 272 | { 273 | BindHitEvent(Prim); 274 | } 275 | } 276 | } 277 | void USensorTouch::UnBindHitEvent(UPrimitiveComponent* InTouchCollision) 278 | { 279 | if (IsValid(InTouchCollision)) 280 | { 281 | InTouchCollision->OnComponentHit.RemoveDynamic(this, &USensorTouch::OnTouchHit); 282 | InTouchCollision->OnComponentBeginOverlap.RemoveDynamic(this, &USensorTouch::OnBeginOverlap); 283 | InTouchCollision->OnComponentEndOverlap.RemoveDynamic(this, &USensorTouch::OnEndOverlap); 284 | } 285 | } 286 | 287 | void USensorTouch::UnBindHitEvent(TArray InTouchCollisions) 288 | { 289 | for (auto Prim : InTouchCollisions) 290 | { 291 | if (Prim) UnBindHitEvent(Prim); 292 | } 293 | } 294 | 295 | 296 | void USensorTouch::InitializeFromReceiver(USenseReceiverComponent* InSenseReceiver) 297 | { 298 | Super::InitializeFromReceiver(InSenseReceiver); 299 | if (GetSenseReceiverComponent() != nullptr && GetSenseReceiverComponent()->GetOwner() != nullptr && bActorRootCollision) 300 | { 301 | const auto Root = GetSenseReceiverComponent()->GetOwner()->GetRootComponent(); 302 | if (Root) 303 | { 304 | const auto Collision = Cast(Root); 305 | if (Collision) 306 | { 307 | SetTouchCollision(Collision); 308 | } 309 | } 310 | } 311 | } 312 | 313 | void USensorTouch::LostCurrentSensed() 314 | { 315 | for (const FChannelSetup& Ch : ChannelSetup) 316 | { 317 | Ch.EmptyUpdate(DetectDepth, true); 318 | } 319 | } 320 | void USensorTouch::UpdateNeedLost() 321 | { 322 | for (auto It : NeedLost) 323 | { 324 | for (const FChannelSetup& Ch : ChannelSetup) 325 | { 326 | Ch.EmptyUpdate(DetectDepth, true); 327 | } 328 | } 329 | NeedLost.Empty(); 330 | SensorTimer.ContinueTimer(); 331 | DetectionLostAndForgetUpdate(); 332 | TreadSafePostUpdate(); 333 | } 334 | 335 | /* 336 | void USensorTouch::Serialize(FArchive& Ar) 337 | { 338 | Super::Serialize(Ar); 339 | if (SensorTests.Num() > 0) 340 | { 341 | auto DSO = Cast(GetDefaultSubobjectByName(TEXT("BoolTest"))); 342 | if (SensorTests[0]) 343 | { 344 | if (SensorTests[0] != DSO) // ...if they don't match, the saved state needs to be fixed up. 345 | { 346 | SensorTests[0]->Rename(nullptr, GetTransientPackage(), REN_DontCreateRedirectors | REN_ForceNoResetLoaders); 347 | SensorTests[0] = DSO; 348 | } 349 | } 350 | else 351 | { 352 | SensorTests[0] = DSO; 353 | } 354 | 355 | } 356 | } 357 | */ 358 | -------------------------------------------------------------------------------- /SenseSystem/Source/SenseSystem/Private/Sensors/Tests/FrustumTest.cpp: -------------------------------------------------------------------------------- 1 | //Copyright 2020 Alexandr Marchenko. All Rights Reserved. 2 | 3 | #include "Sensors/Tests/FrustumTest.h" 4 | #include "Engine/Engine.h" 5 | #include "UObject/Object.h" 6 | #include "Math/UnrealMathUtility.h" 7 | #include "SensedStimulStruct.h" 8 | #include "Sensors/SensorBase.h" 9 | #include "Sensors/Tests/SensorTestBase.h" 10 | 11 | #if WITH_EDITORONLY_DATA 12 | #include "DrawDebugHelpers.h" 13 | #include "SceneManagement.h" 14 | #endif 15 | 16 | 17 | UFrustumTest::UFrustumTest(const FObjectInitializer& ObjectInitializer) : Super(ObjectInitializer) 18 | { 19 | bTestBySingleLocation = true; 20 | TmpData = FFrustumTestData(FOVAngle, AspectRatio, FarPlaneDistance); 21 | } 22 | 23 | UFrustumTest::~UFrustumTest() 24 | {} 25 | 26 | void UFrustumTest::BeginDestroy() 27 | { 28 | Super::BeginDestroy(); 29 | } 30 | 31 | #if WITH_EDITOR 32 | void UFrustumTest::PostEditChangeProperty(struct FPropertyChangedEvent& e) 33 | { 34 | USensorTestBase::PostEditChangeProperty(e); 35 | SetFrustumParam(FOVAngle, AspectRatio, FarPlaneDistance); 36 | TmpData = FFrustumTestData(FOVAngle, AspectRatio, FarPlaneDistance); 37 | InitializeCacheTest(); 38 | } 39 | #endif 40 | 41 | #if WITH_EDITORONLY_DATA 42 | void UFrustumTest::DrawTest(const FSceneView* View, FPrimitiveDrawInterface* PDI) const 43 | { 44 | if (FarPlaneDistance > KINDA_SMALL_NUMBER && FOVAngle > 1.f) 45 | { 46 | const auto FrustumColor = FColor::Yellow; 47 | constexpr uint8 DepthPriorityGroup = SDPG_Foreground; 48 | const FTransform& T = GetSensorTransform(); 49 | FVector Verts[5]; 50 | Verts[0] = T.GetLocation(); 51 | Verts[1] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Max.Y)); 52 | Verts[2] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Min.Y)); 53 | Verts[3] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Min.Y)); 54 | Verts[4] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Max.Y)); 55 | 56 | PDI->DrawLine(Verts[0], Verts[1], FrustumColor, DepthPriorityGroup); 57 | PDI->DrawLine(Verts[0], Verts[2], FrustumColor, DepthPriorityGroup); 58 | PDI->DrawLine(Verts[0], Verts[3], FrustumColor, DepthPriorityGroup); 59 | PDI->DrawLine(Verts[0], Verts[4], FrustumColor, DepthPriorityGroup); 60 | 61 | PDI->DrawLine(Verts[1], Verts[2], FrustumColor, DepthPriorityGroup); 62 | PDI->DrawLine(Verts[2], Verts[3], FrustumColor, DepthPriorityGroup); 63 | PDI->DrawLine(Verts[3], Verts[4], FrustumColor, DepthPriorityGroup); 64 | PDI->DrawLine(Verts[4], Verts[1], FrustumColor, DepthPriorityGroup); 65 | } 66 | } 67 | void UFrustumTest::DrawDebug(const float Duration) const 68 | { 69 | #if ENABLE_DRAW_DEBUG 70 | if (FarPlaneDistance > KINDA_SMALL_NUMBER && FOVAngle > 1.f && GetSensorOwner()) 71 | { 72 | if (const UWorld* World = GetSensorOwner()->GetWorld()) 73 | { 74 | const auto FrustumColor = FColor::Yellow; 75 | constexpr uint8 DepthPriorityGroup = SDPG_Foreground; 76 | const FTransform& T = GetSensorTransform(); 77 | FVector Verts[5]; 78 | Verts[0] = T.GetLocation(); 79 | Verts[1] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Max.Y)); 80 | Verts[2] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Min.Y)); 81 | Verts[3] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Min.Y)); 82 | Verts[4] = T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Max.Y)); 83 | 84 | DrawDebugLine(World, Verts[0], Verts[1], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 85 | DrawDebugLine(World, Verts[0], Verts[2], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 86 | DrawDebugLine(World, Verts[0], Verts[3], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 87 | DrawDebugLine(World, Verts[0], Verts[4], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 88 | 89 | DrawDebugLine(World, Verts[1], Verts[2], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 90 | DrawDebugLine(World, Verts[2], Verts[3], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 91 | DrawDebugLine(World, Verts[3], Verts[4], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 92 | DrawDebugLine(World, Verts[4], Verts[1], FrustumColor, false, Duration, DepthPriorityGroup, 1.5f); 93 | } 94 | } 95 | #endif 96 | } 97 | #endif 98 | 99 | void UFrustumTest::SetFrustumParam(const float FieldOfView, const float Aspect_Ratio, const float FarDistancePlane) 100 | { 101 | FOVAngle = FieldOfView; 102 | AspectRatio = Aspect_Ratio; 103 | FarPlaneDistance = FarDistancePlane; 104 | 105 | TmpData = FFrustumTestData(FOVAngle, AspectRatio, FarPlaneDistance); 106 | InitializeCacheTest(); 107 | } 108 | 109 | void UFrustumTest::SetFrustumSelectionRectangleParam( 110 | const FVector2D Point1, 111 | const FVector2D Point2, 112 | const FVector2D ViewportSize, 113 | const float FOV, 114 | const float FarDistance) 115 | { 116 | AspectRatio = ViewportSize.X / ViewportSize.Y; 117 | FOVAngle = FOV; 118 | FarPlaneDistance = FarDistance; 119 | 120 | TmpData = FFrustumTestData(Point1, Point2, ViewportSize, FOVAngle, FarPlaneDistance); 121 | InitializeCacheTest(); 122 | } 123 | 124 | void UFrustumTest::InitializeFromSensor() 125 | { 126 | check(GetOuter() == GetSensorOwner()); 127 | SensorTransform = GetSensorOwner()->GetSensorTransform(); 128 | 129 | SetFrustumParam(FOVAngle, AspectRatio, FarPlaneDistance); 130 | InitializeCacheTest(); 131 | 132 | if (IsValid(GetSensorOwner())) 133 | { 134 | InitializeFromSensorBP(GetSensorOwner()); 135 | } 136 | } 137 | 138 | void UFrustumTest::InitializeCacheTest() 139 | { 140 | Super::InitializeCacheTest(); 141 | } 142 | 143 | 144 | EUpdateReady UFrustumTest::GetReadyToTest() 145 | { 146 | if (FarPlaneDistance > KINDA_SMALL_NUMBER && FOVAngle > KINDA_SMALL_NUMBER) 147 | { 148 | return Super::GetReadyToTest(); 149 | } 150 | return EUpdateReady::Fail; 151 | } 152 | 153 | bool UFrustumTest::PreTest() 154 | { 155 | Super::PreTest(); 156 | 157 | const FTransform& T = GetSensorTransform(); 158 | 159 | TmpSelfForward = T.GetRotation().GetForwardVector(); 160 | 161 | const FVector L = T.GetLocation(); 162 | AABB_Box = FBox(L, L); 163 | AABB_Box += T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Max.Y)); 164 | AABB_Box += T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Min.Y)); 165 | AABB_Box += T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Max.X, TmpData.Bound.Min.Y)); 166 | AABB_Box += T.TransformPositionNoScale(FVector(FarPlaneDistance, TmpData.Bound.Min.X, TmpData.Bound.Max.Y)); 167 | 168 | return true; 169 | } 170 | 171 | ESenseTestResult UFrustumTest::RunTest(FSensedStimulus& SensedStimulus) const 172 | { 173 | return Super::RunTest(SensedStimulus); 174 | } 175 | 176 | ESenseTestResult UFrustumTest::RunTestForLocation(const FSensedStimulus& SensedStimulus, const FVector& TestLocation, float& ScoreResult) const 177 | { 178 | QUICK_SCOPE_CYCLE_COUNTER(STAT_SenseSys_FrustumTest); 179 | if (AABB_Box.IsInsideOrOn(TestLocation)) //AABB 180 | { 181 | const FVector Delta = TestLocation - GetSensorTransform().GetLocation(); 182 | const float Dot = FVector::DotProduct(TmpSelfForward, Delta); 183 | 184 | if (Dot > 0.f && Dot <= FarPlaneDistance) 185 | { 186 | const FVector RelativeReceiverLoc = GetSensorTransform().InverseTransformVectorNoScale(Delta); 187 | const FVector::FReal RelativeReceiverLocX_Far = FarPlaneDistance / RelativeReceiverLoc.X; 188 | const FVector IntersectPoint = RelativeReceiverLocX_Far * RelativeReceiverLoc; 189 | 190 | if (TmpData.IsInsideBound(IntersectPoint)) 191 | { 192 | float LocScore = 0.f; 193 | if (bScoreDistance) 194 | { 195 | LocScore += RelativeReceiverLocX_Far; 196 | } 197 | if (bScoreScreenDistance) 198 | { 199 | const float S = ScoreByScreenSpaceManhattanDistance(IntersectPoint); 200 | LocScore = bScoreDistance ? (LocScore + S) * 0.5f : S; 201 | } 202 | ScoreResult *= LocScore; 203 | 204 | return (MinScore > ScoreResult) ? ESenseTestResult::NotLost : ESenseTestResult::Sensed; 205 | } 206 | } 207 | } 208 | ScoreResult = 0; 209 | return ESenseTestResult::Lost; 210 | } 211 | 212 | 213 | FFrustumTestData::FFrustumTestData( 214 | const FVector2D Point1, 215 | const FVector2D Point2, 216 | const FVector2D ViewportSize, 217 | const float FOVAngle, 218 | const float FarPlaneDistance) 219 | { 220 | const float AspectRatio = ViewportSize.X / ViewportSize.Y; 221 | const float HozHalfAngleInRadians = FMath::DegreesToRadians(FOVAngle * 0.5f); 222 | 223 | if (FOVAngle > 0 && AspectRatio > KINDA_SMALL_NUMBER) 224 | { 225 | const float BoundX = FarPlaneDistance * FMath::Tan(HozHalfAngleInRadians); 226 | const float BoundY = BoundX / AspectRatio; 227 | 228 | Bound.Max.X = FMath::GetMappedRangeValueUnclamped(FVector2D(0.0, ViewportSize.X), FVector2D(-1.0, 1.0), Point1.X) * BoundX; 229 | Bound.Max.Y = FMath::GetMappedRangeValueUnclamped(FVector2D(0.0, ViewportSize.Y), FVector2D(1.0, -1.0), Point1.Y) * BoundY; 230 | 231 | Bound.Min.X = FMath::GetMappedRangeValueUnclamped(FVector2D(0.0, ViewportSize.X), FVector2D(-1.0, 1.0), Point2.X) * BoundX; 232 | Bound.Min.Y = FMath::GetMappedRangeValueUnclamped(FVector2D(0.0, ViewportSize.Y), FVector2D(1.0, -1.0), Point2.Y) * BoundY; 233 | } 234 | 235 | InitDefault(FarPlaneDistance); 236 | } 237 | 238 | FFrustumTestData::FFrustumTestData(const float FOVAngle, const float AspectRatio, const float FarPlaneDistance) 239 | { 240 | const float HozHalfAngleInRadians = FMath::DegreesToRadians(FOVAngle * 0.5f); 241 | if (FOVAngle > 0) 242 | { 243 | Bound.Max.X = FarPlaneDistance * FMath::Tan(HozHalfAngleInRadians); 244 | Bound.Max.Y = Bound.Max.X / AspectRatio; 245 | Bound.Min = (-Bound.Max); 246 | } 247 | 248 | InitDefault(FarPlaneDistance); 249 | } 250 | 251 | void FFrustumTestData::InitDefault(const float FarDistance) 252 | { 253 | { 254 | const FVector2D TmpMin = Bound.Min; 255 | 256 | Bound.Min.X = FMath::Min(Bound.Min.X, Bound.Max.X); 257 | Bound.Min.Y = FMath::Min(Bound.Min.Y, Bound.Max.Y); 258 | Bound.Max.X = FMath::Max(Bound.Max.X, TmpMin.X); 259 | Bound.Max.Y = FMath::Max(Bound.Max.Y, TmpMin.Y); 260 | Center = Bound.GetCenter(); 261 | const FVector2D Extent = Bound.GetExtent(); 262 | MaxManhattanDistance = Extent.X + Extent.Y; 263 | } 264 | { 265 | const FVector V1(FarDistance, Bound.Max.X, Bound.Max.Y); 266 | const FVector V2(FarDistance, Bound.Min.X, Bound.Min.Y); 267 | const FVector& VMax = Bound.Max.SizeSquared() > Bound.Min.SizeSquared() ? V1 : V2; 268 | MaxRadius = VMax.Size(); 269 | } 270 | { 271 | const FVector2D::FReal MaxX = FMath::Max(FMath::Abs(Bound.Max.X), FMath::Abs(Bound.Min.X)); 272 | const FVector2D::FReal MaxY = FMath::Max(FMath::Abs(Bound.Max.Y), FMath::Abs(Bound.Min.Y)); 273 | MaxCosAngle = FVector::DotProduct(FVector::ForwardVector, FVector(FarDistance, MaxX, MaxY).GetSafeNormal()); 274 | } 275 | } 276 | --------------------------------------------------------------------------------