mirror of
https://github.com/GDQuest/godot-platformer-2d.git
synced 2026-01-23 02:14:19 +00:00
Merge pull request #165 from GDquest/features/heatmap-pathfinding
Heatmap pathfinding for many agents
This commit is contained in:
commit
13f090df51
21 changed files with 973 additions and 25 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "GDNative"]
|
||||
path = GDNative
|
||||
url = https://github.com/GodotNativeTools/godot-cpp
|
||||
1
GDNative
Submodule
1
GDNative
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 7482074779722df2b704e28ef352dd7bf80cc448
|
||||
27
README.org
27
README.org
|
|
@ -133,3 +133,30 @@
|
|||
|
||||
Persona: the learners of the premium course don't want to be spoon-fed ready-made solutions. They enjoy learning and are ready to put in some efforts to improve. They go further than watching the lessons, putting what they learned in practice. They expect quality learning material.
|
||||
|
||||
* Building the C++ Heatmap GDNative binary
|
||||
|
||||
The GDNative folder is a git submodule pointing to the godot-cpp project (pointing to the latest commits as of October 4th) for Godot 3.1. As a result, after cloning, it should be initialized with something like `git submodule update --init --recursive`, or this repo cloned with `--recursive`.
|
||||
|
||||
Bindings for your OS should be generated according to https://docs.godotengine.org/en/3.1/tutorials/plugins/gdnative/gdnative-cpp-example.html
|
||||
|
||||
** Windows
|
||||
|
||||
Requirements: Visual Studio Community 20xx with C++, `libgodot-cpp.windows.xxxxx.64.lib` files for GDNative C++ in `GDNative/bin/`, and GDNative bindings in `GDNative/include/gen/`
|
||||
|
||||
Building the godot bindings:
|
||||
1. Open the `x64 Native Tools Command Prompt for VS 20xx`.
|
||||
2. CD into `/GDNative`
|
||||
3. Run `scons platform=windows target=release bits=64 generate_bindings=yes`
|
||||
|
||||
*** Building and Debugging using Visual Studio Community 20xx
|
||||
|
||||
If `Godot.exe` is in the `PATH` environment variable, the .lib files are built and located in `GDNative/bin/` and bindings in `GDNative/include/gen/`, then the Heatmap project is already configured for Godot building and debugging.
|
||||
|
||||
Building will build the DLL in debug or release mode and put in `assets/libraries/win64`, and debugging the solution in debug mode will launch the project in godot and allow for breakpoints in the C++ code.
|
||||
|
||||
*** Scons without opening Visual Studio
|
||||
|
||||
1. Open the `x64 Native Tools Command Prompt for VS 20xx`.
|
||||
2. CD into the Heatmap source (`cd game/src/Native/Heatmap`)
|
||||
3. `scons platform=windows bits=64 target=release`
|
||||
4. If successful, `libheatmap.DLL` will be built into `assets/libraries/win64`
|
||||
18
game/assets/libraries/heatmap.gdnlib
Normal file
18
game/assets/libraries/heatmap.gdnlib
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
[general]
|
||||
|
||||
singleton=false
|
||||
load_once=true
|
||||
symbol_prefix="godot_"
|
||||
reloadable=true
|
||||
|
||||
[entry]
|
||||
|
||||
OSX.64="res://assets/libraries/osx/libheatmap.dylib"
|
||||
Windows.64="res://assets/libraries/win64/libheatmap.dll"
|
||||
X11.64="res://assets/libraries/x11/libheatmap.so"
|
||||
|
||||
[dependencies]
|
||||
|
||||
OSX.64=[ ]
|
||||
Windows.64=[ ]
|
||||
X11.64=[ ]
|
||||
|
|
@ -59,6 +59,16 @@ _global_script_classes=[ {
|
|||
"language": "GDScript",
|
||||
"path": "res://src/Player/FloorDetector.gd"
|
||||
}, {
|
||||
"base": "SteeringBehavior2D",
|
||||
"class": "FollowHeatmapBehavior2D",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/AI/Steering/Behaviors/Individual/FollowHeatmapBehavior2D.gd"
|
||||
}, {
|
||||
"base": "CanvasItem",
|
||||
"class": "HeatmapGD",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/AI/Heatmap.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "Hit",
|
||||
"language": "GDScript",
|
||||
|
|
@ -79,6 +89,11 @@ _global_script_classes=[ {
|
|||
"language": "GDScript",
|
||||
"path": "res://src/AI/Steering/Behaviors/Individual/InterceptBehavior2D.gd"
|
||||
}, {
|
||||
"base": "Reference",
|
||||
"class": "Pathfinder",
|
||||
"language": "GDScript",
|
||||
"path": "res://src/AI/Pathfinder.gd"
|
||||
}, {
|
||||
"base": "KinematicBody2D",
|
||||
"class": "Player",
|
||||
"language": "GDScript",
|
||||
|
|
@ -140,10 +155,13 @@ _global_script_class_icons={
|
|||
"EvadeBehavior2D": "",
|
||||
"FleeBehavior2D": "",
|
||||
"FloorDetector": "",
|
||||
"FollowHeatmapBehavior2D": "",
|
||||
"HeatmapGD": "",
|
||||
"Hit": "",
|
||||
"Hook": "res://assets/icons/icon_hook.svg",
|
||||
"HookTarget": "",
|
||||
"InterceptBehavior2D": "",
|
||||
"Pathfinder": "",
|
||||
"Player": "",
|
||||
"PrioritySteering2D": "",
|
||||
"SchedulableJob": "",
|
||||
|
|
|
|||
8
game/src/AI/Heatmap.gdns
Normal file
8
game/src/AI/Heatmap.gdns
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[gd_resource type="NativeScript" load_steps=2 format=2]
|
||||
|
||||
[ext_resource path="res://assets/libraries/heatmap.gdnlib" type="GDNativeLibrary" id=1]
|
||||
|
||||
[resource]
|
||||
resource_name = "heatmap"
|
||||
class_name = "Heatmap"
|
||||
library = ExtResource( 1 )
|
||||
86
game/src/AI/Pathfinder.gd
Normal file
86
game/src/AI/Pathfinder.gd
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
"""
|
||||
Finds the path between two points using AStar, in grid coordinates
|
||||
|
||||
Code by razcore as part of the GDQuest OpenRPG project:
|
||||
https://github.com/GDquest/godot-open-rpg
|
||||
It's been modified and extended a little to not allow diagonals, and to allow
|
||||
tiles that lie on the negative side of the Tilemap planes.
|
||||
"""
|
||||
class_name Pathfinder
|
||||
|
||||
|
||||
var astar : AStar = AStar.new()
|
||||
|
||||
var _obstacles : Array
|
||||
var _map_size : Rect2
|
||||
var _x_min: float
|
||||
var _x_max: float
|
||||
var _y_min: float
|
||||
var _y_max: float
|
||||
|
||||
|
||||
func initialize(grid : TileMap, obstacle_tile_ids : Array) -> void:
|
||||
"""
|
||||
Initializes the AStar node: finds all walkable cells
|
||||
and connects all walkable paths
|
||||
"""
|
||||
# Initialize map size and obstacles array
|
||||
_map_size = grid.get_used_rect()
|
||||
_x_min = _map_size.position.x
|
||||
_x_max = _map_size.size.x - _map_size.position.x
|
||||
_y_min = _map_size.position.y
|
||||
_y_max = _map_size.size.y - _map_size.position.y
|
||||
|
||||
for id in obstacle_tile_ids:
|
||||
var occupied_cells = (grid as TileMap).get_used_cells_by_id(id)
|
||||
for cell in occupied_cells:
|
||||
_obstacles.append(cell)
|
||||
|
||||
# Find all walkable cells and store them in an array
|
||||
var points_array : = []
|
||||
for y in range(_y_min, _y_max):
|
||||
for x in range(_x_min, _x_max):
|
||||
var point = Vector2(x, y)
|
||||
if point in _obstacles:
|
||||
continue
|
||||
points_array.append(point)
|
||||
var point_index = calculate_point_index(point)
|
||||
astar.add_point(point_index, Vector3(point.x, point.y, 0))
|
||||
# Loop through all walkable cells and their neighbors
|
||||
# to connect the points
|
||||
for point in points_array:
|
||||
var point_index = calculate_point_index(point)
|
||||
for y in range(0, 3):
|
||||
for x in range(0, 3):
|
||||
var point_relative: = Vector2(point.x + x - 1, point.y + y - 1)
|
||||
|
||||
#only connect south-north and west-east, no diagonals
|
||||
if point_relative.x != point.x and point_relative.y != point.y:
|
||||
continue
|
||||
var point_relative_index: = calculate_point_index(point_relative)
|
||||
|
||||
if (point_relative != point and not is_outside_map_bounds(point_relative)
|
||||
and astar.has_point(point_relative_index)):
|
||||
astar.connect_points(point_index, point_relative_index)
|
||||
|
||||
|
||||
func is_outside_map_bounds(point : Vector2) -> bool:
|
||||
return (point.x < _x_min
|
||||
or point.y < _y_min
|
||||
or point.x >= _x_max
|
||||
or point.y >= _y_max)
|
||||
|
||||
|
||||
func calculate_point_index(point : Vector2) -> int:
|
||||
point -= _map_size.position
|
||||
return int(point.x + _map_size.size.x * point.y)
|
||||
|
||||
|
||||
func find_path(start : Vector2, end : Vector2) -> PoolVector3Array:
|
||||
"""
|
||||
Returns an array of cells that connect the start and end positions
|
||||
in grid coordinates
|
||||
"""
|
||||
var start_index = calculate_point_index(start)
|
||||
var end_index = calculate_point_index(end)
|
||||
return astar.get_point_path(start_index, end_index)
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
extends SteeringBehavior2D
|
||||
class_name FollowHeatmapBehavior2D
|
||||
"""
|
||||
A 2D steering behavior that expects a heatmap to be somewhere in the scene.
|
||||
It uses that to figure out where it should be going - from large values in its cell
|
||||
towards smaller values, with 0 being the goal.
|
||||
"""
|
||||
|
||||
|
||||
var _heatmap
|
||||
|
||||
var _last_point_index: int = INF
|
||||
var _last_velocity: = Vector2(0, 0)
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_heatmap = get_tree().root.find_node("Heatmap", true, false)
|
||||
assert _heatmap
|
||||
|
||||
|
||||
func _calculate_steering_internal(steering: SteeringMotion2D) -> SteeringMotion2D:
|
||||
var actor = get_actor()
|
||||
var point_index: int = _heatmap.calculate_point_index_for_world_position(actor.global_position)
|
||||
if point_index != _last_point_index:
|
||||
_last_point_index = point_index
|
||||
var to_target: Vector2 = _heatmap.best_direction_for(actor.global_position, true)
|
||||
|
||||
steering.velocity = to_target * controller.max_acceleration
|
||||
_last_velocity = steering.velocity
|
||||
else:
|
||||
steering.velocity = _last_velocity
|
||||
|
||||
steering.angular_velocity = 0
|
||||
return steering
|
||||
16
game/src/AI/SwarmerEnemy.gd
Normal file
16
game/src/AI/SwarmerEnemy.gd
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
extends Node2D
|
||||
"""
|
||||
Basic controller for a node2D - its behavior checks the heatmap and points
|
||||
its directional vector towards the less heat (the 'goal')
|
||||
"""
|
||||
|
||||
|
||||
onready var behavior: FollowHeatmapBehavior2D = $BehaviorController2D/FollowHeatmapBehavior2D
|
||||
|
||||
var _motion: SteeringMotion2D = SteeringMotion2D.new()
|
||||
var speed: float = 250
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var desired_velocity: = behavior.calculate_steering(_motion)
|
||||
position += _motion.velocity.normalized() * speed * delta
|
||||
23
game/src/AI/SwarmerEnemy.tscn
Normal file
23
game/src/AI/SwarmerEnemy.tscn
Normal file
|
|
@ -0,0 +1,23 @@
|
|||
[gd_scene load_steps=5 format=2]
|
||||
|
||||
[ext_resource path="res://src/AI/SwarmerEnemy.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/Player/Rectangle.gd" type="Script" id=2]
|
||||
[ext_resource path="res://src/AI/Steering/BehaviorController2D.gd" type="Script" id=3]
|
||||
[ext_resource path="res://src/AI/Steering/Behaviors/Individual/FollowHeatmapBehavior2D.gd" type="Script" id=4]
|
||||
|
||||
[node name="SwarmerEnemy" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
|
||||
[node name="Body" type="Node2D" parent="."]
|
||||
script = ExtResource( 2 )
|
||||
size = Vector2( 24, 24 )
|
||||
outline = Vector2( 6, 6 )
|
||||
color_fill = Color( 0.580392, 0.294118, 0.737255, 1 )
|
||||
color_outline = Color( 0.27451, 0.12549, 0.807843, 1 )
|
||||
|
||||
[node name="BehaviorController2D" type="Node" parent="."]
|
||||
script = ExtResource( 3 )
|
||||
actor_path = NodePath("..")
|
||||
|
||||
[node name="FollowHeatmapBehavior2D" type="Node" parent="BehaviorController2D"]
|
||||
script = ExtResource( 4 )
|
||||
30
game/src/AI/SwarmerSpawner.gd
Normal file
30
game/src/AI/SwarmerSpawner.gd
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
extends Node2D
|
||||
"""
|
||||
Spawns a number of swarmer enemies in the scene, who use the heatmap behavior
|
||||
to chase the player for economical pathfinding.
|
||||
"""
|
||||
|
||||
|
||||
export var spawner_count: = 50
|
||||
export var spawn_per_frame: = 10
|
||||
export var swarmer: PackedScene
|
||||
export var spawn_radius: = 200
|
||||
export var minimum_speed: float = 200
|
||||
export var maximum_speed: float = 300
|
||||
|
||||
|
||||
func _ready():
|
||||
randomize()
|
||||
var r_squared: = spawn_radius*spawn_radius
|
||||
|
||||
for i in range(spawner_count):
|
||||
if i % spawn_per_frame:
|
||||
yield(get_tree(), "idle_frame")
|
||||
var x: = rand_range(-spawn_radius, spawn_radius)
|
||||
var y: = rand_range(-1, 1) * sqrt(r_squared-x*x)
|
||||
|
||||
var instance: = swarmer.instance()
|
||||
instance.set_name("Swarmer%s"% i)
|
||||
add_child(instance)
|
||||
instance.speed = rand_range(minimum_speed, maximum_speed)
|
||||
instance.global_position = global_position + Vector2(x,y)
|
||||
8
game/src/AI/SwarmerSpawner.tscn
Normal file
8
game/src/AI/SwarmerSpawner.tscn
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[gd_scene load_steps=3 format=2]
|
||||
|
||||
[ext_resource path="res://src/AI/SwarmerSpawner.gd" type="Script" id=1]
|
||||
[ext_resource path="res://src/AI/SwarmerEnemy.tscn" type="PackedScene" id=2]
|
||||
|
||||
[node name="SwarmerSpawner" type="Node2D"]
|
||||
script = ExtResource( 1 )
|
||||
swarmer = ExtResource( 2 )
|
||||
File diff suppressed because one or more lines are too long
0
game/src/Native/Heatmap/.gdignore
Normal file
0
game/src/Native/Heatmap/.gdignore
Normal file
292
game/src/Native/Heatmap/Heatmap.cpp
Normal file
292
game/src/Native/Heatmap/Heatmap.cpp
Normal file
|
|
@ -0,0 +1,292 @@
|
|||
#include "Heatmap.h"
|
||||
#include <TileMap.hpp>
|
||||
#include <SceneTree.hpp>
|
||||
#include <Viewport.hpp>
|
||||
#include <deque>
|
||||
#include <TileSet.hpp>
|
||||
#include <algorithm>
|
||||
|
||||
namespace godot {
|
||||
inline float lerp(const float& a, const float& b, const float& t) {
|
||||
return a + t * (b - a);
|
||||
}
|
||||
|
||||
void Heatmap::_register_methods() {
|
||||
//public
|
||||
register_method("_ready", &Heatmap::_ready);
|
||||
register_method("_draw", &Heatmap::_draw);
|
||||
register_method("_process", &Heatmap::_process);
|
||||
register_method("best_direction_for", &Heatmap::best_direction_for);
|
||||
register_method("calculate_point_index", &Heatmap::calculate_point_index);
|
||||
register_method("calculate_point_index_for_world_position", &Heatmap::calculate_point_index_for_world_position);
|
||||
|
||||
//semi-private
|
||||
register_method("_on_Events_player_moved", &Heatmap::on_Events_player_moved);
|
||||
|
||||
//properties
|
||||
register_property<Heatmap, NodePath>("pathfinding_tilemap", &Heatmap::m_pathfinding_tilemap, NodePath());
|
||||
register_property<Heatmap, bool>("draw_debug", &Heatmap::m_draw_debug, false);
|
||||
}
|
||||
|
||||
Heatmap::Heatmap() : m_draw_debug(false), m_grid(nullptr), m_max_heat(0), m_max_heat_cache(0),
|
||||
m_x_max(0), m_y_max(0), m_x_min(0), m_y_min(0), m_updating(false) {
|
||||
}
|
||||
|
||||
Heatmap::~Heatmap() {
|
||||
}
|
||||
|
||||
void Heatmap::_init() {
|
||||
}
|
||||
|
||||
void Heatmap::_ready() {
|
||||
m_grid = (TileMap*)get_node(m_pathfinding_tilemap);
|
||||
if (m_grid == nullptr) {
|
||||
Godot::print_error("No tilemap found for Heatmap node.", __FUNCTION__, __FILE__, __LINE__ - 2);
|
||||
return;
|
||||
}
|
||||
|
||||
m_map_limits = m_grid->get_used_rect();
|
||||
m_x_min = m_map_limits.position.x;
|
||||
m_x_max = m_map_limits.size.x - m_map_limits.position.x;
|
||||
m_y_min = m_map_limits.position.y;
|
||||
m_y_max = m_map_limits.size.y - m_map_limits.position.y;
|
||||
|
||||
unsigned int highest_index = calculate_point_index(m_map_limits.size - m_map_limits.position);
|
||||
m_cells_heat.resize(highest_index);
|
||||
m_cells_heat_cache.resize(highest_index);
|
||||
for (unsigned int i = 0; i < highest_index; ++i) {
|
||||
m_cells_heat[i] = -1;
|
||||
}
|
||||
|
||||
find_all_obstacles();
|
||||
|
||||
get_tree()->get_root()->find_node("Events", true, false)->connect("player_moved", this, "_on_Events_player_moved");
|
||||
}
|
||||
|
||||
//For every cell in 2D array, check the heat and draw a rectangle colored according to its distance from the goal,
|
||||
//get the direction it points to, and draw a simple vector line.
|
||||
void Heatmap::_draw() {
|
||||
if (!m_draw_debug) {
|
||||
return;
|
||||
}
|
||||
|
||||
Rect2 tile;
|
||||
Vector2 cell_size = m_grid->get_cell_size();
|
||||
tile.set_size(cell_size);
|
||||
cell_size /= 2;
|
||||
|
||||
for (int y = int(m_y_min); y<int(m_y_max); ++y) {
|
||||
for (int x = int(m_x_min); x<int(m_x_max); ++x) {
|
||||
Vector2 point = Vector2(float(x), float(y));
|
||||
Vector2 world_position = m_grid->map_to_world(point);
|
||||
tile.set_position(world_position);
|
||||
|
||||
unsigned int cell_index = calculate_point_index(point);
|
||||
int heat = m_cells_heat[cell_index];
|
||||
if (heat == -1) {
|
||||
continue;
|
||||
}
|
||||
float proportion = lerp(0.0f, 1.0f, float(heat) / float(m_max_heat));
|
||||
|
||||
draw_rect(tile, Color(1.0f - proportion, 0, proportion, 0.75f), true);
|
||||
|
||||
world_position += cell_size;
|
||||
Vector2 direction = best_direction_for(point, false);
|
||||
draw_rect(Rect2(world_position.x - 5, world_position.y - 5, 10, 10), Color(0, 1, 0), false);
|
||||
draw_line(world_position, world_position + (direction * 20), Color(1, 1, 1));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Heatmap::_process(float delta) {
|
||||
if (!m_updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Seeing if future::get is ready to deliver data or if the thread is still crunching numbers.
|
||||
//_Is_Ready is not yet standardized, so we check with as immediate a timeout as we can.
|
||||
if (m_future.wait_for(std::chrono::seconds(0)) == std::future_status::ready) {
|
||||
Vector2 player_cell_position = m_future.get();
|
||||
thread_done(player_cell_position);
|
||||
}
|
||||
}
|
||||
|
||||
//for the 3x3 grid surrounding the cell, find the one with the least heat that isn't -1 (invalid), and return a vector
|
||||
//that points in its direction. Note that there could be multiple tiles with the same heat, which could cause
|
||||
Vector2 Heatmap::best_direction_for(Vector2 t_location, bool t_is_world_location) {
|
||||
Vector2 point = t_is_world_location ? m_grid->world_to_map(t_location) : t_location;
|
||||
Vector2 world_location = t_is_world_location ? t_location : m_grid->map_to_world(point);
|
||||
unsigned int cell_index = calculate_point_index(point);
|
||||
|
||||
if (cell_index < 0 || cell_index >= m_cells_heat.size()) {
|
||||
return (m_last_player_cell_position - world_location).normalized();
|
||||
}
|
||||
|
||||
Vector2 best_neighbor = point;
|
||||
int best_heat = m_cells_heat[cell_index];
|
||||
if (best_heat == -1) {
|
||||
return (m_last_player_cell_position - world_location).normalized();
|
||||
}
|
||||
|
||||
for (int y = 0; y < 3; ++y) {
|
||||
for (int x = 0; x < 3; ++x) {
|
||||
Vector2 point_relative = Vector2(point.x + float(x) - 1.0f, point.y + float(y) - 1.0f);
|
||||
|
||||
if (is_out_of_bounds(point_relative) || std::find(m_obstacles.begin(), m_obstacles.end(), point_relative) != m_obstacles.end()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
unsigned int point_relative_index = calculate_point_index(point_relative);
|
||||
if (point_relative_index == cell_index) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (point_relative_index >= 0 && point_relative_index < m_cells_heat.size()) {
|
||||
int heat = m_cells_heat[point_relative_index];
|
||||
|
||||
if (heat == -1) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (heat <= best_heat) {
|
||||
best_heat = heat;
|
||||
best_neighbor = point_relative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector2 world_neighbor = m_grid->map_to_world(best_neighbor);
|
||||
Vector2 direction = (world_neighbor - world_location).normalized();
|
||||
if (direction.length_squared() == 0) {
|
||||
return (m_last_player_cell_position - world_location).normalized();
|
||||
}
|
||||
return direction;
|
||||
}
|
||||
|
||||
unsigned int Heatmap::calculate_point_index(Vector2 t_point) {
|
||||
return int((t_point.x - m_map_limits.position.x) + m_map_limits.size.x * (t_point.y - m_map_limits.position.y));
|
||||
}
|
||||
|
||||
unsigned int Heatmap::calculate_point_index_for_world_position(Vector2 t_world_position) {
|
||||
return calculate_point_index(m_grid->world_to_map(t_world_position));
|
||||
}
|
||||
|
||||
//Runs through every cell and checks if the autotile bitmask of that cell covers the entire cell.
|
||||
//If so, it is an obstacle. This does mean that thin corners can seem traversable, even though
|
||||
//they wouldn't be.
|
||||
void Heatmap::find_all_obstacles() {
|
||||
Ref<TileSet> tileset = m_grid->get_tileset();
|
||||
Array tile_ids = tileset->get_tiles_ids();
|
||||
|
||||
//TODO: Account for non-autotile tilesets.
|
||||
for (int y = m_y_min; y < m_y_max; ++y) {
|
||||
for (int x = m_x_min; x < m_x_max; ++x) {
|
||||
int cell_id = m_grid->get_cell(x, y);
|
||||
if (cell_id == -1 || !tile_ids.has(cell_id)) {
|
||||
continue;
|
||||
}
|
||||
int cell_bitmask = tileset->autotile_get_bitmask(cell_id, m_grid->get_cell_autotile_coord(x, y));
|
||||
TileSet::BitmaskMode mode = tileset->autotile_get_bitmask_mode(cell_id);
|
||||
|
||||
int all_covered = 325; //Bitmask is the sum of TileSet::BitmaskMode enum flags.
|
||||
if (mode == TileSet::BITMASK_3X3 || mode == TileSet::BITMASK_3X3_MINIMAL) {
|
||||
all_covered = 495;
|
||||
}
|
||||
|
||||
if (cell_bitmask == all_covered) {
|
||||
m_obstacles.push_back(Vector2(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//Breadth-first search using a queue.
|
||||
Vector2 Heatmap::refresh_cells_heat(Vector2 t_cell_position) {
|
||||
std::deque<HeatCell> queue;
|
||||
//We begin with 4 cells of goals instead of 1 - this alleviates the problem of multiple cells
|
||||
//having the same amount of heat.
|
||||
queue.push_back(HeatCell(Vector2(t_cell_position.x, t_cell_position.y), 0));
|
||||
queue.push_back(HeatCell(Vector2(t_cell_position.x - 1, t_cell_position.y), 0));
|
||||
queue.push_back(HeatCell(Vector2(t_cell_position.x - 1, t_cell_position.y - 1), 0));
|
||||
queue.push_back(HeatCell(Vector2(t_cell_position.x, t_cell_position.y - 1), 0));
|
||||
m_max_heat_cache = 0;
|
||||
|
||||
while (!queue.empty()) {
|
||||
HeatCell cell = queue.front();
|
||||
queue.pop_front();
|
||||
|
||||
Vector2 position = cell.position;
|
||||
int layer = cell.layer;
|
||||
|
||||
unsigned int index = calculate_point_index(position);
|
||||
if (index < 0 || index >= m_cells_heat_cache.size()) {
|
||||
continue;
|
||||
}
|
||||
m_cells_heat_cache[index] = layer;
|
||||
if (layer > m_max_heat_cache) {
|
||||
m_max_heat_cache = layer;
|
||||
}
|
||||
|
||||
for (int y = 0; y < 3; ++y) {
|
||||
for (int x = 0; x < 3; ++x) {
|
||||
Vector2 point = Vector2(position.x + float(x) - 1.0f, position.y + float(y) - 1.0f);
|
||||
unsigned int cell_index = calculate_point_index(point);
|
||||
HeatCell new_cell = HeatCell(point, layer + 1);
|
||||
|
||||
if ( cell_index != index
|
||||
&& cell_index >= 0 && cell_index < m_cells_heat_cache.size()
|
||||
&& m_cells_heat_cache[cell_index] == -1
|
||||
&& !is_out_of_bounds(point)
|
||||
&& std::find(queue.begin(), queue.end(), new_cell) == queue.end()
|
||||
&& std::find(m_obstacles.begin(), m_obstacles.end(), point) == m_obstacles.end()) {
|
||||
|
||||
queue.push_back(new_cell);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return t_cell_position;
|
||||
}
|
||||
|
||||
//Copies the cached, thread specific versions that were used into those used by the main thread.
|
||||
void Heatmap::thread_done(Vector2 t_cell_position) {
|
||||
m_updating = false;
|
||||
|
||||
m_max_heat = m_max_heat_cache;
|
||||
for (int i = 0; i < m_cells_heat.size(); ++i) {
|
||||
m_cells_heat[i] = m_cells_heat_cache[i];
|
||||
}
|
||||
m_last_player_cell_position = t_cell_position;
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
bool Heatmap::is_out_of_bounds(Vector2 t_position) {
|
||||
return t_position.x < m_x_min || t_position.y < m_y_min
|
||||
|| t_position.x > m_x_max || t_position.y > m_y_max;
|
||||
}
|
||||
|
||||
void Heatmap::on_Events_player_moved(Node2D* t_player) {
|
||||
if (m_updating) {
|
||||
return;
|
||||
}
|
||||
|
||||
Vector2 player_cell_position = m_grid->world_to_map(t_player->get_global_position());
|
||||
bool out_of_bounds = is_out_of_bounds(player_cell_position);
|
||||
Vector2 difference = player_cell_position - m_last_player_cell_position;
|
||||
|
||||
if (!out_of_bounds && (difference.x != 0 || difference.y != 0)) {
|
||||
for (int i = 0; i < m_cells_heat_cache.size(); ++i) {
|
||||
m_cells_heat_cache[i] = -1;
|
||||
}
|
||||
|
||||
m_updating = true;
|
||||
|
||||
// Launch an immediate new thread with ::launch::async.
|
||||
// Without that flag, it waits for a future.get()/wait() before starting work.
|
||||
m_future = std::async(std::launch::async, &Heatmap::refresh_cells_heat, this, player_cell_position);
|
||||
}
|
||||
}
|
||||
}
|
||||
86
game/src/Native/Heatmap/Heatmap.h
Normal file
86
game/src/Native/Heatmap/Heatmap.h
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
// Heatmap
|
||||
// Francois "@Razoric480" Belair
|
||||
//
|
||||
// GDNative Godot Class that uses a floodfill algorithm. Radiating out from the player position, it covers the whole tilemap.
|
||||
// Each step removed adds one to the layer count. This can then be used by agents for quick
|
||||
// lookup based pathfinding, instead of calculating A-star paths or other, potentially more expensive pathfinding routines.
|
||||
|
||||
#ifndef HEATMAP_H
|
||||
#define HEATMAP_H
|
||||
|
||||
#include <Godot.hpp>
|
||||
#include <Node2D.hpp>
|
||||
#include <vector>
|
||||
#include <future>
|
||||
|
||||
namespace godot {
|
||||
//Forward declarations
|
||||
class TileMap;
|
||||
|
||||
// HeatCell. Simple structure used by the `refresh_cells_heat` member function to contain both
|
||||
// position and current layer in the flood fill search.
|
||||
struct HeatCell {
|
||||
HeatCell(Vector2 t_position, int t_layer) {
|
||||
position = t_position;
|
||||
layer = t_layer;
|
||||
}
|
||||
Vector2 position;
|
||||
int layer;
|
||||
|
||||
bool operator ==(HeatCell const& other) {
|
||||
return other.layer == layer && other.position == position;
|
||||
}
|
||||
|
||||
bool operator !=(HeatCell const& other) {
|
||||
return other.layer != layer || other.position != position;
|
||||
}
|
||||
};
|
||||
|
||||
class Heatmap : public Node2D {
|
||||
GODOT_CLASS(Heatmap, Node2D)
|
||||
|
||||
public:
|
||||
Heatmap();
|
||||
~Heatmap();
|
||||
|
||||
static void _register_methods();
|
||||
|
||||
void _init();
|
||||
void _ready();
|
||||
void _draw();
|
||||
void _process(float delta);
|
||||
|
||||
Vector2 best_direction_for(Vector2 t_location, bool t_is_world_location);
|
||||
unsigned int calculate_point_index(Vector2 t_point);
|
||||
unsigned int calculate_point_index_for_world_position(Vector2 t_world_position);
|
||||
|
||||
private:
|
||||
void find_all_obstacles();
|
||||
Vector2 refresh_cells_heat(Vector2 t_cell_position);
|
||||
bool is_out_of_bounds(Vector2 t_position);
|
||||
void on_Events_player_moved(Node2D* t_player);
|
||||
void thread_done(Vector2 t_cell_position);
|
||||
|
||||
private:
|
||||
NodePath m_pathfinding_tilemap;
|
||||
bool m_draw_debug;
|
||||
|
||||
TileMap* m_grid;
|
||||
Rect2 m_map_limits;
|
||||
float m_x_min;
|
||||
float m_x_max;
|
||||
float m_y_min;
|
||||
float m_y_max;
|
||||
int m_max_heat;
|
||||
int m_max_heat_cache;
|
||||
bool m_updating;
|
||||
|
||||
std::vector<int> m_cells_heat;
|
||||
std::vector<int> m_cells_heat_cache;
|
||||
std::vector<Vector2> m_obstacles;
|
||||
Vector2 m_last_player_cell_position;
|
||||
std::future<Vector2> m_future;
|
||||
};
|
||||
}
|
||||
|
||||
#endif /* HEATMAP_H */
|
||||
25
game/src/Native/Heatmap/Heatmap.sln
Normal file
25
game/src/Native/Heatmap/Heatmap.sln
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29230.47
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Heatmap", "Heatmap.vcxproj", "{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|x64 = Debug|x64
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Debug|x64.Build.0 = Debug|x64
|
||||
{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Release|x64.ActiveCfg = Release|x64
|
||||
{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {6B60736E-491D-46DB-B927-593F9A8CEDF2}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
110
game/src/Native/Heatmap/Heatmap.vcxproj
Normal file
110
game/src/Native/Heatmap/Heatmap.vcxproj
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Heatmap.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="gdlibrary.cpp" />
|
||||
<ClCompile Include="Heatmap.cpp" />
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<VCProjectVersion>16.0</VCProjectVersion>
|
||||
<ProjectGuid>{95F50422-B0D5-44A4-8A83-3B56BFC6B7B0}</ProjectGuid>
|
||||
<Keyword>Win32Proj</Keyword>
|
||||
<RootNamespace>Heatmap</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>true</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<UseDebugLibraries>false</UseDebugLibraries>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<WholeProgramOptimization>true</WholeProgramOptimization>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="Shared">
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>true</LinkIncremental>
|
||||
<IncludePath>..\..\..\..\GDNative\include;..\..\..\..\GDNative\include\core;..\..\..\..\GDNative\include\gen;..\..\..\..\GDNative\godot_headers;$(IncludePath)</IncludePath>
|
||||
<TargetName>libheatmap</TargetName>
|
||||
<LibraryPath>..\..\..\..\GDNative\bin;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>..\..\..\game\assets\libraries\win64\</OutDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<IncludePath>..\..\..\..\GDNative\include;..\..\..\..\GDNative\include\core;..\..\..\..\GDNative\include\gen;..\..\..\..\GDNative\godot_headers;$(IncludePath)</IncludePath>
|
||||
<LibraryPath>..\..\..\..\GDNative\bin;$(LibraryPath)</LibraryPath>
|
||||
<OutDir>..\..\..\game\assets\libraries\win64\</OutDir>
|
||||
<TargetName>libheatmap</TargetName>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>_DEBUG;HEATMAP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>libgodot-cpp.windows.debug.64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<PrecompiledHeader>NotUsing</PrecompiledHeader>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<Optimization>MaxSpeed</Optimization>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<IntrinsicFunctions>true</IntrinsicFunctions>
|
||||
<SDLCheck>true</SDLCheck>
|
||||
<PreprocessorDefinitions>NDEBUG;HEATMAP_EXPORTS;_WINDOWS;_USRDLL;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<ConformanceMode>true</ConformanceMode>
|
||||
<PrecompiledHeaderFile>
|
||||
</PrecompiledHeaderFile>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<SubSystem>Console</SubSystem>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<EnableUAC>false</EnableUAC>
|
||||
<AdditionalDependencies>libgodot-cpp.windows.release.64.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
</Link>
|
||||
</ItemDefinitionGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
||||
30
game/src/Native/Heatmap/Heatmap.vcxproj.filters
Normal file
30
game/src/Native/Heatmap/Heatmap.vcxproj.filters
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
|
||||
<Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
|
||||
<Extensions>h;hh;hpp;hxx;hm;inl;inc;ipp;xsd</Extensions>
|
||||
</Filter>
|
||||
<Filter Include="Resource Files">
|
||||
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
|
||||
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="Heatmap.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="gdlibrary.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Heatmap.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
||||
107
game/src/Native/Heatmap/SConstruct
Normal file
107
game/src/Native/Heatmap/SConstruct
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
#!python
|
||||
import os, subprocess
|
||||
|
||||
opts = Variables([], ARGUMENTS)
|
||||
|
||||
# Gets the standard flags CC, CCX, etc.
|
||||
env = DefaultEnvironment()
|
||||
|
||||
# Define our options
|
||||
opts.Add(EnumVariable('target', "Compilation target", 'debug', ['d', 'debug', 'r', 'release']))
|
||||
opts.Add(EnumVariable('platform', "Compilation platform", '', ['', 'windows', 'x11', 'linux', 'osx']))
|
||||
opts.Add(EnumVariable('p', "Compilation target, alias for 'platform'", '', ['', 'windows', 'x11', 'linux', 'osx']))
|
||||
opts.Add(BoolVariable('use_llvm', "Use the LLVM / Clang compiler", 'no'))
|
||||
opts.Add(PathVariable('target_path', 'The path where the lib is installed.', '../../../assets/libraries/'))
|
||||
opts.Add(PathVariable('target_name', 'The library name.', 'libheatmap', PathVariable.PathAccept))
|
||||
|
||||
# Local dependency paths, adapt them to your setup
|
||||
godot_headers_path = "../../../../GDNative/godot_headers/"
|
||||
cpp_bindings_path = "../../../../GDNative/"
|
||||
cpp_library = "libgodot-cpp"
|
||||
|
||||
# only support 64 at this time..
|
||||
bits = 64
|
||||
|
||||
# Updates the environment with the option variables.
|
||||
opts.Update(env)
|
||||
|
||||
# Process some arguments
|
||||
if env['use_llvm']:
|
||||
env['CC'] = 'clang'
|
||||
env['CXX'] = 'clang++'
|
||||
|
||||
if env['p'] != '':
|
||||
env['platform'] = env['p']
|
||||
|
||||
if env['platform'] == '':
|
||||
print("No valid target platform selected.")
|
||||
quit();
|
||||
|
||||
# For the reference:
|
||||
# - CCFLAGS are compilation flags shared between C and C++
|
||||
# - CFLAGS are for C-specific compilation flags
|
||||
# - CXXFLAGS are for C++-specific compilation flags
|
||||
# - CPPFLAGS are for pre-processor flags
|
||||
# - CPPDEFINES are for pre-processor defines
|
||||
# - LINKFLAGS are for linking flags
|
||||
|
||||
# Check our platform specifics
|
||||
if env['platform'] == "osx":
|
||||
env['target_path'] += 'osx/'
|
||||
cpp_library += '.osx'
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CCFLAGS=['-g', '-O2', '-arch', 'x86_64', '-fPIC'])
|
||||
env.Append(LINKFLAGS=['-arch', 'x86_64'])
|
||||
else:
|
||||
env.Append(CCFLAGS=['-g', '-O3', '-arch', 'x86_64', '-fPIC'])
|
||||
env.Append(LINKFLAGS=['-s'])
|
||||
|
||||
elif env['platform'] in ('x11', 'linux'):
|
||||
env['target_path'] += 'x11/'
|
||||
cpp_library += '.linux'
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CCFLAGS=['-fPIC', '-g3', '-Og'])
|
||||
env.Append(CXXFLAGS=['-std=c++17'])
|
||||
else:
|
||||
env.Append(CCFLAGS=['-fPIC', '-O3'])
|
||||
env.Append(CXXFLAGS=['-std=c++17'])
|
||||
|
||||
elif env['platform'] == "windows":
|
||||
env['target_path'] += 'win64/'
|
||||
cpp_library += '.windows'
|
||||
# This makes sure to keep the session environment variables on windows,
|
||||
# that way you can run scons in a vs 2017 prompt and it will find all the required tools
|
||||
env.Append(ENV=os.environ)
|
||||
|
||||
env.Append(CPPDEFINES=['WIN32', '_WIN32', '_WINDOWS', '_CRT_SECURE_NO_WARNINGS'])
|
||||
env.Append(CCFLAGS=['-W3', '-GR'])
|
||||
if env['target'] in ('debug', 'd'):
|
||||
env.Append(CPPDEFINES=['_DEBUG'])
|
||||
env.Append(CCFLAGS=['-EHsc', '-MDd', '-ZI'])
|
||||
env.Append(LINKFLAGS=['-DEBUG'])
|
||||
else:
|
||||
env.Append(CPPDEFINES=['NDEBUG'])
|
||||
env.Append(CCFLAGS=['-O2', '-EHsc', '-MD'])
|
||||
|
||||
if env['target'] in ('debug', 'd'):
|
||||
cpp_library += '.debug'
|
||||
else:
|
||||
cpp_library += '.release'
|
||||
|
||||
cpp_library += '.' + str(bits)
|
||||
|
||||
# make sure our binding library is properly includes
|
||||
env.Append(CPPPATH=['.', godot_headers_path, cpp_bindings_path + 'include/', cpp_bindings_path + 'include/core/', cpp_bindings_path + 'include/gen/'])
|
||||
env.Append(LIBPATH=[cpp_bindings_path + 'bin/'])
|
||||
env.Append(LIBS=[cpp_library])
|
||||
|
||||
# tweak this if you want to use different folders, or more folders, to store your source code in.
|
||||
env.Append(CPPPATH=['./'])
|
||||
sources = Glob('./*.cpp')
|
||||
|
||||
library = env.SharedLibrary(target=env['target_path'] + env['target_name'] , source=sources)
|
||||
|
||||
Default(library)
|
||||
|
||||
# Generates help for the -h scons option.
|
||||
Help(opts.GenerateHelpText(env))
|
||||
15
game/src/Native/Heatmap/gdlibrary.cpp
Normal file
15
game/src/Native/Heatmap/gdlibrary.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
#include "Heatmap.h"
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_init(godot_gdnative_init_options *o) {
|
||||
godot::Godot::gdnative_init(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_gdnative_terminate(godot_gdnative_terminate_options *o) {
|
||||
godot::Godot::gdnative_terminate(o);
|
||||
}
|
||||
|
||||
extern "C" void GDN_EXPORT godot_nativescript_init(void *handle) {
|
||||
godot::Godot::nativescript_init(handle);
|
||||
|
||||
godot::register_class<godot::Heatmap>();
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue