HAxis sos
This commit is contained in:
20
Source/UnrealProject/GUI/Minimap/MiniMap.cpp
Normal file
20
Source/UnrealProject/GUI/Minimap/MiniMap.cpp
Normal file
@@ -0,0 +1,20 @@
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMap.h"
|
||||
|
||||
|
||||
namespace MiniMap
|
||||
{
|
||||
void NodeBase::CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects)
|
||||
{
|
||||
if (objects.empty())
|
||||
return;
|
||||
|
||||
for (auto iter = objects.begin(); iter != objects.end(); iter++)
|
||||
{
|
||||
MinimapHandle& obj = **iter;
|
||||
if (FVector2D::DistSquared(obj.position, position) <= radius_sqr)
|
||||
out_objects.Add(&obj);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
325
Source/UnrealProject/GUI/Minimap/MiniMap.h
Normal file
325
Source/UnrealProject/GUI/Minimap/MiniMap.h
Normal file
@@ -0,0 +1,325 @@
|
||||
#pragma once
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include <unordered_set>
|
||||
using namespace std;
|
||||
|
||||
|
||||
class ACharacterBase;
|
||||
namespace MiniMap
|
||||
{
|
||||
struct Rect
|
||||
{
|
||||
Rect() = default;
|
||||
Rect(float x, float y, float width, float height) : x(x), y(y), width(width), height(height) {}
|
||||
Rect(const FVector2D& position, const FVector2D& size) : x(position.X), y(position.Y), width(size.X), height(size.Y) {}
|
||||
float x;
|
||||
float y;
|
||||
float width;
|
||||
float height;
|
||||
|
||||
inline FVector2D position() const
|
||||
{
|
||||
return FVector2D(x, y);
|
||||
}
|
||||
inline FVector2D size() const
|
||||
{
|
||||
return FVector2D(width, height);
|
||||
}
|
||||
inline bool contains(const FVector2D& pos) const
|
||||
{
|
||||
return pos.X >= x && pos.X <= (x + width) && pos.Y >= y && pos.Y <= (y + height);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
inline Rect GetSubArea(const Rect& area, size_t idx)
|
||||
{
|
||||
const FVector2D half_size = area.size() / 2;
|
||||
const FVector2D position = area.position();
|
||||
switch (idx)
|
||||
{
|
||||
case 0: return Rect(position, half_size);
|
||||
case 1: return Rect(position + FVector2D(half_size.X, 0), half_size);
|
||||
case 2: return Rect(position + half_size, half_size);
|
||||
case 3: return Rect(position + FVector2D(0, half_size.Y), half_size);
|
||||
}
|
||||
return area;
|
||||
}
|
||||
inline bool RectCircleOverlapSquared(const Rect& area, const FVector2D& position, float radius_sqr)
|
||||
{
|
||||
const FVector2D pos = area.position();
|
||||
const float left = area.x;
|
||||
const float bottom = area.y;
|
||||
const float right = area.x + area.width;
|
||||
const float top = area.y + area.height;
|
||||
|
||||
const float x = position.X > right ? right : position.X < left ? left : position.X;
|
||||
const float y = position.Y > top ? top : position.Y < bottom ? bottom : position.Y;
|
||||
|
||||
return FVector2D::DistSquared(position, FVector2D(x, y)) <= radius_sqr;
|
||||
}
|
||||
|
||||
class NonCopyable
|
||||
{
|
||||
public:
|
||||
NonCopyable() = default;
|
||||
NonCopyable(const NonCopyable& other) = delete;
|
||||
NonCopyable& operator=(const NonCopyable& other) = delete;
|
||||
};
|
||||
|
||||
class MinimapHandle;
|
||||
class NodeBase : private NonCopyable
|
||||
{
|
||||
protected:
|
||||
NodeBase(const Rect& area, NodeBase* parent) : area(area), parent(parent) {}
|
||||
|
||||
public:
|
||||
virtual void AddObject(MinimapHandle& obj) = 0;
|
||||
virtual void RemoveObject(MinimapHandle& obj) = 0;
|
||||
virtual void ClearRecursive()
|
||||
{
|
||||
objects.clear();
|
||||
}
|
||||
virtual void ResizeRecursive(const Rect& area)
|
||||
{
|
||||
this->area = area;
|
||||
}
|
||||
|
||||
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects);
|
||||
|
||||
unordered_set<MinimapHandle*> objects;
|
||||
Rect area;
|
||||
NodeBase* parent;
|
||||
};
|
||||
|
||||
class MinimapHandle : private NonCopyable
|
||||
{
|
||||
public:
|
||||
MinimapHandle() : character(nullptr), node(nullptr), root(nullptr) {}
|
||||
MinimapHandle(const FVector2D& position) : character(nullptr), position(position), node(nullptr), root(nullptr) {}
|
||||
~MinimapHandle()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
FVector2D position;
|
||||
class ::ACharacterBase* character;
|
||||
NodeBase* node;
|
||||
NodeBase* root;
|
||||
};
|
||||
|
||||
template<size_t level> class TreeNode : public NodeBase
|
||||
{
|
||||
public:
|
||||
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent), child0(GetSubArea(area, 0), this), child1(GetSubArea(area, 1), this), child2(GetSubArea(area, 2), this), child3(GetSubArea(area, 3), this) {}
|
||||
|
||||
typedef TreeNode<level - 1> child_type;
|
||||
child_type child0;
|
||||
child_type child1;
|
||||
child_type child2;
|
||||
child_type child3;
|
||||
unordered_set<MinimapHandle*> child_objects;
|
||||
|
||||
virtual void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
if (children[i].area.contains(obj.position))
|
||||
{
|
||||
// Register to child
|
||||
child_objects.emplace(&obj);
|
||||
children[i].AddObject(obj);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Register to this node
|
||||
objects.emplace(&obj);
|
||||
obj.node = this;
|
||||
}
|
||||
virtual void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
// Check if present in children
|
||||
auto find_child = child_objects.find(&obj);
|
||||
if (find_child != child_objects.end())
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].RemoveObject(obj);
|
||||
|
||||
child_objects.erase(find_child);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove from node
|
||||
auto find = objects.find(&obj);
|
||||
if (find != objects.end())
|
||||
objects.erase(find);
|
||||
}
|
||||
}
|
||||
virtual void ClearRecursive() override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].ClearRecursive();
|
||||
|
||||
NodeBase::ClearRecursive();
|
||||
child_objects.clear();
|
||||
}
|
||||
virtual void ResizeRecursive(const Rect& area) override
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
children[i].ResizeRecursive(GetSubArea(area, i));
|
||||
|
||||
NodeBase::ResizeRecursive(area);
|
||||
}
|
||||
|
||||
virtual void CircleOverlap(const FVector2D& position, float radius_sqr, TArray<MinimapHandle*>& out_objects) override
|
||||
{
|
||||
if (!child_objects.empty())
|
||||
{
|
||||
child_type* children = &child0;
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
{
|
||||
child_type& child = children[i];
|
||||
if (RectCircleOverlapSquared(child.area, position, radius_sqr))
|
||||
child.CircleOverlap(position, radius_sqr, out_objects);
|
||||
}
|
||||
}
|
||||
|
||||
NodeBase::CircleOverlap(position, radius_sqr, out_objects);
|
||||
}
|
||||
};
|
||||
template<size_t level> class RootNode : public TreeNode<level>
|
||||
{
|
||||
public:
|
||||
RootNode(const Rect& area, NodeBase* parent) : TreeNode<level>(area, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
virtual void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
TreeNode<level>::AddObject(obj);
|
||||
all_objects.emplace(&obj);
|
||||
}
|
||||
virtual void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
TreeNode<level>::RemoveObject(obj);
|
||||
auto find = all_objects.find(&obj);
|
||||
if (find != all_objects.end())
|
||||
all_objects.erase(find);
|
||||
}
|
||||
virtual void ClearRecursive() override
|
||||
{
|
||||
for (auto iter = all_objects.begin(); iter != all_objects.end(); iter++)
|
||||
{
|
||||
(*iter)->node = nullptr;
|
||||
(*iter)->root = nullptr;
|
||||
}
|
||||
all_objects.clear();
|
||||
|
||||
TreeNode<level>::ClearRecursive();
|
||||
}
|
||||
|
||||
void Reparent(MinimapHandle& obj)
|
||||
{
|
||||
TreeNode<level>::RemoveObject(obj);
|
||||
TreeNode<level>::AddObject(obj);
|
||||
}
|
||||
|
||||
unordered_set<MinimapHandle*> all_objects;
|
||||
};
|
||||
template<> class TreeNode<0> : public NodeBase
|
||||
{
|
||||
public:
|
||||
TreeNode(const Rect& area, NodeBase* parent) : NodeBase(area, parent)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void AddObject(MinimapHandle& obj) override
|
||||
{
|
||||
objects.emplace(&obj);
|
||||
obj.node = this;
|
||||
}
|
||||
void RemoveObject(MinimapHandle& obj) override
|
||||
{
|
||||
auto find = objects.find(&obj);
|
||||
if (find != objects.end())
|
||||
objects.erase(find);
|
||||
}
|
||||
};
|
||||
|
||||
template<size_t depth> class QuadTree : private NonCopyable
|
||||
{
|
||||
public:
|
||||
QuadTree() : m_root_node(Rect(0, 0, 1, 1), nullptr) {}
|
||||
QuadTree(const Rect& area) : m_root_node(area, nullptr) {}
|
||||
~QuadTree()
|
||||
{
|
||||
m_root_node.ClearRecursive();
|
||||
}
|
||||
|
||||
void AddObject(MinimapHandle& obj)
|
||||
{
|
||||
if (obj.root == &m_root_node)
|
||||
return;
|
||||
if (obj.root != nullptr)
|
||||
obj.root->RemoveObject(obj);
|
||||
|
||||
// Add object to root
|
||||
m_root_node.AddObject(obj);
|
||||
obj.root = &m_root_node;
|
||||
}
|
||||
void RemoveObject(MinimapHandle& obj)
|
||||
{
|
||||
if (obj.root == nullptr)
|
||||
return;
|
||||
if (obj.root != &m_root_node)
|
||||
return;
|
||||
|
||||
// Remove from root
|
||||
m_root_node.RemoveObject(obj);
|
||||
obj.node = nullptr;
|
||||
obj.root = nullptr;
|
||||
}
|
||||
|
||||
void Update()
|
||||
{
|
||||
// Update positioning
|
||||
for (auto iter = m_root_node.all_objects.begin(); iter != m_root_node.all_objects.end(); iter++)
|
||||
{
|
||||
MinimapHandle& obj = **iter;
|
||||
|
||||
if (!obj.node->area.contains(obj.position) && obj.node->parent)
|
||||
m_root_node.Reparent(obj);
|
||||
}
|
||||
}
|
||||
void Clear()
|
||||
{
|
||||
m_root_node->ClearRecursive();
|
||||
}
|
||||
void Resize(const Rect& area)
|
||||
{
|
||||
m_root_node.ResizeRecursive(area);
|
||||
}
|
||||
|
||||
int32 Num() const
|
||||
{
|
||||
return int32(m_root_node.all_objects.size());
|
||||
}
|
||||
|
||||
bool CircleOverlap(const FVector2D& position, float radius, TArray<MinimapHandle*>& out_objects)
|
||||
{
|
||||
out_objects.SetNum(0);
|
||||
m_root_node.CircleOverlap(position, radius * radius, out_objects);
|
||||
return out_objects.Num() > 0;
|
||||
}
|
||||
|
||||
RootNode<depth> m_root_node;
|
||||
};
|
||||
}
|
||||
10
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.cpp
Normal file
10
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.cpp
Normal file
@@ -0,0 +1,10 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapIconWidget.h"
|
||||
|
||||
|
||||
void UMiniMapIconWidget::SetObjectIcon_Implementation(UTexture2D* texture)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
22
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.h
Normal file
22
Source/UnrealProject/GUI/Minimap/MiniMapIconWidget.h
Normal file
@@ -0,0 +1,22 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MiniMapIconWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapIconWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UPROPERTY(BlueprintReadWrite, Category = "Image")
|
||||
UImage* image;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, BlueprintCallable, Category = "MiniMap")
|
||||
void SetObjectIcon(class UTexture2D* texture);
|
||||
};
|
||||
15
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.cpp
Normal file
15
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapVisionCircle.h"
|
||||
|
||||
|
||||
UMiniMapVisionCircle::UMiniMapVisionCircle(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
void UMiniMapVisionCircle::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
}
|
||||
26
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.h
Normal file
26
Source/UnrealProject/GUI/Minimap/MiniMapVisionCircle.h
Normal file
@@ -0,0 +1,26 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include "MiniMapVisionCircle.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapVisionCircle : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMiniMapVisionCircle(const FObjectInitializer& init);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
|
||||
void SetTexture(UTexture2D* texture);
|
||||
|
||||
UFUNCTION(BlueprintImplementableEvent, Category = "Vision")
|
||||
void SetUV(const FVector2D& uv, const FVector2D& scale);
|
||||
};
|
||||
393
Source/UnrealProject/GUI/Minimap/MiniMapWidget.cpp
Normal file
393
Source/UnrealProject/GUI/Minimap/MiniMapWidget.cpp
Normal file
@@ -0,0 +1,393 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#include "UnrealProject.h"
|
||||
#include "MiniMapVolume.h"
|
||||
#include "MiniMapIconWidget.h"
|
||||
#include "MiniMapVisionCircle.h"
|
||||
#include "NetworkPlayer.h"
|
||||
#include "BossBase.h"
|
||||
#include "NPCBase.h"
|
||||
#include "KOTHBossSpawner.h"
|
||||
#include "KOTHMinionSpawner.h"
|
||||
#include "NetworkCharacter.h"
|
||||
#include "DefaultGameState.h"
|
||||
#include "DefaultPlayerState.h"
|
||||
#include "DefaultPlayerController.h"
|
||||
#include "MiniMapWidget.h"
|
||||
#include "WidgetLayoutLibrary.h"
|
||||
|
||||
#if PLATFORM_SPECIFIC_WIN == 0
|
||||
#include "HeatMapMetrics.h"
|
||||
#endif
|
||||
|
||||
|
||||
float AngleTowards(const FVector2D& from, const FVector2D& to)
|
||||
{
|
||||
const float angle_rad = FMath::Atan2(from.X - to.X, to.Y - from.Y) + PI;
|
||||
return FMath::RadiansToDegrees(angle_rad);
|
||||
}
|
||||
|
||||
UMiniMapWidget::UMiniMapWidget(const FObjectInitializer& init) : Super(init)
|
||||
{
|
||||
viewRadius = 2000;
|
||||
}
|
||||
|
||||
void UMiniMapWidget::NativeConstruct()
|
||||
{
|
||||
Super::NativeConstruct();
|
||||
|
||||
// Find the minimap volume
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(GetWorld()->GetGameState());
|
||||
if(IsValid(gameState))
|
||||
{
|
||||
for (TActorIterator<AMiniMapVolume> iter(GetWorld()); iter; ++iter)
|
||||
{
|
||||
AMiniMapVolume* const volume = *iter;
|
||||
volume->SetActorRotation(FRotator(0, 0, 0));
|
||||
|
||||
const FVector pos = volume->GetActorLocation();
|
||||
const FVector area = volume->area->GetScaledBoxExtent();
|
||||
m_size = FMath::Max(area.X, area.Y);
|
||||
m_min.X = -m_size + pos.X;
|
||||
m_min.Y = -m_size + pos.Y;
|
||||
m_max.X = m_size + pos.X;
|
||||
m_max.Y = m_size + pos.Y;
|
||||
m_size *= 2;
|
||||
|
||||
m_viewRadius = (viewRadius / (area.X * 2));
|
||||
|
||||
gameState->minimapQuadtree.Resize(MiniMap::Rect(FVector2D(m_min), FVector2D(m_size, m_size)));
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
JERROR("Failed to locate Mini Map volume");
|
||||
}
|
||||
|
||||
void UMiniMapWidget::NativeTick(const FGeometry& geometry, float deltaTime)
|
||||
{
|
||||
Super::NativeTick(geometry, deltaTime);
|
||||
|
||||
m_arrowAnim = FMath::Fmod(m_arrowAnim + deltaTime, 1.0f);
|
||||
|
||||
// Reset the state of all current widgets
|
||||
{
|
||||
// Set widgets hidden
|
||||
for (int32 i = 0; i < m_miniMapIconWidgetPool.Num(); i++)
|
||||
{
|
||||
m_miniMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
// Reset rotations
|
||||
FWidgetTransform trans = m_miniMapIconWidgetPool[i]->image->RenderTransform;
|
||||
trans.Angle = 0;
|
||||
m_miniMapIconWidgetPool[i]->image->SetRenderTransform(trans);
|
||||
}
|
||||
|
||||
// Set existing arrows hidden
|
||||
for (int32 i = 0; i < m_arrowMapIconWidgetPool.Num(); i++)
|
||||
m_arrowMapIconWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
|
||||
// Allocate the neccesairy vision circles
|
||||
for (int32 i = 0; i < m_viewCircleWidgetPool.Num(); i++)
|
||||
m_viewCircleWidgetPool[i]->SetVisibility(ESlateVisibility::Hidden);
|
||||
}
|
||||
|
||||
// Begin the rendering
|
||||
UWorld* const world = GetWorld();
|
||||
if (!IsValid(world)) return;
|
||||
ADefaultGameState* const gameState = Cast<ADefaultGameState>(world->GetGameState());
|
||||
if (!IsValid(gameState))
|
||||
return;
|
||||
|
||||
// Get the local team
|
||||
m_localTeam = -1;
|
||||
AController* controller = world->GetGameInstance()->GetFirstLocalPlayerController();
|
||||
ACharacterBase* localPlayer = nullptr;
|
||||
if (IsValid(controller))
|
||||
{
|
||||
ADefaultPlayerController* const playerController = Cast<ADefaultPlayerController>(controller);
|
||||
localPlayer = Cast<ACharacterBase>(playerController->GetPawn());
|
||||
if (playerController->PlayerState)
|
||||
m_localTeam = Cast<ADefaultPlayerState>(playerController->PlayerState)->GetTeam();
|
||||
}
|
||||
|
||||
// Failed to fetch local team
|
||||
if (m_localTeam == -1)
|
||||
return;
|
||||
|
||||
gameState->minimapQuadtree.Update();
|
||||
|
||||
map<int32, unordered_set<ACharacterBase*>> drawActors;
|
||||
unordered_set<ACharacterBase*>& friendlyPlayerSet = drawActors.emplace(m_localTeam, unordered_set<ACharacterBase*>()).first->second;
|
||||
|
||||
// Fetch the friendly viewpoints
|
||||
for (TActorIterator<ACharacterBase> iter(world); iter; ++iter)
|
||||
{
|
||||
ACharacterBase* const character = *iter;
|
||||
if (IsValid(character))
|
||||
{
|
||||
if (character->GetTeam() == m_localTeam)
|
||||
friendlyPlayerSet.emplace(character);
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch all the visible units around the friendly viewpoints
|
||||
for (auto iter = friendlyPlayerSet.begin(); iter != friendlyPlayerSet.end(); iter++)
|
||||
{
|
||||
ACharacterBase* const character = (*iter);
|
||||
FVector2D position = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
|
||||
if (gameState->minimapQuadtree.CircleOverlap(position, character->visionRadius, m_handleBuffer))
|
||||
{
|
||||
for (int32 i = 0; i < m_handleBuffer.Num(); i++)
|
||||
{
|
||||
ACharacterBase* const visibleCharacter = m_handleBuffer[i]->character;
|
||||
if(IsValid(visibleCharacter))
|
||||
drawActors[visibleCharacter->GetTeam()].emplace(visibleCharacter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Fetch the minion camps
|
||||
TArray<AKOTHSpawnerBase*> minionCamps;
|
||||
AKOTHBossSpawner* bossCamp = nullptr;
|
||||
FVector2D bossCampPos;
|
||||
for (TActorIterator<AKOTHSpawnerBase> iter(world); iter; ++iter)
|
||||
{
|
||||
AKOTHSpawnerBase* camp = *iter;
|
||||
if (camp->IsA<AKOTHBossSpawner>())
|
||||
{
|
||||
bossCamp = Cast<AKOTHBossSpawner>(camp);
|
||||
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
|
||||
bossCampPos = ConvertWorldToMinimap(charPos);
|
||||
}
|
||||
minionCamps.Add(camp);
|
||||
}
|
||||
|
||||
// Draw the minion camps
|
||||
m_widgetIndex = 0;
|
||||
m_arrowIndex = 0;
|
||||
m_circleIndex = 0;
|
||||
for (int32 i = 0; i < minionCamps.Num(); i++)
|
||||
{
|
||||
UMiniMapIconWidget* const widget = m_AllocateWidget();
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
|
||||
widget->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
AKOTHSpawnerBase* const camp = minionCamps[i];
|
||||
const FVector2D charPos = FVector2D(camp->GetActorLocation().X, camp->GetActorLocation().Y);
|
||||
FVector2D relativePos = ConvertWorldToMinimap(charPos);
|
||||
|
||||
// Set sprite position
|
||||
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
|
||||
|
||||
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
|
||||
slot->SetSize(spriteSize);
|
||||
SetObjectIcon(campIcon, widget);
|
||||
|
||||
FLinearColor color = enemyColor;
|
||||
if (camp->team >= NPCTeam::Team1) // NPC Controlled
|
||||
color = playerColor;
|
||||
else if (int32(camp->team) == int32(m_localTeam)) // Friendly
|
||||
color = allyColor;
|
||||
else if (int32(camp->team) == 0) // Contested
|
||||
color = neutralColor;
|
||||
widget->image->Brush.TintColor = color;
|
||||
|
||||
// Draw arrows
|
||||
if (bossCamp && int32(camp->team) > 0 && camp->team < NPCTeam::Team1 && camp != bossCamp)
|
||||
{
|
||||
const int32 arrowCount = int32(FVector2D::Distance(relativePos, bossCampPos) * 25);
|
||||
if (arrowCount > 0)
|
||||
{
|
||||
const float step = 1.0f / float(arrowCount);
|
||||
for (int32 i = 0; i < arrowCount; i++)
|
||||
{
|
||||
UMiniMapIconWidget* arrow = m_AllocateArrow();
|
||||
arrow->SetVisibility(ESlateVisibility::Visible);
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(arrow->Slot);
|
||||
|
||||
// Lerp towards the target
|
||||
color.A = arrowCount > 1 ? (i == 0 ? m_arrowAnim : i == arrowCount - 1 ? (1.f - m_arrowAnim) : 1) : 1;
|
||||
const FVector2D setPos = FMath::Lerp(relativePos, bossCampPos, step * float(i) + step * m_arrowAnim);
|
||||
FWidgetTransform trans = arrow->image->RenderTransform;
|
||||
trans.Angle = AngleTowards(setPos, bossCampPos);
|
||||
arrow->image->SetRenderTransform(trans);
|
||||
arrow->image->Brush.TintColor = color;
|
||||
|
||||
const FVector2D spriteSize = geometry.GetLocalSize() * 0.04f;
|
||||
slot->SetSize(spriteSize);
|
||||
slot->SetPosition(geometry.GetLocalSize() * setPos - spriteSize * 0.5f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Generate draw order (Draw local team last)
|
||||
TArray<int32> teamDrawOrder;
|
||||
for (auto iter = drawActors.rbegin(); iter != drawActors.rend(); iter++)
|
||||
{
|
||||
if (iter->first == m_localTeam)
|
||||
continue;
|
||||
teamDrawOrder.Add(iter->first);
|
||||
}
|
||||
teamDrawOrder.Add(m_localTeam);
|
||||
|
||||
// Draw the actual characters to the minimap
|
||||
int32 circleIndex = 0;
|
||||
for (size_t i = 0; i < teamDrawOrder.Num(); i++)
|
||||
{
|
||||
const int32 team = teamDrawOrder[i];
|
||||
auto& set = drawActors[team];
|
||||
for (auto charIter = set.begin(); charIter != set.end(); charIter++)
|
||||
{
|
||||
ACharacterBase* const character = *charIter;
|
||||
if (character == localPlayer)
|
||||
continue;
|
||||
|
||||
UMiniMapIconWidget* const widget = m_AllocateWidget();
|
||||
m_DrawCreature(geometry, widget, character);
|
||||
}
|
||||
}
|
||||
|
||||
// Draw the local player last
|
||||
if (localPlayer)
|
||||
m_DrawCreature(geometry, m_AllocateWidget(), localPlayer);
|
||||
}
|
||||
|
||||
void UMiniMapWidget::m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character)
|
||||
{
|
||||
widget->SetVisibility(ESlateVisibility::Visible);
|
||||
UCanvasPanelSlot* const slot = Cast<UCanvasPanelSlot>(widget->Slot);
|
||||
|
||||
const int32 actorTeam = character->GetTeam();
|
||||
const FVector2D charPos = FVector2D(character->GetActorLocation().X, character->GetActorLocation().Y);
|
||||
FVector2D relativePos = ConvertWorldToMinimap(charPos);
|
||||
|
||||
// Rotation
|
||||
const float drawPlayerAngle = character->GetActorRotation().Yaw;
|
||||
|
||||
// Set sprite position
|
||||
FVector2D spriteSize = geometry.GetLocalSize() * 0.07f;
|
||||
|
||||
slot->SetPosition(geometry.GetLocalSize() * relativePos - spriteSize * 0.5f);
|
||||
slot->SetSize(spriteSize);
|
||||
|
||||
FWidgetTransform trans = widget->image->RenderTransform;
|
||||
trans.Angle = 0;
|
||||
|
||||
// Set icon based on the object type
|
||||
if (character->IsA<ABossBase>())
|
||||
SetObjectIcon(bossIcon, widget);
|
||||
else if (character->IsA<ANPCBase>())
|
||||
SetObjectIcon(creatureIcon, widget);
|
||||
else
|
||||
{
|
||||
trans.Angle = drawPlayerAngle;
|
||||
SetObjectIcon(playerIcon, widget);
|
||||
}
|
||||
widget->image->SetRenderTransform(trans);
|
||||
|
||||
// Set the color
|
||||
if (!(character->IsA<ANetworkPlayer>() && character->IsLocallyControlled()))
|
||||
{
|
||||
if (actorTeam != m_localTeam)
|
||||
{
|
||||
// Neutral or enemy?
|
||||
if (actorTeam == 0)
|
||||
widget->image->Brush.TintColor = neutralColor;
|
||||
else
|
||||
widget->image->Brush.TintColor = enemyColor;
|
||||
}
|
||||
else
|
||||
widget->image->Brush.TintColor = allyColor;
|
||||
}
|
||||
else
|
||||
widget->image->Brush.TintColor = playerColor;
|
||||
|
||||
// Update the view circle
|
||||
if (actorTeam == m_localTeam)
|
||||
{
|
||||
UMiniMapVisionCircle* const circle = m_AllocateVisionCircle();
|
||||
circle->SetVisibility(ESlateVisibility::Visible);
|
||||
|
||||
UCanvasPanelSlot* const circleSlot = Cast<UCanvasPanelSlot>(circle->Slot);
|
||||
if (IsValid(circleSlot))
|
||||
{
|
||||
const float diameter = (character->visionRadius * 2) / m_size;
|
||||
FVector2D size = geometry.GetLocalSize() * diameter;
|
||||
size = FVector2D(FMath::RoundToFloat(size.X), FMath::RoundToFloat(size.Y));
|
||||
FVector2D pos = geometry.GetLocalSize() * relativePos - size * 0.5f;
|
||||
pos = FVector2D(FMath::RoundToFloat(pos.X), FMath::RoundToFloat(pos.Y));
|
||||
|
||||
circleSlot->SetPosition(pos);
|
||||
circleSlot->SetSize(size);
|
||||
circle->SetUV(pos / geometry.GetLocalSize(), size / geometry.GetLocalSize());
|
||||
}
|
||||
}
|
||||
}
|
||||
UMiniMapIconWidget* UMiniMapWidget::m_AllocateWidget()
|
||||
{
|
||||
UMiniMapIconWidget* widget = nullptr;
|
||||
if (m_widgetIndex >= m_miniMapIconWidgetPool.Num())
|
||||
{
|
||||
widget = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
|
||||
iconLayer->AddChild(widget);
|
||||
m_miniMapIconWidgetPool.Add(widget);
|
||||
}
|
||||
else
|
||||
widget = m_miniMapIconWidgetPool[m_widgetIndex];
|
||||
|
||||
m_widgetIndex++;
|
||||
return widget;
|
||||
}
|
||||
UMiniMapIconWidget* UMiniMapWidget::m_AllocateArrow()
|
||||
{
|
||||
UMiniMapIconWidget* arrow = nullptr;
|
||||
if (m_arrowIndex >= m_arrowMapIconWidgetPool.Num())
|
||||
{
|
||||
arrow = CreateWidget<UMiniMapIconWidget>(GetWorld(), iconWidget);
|
||||
arrowLayer->AddChild(arrow);
|
||||
m_arrowMapIconWidgetPool.Add(arrow);
|
||||
SetObjectIcon(arrowIcon, arrow);
|
||||
}
|
||||
else
|
||||
arrow = m_arrowMapIconWidgetPool[m_arrowIndex];
|
||||
|
||||
m_arrowIndex++;
|
||||
return arrow;
|
||||
}
|
||||
UMiniMapVisionCircle* UMiniMapWidget::m_AllocateVisionCircle()
|
||||
{
|
||||
UMiniMapVisionCircle* circle = nullptr;
|
||||
if (m_circleIndex >= m_viewCircleWidgetPool.Num())
|
||||
{
|
||||
circle = CreateWidget<UMiniMapVisionCircle>(GetWorld(), visionCircleWidget);
|
||||
visionLayer->AddChild(circle);
|
||||
m_viewCircleWidgetPool.Add(circle);
|
||||
circle->SetTexture(backgroundTexture);
|
||||
}
|
||||
else
|
||||
circle = m_viewCircleWidgetPool[m_circleIndex];
|
||||
|
||||
m_circleIndex++;
|
||||
return circle;
|
||||
}
|
||||
|
||||
FVector2D UMiniMapWidget::ConvertWorldToMinimap(const FVector2D& pos)
|
||||
{
|
||||
const FVector2D scale = FVector2D(m_max.X - m_min.X, m_max.Y - m_min.Y);
|
||||
FVector2D relativePos = (pos - m_min) / scale;
|
||||
FVector2D returnPos = FVector2D(relativePos.Y, relativePos.X);
|
||||
returnPos.Y = 1.0f - returnPos.Y;
|
||||
return returnPos;
|
||||
}
|
||||
|
||||
void UMiniMapWidget::SetObjectIcon_Implementation(UTexture2D* texture, UMiniMapIconWidget* widget)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
|
||||
void UMiniMapWidget::SetMinimapInfo_Implementation(FVector2D player0, FVector2D player1, float radius)
|
||||
{
|
||||
// No implementation
|
||||
}
|
||||
89
Source/UnrealProject/GUI/Minimap/MiniMapWidget.h
Normal file
89
Source/UnrealProject/GUI/Minimap/MiniMapWidget.h
Normal file
@@ -0,0 +1,89 @@
|
||||
// Project Lab - NHTV Igad
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Blueprint/UserWidget.h"
|
||||
#include <map>
|
||||
#include "Minimap.h"
|
||||
#include "MiniMapWidget.generated.h"
|
||||
|
||||
/**
|
||||
*
|
||||
*/
|
||||
UCLASS()
|
||||
class UNREALPROJECT_API UMiniMapWidget : public UUserWidget
|
||||
{
|
||||
GENERATED_BODY()
|
||||
|
||||
public:
|
||||
UMiniMapWidget(const FObjectInitializer& init);
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
|
||||
void SetObjectIcon(class UTexture2D* texture, class UMiniMapIconWidget* widget);
|
||||
|
||||
virtual void NativeConstruct() override;
|
||||
virtual void NativeTick(const FGeometry& geometry, float deltaTime) override;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
float viewRadius;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor playerColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor allyColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor enemyColor;
|
||||
UPROPERTY(EditAnywhere, Category = "Minimap")
|
||||
FLinearColor neutralColor;
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* playerIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* creatureIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* bossIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* campIcon;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
UTexture2D* arrowIcon;
|
||||
|
||||
UFUNCTION(BlueprintNativeEvent, Category = "MiniMap")
|
||||
void SetMinimapInfo(FVector2D player0, FVector2D player1, float radius);
|
||||
|
||||
FVector2D ConvertWorldToMinimap(const FVector2D& pos);
|
||||
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
TSubclassOf<class UMiniMapIconWidget> iconWidget;
|
||||
UPROPERTY(EditAnywhere, Category = "MiniMap")
|
||||
TSubclassOf<class UMiniMapVisionCircle> visionCircleWidget;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* iconLayer;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* arrowLayer;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UTexture2D* backgroundTexture;
|
||||
UPROPERTY(BlueprintReadWrite, Category = "MiniMap")
|
||||
UCanvasPanel* visionLayer;
|
||||
|
||||
private:
|
||||
void m_DrawCreature(const FGeometry& geometry, UMiniMapIconWidget* widget, class ACharacterBase* character);
|
||||
UMiniMapIconWidget* m_AllocateWidget();
|
||||
UMiniMapIconWidget* m_AllocateArrow();
|
||||
UMiniMapVisionCircle* m_AllocateVisionCircle();
|
||||
|
||||
TArray<class UMiniMapIconWidget*> m_miniMapIconWidgetPool;
|
||||
TArray<class UMiniMapIconWidget*> m_arrowMapIconWidgetPool;
|
||||
TArray<class UMiniMapVisionCircle*> m_viewCircleWidgetPool;
|
||||
TArray<MiniMap::MinimapHandle*> m_handleBuffer;
|
||||
|
||||
FVector2D m_min;
|
||||
FVector2D m_max;
|
||||
float m_size;
|
||||
float m_viewRadius;
|
||||
float m_arrowAnim;
|
||||
int32 m_localTeam;
|
||||
|
||||
int32 m_widgetIndex;
|
||||
int32 m_arrowIndex;
|
||||
int32 m_circleIndex;
|
||||
};
|
||||
Reference in New Issue
Block a user