Просмотр исходного кода

implemented collision detection

Kolja Strohm 2 лет назад
Родитель
Сommit
28972d96d1
1 измененных файлов с 267 добавлено и 3 удалено
  1. 267 3
      FactoryCraft/Entity.cpp

+ 267 - 3
FactoryCraft/Entity.cpp

@@ -116,11 +116,275 @@ bool Entity::tick(double time)
 			speed.x = norm.x * maxMovementSpeed;
 			speed.y = norm.y * maxMovementSpeed;
 		}
-		// TODO: collision check
-		pos += speed * (float)time;
+		// collision checking
+		Vec3<float> minP = model->getMinPos();
+		Vec3<float> maxP = model->getMaxPos();
+		Vec3<float> worldBoundingBox[8];
+		worldBoundingBox[0] = applyWorldTransformation(minP);
+		worldBoundingBox[1] = applyWorldTransformation({ minP.x, minP.y, maxP.z });
+		worldBoundingBox[2] = applyWorldTransformation({ minP.x, maxP.y, minP.z });
+		worldBoundingBox[3] = applyWorldTransformation({ maxP.x, minP.y, minP.z });
+		worldBoundingBox[4] = applyWorldTransformation({ maxP.x, minP.y, maxP.z });
+		worldBoundingBox[5] = applyWorldTransformation({ maxP.x, maxP.y, minP.z });
+		worldBoundingBox[6] = applyWorldTransformation({ minP.x, maxP.y, maxP.z });
+		worldBoundingBox[7] = applyWorldTransformation(maxP);
+		Vec3<float> worldBoundingBoxFloor[8];
+		for (int i = 0; i < 8; i++)
+		{
+			worldBoundingBoxFloor[i] = Vec3<float>(floor(worldBoundingBox[i].x), floor(worldBoundingBox[i].y), floor(worldBoundingBox[i].z));
+		}
+		Vec3<float> frameSpeed = speed * (float)time;
+		bool hasCollided = 0;
+		while (true)
+		{
+			float tf = 1.f;
+			int collType = 0;
+			int updateType = 0;
+			int updateI = 0;
+			if (frameSpeed.x > 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.x) >= abs(worldBoundingBoxFloor[i].x + 1.f - worldBoundingBox[i].x))
+					{
+						float xt = (worldBoundingBoxFloor[i].x + 1.f - worldBoundingBox[i].x) / frameSpeed.x;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * xt;
+						if (tmp.y >= worldBoundingBoxFloor[i].y && tmp.y < worldBoundingBoxFloor[i].y + 1.f && tmp.z >= worldBoundingBoxFloor[i].z && tmp.z < worldBoundingBoxFloor[i].z + 1.f)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x + 1, (int)worldBoundingBoxFloor[i].y, (int)worldBoundingBoxFloor[i].z });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (xt < tf)
+								{
+									tf = xt;
+									collType = 1;
+									updateType = 0;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (xt < tf)
+								{
+									tf = xt;
+									collType = 0;
+									updateType = 1;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (frameSpeed.x < 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.x) >= abs(worldBoundingBoxFloor[i].x - worldBoundingBox[i].x))
+					{
+						float xt = (worldBoundingBoxFloor[i].x - worldBoundingBox[i].x) / frameSpeed.x;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * xt;
+						if (tmp.y >= worldBoundingBoxFloor[i].y && tmp.y < worldBoundingBoxFloor[i].y + 1.f && tmp.z >= worldBoundingBoxFloor[i].z && tmp.z < worldBoundingBoxFloor[i].z + 1.f)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x - 1, (int)worldBoundingBoxFloor[i].y, (int)worldBoundingBoxFloor[i].z });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (xt < tf)
+								{
+									tf = xt;
+									collType = 1;
+									updateType = 0;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (xt < tf)
+								{
+									tf = xt;
+									collType = 0;
+									updateType = 1;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (frameSpeed.y > 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.y) >= abs(worldBoundingBoxFloor[i].y + 1.f - worldBoundingBox[i].y))
+					{
+						float yt = (worldBoundingBoxFloor[i].y + 1.f - worldBoundingBox[i].y) / frameSpeed.y;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * yt;
+						if (tmp.x >= worldBoundingBoxFloor[i].x && tmp.x < worldBoundingBoxFloor[i].x + 1.f && tmp.z >= worldBoundingBoxFloor[i].z && tmp.z < worldBoundingBoxFloor[i].z + 1.f)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x, (int)worldBoundingBoxFloor[i].y + 1, (int)worldBoundingBoxFloor[i].z });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (yt < tf)
+								{
+									tf = yt;
+									collType = 2;
+									updateType = 0;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (yt < tf)
+								{
+									tf = yt;
+									collType = 0;
+									updateType = 2;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (frameSpeed.y < 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.y) >= abs(worldBoundingBoxFloor[i].y - worldBoundingBox[i].y))
+					{
+						float yt = (worldBoundingBoxFloor[i].y - worldBoundingBox[i].y) / frameSpeed.y;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * yt;
+						if (tmp.x >= worldBoundingBoxFloor[i].x && tmp.x < worldBoundingBoxFloor[i].x + 1.f && tmp.z >= worldBoundingBoxFloor[i].z && tmp.z < worldBoundingBoxFloor[i].z + 1.f)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x, (int)worldBoundingBoxFloor[i].y - 1, (int)worldBoundingBoxFloor[i].z });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (yt < tf)
+								{
+									tf = yt;
+									collType = 2;
+									updateType = 0;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (yt < tf)
+								{
+									tf = yt;
+									collType = 0;
+									updateType = 2;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (frameSpeed.z > 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.z) >= abs(worldBoundingBoxFloor[i].z + 1.f - worldBoundingBox[i].z))
+					{
+						float zt = (worldBoundingBoxFloor[i].z + 1.f - worldBoundingBox[i].z) / frameSpeed.z;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * zt;
+						if (zt <= 1.f && tmp.x >= worldBoundingBoxFloor[i].x && tmp.x < worldBoundingBoxFloor[i].x + 1.f && tmp.y >= worldBoundingBoxFloor[i].y && tmp.y < worldBoundingBoxFloor[i].y + 1.f)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x, (int)worldBoundingBoxFloor[i].y, (int)worldBoundingBoxFloor[i].z + 1 });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (zt < tf)
+								{
+									tf = zt;
+									collType = 3;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (zt < tf)
+								{
+									tf = zt;
+									collType = 0;
+									updateType = 3;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (frameSpeed.z < 0)
+			{
+				for (int i = 0; i < 8; i++)
+				{
+					if (abs(frameSpeed.z) >= abs(worldBoundingBoxFloor[i].z - worldBoundingBox[i].z))
+					{
+						float zt = (worldBoundingBoxFloor[i].z - worldBoundingBox[i].z) / frameSpeed.z;
+						Vec3<float> tmp = worldBoundingBox[i] + frameSpeed * zt;
+						if (tmp.x >= worldBoundingBoxFloor[i].x && tmp.x < worldBoundingBoxFloor[i].x + 1.f && tmp.y >= worldBoundingBoxFloor[i].y && tmp.y < worldBoundingBoxFloor[i].y + 1)
+						{
+							Block* b = currentGame->zBlockAt(Vec3<int>{ (int)worldBoundingBoxFloor[i].x, (int)worldBoundingBoxFloor[i].y, (int)worldBoundingBoxFloor[i].z - 1 });
+							if (b) // TODO: ignore passable blocks
+							{
+								if (zt < tf)
+								{
+									tf = zt;
+									collType = 3;
+									updateType = 0;
+								}
+								hasCollided = 1;
+							}
+							else
+							{
+								if (zt < tf)
+								{
+									tf = zt;
+									collType = 0;
+									updateType = 3;
+									updateI = i;
+								}
+							}
+						}
+					}
+				}
+			}
+			if (collType == 1)
+				frameSpeed.x = tf > 0.1f ? frameSpeed.x * (tf - 0.1f) : 0.f;
+			if (collType == 2)
+				frameSpeed.y = tf > 0.1f ? frameSpeed.y * (tf - 0.1f) : 0.f;
+			if (collType == 3)
+				frameSpeed.z = tf > 0.1f ? frameSpeed.z * (tf - 0.1f) : 0.f;
+			if (updateType == 1)
+			{
+				if ((int)worldBoundingBoxFloor[updateI].x < (int)floor(worldBoundingBox[updateI].x + frameSpeed.x))
+					worldBoundingBoxFloor[updateI].x++;
+				if ((int)worldBoundingBoxFloor[updateI].x > (int)floor(worldBoundingBox[updateI].x + frameSpeed.x))
+					worldBoundingBoxFloor[updateI].x--;
+			}
+			if (updateType == 2)
+			{
+				if ((int)worldBoundingBoxFloor[updateI].y < (int)floor(worldBoundingBox[updateI].y + frameSpeed.y))
+					worldBoundingBoxFloor[updateI].y++;
+				if ((int)worldBoundingBoxFloor[updateI].y > (int)floor(worldBoundingBox[updateI].y + frameSpeed.y))
+					worldBoundingBoxFloor[updateI].y--;
+			}
+			if (updateType == 3)
+			{
+				if ((int)worldBoundingBoxFloor[updateI].z < (int)floor(worldBoundingBox[updateI].z + frameSpeed.z))
+					worldBoundingBoxFloor[updateI].z++;
+				if ((int)worldBoundingBoxFloor[updateI].z > (int)floor(worldBoundingBox[updateI].z + frameSpeed.z))
+					worldBoundingBoxFloor[updateI].z--;
+			}
+			if (updateType || collType)
+				continue;
+			break;
+		}
+		pos += frameSpeed;
 		currentGame->zKamera()->setPosition(pos + Vec3<float>(0.f, 0.f, 1.5f));
 		((Game*)(Menu*)menuRegister->get("game"))->updatePosition(pos, 0, { 0, 0, 0 });
-		if (flags != lastFlags || direction != lastDirection || timeSinceSync >= 1)
+		if (flags != lastFlags || direction != lastDirection || timeSinceSync >= 1 || hasCollided)
 		{
 			if (timeSinceSync > 0)
 			{