MySQL Query для получения строк в сфере (координаты X, Y, Z)? - PullRequest
4 голосов
/ 21 марта 2012

Я создаю плагин для игры под названием Minecraft с Bukkit API.

У меня есть таблица базы данных с именем Reinforcements со следующими полями: x integer, y integer, z integer. Армирующий блок - это защищенный блок, то есть он не может быть уничтожен.

Я использую EntityExplodeEvent для проверки на взрывы TNT.

Я перебираю event.blocklist() и сравниваю каждый блок с записями в таблице подкреплений. Если он существует, то предотвратите повреждение усиленного блока при взрыве, используя event.blocklist().remove.

Я могу сделать это, получив наименьшее и наибольшее из каждой координаты (x, y, z), а затем проверив строки базы данных между этими двумя числами. Проблема в том, что это куб. Я должен проверять сферу. Как мне это сделать?

Вот что я получил до сих пор, обратите внимание: я знаю, что это не совсем проблема с оператором select, поскольку я могу сравнить возвращенные строки с event.blocklist(), но мне нужно знать, как это сделать, когда Я иду, чтобы сделать заявление об обновлении позже.

Причина, по которой мне нужно знать, как проверять строки в сфере, заключается в том, что в конечном итоге я добавлю дополнительное поле в таблицу подкреплений под названием 'durability integer', которое будет уменьшаться после каждого взрыва. Поскольку взрыв - это сфера, запрос на обновление должен обновлять только строки в этой сфере, а не куб.

Кто-нибудь? Спасибо

Текущий MySQL Query

"SELECT X, Y, Z FROM REINFORCEMENTS WHERE DURABILITY >= 1 " +
                "AND x<=? AND x>=? AND y<=? AND y>=? AND z<=? AND z>=? AND world=?");

Полный код

@EventHandler
    public void checkForExplosion(EntityExplodeEvent event){

        if(event.isCancelled()){
            return;
        }

        //Store blocks that are inside explosion's blast radius
        List<Block> blastRadiusBlocks = event.blockList();

        //If explosion occurs in mid air it returns 0 so no need to go any
        //further since there are no blocks inside the explosions radius
        if(blastRadiusBlocks.size() < 1){
            return;
        }

        HashMap<Coordinate, Block> affectedBlocks = new HashMap<Coordinate, Block>();

        //Initialize min & max X,Y,Z coordinates
        int smallestX = blastRadiusBlocks.get(0).getX();
        int largestX = smallestX;
        int smallestY = blastRadiusBlocks.get(0).getY();
        int largestY = smallestY;
        int smallestZ = blastRadiusBlocks.get(0).getZ();
        int largestZ = smallestZ;

        //World Name
        String worldName = blastRadiusBlocks.get(0).getWorld().getName();
        World world = this.myPlugin.getServer().getWorld(worldName);

        //Find min & max X,Y,Z coordinates
        for(int i = 0; i < blastRadiusBlocks.size(); i++){
            Block block = blastRadiusBlocks.get(i);
            int blockX = block.getX();
            int blockY = block.getY();
            int blockZ = block.getZ();

            if(blockX < smallestX){
                smallestX = blockX;
            }

            if(blockX > largestX){
                largestX = blockX;
            }

            if(blockY < smallestY){
                smallestY = blockY;
            }

            if(blockY > largestY){
                largestY = blockY;
            }

            if(blockZ < smallestZ){
                smallestZ = blockZ;
            }

            if(blockZ > largestZ){
                largestZ = blockZ;
            }

            //Instantiate Coordinate class passing in parameters
            Coordinate coordinate = new Coordinate(world, blockX, blockY, blockZ);
            //Put a new entry of type Coordinate as key and type Block as value
            affectedBlocks.put(coordinate, block);
        }

        try {
            //Query database for any reinforced blocks that may be in the blast radius
            //Reinforced blocks should have a durability > 0 (aka >= 1)
            PreparedStatement ask = this.conn.prepareStatement(
                "SELECT X, Y, Z FROM REINFORCEMENTS WHERE DURABILITY >= 1 " +
                "AND x<=? AND x>=? AND y<=? AND y>=? AND z<=? AND z>=? AND world=?");
            ask.setInt(1, largestX);
            ask.setInt(2, smallestX);
            ask.setInt(3, largestY);
            ask.setInt(4, smallestY);
            ask.setInt(5, largestZ);
            ask.setInt(6, smallestZ);
            ask.setString(7, worldName);
            ask.execute();
            ResultSet result = ask.getResultSet();

            //If there was some found, loop through each one
            while(result.next()){
                //Get X,Y,Z coords of reinforced block
                int x = result.getInt(1);
                int y = result.getInt(2);
                int z = result.getInt(3);

                //Pass in x, y, z of reinforced block into affectedBlocks HashMap to instantiate a Block
                Block protectedBlock = affectedBlocks.get(new Coordinate(world, x, y, z));
                //Then remove the protectedBlock from explosion list
                event.blockList().remove(protectedBlock);
            }

            result.close();
            ask.close();

        } catch (SQLException e) {
            System.err.println("Citadel - Select Reinforcement can't keep up (possibly too many explosions):\n" + e);
        }
}

Ответы [ 2 ]

2 голосов
/ 21 марта 2012
SELECT X, Y, Z FROM REINFORCEMENTS 
WHERE DURABILITY >= 1 
AND world=?
AND (POW((X-?),2)+POW((Y-?),2)+POW((Z-?),2))<POW(?,2)

С параметрами woldID, X, Y, Z взрыва и радиус взрыва

2 голосов
/ 21 марта 2012

если ваша сфера центрирована в x0, y0 z0 и имеет радиус r, то вам нужно:

select ... from ... where ((x-x0)*(x-x0)+(y-y0)*(y-y0)+(z-z0)*(z-z0) < r*r);

(это просто Пифагор в 3D).

...