Skip to content

Instantly share code, notes, and snippets.

@Muqsit
Created April 23, 2024 00:53
Show Gist options
  • Save Muqsit/68d6165674307ab93b191354500dc0f8 to your computer and use it in GitHub Desktop.
Save Muqsit/68d6165674307ab93b191354500dc0f8 to your computer and use it in GitHub Desktop.
Pathfinding Block Position Mesh for Arbitrary-Sized Mobs
<?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