mirror of
https://github.com/Heretek-AI/GDevelop.git
synced 2026-07-23 10:35:35 -04:00
202 lines
6.7 KiB
C++
202 lines
6.7 KiB
C++
/**
|
|
|
|
GDevelop - Pathfinding Behavior Extension
|
|
Copyright (c) 2010-2016 Florian Rival (Florian.Rival@gmail.com)
|
|
This project is released under the MIT License.
|
|
*/
|
|
/**
|
|
* @file Tests for the Pathfinding extension.
|
|
*/
|
|
#define CATCH_CONFIG_MAIN
|
|
#include "catch.hpp"
|
|
#include "GDCore/CommonTools.h"
|
|
#include "GDCore/Project/ClassWithObjects.h"
|
|
#include "GDCore/Project/Layout.h"
|
|
#include "GDCore/Project/Object.h"
|
|
#include "GDCpp/RuntimeScene.h"
|
|
#include "GDCpp/RuntimeGame.h"
|
|
#include "GDCpp/RuntimeObject.h"
|
|
#include "GDCpp/Extensions/Builtin/ObjectTools.h"
|
|
#include "../PathfindingBehavior.h"
|
|
#include "../PathfindingObstacleBehavior.h"
|
|
|
|
//Mock objects that can have a specific size
|
|
class ResizableRuntimeObject : public RuntimeObject {
|
|
public:
|
|
ResizableRuntimeObject(RuntimeScene & scene, const gd::Object & obj) :
|
|
RuntimeObject(scene, obj)
|
|
{}
|
|
|
|
float GetWidth() const override { return width; }
|
|
float GetHeight() const override { return height; }
|
|
void SetWidth(float newWidth) override { width = newWidth; }
|
|
void SetHeight(float newHeight) override { height = newHeight; }
|
|
|
|
private:
|
|
float width;
|
|
float height;
|
|
};
|
|
|
|
TEST_CASE( "PathfindingBehavior", "[game-engine][pathfinding]" ) {
|
|
SECTION("Basics") {
|
|
//Prepare some objects and the context
|
|
RuntimeGame game;
|
|
gd::Object playerObj("player");
|
|
PathfindingBehavior * behavior = new PathfindingBehavior();
|
|
behavior->SetName("Pathfinding");
|
|
playerObj.AddBehavior(behavior);
|
|
|
|
RuntimeScene scene(NULL, &game);
|
|
RuntimeObject player(scene, playerObj);
|
|
|
|
PathfindingBehavior * runtimeBehavior =
|
|
static_cast<PathfindingBehavior *>(player.GetBehaviorRawPointer("Pathfinding"));
|
|
|
|
//Check that a path can be computed without obstacles
|
|
runtimeBehavior->MoveTo(scene, 1200, 1300);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 66);
|
|
REQUIRE(runtimeBehavior->GetNodeX(65) == 1200); //Check that destination
|
|
REQUIRE(runtimeBehavior->GetNodeY(65) == 1300); //is correct
|
|
|
|
//Add an obstacle
|
|
gd::Object obstacleObj("obstacle");
|
|
obstacleObj.AddBehavior(new PathfindingObstacleBehavior());
|
|
std::shared_ptr<ResizableRuntimeObject> obstacle(new ResizableRuntimeObject(scene, obstacleObj));
|
|
|
|
scene.objectsInstances.AddObject(obstacle);
|
|
|
|
obstacle->SetX(1100);
|
|
obstacle->SetY(1200);
|
|
obstacle->SetWidth(200);
|
|
obstacle->SetHeight(200);
|
|
scene.RenderAndStep();
|
|
|
|
runtimeBehavior->MoveTo(scene, 1200, 1300);
|
|
REQUIRE(runtimeBehavior->PathFound() == false);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 0);
|
|
}
|
|
SECTION("Obstacle in the middle") {
|
|
//Prepare some objects and the context
|
|
RuntimeGame game;
|
|
|
|
gd::Object playerObj("player");
|
|
auto behavior = new PathfindingBehavior();
|
|
behavior->SetName("Pathfinding");
|
|
playerObj.AddBehavior(behavior);
|
|
|
|
gd::Object obstacleObj("obstacle");
|
|
obstacleObj.AddBehavior(new PathfindingObstacleBehavior());
|
|
|
|
RuntimeScene scene(NULL, &game);
|
|
std::shared_ptr<RuntimeObject> player(new RuntimeObject(scene, playerObj));
|
|
std::shared_ptr<ResizableRuntimeObject> obstacle(new ResizableRuntimeObject(scene, obstacleObj));
|
|
scene.objectsInstances.AddObject(player);
|
|
scene.objectsInstances.AddObject(obstacle);
|
|
|
|
obstacle->SetX(300);
|
|
obstacle->SetY(600);
|
|
obstacle->SetWidth(32);
|
|
obstacle->SetHeight(32);
|
|
scene.RenderAndStep();
|
|
|
|
PathfindingBehavior * runtimeBehavior =
|
|
static_cast<PathfindingBehavior *>(player->GetBehaviorRawPointer("Pathfinding"));
|
|
|
|
//Check that a path changes when adding obstacles
|
|
runtimeBehavior->MoveTo(scene, 1200, 1300);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeX(30) == 540);
|
|
REQUIRE(runtimeBehavior->GetNodeY(30) == 600);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 66);
|
|
|
|
//Enlarge the obstacle
|
|
obstacle->SetWidth(600);
|
|
scene.RenderAndStep();
|
|
|
|
runtimeBehavior->MoveTo(scene, 1200, 1300);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 77);
|
|
|
|
//Enlarge more
|
|
obstacle->SetX(0);
|
|
obstacle->SetWidth(1300);
|
|
scene.RenderAndStep();
|
|
|
|
runtimeBehavior->MoveTo(scene, 1200, 1300);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 92);
|
|
}
|
|
SECTION("Obstacles making a corridor") {
|
|
//Prepare some objects and the context
|
|
RuntimeGame game;
|
|
|
|
gd::Object playerObj("player");
|
|
auto behavior = new PathfindingBehavior();
|
|
behavior->SetName("Pathfinding");
|
|
playerObj.AddBehavior(behavior);
|
|
|
|
gd::Object obstacleObj("obstacle");
|
|
obstacleObj.AddBehavior(new PathfindingObstacleBehavior());
|
|
|
|
RuntimeScene scene(NULL, &game);
|
|
std::shared_ptr<RuntimeObject> player(new RuntimeObject(scene, playerObj));
|
|
std::shared_ptr<ResizableRuntimeObject> obstacle1(new ResizableRuntimeObject(scene, obstacleObj));
|
|
std::shared_ptr<ResizableRuntimeObject> obstacle2(new ResizableRuntimeObject(scene, obstacleObj));
|
|
scene.objectsInstances.AddObject(player);
|
|
scene.objectsInstances.AddObject(obstacle1);
|
|
scene.objectsInstances.AddObject(obstacle2);
|
|
|
|
obstacle1->SetX(-20);
|
|
obstacle2->SetX(20);
|
|
obstacle1->SetWidth(20);
|
|
obstacle1->SetHeight(20);
|
|
obstacle2->SetWidth(20);
|
|
obstacle2->SetHeight(20);
|
|
player->SetX(5);
|
|
player->SetY(25);
|
|
scene.RenderAndStep();
|
|
|
|
PathfindingBehavior * runtimeBehavior =
|
|
static_cast<PathfindingBehavior *>(player->GetBehaviorRawPointer("Pathfinding"));
|
|
|
|
//Check that a path changes when adding obstacles
|
|
runtimeBehavior->MoveTo(scene, 5, -5);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 2);
|
|
}
|
|
SECTION("Diagonals") {
|
|
//Prepare some objects and the context
|
|
RuntimeGame game;
|
|
|
|
gd::Object playerObj("player");
|
|
auto behavior = new PathfindingBehavior();
|
|
behavior->SetName("Pathfinding");
|
|
playerObj.AddBehavior(behavior);
|
|
|
|
RuntimeScene scene(NULL, &game);
|
|
std::shared_ptr<RuntimeObject> player(new RuntimeObject(scene, playerObj));
|
|
scene.objectsInstances.AddObject(player);
|
|
|
|
PathfindingBehavior * runtimeBehavior =
|
|
static_cast<PathfindingBehavior *>(player->GetBehaviorRawPointer("Pathfinding"));
|
|
|
|
//Test a specific path that can lead to false computations
|
|
//in case the algorithm open nodes list is not implemented properly
|
|
//and can remove node with same cost.
|
|
runtimeBehavior->MoveTo(scene, 1*20, 4*20);
|
|
REQUIRE(runtimeBehavior->PathFound() == true);
|
|
REQUIRE(runtimeBehavior->GetNodeCount() == 5);
|
|
REQUIRE(runtimeBehavior->GetNodeX(0) == 0);
|
|
REQUIRE(runtimeBehavior->GetNodeY(0) == 0);
|
|
REQUIRE(runtimeBehavior->GetNodeX(1) == 0);
|
|
REQUIRE(runtimeBehavior->GetNodeY(1) == 20);
|
|
REQUIRE(runtimeBehavior->GetNodeX(2) == 0);
|
|
REQUIRE(runtimeBehavior->GetNodeY(2) == 40);
|
|
REQUIRE(runtimeBehavior->GetNodeX(3) == 0);
|
|
REQUIRE(runtimeBehavior->GetNodeY(3) == 60);
|
|
REQUIRE(runtimeBehavior->GetNodeX(4) == 20);
|
|
REQUIRE(runtimeBehavior->GetNodeY(4) == 80);
|
|
}
|
|
}
|