Added base physics

This commit is contained in:
Shoghi Cervantes 2014-05-25 12:31:00 +02:00
parent ec055fd8d1
commit 4f2856dc09
9 changed files with 360 additions and 25 deletions

View File

@ -20,6 +20,7 @@
*/ */
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\math\AxisAlignedBB;
/** /**
@ -41,4 +42,8 @@ class Air extends Transparent{
} }
public function collidesWithBB(AxisAlignedBB $bb, &$list = array()){
}
} }

View File

@ -24,8 +24,11 @@
*/ */
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\Position; use pocketmine\level\Position;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\metadata\Metadatable; use pocketmine\metadata\Metadatable;
use pocketmine\metadata\MetadataValue; use pocketmine\metadata\MetadataValue;
use pocketmine\Player; use pocketmine\Player;
@ -710,6 +713,26 @@ abstract class Block extends Position implements Metadatable{
*/ */
abstract function onBreak(Item $item); abstract function onBreak(Item $item);
/**
* Checks for collision against an AxisAlignedBB
*
* @param AxisAlignedBB $bb
* @param Block[] $list
*/
public function collidesWithBB(AxisAlignedBB $bb, &$list = array()){
$bb2 = new AxisAlignedBB(
$this->x,
$this->y,
$this->z,
$this->x + 1,
$this->y + 1,
$this->z + 1
);
if($bb2->intersectsWith($bb)){
$list[] = $bb2;
}
}
/** /**
* Places the Block, using block space and block target, and side. Returns if the block has been placed. * Places the Block, using block space and block target, and side. Returns if the block has been placed.
* *

View File

@ -21,8 +21,11 @@
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\entity\Entity;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector3;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\Server; use pocketmine\Server;

View File

@ -27,6 +27,7 @@ use pocketmine\nbt\tag\Compound;
use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\Short;
use pocketmine\nbt\tag\String; use pocketmine\nbt\tag\String;
use pocketmine\network\protocol\AddItemEntityPacket; use pocketmine\network\protocol\AddItemEntityPacket;
use pocketmine\network\protocol\SetEntityMotionPacket;
use pocketmine\Player; use pocketmine\Player;
class DroppedItem extends Entity{ class DroppedItem extends Entity{
@ -38,6 +39,12 @@ class DroppedItem extends Entity{
/** @var Item */ /** @var Item */
protected $item; protected $item;
public $width = 0.25;
public $length = 0.25;
public $height = 0.25;
protected $gravity = 0.04;
protected $drag = 0.02;
protected function initEntity(){ protected function initEntity(){
//TODO: upgrade old numeric entity ids //TODO: upgrade old numeric entity ids
$this->namedtag->id = new String("id", "Item"); $this->namedtag->id = new String("id", "Item");
@ -156,6 +163,13 @@ class DroppedItem extends Entity{
$pk->metadata = $this->getData(); $pk->metadata = $this->getData();
$player->dataPacket($pk); $player->dataPacket($pk);
$pk = new SetEntityMotionPacket;
$pk->eid = $this->getID();
$pk->speedX = $this->motionX;
$pk->speedY = $this->motionY;
$pk->speedZ = $this->motionZ;
$player->dataPacket($pk);
parent::spawnTo($player); parent::spawnTo($player);
} }
} }

View File

@ -49,6 +49,7 @@ use pocketmine\network\protocol\SetTimePacket;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\plugin\Plugin; use pocketmine\plugin\Plugin;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\block\Block;
abstract class Entity extends Position implements Metadatable{ abstract class Entity extends Position implements Metadatable{
public static $entityCount = 1; public static $entityCount = 1;
@ -83,6 +84,7 @@ abstract class Entity extends Position implements Metadatable{
public $lastYaw; public $lastYaw;
public $lastPitch; public $lastPitch;
/** @var AxisAlignedBB */
public $boundingBox; public $boundingBox;
public $onGround; public $onGround;
public $positionChanged; public $positionChanged;
@ -105,6 +107,8 @@ abstract class Entity extends Position implements Metadatable{
public $airTicks; public $airTicks;
public $namedtag; public $namedtag;
protected $isColliding = false;
protected $inWater; protected $inWater;
public $noDamageTicks; public $noDamageTicks;
private $justCreated; private $justCreated;
@ -113,6 +117,9 @@ abstract class Entity extends Position implements Metadatable{
protected $spawnTime; protected $spawnTime;
protected $gravity;
protected $drag;
/** @var Server */ /** @var Server */
protected $server; protected $server;
@ -173,6 +180,7 @@ abstract class Entity extends Position implements Metadatable{
$this->lastUpdate = $this->spawnTime = microtime(true); $this->lastUpdate = $this->spawnTime = microtime(true);
$this->justCreated = false; $this->justCreated = false;
$this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this)); $this->server->getPluginManager()->callEvent(new EntitySpawnEvent($this));
$this->scheduleUpdate();
} }
public function saveNBT(){ public function saveNBT(){
@ -268,8 +276,19 @@ abstract class Entity extends Position implements Metadatable{
return false; return false;
} }
$hasUpdate = false;
$timeNow = microtime(true); $timeNow = microtime(true);
$this->ticksLived += ($timeNow - $this->lastUpdate) * 20; $ticksOffset = min(20, max(1, floor(($timeNow - $this->lastUpdate) * 20))); //Simulate min one tic and max 20
$this->ticksLived += $ticksOffset;
if(!($this instanceof Player)){
for($tick = 0; $tick < $ticksOffset; ++$tick){
$this->move($this->motionX, $this->motionY, $this->motionZ);
if($this->motionX == 0 and $this->motionY == 0 and $this->motionZ == 0){
break;
}
}
}
if($this->handleWaterMovement()){ if($this->handleWaterMovement()){
$this->fallDistance = 0; $this->fallDistance = 0;
@ -345,7 +364,7 @@ abstract class Entity extends Position implements Metadatable{
$this->lastUpdate = $timeNow; $this->lastUpdate = $timeNow;
return false; return !($this instanceof Player) and ($this->motionX != 0 or $this->motionY == 0 or $this->motionZ == 0 or $hasUpdate);
} }
public final function scheduleUpdate(){ public final function scheduleUpdate(){
@ -469,12 +488,242 @@ abstract class Entity extends Position implements Metadatable{
return new Position($this->x, $this->y, $this->z, $this->level); return new Position($this->x, $this->y, $this->z, $this->level);
} }
public function move(Vector3 $displacement){ public function collision(){
if($displacement->x == 0 and $displacement->y == 0 and $displacement->z == 0){ $this->isColliding = true;
$this->fallDistance = 0;
}
/**
* Returns the entities near the current one inside the AxisAlignedBB
*
* @param AxisAlignedBB $bb
*
* @return Entity[]
*/
public function getNearbyEntities(AxisAlignedBB $bb){
$nearby = [];
$minX = ($bb->minX - 2) >> 4;
$maxX = ($bb->maxX + 2) >> 4;
$minZ = ($bb->minZ - 2) >> 4;
$maxZ = ($bb->maxX + 2) >> 4;
for($x = $minX; $x <= $maxX; ++$x){
for($z = $minZ; $z <= $maxZ; ++$z){
if($this->getLevel()->isChunkLoaded($x, $z)){
foreach($this->getLevel()->getChunkEntities($x, $z) as $ent){
if($ent !== $this and $ent->boundingBox->intersectsWith($this->boundingBox)){
$nearby[] = $ent;
}
}
}
}
}
return $nearby;
}
public function move($dx, $dy, $dz){
//$collision = [];
//$this->checkBlockCollision($collision);
if($dx == 0 and $dz == 0 and $dy == 0){
return; return;
} }
$this->scheduleUpdate(); $ox = $this->x;
$oy = $this->y;
$oz = $this->z;
if($this->isColliding){ //With an entity
$this->isColliding = false;
$dx *= 0.25;
$dy *= 0.05;
$dz *= 0.25;
$this->motionX = 0;
$this->motionY = 0;
$this->motionZ = 0;
}
$movX = $dx;
$movY = $dy;
$movZ = $dz;
/*$sneakFlag = $this->onGround and $this instanceof Player;
if($sneakFlag){
for($mov = 0.05; $dx != 0.0 and count($this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, -1, 0))) === 0; $movX = $dx){
if($dx < $mov and $dx >= -$mov){
$dx = 0;
}elseif($dx > 0){
$dx -= $mov;
}else{
$dx += $mov;
}
}
for(; $dz != 0.0 and count($this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox(0, -1, $dz))) === 0; $movZ = $dz){
if($dz < $mov and $dz >= -$mov){
$dz = 0;
}elseif($dz > 0){
$dz -= $mov;
}else{
$dz += $mov;
}
}
//TODO: big messy loop
}*/
if(count($this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox(0, $dy, 0))) > 0){
$dy = 0;
$dx = 0;
$dz = 0;
}
$fallingFlag = $this->onGround or ($dy != $movY and $movY < 0);
if($dx != 0){
if(count($this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($dx, 0, 0))) > 0){
$dy = 0;
$dx = 0;
$dz = 0;
}
}
if($dz != 0){
if(count($this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox(0, 0, $dz))) > 0){
$dy = 0;
$dx = 0;
$dz = 0;
}
}
if($movX != $dx or $movZ != $dz or $fallingFlag){
$cx = $dx;
$cy = $dy;
$cz = $dz;
$dx = $movX;
$dy = 0.05;
$dz = $movZ;
$oldBB = clone $this->boundingBox;
$list = $this->getLevel()->getCollisionCubes($this, $this->boundingBox->getOffsetBoundingBox($movX, $dy, $movZ));
foreach($list as $bb){
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
}
$this->boundingBox->addCoord(0, $dy, 0);
if($movY != $dy){
$dx = 0;
$dy = 0;
$dz = 0;
}
foreach($list as $bb){
$dx = $bb->calculateXOffset($this->boundingBox, $dx);
}
$this->boundingBox->addCoord($dx, 0, 0);
if($movX != $dx){
$dx = 0;
$dy = 0;
$dz = 0;
}
foreach($list as $bb){
$dz = $bb->calculateZOffset($this->boundingBox, $dz);
}
$this->boundingBox->addCoord(0, 0, $dz);
if($movZ != $dz){
$dx = 0;
$dy = 0;
$dz = 0;
}
if($movY != $dy){
$dy = -0.05;
foreach($list as $bb){
$dy = $bb->calculateYOffset($this->boundingBox, $dy);
}
$this->boundingBox->addCoord(0, $dy, 0);
}
if($cx * $cx + $cz * $cz > $dx * $dx + $dz * $dz){
$dx = $cx;
$dy = $cy;
$dz = $cz;
$this->boundingBox->setBB($oldBB);
}
}
$this->x = ($this->boundingBox->minX + $this->boundingBox->maxX) / 2;
$this->y = $this->boundingBox->minY + $this->height;
$this->z = ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2;
$this->onGround = $movY != $dy and $movY < 0;
$this->updateFallState($dy, $this->onGround);
if($movX != $dx){
$this->motionX = 0;
}else{
$this->motionX -= $this->motionX * $this->drag;
}
if($movY != $dy){
$this->motionY = 0;
}else{
$this->motionY -= $this->motionY * $this->drag;
}
if($movZ != $dz){
$this->motionZ = 0;
}else{
$this->motionY -= $this->motionY * $this->drag;
}
//$this->boundingBox->addCoord($dx, $dy, $dz);
//$this->x += $dx;
//$this->y += $dy;
//$this->z += $dz;
$cx = $this->x - $ox;
$cy = $this->y - $oy;
$cz = $this->z - $oz;
//TODO: vehicle collision events (first we need to spawn them!)
}
/**
* @param Block[] $list
*
* @return Block[]
*/
protected function checkBlockCollision(&$list = array()){
$minX = floor($this->boundingBox->minX + 0.001);
$minY = floor($this->boundingBox->minY + 0.001);
$minZ = floor($this->boundingBox->minZ + 0.001);
$maxX = floor($this->boundingBox->maxX + 0.001);
$maxY = floor($this->boundingBox->maxY + 0.001);
$maxZ = floor($this->boundingBox->maxZ + 0.001);
for($z = $minZ; $z <= $maxZ; ++$z){
for($x = $minX; $x <= $maxX; ++$x){
for($y = $minY; $y <= $maxY; ++$y){
$this->getLevel()->getBlock(new Vector3($x, $y, $z))->collidesWithBB($this->boundingBox, $list);
}
}
}
return $list;
} }
public function setPositionAndRotation(Vector3 $pos, $yaw, $pitch){ public function setPositionAndRotation(Vector3 $pos, $yaw, $pitch){

View File

@ -42,6 +42,10 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
/** @var PlayerInventory */ /** @var PlayerInventory */
protected $inventory; protected $inventory;
public $width = 0.6;
public $length = 0.6;
public $height = 1.8;
public function getInventory(){ public function getInventory(){
return $this->inventory; return $this->inventory;
} }
@ -68,9 +72,6 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{
} }
} }
$this->height = 1.8; //Or 1.62?
$this->width = 0.6;
parent::initEntity(); parent::initEntity();
} }

View File

@ -23,6 +23,10 @@ namespace pocketmine\entity;
abstract class Living extends Entity implements Damageable{ abstract class Living extends Entity implements Damageable{
protected $gravity = 0.08;
protected $drag = 0.02;
protected function initEntity(){ protected function initEntity(){
} }

View File

@ -35,6 +35,7 @@ use pocketmine\item\Item;
use pocketmine\level\format\pmf\LevelFormat; use pocketmine\level\format\pmf\LevelFormat;
use pocketmine\level\generator\Flat; use pocketmine\level\generator\Flat;
use pocketmine\level\generator\Generator; use pocketmine\level\generator\Generator;
use pocketmine\math\AxisAlignedBB;
use pocketmine\math\Vector2; use pocketmine\math\Vector2;
use pocketmine\math\Vector3 as Vector3; use pocketmine\math\Vector3 as Vector3;
use pocketmine\nbt\NBT; use pocketmine\nbt\NBT;
@ -323,7 +324,7 @@ class Level{
//Do chunk updates //Do chunk updates
while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){ while($this->updateQueue->count() > 0 and $this->updateQueue->current()["priority"] <= $currentTick){
$block = $this->getBlockRaw($this->updateQueue->extract()["data"]); $block = $this->getBlock($this->updateQueue->extract()["data"]);
$block->onUpdate(self::BLOCK_UPDATE_SCHEDULED); $block->onUpdate(self::BLOCK_UPDATE_SCHEDULED);
} }
@ -332,7 +333,7 @@ class Level{
for($Y = 0; $Y < 8; ++$Y){ for($Y = 0; $Y < 8; ++$Y){
if(!$this->level->isMiniChunkEmpty($X, $Z, $Y)){ if(!$this->level->isMiniChunkEmpty($X, $Z, $Y)){
for($i = 0; $i < 3; ++$i){ for($i = 0; $i < 3; ++$i){
$block = $this->getBlockRaw(new Vector3(($X << 4) + mt_rand(0, 15), ($Y << 4) + mt_rand(0, 15), ($Z << 4) + mt_rand(0, 15))); $block = $this->getBlock(new Vector3(($X << 4) + mt_rand(0, 15), ($Y << 4) + mt_rand(0, 15), ($Z << 4) + mt_rand(0, 15)));
if($block instanceof Block){ if($block instanceof Block){
if($block->onUpdate(self::BLOCK_UPDATE_RANDOM) === self::BLOCK_UPDATE_NORMAL){ if($block->onUpdate(self::BLOCK_UPDATE_RANDOM) === self::BLOCK_UPDATE_NORMAL){
$this->updateAround($block, self::BLOCK_UPDATE_NORMAL); $this->updateAround($block, self::BLOCK_UPDATE_NORMAL);
@ -510,6 +511,39 @@ class Level{
return Block::get($b[0], $b[1], new Position($pos->x, $pos->y, $pos->z, $this)); return Block::get($b[0], $b[1], new Position($pos->x, $pos->y, $pos->z, $this));
} }
/**
* @param Entity $entity
* @param AxisAlignedBB $bb
*
* @return AxisAlignedBB[]
*/
public function getCollisionCubes(Entity $entity, AxisAlignedBB $bb){
$minX = floor($bb->minX);
$minY = floor($bb->minY);
$minZ = floor($bb->minZ);
$maxX = floor($bb->maxX + 1);
$maxY = floor($bb->maxY + 1);
$maxZ = floor($bb->maxZ + 1);
$collides = [];
for($z = $minZ; $z < $maxZ; ++$z){
for($x = $minX; $x < $maxX; ++$x){
if($this->isChunkLoaded($x >> 4, $z >> 4)){
for($y = $minY - 1; $y < $maxY; ++$y){
$this->getBlock(new Vector3($x, $y, $z))->collidesWithBB($bb, $collides);
}
}
}
}
foreach($entity->getNearbyEntities($bb->expand(0.25, 0.25, 0.25)) as $ent){
$collides[] = $ent->boundingBox;
}
return $collides;
}
/** /**
* @param Vector3 $pos * @param Vector3 $pos
* @param Block $block * @param Block $block
@ -624,7 +658,7 @@ class Level{
//TODO: add random motion with physics //TODO: add random motion with physics
"Motion" => new Enum("Motion", [ "Motion" => new Enum("Motion", [
new Double("", 0), new Double("", 0),
new Double("", 0), new Double("", 0.05),
new Double("", 0) new Double("", 0)
]), ]),
"Rotation" => new Enum("Rotation", [ "Rotation" => new Enum("Rotation", [
@ -991,6 +1025,10 @@ class Level{
return []; return [];
} }
public function isChunkLoaded($X, $Z){
return $this->level->isChunkLoaded($X, $Z);
}
/** /**
* Loads a chunk * Loads a chunk
* *

View File

@ -54,26 +54,25 @@ class AxisAlignedBB{
} }
public function addCoord($x, $y, $z){ public function addCoord($x, $y, $z){
$vec = clone $this;
if($x < 0){ if($x < 0){
$vec->minX += $x; $this->minX += $x;
}elseif($x > 0){ }elseif($x > 0){
$vec->maxX += $x; $this->maxX += $x;
} }
if($y < 0){ if($y < 0){
$vec->minY += $y; $this->minY += $y;
}elseif($y > 0){ }elseif($y > 0){
$vec->maxY += $y; $this->maxY += $y;
} }
if($z < 0){ if($z < 0){
$vec->minZ += $z; $this->minZ += $z;
}elseif($z > 0){ }elseif($z > 0){
$vec->maxZ += $z; $this->maxZ += $z;
} }
return $vec; return $this;
} }
public function expand($x, $y, $z){ public function expand($x, $y, $z){
@ -197,14 +196,13 @@ class AxisAlignedBB{
} }
public function intersectsWith(AxisAlignedBB $bb){ public function intersectsWith(AxisAlignedBB $bb){
if($bb->maxX <= $this->minX or $bb->minX >= $this->maxX){ if($bb->maxX > $this->minX and $bb->minX < $this->maxX){
return false; if($bb->maxY > $this->minY and $bb->minY < $this->maxY){
return $bb->maxZ > $this->minZ and $bb->minZ < $this->maxZ;
} }
if($bb->maxY <= $this->minY or $bb->minY >= $this->maxY){
return false;
} }
return $bb->maxZ > $this->minZ and $bb->minZ < $this->maxZ; return false;
} }
public function isVectorInside(Vector3 $vector){ public function isVectorInside(Vector3 $vector){