Browse Source

Added Entity LOD, ProceduralWorld; small fixes/convenience

- Added EntityPlayerSharedData and associated functions for providing easy access to player data
- Added EntityLevelOfDetail which will tell modules how much they should simulate things based on where the player is at
- Noise now takes a params struct making the interface much less ugly
- Added comment stating that Z will be the up axis (this is how Unreal works too)
- Added some logging to help indicate what the behavior is when registering component managers. This whole subsystem needs a rework
- Fixed ComponentManager UnsubscribeEntities printing the entity list before removing irrelevant entries
combatComponentRefactor
Macoy Madson 6 years ago
parent
commit
e052907c23
  1. 27
      TODO.txt
  2. 3
      src/entityComponentSystem/ComponentManager.cpp
  3. 1
      src/entityComponentSystem/ComponentTypes.hpp
  4. 12
      src/entityComponentSystem/EntityComponentManager.cpp
  5. 21
      src/entityComponentSystem/EntityComponentManager.hpp
  6. 35
      src/entityComponentSystem/EntitySharedData.cpp
  7. 13
      src/entityComponentSystem/EntitySharedData.hpp
  8. 23
      src/game/EntityLevelOfDetail.cpp
  9. 18
      src/game/EntityLevelOfDetail.hpp
  10. 2
      src/game/InteractComponentManager.hpp
  11. 3
      src/game/Jamfile
  12. 6
      src/game/agent/AgentComponentManager.cpp
  13. 5
      src/game/agent/MovementManager.hpp
  14. 2
      src/project/galavantSublime/galavant.sublime-project
  15. 20
      src/thirdPartyWrapper/noise/noise.cpp
  16. 71
      src/thirdPartyWrapper/noise/noise.hpp
  17. 4
      src/world/Jamfile
  18. 2
      src/world/Position.hpp
  19. 90
      src/world/ProceduralWorld.cpp
  20. 38
      src/world/ProceduralWorld.hpp

27
TODO.txt

@ -1,13 +1,32 @@
----------------------------------------------------------------------------------------------------
TODO
----------------------------------------------------------------------------------------------------
Soak test editor - after ~1 hour it was looking pretty glitchy
What does agent component manager do with triggers once plan is done? How to know plan is done?
statics don't work well with hot reloading
Need some sort of system which makes it easy to tell static shit to reload?
Play with Polyvox chunk scale
**** LEFT OFF AT:
Pickups sort of working, sometimes they aren't picked up, Actors are being destroyed strangely
Agents should be destroyed by the AgentComponentManager if DieNow, but they don't seem to be hitting it
Possibility that pickup actor is falling through the floor (invisible other component hitting KillZ?) YES, it's KillZ
Chunks are fucked
----------------------------------------------------------------------------------------------------
DOING
----------------------------------------------------------------------------------------------------
Added TrackActorLifetime, which is untested
Is this executed when pending kills will be sitting around, or right after cleanup?
Added Interact to player, but does not seem to work
Added UnrealMovementComponent Actor/Character spawning
----------------------------------------------------------------------------------------------------
DONE
----------------------------------------------------------------------------------------------------
Added EntityPlayerSharedData, which is untested

3
src/entityComponentSystem/ComponentManager.cpp

@ -30,7 +30,8 @@ void ComponentManager::UnsubscribeEntities(const EntityList& entities)
// Remove from subscribers
EntityListRemoveNonUniqueEntitiesInSuspect(entitiesToUnsubscribe, Subscribers);
LOGD << "Manager " << (int)Type << " unsubscribed " << entities.size() << " entities";
LOGD << "Manager " << (int)Type << " unsubscribed " << entitiesToUnsubscribe.size()
<< " entities";
}
}
}

1
src/entityComponentSystem/ComponentTypes.hpp

@ -10,6 +10,7 @@ enum class ComponentType : unsigned int
Movement,
Agent,
Plan,
Interact,
ComponentType_count
};

12
src/entityComponentSystem/EntityComponentManager.cpp

@ -1,5 +1,7 @@
#include "EntityComponentManager.hpp"
#include "util/Logging.hpp"
#include <cassert>
namespace gv
@ -34,6 +36,10 @@ bool EntityComponentManager::AddComponentManagerOfType(ComponentType type,
assert(findIt == ComponentManagers.end());
ComponentManagers[type] = manager;
if (type != manager->GetType())
LOGW << "ComponentManager of type " << (int)manager->GetType()
<< " registered for type " << (int)type;
}
return false;
@ -41,6 +47,12 @@ bool EntityComponentManager::AddComponentManagerOfType(ComponentType type,
bool EntityComponentManager::AddComponentManager(ComponentManager *manager)
{
if (!manager)
return false;
if (manager->GetType() == ComponentType::None)
LOGE << "Registered ComponentManager " << manager << " with no Type!";
return AddComponentManagerOfType(manager->GetType(), manager);
}

21
src/entityComponentSystem/EntityComponentManager.hpp

@ -24,7 +24,7 @@ private:
typedef std::map<ComponentType, ComponentManager *> ComponentManagerMap;
typedef ComponentManagerMap::iterator ComponentManagerMapIterator;
static EntityComponentManager* Singleton;
static EntityComponentManager *Singleton;
ComponentManagerMap ComponentManagers;
@ -71,6 +71,23 @@ public:
// the ActiveEntities list)
void DestroyAllEntities();
static EntityComponentManager* GetSingleton();
static EntityComponentManager *GetSingleton();
};
// Convenience function
// TODO: Make it so ComponentManagers can just be functions instead of singletons
template <class T>
T *GetComponentManagerForType(ComponentType type)
{
EntityComponentManager *entityComponentManager = EntityComponentManager::GetSingleton();
if (entityComponentManager)
{
T *componentManager =
static_cast<T *>(entityComponentManager->GetComponentManagerForType(type));
return componentManager;
}
return nullptr;
}
};

35
src/entityComponentSystem/EntitySharedData.cpp

@ -15,6 +15,8 @@ struct EntitySharedData
// Entity positions owned by an external module
EntityPositionRefMap EntityPositionRefs;
EntityPlayerSharedData PlayerData;
};
static EntitySharedData s_Data;
@ -127,4 +129,37 @@ Position* EntityGetPosition(const Entity& entity)
return &findUnowned->second;
}
const EntityPlayerSharedData& EntityPlayerGetSharedData()
{
return s_Data.PlayerData;
}
void EntityPlayerRegisterPosition(const Entity entity, Position* position)
{
if (s_Data.PlayerData.PlayerEntity)
{
LOGE << "A Player Entity " << s_Data.PlayerData.PlayerEntity
<< " has already been registered (tried to register " << entity << ")!";
return;
}
s_Data.PlayerData.PlayerEntity = entity;
s_Data.PlayerData.PlayerPosition = position;
// Add to owned lists as well
gv::EntityList playerEntities = {entity};
gv::PositionRefList playerPositionList = {position};
gv::EntityCreatePositions(playerEntities, playerPositionList);
}
void EntityPlayerUnregisterPosition()
{
if (s_Data.PlayerData.PlayerEntity)
{
gv::EntityList playerEntities = {s_Data.PlayerData.PlayerEntity};
gv::EntityDestroyPositions(playerEntities);
s_Data.PlayerData.PlayerEntity = 0;
s_Data.PlayerData.PlayerPosition = nullptr;
}
}
}

13
src/entityComponentSystem/EntitySharedData.hpp

@ -7,7 +7,7 @@ namespace gv
{
// TODO: Make it such that things can own the position and this module is used for lookup externally
// only. If nothing owns the position this module will anonymously own it until someone steps up
// This is so I can follow the principle that "That which changes the data, owns the data". It also
// This is so I can follow the principle that "That which changes the data, owns the data". It also
// seems fucking awful to do a map lookup every time anyone wants to read or write a position
// TODO: Make it clear that we expect the given positions to hang around
@ -20,4 +20,15 @@ void EntitySetPosition(const Entity& entity, const Position& position);
void EntityGetPositions(const EntityList& entities, PositionRefList& positionsOut);
// If an entity doesn't have a position, it will be created at 0, 0, 0
Position* EntityGetPosition(const Entity& entity);
struct EntityPlayerSharedData
{
// For ease of access
Entity PlayerEntity;
const Position* PlayerPosition;
};
const EntityPlayerSharedData& EntityPlayerGetSharedData();
void EntityPlayerRegisterPosition(const Entity entity, Position* position);
void EntityPlayerUnregisterPosition();
};

23
src/game/EntityLevelOfDetail.cpp

@ -0,0 +1,23 @@
#include "game/EntityLevelOfDetail.hpp"
#include "entityComponentSystem/EntitySharedData.hpp"
namespace gv
{
namespace EntityLOD
{
EntityLODSettings g_EntityLODSettings;
bool ShouldRenderForPlayer(Position& position)
{
const EntityPlayerSharedData& playerData = EntityPlayerGetSharedData();
const Position* playerPosition = playerData.PlayerPosition;
if (playerPosition)
{
return (playerPosition->ManhattanTo(position) <= g_EntityLODSettings.PlayerManhattanViewDistance);
}
else
// When there's no player nothing should be rendered
return false;
}
}
}

18
src/game/EntityLevelOfDetail.hpp

@ -0,0 +1,18 @@
#pragma once
#include "world/Position.hpp"
namespace gv
{
namespace EntityLOD
{
struct EntityLODSettings
{
float PlayerManhattanViewDistance;
};
extern EntityLODSettings g_EntityLODSettings;
bool ShouldRenderForPlayer(Position& position);
}
}

2
src/game/InteractComponentManager.hpp

@ -24,6 +24,8 @@ private:
PickupList Pickups;
protected:
ComponentType Type = ComponentType::Interact;
virtual void UnsubscribeEntitiesInternal(const EntityList& entities);
public:

3
src/game/Jamfile

@ -6,7 +6,8 @@ Library libGalaGame : agent/PlanComponentManager.cpp
agent/AgentComponentManager.cpp
agent/htnTasks/MovementTasks.cpp
agent/htnTasks/InteractTasks.cpp
InteractComponentManager.cpp ;
InteractComponentManager.cpp
EntityLevelOfDetail.cpp ;
LinkLibraries libGalaGame : libGalaAi libGalaWorld ;

6
src/game/agent/AgentComponentManager.cpp

@ -82,6 +82,9 @@ void AgentComponentManager::Update(float deltaSeconds)
{
need.LastUpdateTime = WorldTime;
LOGV_IF(DebugPrint) << "Agent Entity " << currentEntity << " updated need "
<< need.Def->Name << " to level " << need.Level;
for (const NeedLevelTrigger& needLevelTrigger : need.Def->LevelTriggers)
{
bool needTriggerHit = (needLevelTrigger.GreaterThanLevel &&
@ -95,6 +98,9 @@ void AgentComponentManager::Update(float deltaSeconds)
AgentGoal::GoalType::GetResource,
needLevelTrigger.WorldResource};
AddGoalIfUniqueType(goals, newNeedResourceGoal);
LOGD_IF(DebugPrint) << "Agent Entity " << currentEntity
<< " has hit need trigger for need "
<< need.Def->Name;
}
else if (needLevelTrigger.DieNow)
{

5
src/game/agent/MovementManager.hpp

@ -6,8 +6,9 @@
namespace gv
{
/* --MovementManager--
This was needed so that I could have a heavily Unreal-specific class (TestMovementComponent) without
needing to compromise the Galavant lib. I'll probably think of a better way to do this eventually
This was needed so that I could have a heavily Unreal-specific class (UnrealMovementComponent)
without needing to compromise the Galavant lib. I'll probably think of a better way to do this
eventually
*/
class MovementManager
{

2
src/project/galavantSublime/galavant.sublime-project

@ -4,7 +4,7 @@
{
"path": "../../../../galavant",
"name": "Galavant",
"folder_exclude_patterns": ["project"],
"folder_exclude_patterns": ["project", "thirdParty"],
"file_include_patterns": ["*.c", "*.cpp", "*.h", "*.hpp", "*.txt", "Jam*", "*.md"]
},
{

20
src/thirdPartyWrapper/noise/noise.cpp

@ -81,8 +81,8 @@ float gv::Noise2d::scaledRawNoise2d(float x, float y, float lowBound, float high
}
float gv::Noise2d::scaledOctaveNoise2d(float x, float y, float lowBound, float highBound,
int octaves, float scale, float persistence,
float lacunarity)
int octaves, float scale, float persistence,
float lacunarity)
{
float frequency = scale;
float amplitude = 1;
@ -101,6 +101,12 @@ float gv::Noise2d::scaledOctaveNoise2d(float x, float y, float lowBound, float h
return scaleNoiseValue(value / maxAmplitude, lowBound, highBound);
}
float gv::Noise2d::scaledOctaveNoise2d(float x, float y, ScaledOctaveNoiseParams& params)
{
return scaledOctaveNoise2d(x, y, params.lowBound, params.highBound, params.octaves,
params.scale, params.persistence, params.lacunarity);
}
class noise3dHolder : public gv::noiseHolder
{
private:
@ -140,8 +146,8 @@ float gv::Noise3d::scaledRawNoise3d(float x, float y, float z, float lowBound, f
}
float gv::Noise3d::scaledOctaveNoise3d(float x, float y, float z, float lowBound, float highBound,
int octaves, float scale, float persistence,
float lacunarity)
int octaves, float scale, float persistence,
float lacunarity)
{
float frequency = scale;
float amplitude = 1;
@ -159,4 +165,10 @@ float gv::Noise3d::scaledOctaveNoise3d(float x, float y, float z, float lowBound
return scaleNoiseValue(value / maxAmplitude, lowBound, highBound);
}
float gv::Noise3d::scaledOctaveNoise3d(float x, float y, float z, ScaledOctaveNoiseParams& params)
{
return scaledOctaveNoise3d(x, y, z, params.lowBound, params.highBound, params.octaves,
params.scale, params.persistence, params.lacunarity);
}
#endif

71
src/thirdPartyWrapper/noise/noise.hpp

@ -6,6 +6,30 @@ namespace gv
// Needed to avoid multiple defintions of OpenSimplex noise template metaprogram thingies
class noiseHolder;
/* Scaled Octave Noise Params:
* See http://libnoise.sourceforge.net/glossary/#octave for a great
* description of how this works
* */
struct ScaledOctaveNoiseParams
{
// The lowest and highest value to map the noise to
float lowBound;
float highBound;
// The number of octaves to add (more = more detail, but slower)
int octaves;
// The starting scale of the first octave
float scale;
// Multiplied by the amplitude each octave, making subsequent octave values have less weight
float persistence;
// The scale is multiplied by this value every octave, changing the frequency. The true "octave"
// value for lacunarity is 2
float lacunarity;
};
/* --Noise2d--
* This class uses OpenSimplex noise to generate noise given an x and y
* coordinate.
@ -25,25 +49,11 @@ public:
// Scales a raw noise value to be between lowBound and highBound
float scaledRawNoise2d(float x, float y, float lowBound, float highBound);
/* Adds multiple layers of varying amplitudes together, then scales
* the value to be between lowBound and highBound
*
* Parameters:
* x, y The point
* lowBound, highBound The lowest and highest value to map the noise to
* scale The starting scale of the first octave
* octaves The number of octaves to add
* persistence Multiplied by the amplitude each octave, making
* subsequent octave values have less weight
* lacunarity The scale is multiplied by this value every
* octave, changing the frequency. The true
* "octave" value for lacunarity is 2
*
* See http://libnoise.sourceforge.net/glossary/#octave for a great
* description of how this works
* */
// See ScaledOctaveNoiseParams to know what each parameter does
float scaledOctaveNoise2d(float x, float y, float lowBound, float highBound, int octaves,
float scale, float persistence, float lacunarity);
float scale, float persistence, float lacunarity);
float scaledOctaveNoise2d(float x, float y, ScaledOctaveNoiseParams& params);
};
/* --Noise3d--
@ -65,27 +75,12 @@ public:
// Scales a raw noise value to be between lowBound and highBound
float scaledRawNoise3d(float x, float y, float z, float lowBound, float highBound);
/* Adds multiple layers of varying amplitudes together, then scales
* the value to be between lowBound and highBound
*
* Parameters:
* x, y, z The point
* lowBound, highBound The lowest and highest value to map the noise to
* scale The starting scale of the first octave
* octaves The number of octaves to add
* persistence Multiplied by the amplitude each octave, making
* subsequent octave values have less weight
* lacunarity The scale is multiplied by this value every
* octave, changing the frequency. The true
* "octave" value for lacunarity is 2
*
* See http://libnoise.sourceforge.net/glossary/#octave for a great
* description of how this works
* */
float scaledOctaveNoise3d(float x, float y, float z, float lowBound, float highBound, int octaves,
float scale, float persistence, float lacunarity);
};
// See ScaledOctaveNoiseParams to know what each parameter does
float scaledOctaveNoise3d(float x, float y, float z, float lowBound, float highBound,
int octaves, float scale, float persistence, float lacunarity);
float scaledOctaveNoise3d(float x, float y, float z, ScaledOctaveNoiseParams& params);
};
}
#endif

4
src/world/Jamfile

@ -2,6 +2,8 @@ SubDir . src world ;
SubDirC++Flags $(ALLLIBSC++FLAGS) ;
Library libGalaWorld : Position.cpp WorldResourceLocator.cpp ;
Library libGalaWorld : Position.cpp
WorldResourceLocator.cpp
ProceduralWorld.cpp ;
MakeLocate libGalaWorld.a : lib ;

2
src/world/Position.hpp

@ -7,6 +7,8 @@
namespace gv
{
extern const float POSITION_TOLERANCE;
// Galavant will use Z as the up axis
struct Position
{
float X = 0.f;

90
src/world/ProceduralWorld.cpp

@ -0,0 +1,90 @@
#include "ProceduralWorld.hpp"
namespace gv
{
namespace ProceduralWorld
{
static ProceduralWorldParams s_WorldParams;
// This isn't fantastic, but I can replace it with a better system once one is needed
ProceduralWorldParams& GetCurrentActiveWorldParams()
{
return s_WorldParams;
}
static float GetTileHeightForWorldPosition(const Position& tileWorldPosition)
{
static int seed = 0;
static gv::Noise2d* noise2dGenerator = nullptr;
if (!noise2dGenerator || seed != s_WorldParams.Seed)
{
seed = s_WorldParams.Seed;
noise2dGenerator = new gv::Noise2d(seed);
}
/*float tileHeight = noise2dGenerator->scaledOctaveNoise2d(
tileWorldPosition.X, tileWorldPosition.Y, s_WorldParams.ScaledNoiseParams);*/
float tileHeight = 128.f; // FOR DEBUGGING ONLY; DELETE ME!
return tileHeight;
}
void GetCellTileMapForPosition(const Position& cellPosition, WorldCellTileMap* tileMapOut)
{
if (!tileMapOut)
return;
for (int tileY = 0; tileY < WORLD_CELL_Y_SIZE; tileY++)
{
for (int tileX = 0; tileX < WORLD_CELL_X_SIZE; tileX++)
{
// Don't ever care about Z axis because we're working in 2D
int tileCoord[] = {tileX, tileY, 0};
Position tileWorldPosition(cellPosition);
for (int i = 0; i < 3; i++)
tileWorldPosition[i] += tileCoord[i] * s_WorldParams.WorldCellTileSize[i];
float tileHeight = GetTileHeightForWorldPosition(tileWorldPosition);
// Remap tile height, if necessary
if (s_WorldParams.ScaledNoiseParams.highBound != 255.f)
tileHeight = (tileHeight / s_WorldParams.ScaledNoiseParams.highBound) * 255.f;
tileMapOut->TileHeights[tileY][tileX] = (unsigned char)tileHeight;
}
}
}
void SampleWorldCellHeights(const Position& center, int width, int height,
unsigned char* sampleHeights2d)
{
if (!sampleHeights2d)
return;
for (int y = 0; y < height; y++)
{
for (int x = 0; x < width; x++)
{
int worldCellSize[] = {WORLD_CELL_X_SIZE, WORLD_CELL_Y_SIZE, WORLD_CELL_Z_SIZE};
int sampleCoord[] = {x - (width / 2), y - (height / 2), 0};
Position cellWorldPosition(center);
for (int i = 0; i < 3; i++)
cellWorldPosition[i] +=
sampleCoord[i] * (s_WorldParams.WorldCellTileSize[i] * worldCellSize[i]);
float tileHeight = GetTileHeightForWorldPosition(cellWorldPosition);
// Remap tile height, if necessary
if (s_WorldParams.ScaledNoiseParams.highBound != 255.f)
tileHeight = (tileHeight / s_WorldParams.ScaledNoiseParams.highBound) * 255.f;
sampleHeights2d[(y * width) + x] = (unsigned char)tileHeight;
}
}
}
}
}

38
src/world/ProceduralWorld.hpp

@ -0,0 +1,38 @@
#pragma once
#include "Position.hpp"
#include "thirdPartyWrapper/noise/noise.hpp"
#define WORLD_CELL_X_SIZE 16
#define WORLD_CELL_Y_SIZE 16
#define WORLD_CELL_Z_SIZE 255
namespace gv
{
namespace ProceduralWorld
{
struct ProceduralWorldParams
{
int Seed;
ScaledOctaveNoiseParams ScaledNoiseParams;
// The size of a single 3D tile.
float WorldCellTileSize[3];
};
ProceduralWorldParams& GetCurrentActiveWorldParams();
// TODO: This assumes a 2D world. We need to figure the world out
struct WorldCellTileMap
{
unsigned char TileHeights[WORLD_CELL_Y_SIZE][WORLD_CELL_X_SIZE];
};
void GetCellTileMapForPosition(const Position& cellPosition, WorldCellTileMap* tileMapOut);
// Sample (width * height) cell heights (e.g. for world map)
void SampleWorldCellHeights(const Position& center, int width, int height,
unsigned char* sampleHeights2d);
}
}
Loading…
Cancel
Save