Browse Source

AActor Entity integration, Actor Entity management, Fix segfault

- Added an Entity field to AActor; this change needs to be applied to the UE4 code manually. The patch is in Source/UnrealEnginePatches/AActorEntityIntegration.patch
- DPI scaling for the editor is now done explictly by GalavantUnrealMain; this is so I automatically have my desired DPI settings. This is a hack and will probably trip up anyone else who uses GalavantUnreal
- Fixed segfault involving UnrealMovementComponent having dead Entities subscribed to it after Play In Editor has stopped and restarted. This occurred because I had transitioned to using static initialization to auto-register ComponentManagers, but for some reason (hotreloading or library separation) UnrealMovementComponent wasn't being added to the static ComponentManagers list. ComponentManagers are now explicitly added, like before
- Made small changes to test Needs: death now happens through conscious state; blood need has trigger to make fainting happen if blood level is low
- CombatComponentManager's Update() is now actually executed. I must've forgotten to add it before
- Big refactor of ActorEntityManagement. Due to AActor Entity integration, the exact entity can be notified. I removed all the code which iterates over all actors as it is now unneeded. All Actor creation should now be done through ActorEntityManagement::CreateActorForEntity(). ActorEntities can now only have one callback, which is set during creation. At the moment, lifetime is managed via callback as well as the ability to check all Actor pointers to ensure it is a tracked actor. The latter option should be removed once the callback solution proves its robustness. I'm not going to do it right now because I want to work on other things
- Made several changes to UnrealMovementComponent's actor management in order to prevent the segfault from happening. I added a debug display which is useful for seeing discrepancies in display/spawning at runtime. More work is needed on actor management. I'm noticing sometimes Actors should be right in front of player but are gone; they don't think they should exist either, which is perplexing
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
6693f420e5
  1. 3
      GalavantUnreal/Config/DefaultEngine.ini
  2. BIN
      GalavantUnreal/Content/TopDownCPP/Maps/TopDownExampleMap.umap
  3. BIN
      GalavantUnreal/Content/TopDownCPP/Maps/TopDownExampleMap_BuiltData.uasset
  4. 154
      GalavantUnreal/Source/GalavantUnreal/ActorEntityManagement.cpp
  5. 32
      GalavantUnreal/Source/GalavantUnreal/ActorEntityManagement.h
  6. 135
      GalavantUnreal/Source/GalavantUnreal/GalaEntityComponents/UnrealMovementComponent.cpp
  7. 8
      GalavantUnreal/Source/GalavantUnreal/GalaEntityComponents/UnrealMovementComponent.hpp
  8. 35
      GalavantUnreal/Source/GalavantUnreal/GalavantUnrealMain.cpp
  9. 65
      GalavantUnreal/Source/UnrealEnginePatches/AActorEntityIntegration.patch
  10. 13
      README.md

3
GalavantUnreal/Config/DefaultEngine.ini

@ -19,7 +19,7 @@ CrosshairsCursor=None
GrabHandCursor=None
GrabHandClosedCursor=None
SlashedCircleCursor=None
ApplicationScale=1.528206
ApplicationScale=1.530000
UIScaleRule=ShortestSide
CustomScalingRuleClass=None
UIScaleCurve=(EditorCurveData=(PreInfinityExtrap=RCCE_Constant,PostInfinityExtrap=RCCE_Constant,Keys=((Time=480.000000,Value=0.444000),(Time=720.000000,Value=0.666000),(Time=1080.000000,Value=1.000000),(Time=8640.000000,Value=8.000000)),DefaultValue=340282346638528859811704183484516925440.000000),ExternalCurve=None)
@ -112,4 +112,3 @@ AsyncSceneSmoothingFactor=0.990000
InitialAverageFrameRate=0.016667
PhysXTreeRebuildRate=10

BIN
GalavantUnreal/Content/TopDownCPP/Maps/TopDownExampleMap.umap

Binary file not shown.

BIN
GalavantUnreal/Content/TopDownCPP/Maps/TopDownExampleMap_BuiltData.uasset

Binary file not shown.

154
GalavantUnreal/Source/GalavantUnreal/ActorEntityManagement.cpp

@ -10,76 +10,37 @@
namespace ActorEntityManager
{
struct ActorEntity
struct TrackedActor
{
AActor* Actor;
gv::Entity Entity;
};
bool IsBeingDestroyed;
TrackActorLifetimeCallback OnDestroyCallback;
typedef std::vector<ActorEntity> ActorEntityList;
ActorEntityList s_ActorEntities;
void operator=(const TrackedActor& other)
{
IsBeingDestroyed = other.IsBeingDestroyed;
OnDestroyCallback = other.OnDestroyCallback;
}
};
typedef std::vector<TrackActorLifetimeCallback> TrackActorLifetimeCallbackList;
typedef std::map<const AActor*, TrackActorLifetimeCallbackList> ActorLifetimeCallbacks;
ActorLifetimeCallbacks s_ActorLifetimeCallbacks;
typedef std::map<const AActor*, TrackedActor> TrackedActorMap;
TrackedActorMap s_TrackedActors;
void Clear()
{
s_ActorEntities.clear();
s_ActorLifetimeCallbacks.clear();
s_TrackedActors.clear();
}
void AddActorEntity(AActor* actor, gv::Entity entity)
{
s_ActorEntities.push_back({actor, entity});
}
typedef std::vector<AActor*> ActiveActorList;
ActiveActorList s_ActiveActors;
void DestroyEntitiesWithDestroyedActors(UWorld* world,
gv::EntityComponentManager* entityComponentSystem)
bool IsActorActive(AActor* actor)
{
if (world && entityComponentSystem)
for (AActor* currentActor : s_ActiveActors)
{
const TArray<class ULevel*>& levels = world->GetLevels();
gv::EntityList entitiesToDestroy;
// This O(n * m) through all actors and entities is brutal. I should hook into Actor
// destruction (i.e. by adding some callback or something to AActor base class), but
// for now this is okay
int lastGoodActorEntityIndex = s_ActorEntities.size() - 1;
for (int i = lastGoodActorEntityIndex; i >= 0; i--)
{
ActorEntity& actorEntity = s_ActorEntities[i];
bool found = false;
for (const ULevel* level : levels)
{
for (const AActor* actor : level->Actors)
{
if (actorEntity.Actor == actor)
{
found = true;
break;
}
}
if (found)
break;
}
if (!found)
{
actorEntity = s_ActorEntities[lastGoodActorEntityIndex--];
entitiesToDestroy.push_back(actorEntity.Entity);
}
}
if (!entitiesToDestroy.empty())
{
LOGI << "Destroying " << entitiesToDestroy.size()
<< " entities because their Actors were destroyed";
s_ActorEntities.resize(lastGoodActorEntityIndex + 1);
entityComponentSystem->MarkDestroyEntities(entitiesToDestroy);
}
if (actor == currentActor)
return true;
}
return false;
}
void TrackActorLifetime(AActor* actor, TrackActorLifetimeCallback callback)
@ -87,71 +48,32 @@ void TrackActorLifetime(AActor* actor, TrackActorLifetimeCallback callback)
if (!actor)
return;
ActorLifetimeCallbacks::iterator findIt = s_ActorLifetimeCallbacks.find(actor);
if (findIt != s_ActorLifetimeCallbacks.end())
findIt->second.push_back(callback);
else
s_ActorLifetimeCallbacks[actor] = {callback};
s_TrackedActors[actor] = {false, callback};
s_ActiveActors.push_back(actor);
}
void UpdateNotifyOnActorDestroy(UWorld* world)
void UnrealActorEntityOnDestroy(AActor* actor, int entity)
{
if (!world)
return;
std::vector<const AActor*> liveActors;
const ULevel* level = world->GetCurrentLevel();
LOGD << "Actor assigned to entity " << entity << " was destroyed";
if (!level)
return;
for (const AActor* actor : level->Actors)
TrackedActorMap::iterator findIt = s_TrackedActors.find(actor);
if (findIt == s_TrackedActors.end())
{
if (!actor)
continue;
liveActors.push_back(actor);
if (actor->IsPendingKillPending())
{
ActorLifetimeCallbacks::iterator findIt = s_ActorLifetimeCallbacks.find(actor);
if (findIt == s_ActorLifetimeCallbacks.end())
continue;
LOGE << "Actor had entity but no lifetime callbacks! Something should be making sure their "
"Actors are valid but aren't. The Entity will be destroyed";
gv::g_EntityComponentManager.MarkDestroyEntities({(unsigned int)entity});
return;
}
for (TrackActorLifetimeCallback callbackOnDestroy : findIt->second)
callbackOnDestroy(actor);
findIt->second.IsBeingDestroyed = true;
// The actor has been removed, so there's no reason to track its lifetime
s_ActorLifetimeCallbacks.erase(findIt);
}
}
findIt->second.OnDestroyCallback(actor);
// If an actor has been removed before we've had a chance to check IsPendingKillPending(), we
// need to make sure we detect it
for (ActorLifetimeCallbacks::iterator trackingIt = s_ActorLifetimeCallbacks.begin();
trackingIt != s_ActorLifetimeCallbacks.end();)
{
bool stillAlive = false;
for (const AActor* currentActor : liveActors)
{
if (trackingIt->first == currentActor)
{
stillAlive = true;
break;
}
}
if (!stillAlive)
{
for (TrackActorLifetimeCallback callbackOnDestroy : trackingIt->second)
callbackOnDestroy(trackingIt->first);
// The actor has been removed, so there's no reason to track its lifetime
trackingIt = s_ActorLifetimeCallbacks.erase(trackingIt);
}
else
++trackingIt;
}
// The actor has been removed, so there's no reason to track its lifetime
// Note that whatever happens in callbackOnDestroy() could spawn and track an Actor which has
// the same address as the last one (maybe, I'm not sure how Unreal manages Actor memory). Only
// destroy this TrackedActor if we are certain they didn't re-add the callback
if (findIt->second.IsBeingDestroyed)
s_TrackedActors.erase(findIt);
}
}

32
GalavantUnreal/Source/GalavantUnreal/ActorEntityManagement.h

@ -1,7 +1,10 @@
#pragma once
#include "entityComponentSystem/EntityTypes.hpp"
#include "entityComponentSystem/EntityComponentManager.hpp"
#include "world/Position.hpp"
#include "Utilities/ConversionHelpers.h"
#include <functional>
@ -12,13 +15,30 @@ namespace ActorEntityManager
{
void Clear();
// Simple setup: if actor is destroyed, destroy entity
void AddActorEntity(AActor* actor, gv::Entity entity);
void DestroyEntitiesWithDestroyedActors(UWorld* world,
gv::EntityComponentManager* entityComponentSystem);
// Advanced setup: if actor is destroyed, call callback
typedef std::function<void(const AActor*)> TrackActorLifetimeCallback;
void TrackActorLifetime(AActor* actor, TrackActorLifetimeCallback callback);
void UpdateNotifyOnActorDestroy(UWorld* world);
bool IsActorActive(AActor* actor);
// I'm requiring the lifetime callback at the moment while I figure out the most stable way to
// manage lifetime
template <class T>
T* CreateActorForEntity(UWorld* world, TSubclassOf<T> actorType, gv::Entity entity,
const gv::Position& position, TrackActorLifetimeCallback callback)
{
if (!world)
return nullptr;
FActorSpawnParameters spawnParams;
FVector positionVector(ToFVector(position));
FRotator defaultRotation(0.f, 0.f, 0.f);
T* actor = (T*)world->SpawnActor<T>(actorType, positionVector, defaultRotation, spawnParams);
if (actor)
{
actor->Entity = (int)entity;
TrackActorLifetime(actor, callback);
}
return actor;
}
}

135
GalavantUnreal/Source/GalavantUnreal/GalaEntityComponents/UnrealMovementComponent.cpp

@ -52,8 +52,6 @@ void UnrealMovementComponent::Update(float deltaSeconds)
it != gv::PooledComponentManager<UnrealMovementComponentData>::NULL_POOL_ITERATOR;
currentComponent = GetNextActivePooledComponent(it))
{
ACharacter* currentCharacter = currentComponent->data.Character;
AActor* currentActor = currentComponent->data.Actor;
gv::Position& worldPosition = currentComponent->data.WorldPosition;
gv::Position& goalWorldPosition = currentComponent->data.GoalWorldPosition;
float goalManDistanceTolerance = currentComponent->data.GoalManDistanceTolerance;
@ -62,14 +60,46 @@ void UnrealMovementComponent::Update(float deltaSeconds)
bool shouldActorExist = gv::EntityLOD::ShouldRenderForPlayer(worldPosition);
bool moveActor = false;
USceneComponent* sceneComponent = nullptr;
// Debug Actor/Entity lifetime
if (GEngine)
{
bool spawnDiscrepancy =
(shouldActorExist &&
(!currentComponent->data.Actor && !currentComponent->data.Character)) ||
(!shouldActorExist &&
(currentComponent->data.Actor || currentComponent->data.Character));
// Use entity as string key so it'll overwrite
GEngine->AddOnScreenDebugMessage(
/*key=*/(uint64)currentComponent->entity, /*timeToDisplay=*/1.5f,
spawnDiscrepancy ? FColor::Red : FColor::Green,
FString::Printf(TEXT("Entity %d Actor: %d Character: %d Should Render: %d"),
currentComponent->entity, currentComponent->data.Actor,
currentComponent->data.Character, shouldActorExist));
}
SpawnActorIfNecessary(currentComponent);
if (currentActor)
sceneComponent = currentActor->GetRootComponent();
else if (currentCharacter)
sceneComponent = currentCharacter->GetRootComponent();
USceneComponent* sceneComponent = nullptr;
if (currentComponent->data.Actor)
{
if (!ActorEntityManager::IsActorActive(currentComponent->data.Actor))
{
currentComponent->data.Actor = nullptr;
LOGW << "Entity " << currentComponent->entity << " had pointer to inactive actor!";
}
else
sceneComponent = currentComponent->data.Actor->GetRootComponent();
}
else if (currentComponent->data.Character)
{
if (!ActorEntityManager::IsActorActive(currentComponent->data.Character))
{
currentComponent->data.Character = nullptr;
LOGW << "Entity " << currentComponent->entity << " had pointer to inactive actor!";
}
else
sceneComponent = currentComponent->data.Character->GetRootComponent();
}
if (!sceneComponent && shouldActorExist)
{
@ -77,12 +107,23 @@ void UnrealMovementComponent::Update(float deltaSeconds)
"was the actor destroyed? It should "
"be respawned later...";
// Nothing in this block below makes sense anymore
// If the actor/character has been destroyed for some reason, make sure we reset these
// so it'll be spawned. All actors/characters should have scene components
currentActor = nullptr;
currentCharacter = nullptr;
/*currentComponent->data.Actor = nullptr;
currentComponent->data.Character = nullptr;*/
// Don't destroy an entity just because it has lost its actor. Why was this code here?
// entitiesToUnsubscribe.push_back(currentComponent->entity);
continue;
// continue;
}
// Destroy the actor if we are far away
else if (!shouldActorExist &&
(currentComponent->data.Actor || currentComponent->data.Character))
{
DestroyActor(currentComponent);
LOGD << "Entity " << currentComponent->entity
<< " destroyed its actor because it shouldn't be rendered to the player";
}
else
moveActor = true;
@ -91,28 +132,16 @@ void UnrealMovementComponent::Update(float deltaSeconds)
if (moveActor && sceneComponent)
{
// TODO: This motherfucker is still crashing
/*LOGD << "Entity " << currentComponent->entity << " actor "
<< &currentComponent->data.Actor << " Character "
<< &currentComponent->data.Character;*/
trueWorldPosition = sceneComponent->GetComponentLocation();
worldPosition = ToPosition(trueWorldPosition);
// LOGD << "Done retrieving position";
}
else
trueWorldPosition = ToFVector(worldPosition);
// Destroy the actor if we are far away
if (!shouldActorExist && (currentActor || currentCharacter))
{
if (currentActor)
currentActor->Destroy();
if (currentCharacter)
currentCharacter->Destroy();
currentActor = currentCharacter = nullptr;
moveActor = false;
LOGD << "Entity " << currentComponent->entity
<< " destroyed its actor because it shouldn't be rendered to the player";
}
// Decide where we're going to go
{
if (goalWorldPosition)
@ -164,11 +193,11 @@ void UnrealMovementComponent::Update(float deltaSeconds)
if (moveActor)
{
if (currentActor)
if (currentComponent->data.Actor)
sceneComponent->AddLocalOffset(deltaVelocity, false, nullptr,
ETeleportType::None);
else if (currentCharacter)
currentCharacter->AddMovementInput(deltaVelocity, 1.f);
else if (currentComponent->data.Character)
currentComponent->data.Character->AddMovementInput(deltaVelocity, 1.f);
}
worldPosition += ToPosition(deltaVelocity);
@ -224,10 +253,7 @@ void UnrealMovementComponent::UnsubscribePoolEntitiesInternal(
if (!currentComponent)
continue;
if (currentComponent->data.Character)
currentComponent->data.Character->Destroy();
if (currentComponent->data.Actor)
currentComponent->data.Actor->Destroy();
DestroyActor(currentComponent);
if (currentComponent->data.ResourceType != gv::WorldResourceType::None)
{
@ -289,7 +315,7 @@ void UnrealMovementComponent::SpawnActorIfNecessary(
ACharacter* newCharacter = nullptr;
// TODO: Store rotation
FRotator defaultRotation(0.f, 0.f, 0.f);
// FRotator defaultRotation(0.f, 0.f, 0.f);
FVector position(ToFVector(component->data.WorldPosition));
FActorSpawnParameters spawnParams;
@ -315,14 +341,17 @@ void UnrealMovementComponent::SpawnActorIfNecessary(
if (component->data.SpawnParams.ActorToSpawn)
{
newActor = (AActor*)World->SpawnActor<AActor>(component->data.SpawnParams.ActorToSpawn,
position, defaultRotation, spawnParams);
newActor = ActorEntityManager::CreateActorForEntity<AActor>(
World, component->data.SpawnParams.ActorToSpawn, component->entity,
ToPosition(position),
std::bind(&UnrealMovementComponent::OnActorDestroyed, this, std::placeholders::_1));
}
else if (component->data.SpawnParams.CharacterToSpawn)
{
newCharacter = (ACharacter*)World->SpawnActor<ACharacter>(
component->data.SpawnParams.CharacterToSpawn, position, defaultRotation,
spawnParams);
newCharacter = ActorEntityManager::CreateActorForEntity<ACharacter>(
World, component->data.SpawnParams.CharacterToSpawn, component->entity,
ToPosition(position),
std::bind(&UnrealMovementComponent::OnActorDestroyed, this, std::placeholders::_1));
}
else
{
@ -334,16 +363,26 @@ void UnrealMovementComponent::SpawnActorIfNecessary(
LOGE << "Unable to spawn entity " << component->entity << "!";
else
{
ActorEntityManager::TrackActorLifetime(
newCharacter ? newCharacter : newActor,
std::bind(&UnrealMovementComponent::OnActorDestroyed, this, std::placeholders::_1));
component->data.Actor = newActor;
component->data.Character = newCharacter;
if (newActor)
newActor->Entity = component->entity;
}
}
}
void UnrealMovementComponent::DestroyActor(
gv::PooledComponent<UnrealMovementComponentData>* component)
{
if (component->data.Actor)
component->data.Actor->Destroy();
if (component->data.Character)
component->data.Character->Destroy();
component->data.Actor = component->data.Character = nullptr;
}
// @Callback: TrackActorLifetimeCallback
void UnrealMovementComponent::OnActorDestroyed(const AActor* actor)
{
@ -356,12 +395,16 @@ void UnrealMovementComponent::OnActorDestroyed(const AActor* actor)
{
if (currentComponent->data.Actor == actor || currentComponent->data.Character == actor)
{
LOGD << "Entity " << currentComponent->entity << " had its actor " << actor
<< " destroyed (possibly against its will)";
if (gv::EntityLOD::ShouldRenderForPlayer(currentComponent->data.WorldPosition))
{
LOGD << "Entity " << currentComponent->entity << " had its actor " << actor
<< " destroyed (possibly against its will); it is in player view";
}
currentComponent->data.Actor = nullptr;
currentComponent->data.Character = nullptr;
SpawnActorIfNecessary(currentComponent);
// Actors will be respawned during Update() if necessary
// SpawnActorIfNecessary(currentComponent);
}
}
}

8
GalavantUnreal/Source/GalavantUnreal/GalaEntityComponents/UnrealMovementComponent.hpp

@ -26,8 +26,8 @@ struct UnrealMovementComponentData
{
MovementComponentActorSpawnParams SpawnParams;
ACharacter* Character;
AActor* Actor;
ACharacter* Character = nullptr;
AActor* Actor = nullptr;
gv::Position WorldPosition;
@ -36,7 +36,7 @@ struct UnrealMovementComponentData
float MaxSpeed = 500.f;
// If provided, this entity will be registered in the WorldResourceLocator under this type
gv::WorldResourceType ResourceType;
gv::WorldResourceType ResourceType = gv::WorldResourceType::None;
// The last position we told the ResourceLocator we were at (used so that when we move we can
// find the agent to move in ResourceLocator)
@ -48,6 +48,7 @@ class UnrealMovementComponent : public gv::PooledComponentManager<UnrealMovement
{
private:
void SpawnActorIfNecessary(gv::PooledComponent<UnrealMovementComponentData>* component);
void DestroyActor(gv::PooledComponent<UnrealMovementComponentData>* component);
UWorld* World;
@ -79,7 +80,6 @@ public:
virtual void PathEntitiesTo(const gv::EntityList& entities, const gv::PositionList& positions);
void OnActorDestroyed(const AActor* actor);
};
extern UnrealMovementComponent g_UnrealMovementComponentManager;

35
GalavantUnreal/Source/GalavantUnreal/GalavantUnrealMain.cpp

@ -98,6 +98,11 @@ AGalavantUnrealMain::AGalavantUnrealMain()
}
InitializeProceduralWorld();
// This is a complete hack and should only be for Macoy's setup
// Scale everything (including the engine UI) to my DPI settings
// https://answers.unrealengine.com/questions/247475/why-would-setting-games-screen-resolution-in-gameu.html
FSlateApplication::Get().SetApplicationScale(1.53f);
}
void InitializeResources()
@ -125,7 +130,7 @@ void InitializeResources()
Htn::g_TaskDictionary.GetResource(RESKEY("InteractPickup")), parameters};
Htn::TaskCallList getResourceTasks = {getResourceCall, pickupResourceCall};
static gv::AgentGoalDef s_findFoodGoalDef{gv::AgentGoalDef::GoalType::HtnPlan,
/*NumRetriesIfFailed=*/2};
/*NumRetriesIfFailed=*/2, getResourceTasks};
gv::g_AgentGoalDefDictionary.AddResource(RESKEY("FindFood"), &s_findFoodGoalDef);
}
@ -148,7 +153,7 @@ void InitializeResources()
gv::NeedLevelTrigger deathByStarvation;
deathByStarvation.Condition = gv::NeedLevelTrigger::ConditionType::GreaterThanLevel;
deathByStarvation.Level = 290.f;
deathByStarvation.DieNow = true;
deathByStarvation.SetConsciousState = gv::AgentConsciousState::Dead;
TestHungerNeed.LevelTriggers.push_back(deathByStarvation);
}
@ -169,9 +174,15 @@ void InitializeResources()
// Blood Need Triggers
{
gv::NeedLevelTrigger lowBloodUnconscious;
lowBloodUnconscious.Condition = gv::NeedLevelTrigger::ConditionType::LessThanLevel;
lowBloodUnconscious.Level = 20.f;
lowBloodUnconscious.SetConsciousState = gv::AgentConsciousState::Unconscious;
BloodNeed.LevelTriggers.push_back(lowBloodUnconscious);
gv::NeedLevelTrigger deathByBleedingOut;
deathByBleedingOut.Condition = gv::NeedLevelTrigger::ConditionType::Zero;
deathByBleedingOut.DieNow = true;
deathByBleedingOut.SetConsciousState = gv::AgentConsciousState::Dead;
BloodNeed.LevelTriggers.push_back(deathByBleedingOut);
}
@ -325,6 +336,16 @@ void AGalavantUnrealMain::InitializeGalavant()
// Initialize Entity Components
{
// I originally made this happen via static initialization, but using that in combination
// with A) split Galavant static library and GalavantUnreal library and B) Unreal
// Hotreloading caused issues (mainly that Unreal-specific ComponentManagers weren't being
// registered in the same list)
gv::g_EntityComponentManager.AddComponentManager(&gv::g_InteractComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_CombatComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_AgentComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&gv::g_PlanComponentManager);
gv::g_EntityComponentManager.AddComponentManager(&g_UnrealMovementComponentManager);
g_UnrealMovementComponentManager.Initialize(GetWorld(), &TaskEventCallbacks);
{
@ -408,19 +429,13 @@ void AGalavantUnrealMain::Tick(float DeltaTime)
UWorld* world = GetWorld();
// Make sure the Entity Component System knows if an Actor associated with an Entity has been
// destroyed. Component Managers should be able to trust that their Subscribers have valid data.
// This wouldn't be needed if Unreal would stop killing our Actors for strange reasons or if I
// added a ECS hookup to the AActor base class
ActorEntityManager::UpdateNotifyOnActorDestroy(world);
ActorEntityManager::DestroyEntitiesWithDestroyedActors(world, &gv::g_EntityComponentManager);
// Destroy entities now because Unreal might have destroyed actors, so we don't want our code to
// break not knowing that
gv::g_EntityComponentManager.DestroyEntitiesPendingDestruction();
GalavantMain.Update(DeltaTime);
gv::g_CombatComponentManager.Update(DeltaTime);
gv::g_AgentComponentManager.Update(DeltaTime);
gv::g_PlanComponentManager.Update(DeltaTime);
g_UnrealMovementComponentManager.Update(DeltaTime);

65
GalavantUnreal/Source/UnrealEnginePatches/AActorEntityIntegration.patch

@ -0,0 +1,65 @@
From 48ddb51b9790eba1cee74bae9ddb12c202cf9a47 Mon Sep 17 00:00:00 2001
From: Macoy Madson <macoymadson@gmail.com>
Date: Thu, 28 Sep 2017 19:03:32 -0700
Subject: [PATCH] Added very basic Entity integration and lifetime callback to
AActor
---
Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h | 13 +++++++++++++
Engine/Source/Runtime/Engine/Private/Actor.cpp | 2 ++
2 files changed, 15 insertions(+)
diff --git a/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h b/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
index 43f7768..e00a273 100644
--- a/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
+++ b/Engine/Source/Runtime/Engine/Classes/GameFramework/Actor.h
@@ -90,6 +90,12 @@ class ENGINE_API AActor : public UObject
*/
GENERATED_BODY()
+
+/* Galavant-specific code */
+public:
+ UPROPERTY(BlueprintReadOnly, Category="Galavant")
+ int Entity;
+
public:
/**
@@ -3045,6 +3051,9 @@ private:
}
};
+/* Galavant-specific code */
+using ActorEntityOnDestroyFunc = void (*)(AActor* actor, int entity);
+extern ActorEntityOnDestroyFunc ActorEntityOnDestroy;
struct FMarkActorIsBeingDestroyed
{
@@ -3052,6 +3061,10 @@ private:
FMarkActorIsBeingDestroyed(AActor* InActor)
{
InActor->bActorIsBeingDestroyed = true;
+
+ /* Galavant-specific code */
+ if (InActor->Entity && ActorEntityOnDestroy)
+ ActorEntityOnDestroy(InActor, InActor->Entity);
}
friend UWorld;
diff --git a/Engine/Source/Runtime/Engine/Private/Actor.cpp b/Engine/Source/Runtime/Engine/Private/Actor.cpp
index 48ad3cd..55c71ff 100644
--- a/Engine/Source/Runtime/Engine/Private/Actor.cpp
+++ b/Engine/Source/Runtime/Engine/Private/Actor.cpp
@@ -61,6 +61,8 @@ FUObjectAnnotationSparseBool GSelectedActorAnnotation;
FOnProcessEvent AActor::ProcessEventDelegate;
#endif
+ActorEntityOnDestroyFunc ActorEntityOnDestroy = nullptr;
+
uint32 AActor::BeginPlayCallDepth = 0;
AActor::AActor()
--
2.7.4

13
README.md

@ -1,8 +1,19 @@
# galavant-unreal
A Galavant front-end using Unreal Engine 4
This will be a really messy project doing various testing/experimentation.
## Dependencies
This project depends on
1. [Galavant](https://github.com/makuto/galavant), which should be cloned into galavant-unreal/GalavantUnreal/ThirdParty/galavant (or use a symlink) (MIT License)
2. [PolyVox](https://bitbucket.org/volumesoffun/polyvox), which should be cloned into galavant-unreal/GalavantUnreal/ThirdParty/polyvox (pseudo-public domain)
2. [PolyVox](https://bitbucket.org/volumesoffun/polyvox), which should be cloned into galavant-unreal/GalavantUnreal/ThirdParty/polyvox (pseudo-public domain)
## Building
This section is WIP.
### Unreal Engine Code Modifications
Galavant requires some modifications to the UE4 code. These can be found in Source/UnrealEnginePatches and can be applied using git. (TODO: Make this process convenient)
Loading…
Cancel
Save