Created
April 23, 2024 00:53
-
-
Save Muqsit/68d6165674307ab93b191354500dc0f8 to your computer and use it in GitHub Desktop.
Pathfinding Block Position Mesh for Arbitrary-Sized Mobs
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?php | |
declare(strict_types=1); | |
use pocketmine\math\AxisAlignedBB; | |
use pocketmine\math\Facing; | |
use pocketmine\math\Vector3; | |
use pocketmine\player\Player; | |
use pocketmine\scheduler\CancelTaskException; | |
use pocketmine\scheduler\ClosureTask; | |
use pocketmine\Server; | |
use pocketmine\world\particle\CriticalParticle; | |
use pocketmine\world\World; | |
$server = Server::getInstance(); | |
assert(isset($sender) && $sender instanceof Player); | |
$plugin = $server->getPluginManager()->getPlugin("Scripter"); | |
assert($plugin !== null); | |
/** | |
* Builds a mesh of traversable block positions for an entity of size ($width x $height). | |
* $min and $max vectors constrict the mesh to a definite region. | |
* $origin is the block position of any traversable point in the mesh (<-- position where the entity would be spawned). | |
* | |
* @param World $world | |
* @param Vector3 $min | |
* @param Vector3 $max | |
* @param Vector3 $origin | |
* @param float $width | |
* @param float $height | |
* @return array<int, array{int, int, int}> | |
*/ | |
$build_mesh = static function(World $world, Vector3 $min, Vector3 $max, Vector3 $origin, float $width, float $height) : array{ | |
// perform breadth-first search to find all walkable block positions | |
/** @var SplQueue<Vector3> $queue */ | |
$queue = new SplQueue(); | |
$queue->push($origin); | |
$result = [World::blockHash($origin->x, $origin->y, $origin->z) => [$origin->x, $origin->y, $origin->z]]; | |
$bb = new AxisAlignedBB(-$width / 2, 0, -$width / 2, $width / 2, $height, $width / 2); | |
while(!$queue->isEmpty()){ | |
$current = $queue->dequeue(); | |
// constrict our search to $min, $max | |
if($current->x <= $min->x || $current->x >= $max->x){ | |
continue; | |
} | |
if($current->y <= $min->y || $current->y >= $max->y){ | |
continue; | |
} | |
if($current->z <= $min->z || $current->z >= $max->z){ | |
continue; | |
} | |
foreach(Facing::HORIZONTAL as $facing){ | |
$block_side = $current->getSide($facing); | |
$hash = World::blockHash($block_side->x, $block_side->y, $block_side->z); | |
if(isset($result[$hash])){ | |
continue; // we already visited this position | |
} | |
// determine where we must move ($target) -- forward, ascend, descend, or none. | |
$move_neighbor = $bb->offsetCopy($block_side->x + 0.5, $block_side->y, $block_side->z + 0.5); | |
if(count($world->getBlockCollisionBoxes($move_neighbor)) === 0){ // no block is obstructing our path | |
if(count($world->getBlockCollisionBoxes($move_neighbor->offsetCopy(0, -1, 0))) > 0){ // there is a block below to stand on, can proceed forward | |
$target = $block_side; | |
}elseif(count($world->getBlockCollisionBoxes($move_neighbor->offsetCopy(0, -2, 0))) > 0){ // no floor but there is a block below floor, can safely descend | |
$target = $block_side->down(); | |
}else{ | |
$target = null; | |
} | |
}elseif(count($world->getBlockCollisionBoxes($move_neighbor->offsetCopy(0, 1, 0))) === 0){ // empty space above for us to jump, can ascend | |
$target = $block_side->up(); | |
}else{ | |
$target = null; | |
} | |
if($target === null){ // cannot proceed further | |
continue; | |
} | |
$queue->enqueue($target); | |
$result[$hash] = [$target->x, $target->y, $target->z]; | |
} | |
} | |
return $result; | |
}; | |
// constrict our search to this region | |
$p1 = new Vector3(986, 62, 400); | |
$p2 = new Vector3(896, 42, 327); | |
$min = Vector3::minComponents($p1, $p2); | |
$max = Vector3::maxComponents($p1, $p2); | |
// the "walkable entity"'s properties | |
$world = $sender->getWorld(); | |
$origin = new Vector3(982, 54, 370); // block pos the entity is standing at ($entity->getPosition()->floor()) | |
$width = 0.6; | |
$height = 2; | |
// render the mesh | |
$mesh = $build_mesh($world, $min, $max, $origin, $width, $height); | |
$ticks = 0; | |
$particle = new CriticalParticle(); | |
$plugin->getScheduler()->scheduleRepeatingTask(new ClosureTask(function() use($world, $mesh, $particle, &$ticks) : void{ | |
foreach($mesh as [$x, $y, $z]){ | |
$pos = new Vector3($x + 0.5, $y + 0.5, $z + 0.5); | |
$world->addParticle($pos, $particle); | |
} | |
++$ticks < 30 || throw new CancelTaskException(); | |
}), 10); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment