90 lines
3.7 KiB
GDScript
90 lines
3.7 KiB
GDScript
extends Node3D
|
|
class_name PathfindingGridLoader
|
|
|
|
var loading: bool = false # when this is true the grid starts loading
|
|
var loading_done: bool = false # when the grid is loaded, this becomes true
|
|
|
|
@export var height: float= 0.2 ##Choose a height that is just above the floor
|
|
@export var lengthX: int = 50 ##How far the grid generates in the x direction, generates as a rectangle
|
|
@export var lengthZ: int = 50 ##How far the grid generates in the z direction, generates as a rectangle
|
|
@export var point_density: float = 1 ##How many points per unit of grid, cannot be = 0
|
|
@export var minimum_connections_per_point: int = 3
|
|
@export var wallCollisionLayer: int = 0b100
|
|
|
|
var astar: AStar2D = AStar2D.new()
|
|
|
|
func _physics_process(_delta: float) -> void:
|
|
if loading == false: return
|
|
if loading_done == true: return
|
|
generatePoints()
|
|
connectPointsToNeighbors()
|
|
killPointsWithoutFriends(minimum_connections_per_point)
|
|
addWeightToPoints()
|
|
loading = false
|
|
loading_done = true
|
|
|
|
func loadGrid() -> void:
|
|
loading = true
|
|
|
|
func castRay(startPoint:Vector3,endPoint: Vector3,collisionMask:int = 0xFFFFFFFF,hitFromInside: bool = true) -> bool: ##Returns true if the ray collides and false if it doesn't
|
|
var space_state = get_world_3d().direct_space_state
|
|
var rayQueryParam = PhysicsRayQueryParameters3D.create(startPoint,endPoint)
|
|
rayQueryParam.collision_mask = collisionMask
|
|
rayQueryParam.hit_from_inside = hitFromInside
|
|
return space_state.intersect_ray(rayQueryParam).size()
|
|
|
|
func checkPoint(point: Vector2) -> bool: ## only ever call this from physics process as it uses physics for raycast and may not work on idle callback
|
|
var floorDetected: bool = castRay(Vector3(point.x,height,point.y),Vector3(point.x,-(height*1.5),point.y))
|
|
var wallDetected: bool = castRay(Vector3(point.x,height,point.y),Vector3(point.x,-(height*1.5),point.y),wallCollisionLayer) #Walls are on collision layer 3 (0b100)
|
|
if !wallDetected && floorDetected:
|
|
return true
|
|
else:
|
|
return false
|
|
|
|
func getPointsInRadius(origin: Vector2,radius: float, maxPoints: int = 9) -> Array[int]: #Origin point is always index 0 in the array
|
|
var pointIDs: Array[int]
|
|
for point in maxPoints:
|
|
var id = astar.get_closest_point(origin)
|
|
if (astar.get_point_position(id) - origin).length() <= radius:
|
|
pointIDs.push_back(id)
|
|
astar.set_point_disabled(id)
|
|
for id in pointIDs:
|
|
astar.set_point_disabled(id,false)
|
|
return pointIDs
|
|
|
|
func connectIfValid(id1: int, id2: int):
|
|
var point1 := astar.get_point_position(id1)
|
|
var point2 := astar.get_point_position(id2)
|
|
|
|
if !castRay(Vector3(point1.x,height,point1.y),Vector3(point2.x,height,point2.y)):
|
|
astar.connect_points(id1,id2)
|
|
|
|
func generatePoints() -> void:
|
|
var currentPoint: Vector2
|
|
var currentID: int = 0
|
|
for x in lengthX*point_density:
|
|
for z in lengthZ*point_density:
|
|
currentPoint = Vector2((x/point_density + self.global_position.x),z/point_density + self.global_position.z)
|
|
if checkPoint(currentPoint):
|
|
astar.add_point(currentID,currentPoint)
|
|
currentID += 1
|
|
|
|
func connectPointsToNeighbors() -> void:
|
|
for id in astar.get_point_ids():
|
|
var neighborPoints: Array[int]
|
|
neighborPoints = getPointsInRadius(astar.get_point_position(id),(1/point_density)*1.5)
|
|
for point in neighborPoints.size()-1:
|
|
connectIfValid(neighborPoints[0],neighborPoints[point+1])
|
|
|
|
func killPointsWithoutFriends(minConnections: int) -> void: ##Really mean and evil and fucked up function
|
|
var cullPoints: Array[int]
|
|
for id in astar.get_point_ids():
|
|
if astar.get_point_connections(id).size() < minConnections:
|
|
cullPoints.push_back(id)
|
|
for point in cullPoints:
|
|
astar.remove_point(point)
|
|
|
|
func addWeightToPoints() -> void: #Add weight depending on number of connections
|
|
for id in astar.get_point_ids():
|
|
astar.set_point_weight_scale(id,8 - astar.get_point_connections(id).size())
|