Cannot respawn inside other players, unless all are blocked.
This commit is contained in:
@@ -78,13 +78,21 @@ bool AABBvsTriangles(const AABB& box,
|
|||||||
bool& isOnGround,
|
bool& isOnGround,
|
||||||
glm::vec3& outResolutionVector);
|
glm::vec3& outResolutionVector);
|
||||||
|
|
||||||
|
//Detects collision, but does not resolve.
|
||||||
|
bool AABBvsTriangles(const AABB& box,
|
||||||
|
const RawModel::Vertex* modelVertices,
|
||||||
|
const std::vector<unsigned int>& modelIndices,
|
||||||
|
const glm::mat4& modelMatrix);
|
||||||
|
|
||||||
//Return true if the boxes are intersecting.
|
//Return true if the boxes are intersecting.
|
||||||
bool AABBVsAABB(const AABB& a, const AABB& b);
|
bool AABBVsAABB(const AABB& a, const AABB& b);
|
||||||
//Return true if the boxes are intersecting.
|
//Return true if the boxes are intersecting.
|
||||||
//Also outputs the minimum translation that box [a] would need in order to resolve collision.
|
//Also outputs the minimum translation that box [a] would need in order to resolve collision.
|
||||||
bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation);
|
bool AABBVsAABB(const AABB& a, const AABB& b, glm::vec3& minimumTranslation);
|
||||||
|
|
||||||
// Calculates an absolute AABB from an entity AABB component
|
// Calculates an absolute AABB from an entity AABB component or Model component.
|
||||||
|
// if takeModelBox is true, the AABB component will be ignored and box is calculated from Model.
|
||||||
|
// if takeModelBox is false, the AABB component will be prefered, if it exists.
|
||||||
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox = false);
|
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox = false);
|
||||||
boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity);
|
boost::optional<EntityAABB> AbsoluteAABBExplosionEffect(EntityWrapper& entity);
|
||||||
//Returns the first entity hit by the input ray. entitiesPotentiallyHitSorted needs to be sorted
|
//Returns the first entity hit by the input ray. entitiesPotentiallyHitSorted needs to be sorted
|
||||||
|
|||||||
@@ -15,11 +15,16 @@ class SpawnerSystem : public System
|
|||||||
public:
|
public:
|
||||||
SpawnerSystem(SystemParams params);
|
SpawnerSystem(SystemParams params);
|
||||||
|
|
||||||
static EntityWrapper Spawn(EntityWrapper spawner, EntityWrapper parent = EntityWrapper::Invalid);
|
// If dontCollideComponent is set, to e.g. "Player", then all the spawner
|
||||||
|
// will try to pick a spawn location so that the spawned entity doesn't
|
||||||
|
// collide with anything that has that component and is collidable.
|
||||||
|
static EntityWrapper Spawn(EntityWrapper spawner, EntityWrapper parent = EntityWrapper::Invalid, const std::string& dontCollideComponent = "");
|
||||||
|
|
||||||
private:
|
private:
|
||||||
EventRelay<SpawnerSystem, Events::SpawnerSpawn> m_OnSpawnerSpawn;
|
EventRelay<SpawnerSystem, Events::SpawnerSpawn> m_OnSpawnerSpawn;
|
||||||
bool OnSpawnerSpawn(Events::SpawnerSpawn& e);
|
bool OnSpawnerSpawn(Events::SpawnerSpawn& e);
|
||||||
|
static void transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint);
|
||||||
|
static bool spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent);
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
@@ -366,7 +366,8 @@ bool AABBvsTriangle(const AABB& box,
|
|||||||
float verticalStepHeight,
|
float verticalStepHeight,
|
||||||
bool& isOnGround,
|
bool& isOnGround,
|
||||||
glm::vec3& boxVelocity,
|
glm::vec3& boxVelocity,
|
||||||
glm::vec3& outResolution)
|
glm::vec3& outResolution,
|
||||||
|
bool resolveCollision)
|
||||||
{
|
{
|
||||||
//Check so we don't have a zero area triangle when calculating the normal.
|
//Check so we don't have a zero area triangle when calculating the normal.
|
||||||
//Also, don't check a triangle facing away from the player.
|
//Also, don't check a triangle facing away from the player.
|
||||||
@@ -426,7 +427,7 @@ bool AABBvsTriangle(const AABB& box,
|
|||||||
//if projections don't overlap, return false.
|
//if projections don't overlap, return false.
|
||||||
if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) {
|
if (!rectangleVsTriangle(boxMin, boxMax, t2D, resolutionVector, resolutionDist, pushedFromTriangleLine)) {
|
||||||
return false;
|
return false;
|
||||||
} else {
|
} else if (resolveCollision) {
|
||||||
//Overwrite the smallest resolution if this is smaller.
|
//Overwrite the smallest resolution if this is smaller.
|
||||||
if (resolutionDist < resolveShortest.DistanceSq) {
|
if (resolutionDist < resolveShortest.DistanceSq) {
|
||||||
resolveShortest.Vector = glm::vec3(0.f);
|
resolveShortest.Vector = glm::vec3(0.f);
|
||||||
@@ -463,6 +464,11 @@ bool AABBvsTriangle(const AABB& box,
|
|||||||
if (glm::abs(t) > 1) {
|
if (glm::abs(t) > 1) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!resolveCollision) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
glm::vec3 cornerResolution = (1+t) * diagonal;
|
glm::vec3 cornerResolution = (1+t) * diagonal;
|
||||||
//Overwrite the smallest resolution if cornerResolution is smaller.
|
//Overwrite the smallest resolution if cornerResolution is smaller.
|
||||||
float lenSq = glm::length2(cornerResolution);
|
float lenSq = glm::length2(cornerResolution);
|
||||||
@@ -537,7 +543,8 @@ bool AABBvsTriangles(const AABB& box,
|
|||||||
glm::vec3& boxVelocity,
|
glm::vec3& boxVelocity,
|
||||||
float verticalStepHeight,
|
float verticalStepHeight,
|
||||||
bool& isOnGround,
|
bool& isOnGround,
|
||||||
glm::vec3& outResolutionVector)
|
glm::vec3& outResolutionVector,
|
||||||
|
bool resolveCollision)
|
||||||
{
|
{
|
||||||
bool hit = false;
|
bool hit = false;
|
||||||
|
|
||||||
@@ -553,7 +560,7 @@ bool AABBvsTriangles(const AABB& box,
|
|||||||
};
|
};
|
||||||
glm::vec3 outVec;
|
glm::vec3 outVec;
|
||||||
bool collideWithGround = isOnGround;
|
bool collideWithGround = isOnGround;
|
||||||
if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec)) {
|
if (AABBvsTriangle(newBox, triVertices, originalBoxVelocity, verticalStepHeight, collideWithGround, boxVelocity, outVec, resolveCollision)) {
|
||||||
hit = true;
|
hit = true;
|
||||||
outResolutionVector += outVec;
|
outResolutionVector += outVec;
|
||||||
newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size());
|
newBox = AABB::FromOriginSize(newBox.Origin() + outVec, newBox.Size());
|
||||||
@@ -569,6 +576,44 @@ bool AABBvsTriangles(const AABB& box,
|
|||||||
return hit;
|
return hit;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool AABBvsTriangles(const AABB& box,
|
||||||
|
const RawModel::Vertex* modelVertices,
|
||||||
|
const std::vector<unsigned int>& modelIndices,
|
||||||
|
const glm::mat4& modelMatrix,
|
||||||
|
glm::vec3& boxVelocity,
|
||||||
|
float verticalStepHeight,
|
||||||
|
bool& isOnGround,
|
||||||
|
glm::vec3& outResolutionVector)
|
||||||
|
{
|
||||||
|
return AABBvsTriangles(box,
|
||||||
|
modelVertices,
|
||||||
|
modelIndices,
|
||||||
|
modelMatrix,
|
||||||
|
boxVelocity,
|
||||||
|
verticalStepHeight,
|
||||||
|
isOnGround,
|
||||||
|
outResolutionVector,
|
||||||
|
true);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool AABBvsTriangles(const AABB& box,
|
||||||
|
const RawModel::Vertex* modelVertices,
|
||||||
|
const std::vector<unsigned int>& modelIndices,
|
||||||
|
const glm::mat4& modelMatrix)
|
||||||
|
{
|
||||||
|
glm::vec3 vel, outres;
|
||||||
|
bool g;
|
||||||
|
return AABBvsTriangles(box,
|
||||||
|
modelVertices,
|
||||||
|
modelIndices,
|
||||||
|
modelMatrix,
|
||||||
|
vel,
|
||||||
|
0.f,
|
||||||
|
g,
|
||||||
|
outres,
|
||||||
|
false);
|
||||||
|
}
|
||||||
|
|
||||||
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox)
|
boost::optional<EntityAABB> EntityAbsoluteAABB(EntityWrapper& entity, bool takeModelBox)
|
||||||
{
|
{
|
||||||
AABB modelSpaceBox;
|
AABB modelSpaceBox;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
//This should be set by the config anyway.
|
//This should be set by the config anyway.
|
||||||
float PlayerSpawnSystem::m_RespawnTime = 15.0f;
|
float PlayerSpawnSystem::m_RespawnTime = 15.0f;
|
||||||
|
|
||||||
PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params)
|
PlayerSpawnSystem::PlayerSpawnSystem(SystemParams params)
|
||||||
: System(params)
|
: System(params)
|
||||||
, m_Timer(0.f)
|
, m_Timer(0.f)
|
||||||
{
|
{
|
||||||
@@ -49,7 +49,7 @@ void PlayerSpawnSystem::Update(double dt)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Spawn the player!
|
// Spawn the player!
|
||||||
EntityWrapper player = SpawnerSystem::Spawn(spawner);
|
EntityWrapper player = SpawnerSystem::Spawn(spawner, EntityWrapper::Invalid, "Player");
|
||||||
// Set the player team affiliation
|
// Set the player team affiliation
|
||||||
player["Team"]["Team"] = req.Team;
|
player["Team"]["Team"] = req.Team;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
#include "Systems/SpawnerSystem.h"
|
#include "Systems/SpawnerSystem.h"
|
||||||
|
#include "Collision/Collision.h"
|
||||||
|
|
||||||
SpawnerSystem::SpawnerSystem(SystemParams params)
|
SpawnerSystem::SpawnerSystem(SystemParams params)
|
||||||
: System(params)
|
: System(params)
|
||||||
{
|
{
|
||||||
EVENT_SUBSCRIBE_MEMBER(m_OnSpawnerSpawn, &SpawnerSystem::OnSpawnerSpawn);
|
EVENT_SUBSCRIBE_MEMBER(m_OnSpawnerSpawn, &SpawnerSystem::OnSpawnerSpawn);
|
||||||
}
|
}
|
||||||
|
|
||||||
EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/)
|
EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /*= EntityWrapper::Invalid*/, const std::string& dontCollideComponent)
|
||||||
{
|
{
|
||||||
// Spawn the entity in the parent's world if it exists, otherwise in the spawner's world
|
// Spawn the entity in the parent's world if it exists, otherwise in the spawner's world
|
||||||
World* world = parent.World;
|
World* world = parent.World;
|
||||||
@@ -14,17 +15,41 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
|
|||||||
world = spawner.World;
|
world = spawner.World;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Load the entity file and parse it
|
||||||
|
const std::string& entityFilePath = spawner["Spawner"]["EntityFile"];
|
||||||
|
auto entityFile = ResourceManager::Load<EntityFile>(entityFilePath);
|
||||||
|
if (entityFile == nullptr) {
|
||||||
|
return EntityWrapper::Invalid;
|
||||||
|
}
|
||||||
|
EntityFileParser parser(entityFile);
|
||||||
|
EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID));
|
||||||
|
|
||||||
|
//If the spawned entity is collideable, then we must not spawn it where it collides with something that
|
||||||
|
//has a dontCollideComponent attached.
|
||||||
|
bool spawnOnCollidable = dontCollideComponent.empty() || !spawnedEntity.HasComponent("Collidable");
|
||||||
|
if (!spawnOnCollidable) {
|
||||||
|
boost::optional<EntityAABB> optBox = Collision::EntityAbsoluteAABB(spawnedEntity);
|
||||||
|
//If we can't calculate the box for some reason, then just spawn somewhere anyway.
|
||||||
|
if (!optBox) {
|
||||||
|
spawnOnCollidable = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Find any SpawnPoints existing as children of spawner
|
// Find any SpawnPoints existing as children of spawner
|
||||||
auto children = spawner.World->GetChildren(spawner.ID);
|
auto children = spawner.World->GetChildren(spawner.ID);
|
||||||
std::vector<EntityWrapper> spawnPoints;
|
std::vector<EntityWrapper> spawnPoints;
|
||||||
for (auto kv = children.first; kv != children.second; ++kv) {
|
for (auto kv = children.first; kv != children.second; ++kv) {
|
||||||
const EntityID& child = kv->second;
|
const EntityID& child = kv->second;
|
||||||
if (spawner.World->HasComponent(child, "SpawnPoint")) {
|
if (spawner.World->HasComponent(child, "SpawnPoint")) {
|
||||||
spawnPoints.push_back(EntityWrapper(spawner.World, child));
|
EntityWrapper spawnPoint = EntityWrapper(spawner.World, child);
|
||||||
|
if (spawnOnCollidable || !spawnedEntityIsColliding(spawnedEntity, spawnPoint, dontCollideComponent)) {
|
||||||
|
spawnPoints.push_back(spawnPoint);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Choose a random SpawnPoint
|
// Choose a random SpawnPoint
|
||||||
|
// If there are no children, or if they are all blocked, then the entity will be spawned at the spawner itself.
|
||||||
EntityWrapper spawnPoint = spawner;
|
EntityWrapper spawnPoint = spawner;
|
||||||
if (!spawnPoints.empty()) {
|
if (!spawnPoints.empty()) {
|
||||||
if (spawnPoints.size() > 1) {
|
if (spawnPoints.size() > 1) {
|
||||||
@@ -39,25 +64,61 @@ EntityWrapper SpawnerSystem::Spawn(EntityWrapper spawner, EntityWrapper parent /
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the entity file and parse it
|
|
||||||
const std::string& entityFilePath = spawner["Spawner"]["EntityFile"];
|
|
||||||
auto entityFile = ResourceManager::Load<EntityFile>(entityFilePath);
|
|
||||||
if (entityFile == nullptr) {
|
|
||||||
return EntityWrapper::Invalid;
|
|
||||||
}
|
|
||||||
EntityFileParser parser(entityFile);
|
|
||||||
EntityWrapper spawnedEntity(world, parser.MergeEntities(world, parent.ID));
|
|
||||||
|
|
||||||
if (spawnPoint != parent) {
|
if (spawnPoint != parent) {
|
||||||
// Set its position and orientation to that of the SpawnPoint
|
transformEntityToSpawnPoint(spawnedEntity, spawnPoint);
|
||||||
spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID);
|
|
||||||
// TODO: Quaternions, bitch
|
|
||||||
spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return spawnedEntity;
|
return spawnedEntity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SpawnerSystem::transformEntityToSpawnPoint(EntityWrapper spawnedEntity, EntityWrapper spawnPoint)
|
||||||
|
{
|
||||||
|
// Set its position and orientation to that of the SpawnPoint
|
||||||
|
spawnedEntity["Transform"]["Position"] = Transform::AbsolutePosition(spawnPoint.World, spawnPoint.ID);
|
||||||
|
// TODO: Quaternions, bitch
|
||||||
|
spawnedEntity["Transform"]["Orientation"] = glm::eulerAngles(Transform::AbsoluteOrientation(spawnPoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
bool SpawnerSystem::spawnedEntityIsColliding(EntityWrapper spawnedEntity, EntityWrapper spawnPoint, const std::string& dontCollideComponent)
|
||||||
|
{
|
||||||
|
transformEntityToSpawnPoint(spawnedEntity, spawnPoint);
|
||||||
|
//Check if the spawned entity collides with anything, and if so, continue to the next spawnpoint.
|
||||||
|
EntityAABB spawnedBox = *Collision::EntityAbsoluteAABB(spawnedEntity);
|
||||||
|
const ComponentPool* otherSpawnedEntities = spawnPoint.World->GetComponents(dontCollideComponent);
|
||||||
|
for (const auto& obj : *otherSpawnedEntities) {
|
||||||
|
if (spawnedEntity.ID == obj.EntityID) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
EntityWrapper otherEntity = EntityWrapper(spawnPoint.World, obj.EntityID);
|
||||||
|
if (!otherEntity.HasComponent("Collidable")) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
auto otherBox = Collision::EntityAbsoluteAABB(otherEntity);
|
||||||
|
if (!otherBox) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (Collision::AABBVsAABB(spawnedBox, *otherBox)) {
|
||||||
|
if (!spawnedBox.Entity.HasComponent("Model")) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
RawModel* model = nullptr;
|
||||||
|
try {
|
||||||
|
model = ResourceManager::Load<RawModel, true>(otherEntity["Model"]["Resource"]);
|
||||||
|
} catch (const std::exception&) {
|
||||||
|
}
|
||||||
|
|
||||||
|
if (model != nullptr && Collision::AABBvsTriangles(
|
||||||
|
spawnedBox,
|
||||||
|
model->Vertices(),
|
||||||
|
model->m_Indices,
|
||||||
|
Transform::ModelMatrix(otherEntity))) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
bool SpawnerSystem::OnSpawnerSpawn(Events::SpawnerSpawn& e)
|
bool SpawnerSystem::OnSpawnerSpawn(Events::SpawnerSpawn& e)
|
||||||
{
|
{
|
||||||
EntityWrapper spawnedEntity = Spawn(e.Spawner, e.Parent);
|
EntityWrapper spawnedEntity = Spawn(e.Spawner, e.Parent);
|
||||||
|
|||||||
Reference in New Issue
Block a user