Как отличить стену от пола при столкновении с местностью? - PullRequest
0 голосов
/ 23 июня 2019

Я программировал систему обнаружения и реагирования на столкновения для своей воксельной игры, и после нескольких переписываний я нашел довольно стабильное и надежное решение.Проблема, с которой я столкнулся сейчас, заключается в том, что мне нужно различать, падает ли игрок через пол или идет в стену.На данный момент у меня есть только код, если скорость у отрицательна, затем исправьте вверх, но это заставляет игрока поднимать стены при ходьбе против одной, падая.

Моим первым решением было вычислить направлениеигрок возвращается, вычитая предыдущую позицию и блок, с которым он в данный момент сталкивается.Затем я «смотрю» в этом направлении на блоки у ног игроков, если я не нахожу их, полагаю, что игрок идет к стене, и поэтому вместо этого корректирую по горизонтали.Если я нахожу блоки, я полагаю, что игрок застрял в земле и исправил вверх.

Это текущий код столкновения:

    private void checkCollision() {
        if (this.world != null) {
            if (this.collision) {
                float length = (float) Math.sqrt(this.velocityX * this.velocityX + this.velocityY * this.velocityY + this.velocityZ * this.velocityZ);
                if (length == 0.0) {
                    this.predictedX = this.posX;
                    this.predictedY = this.posY;
                    this.predictedZ = this.posZ;
                } else {
                    float nVelocityX = this.velocityX / length;
                    float nVelocityY = this.velocityY / length;
                    float nVelocityZ = this.velocityZ / length;

                    float advancement = Math.min(1.0f, length);
                    float lengthLeft = Math.max(length - 1.0f, 0.0f);

                    boolean collided;
                    this.boundingBox.setCenterXY(this.posX, this.posY, this.posZ);

                    loop:
                    while (advancement <= length) {
                        this.predictedX = this.posX + nVelocityX * advancement;
                        this.predictedY = this.posY + nVelocityY * advancement;
                        this.predictedZ = this.posZ + nVelocityZ * advancement;

                        // Update bb
                        this.prevBoundingBox.set(this.boundingBox);
                        this.boundingBox.setCenterXY(this.predictedX, this.predictedY, this.predictedZ);

                        // Loop through all blocks the bounding box could collide with from top to bottom
                        for (int y = (int) Math.floor(this.boundingBox.getMaxY()); y >= Math.floor(this.boundingBox.getMinY()); y--) {
                            for (int x = (int) Math.floor(this.boundingBox.getMinX()); x < this.boundingBox.getMaxX(); x++) {
                                for (int z = (int) Math.floor(this.boundingBox.getMinZ()); z < this.boundingBox.getMaxZ(); z++) {
                                    if (y >= 0 && y < IChunk.BLOCK_COUNT) {
                                        collided = false;
                                        IChunk chunk = this.world.getChunk(x >> ISection.BLOCK_TO_CHUNK, z >> ISection.BLOCK_TO_CHUNK, false);

                                        Block block;
                                        if (chunk != null) {
                                            block = BlockRegistry.getBlock(chunk.getBlock(x, y, z));
                                        } else {
                                            block = BlockRegistry.getBlock(1);
                                        }

                                        if (block.isSolid()) {
                                            RESULT.facing = null;
                                            if (nVelocityY < 0.0f) {
                                                int minX = x;
                                                int maxX = x;
                                                int minZ = z;
                                                int maxZ = z;

                                                float distX = this.prevBoundingBox.getMinX() - x;
                                                float distZ = this.prevBoundingBox.getMinZ() - z;

                                                if (distX < 0.0f) {
                                                    minX -= (int) Math.floor(this.width);
                                                    maxX--;
                                                } else if (distX > 0.0f) {
                                                    minX++;
                                                    maxX += (int) Math.floor(this.width);
                                                }

                                                if (distZ < 0.0f) {
                                                    minZ -= (int) Math.floor(this.depth);
                                                    maxZ--;
                                                } else if (distZ > 0.0f) {
                                                    minZ++;
                                                    maxZ += (int) Math.floor(this.depth);
                                                }

                                                int blockY = (int) Math.floor(this.prevBoundingBox.getMinY());
                                                correction:
                                                for (int blockX = minX; blockX <= maxX; blockX++) {
                                                    for (int blockZ = minZ; blockZ <= maxZ; blockZ++) {
                                                        if (blockX == x && blockZ == z) continue;
                                                        int blockId = this.world.getBlock(blockX, blockY, blockZ);

                                                        if (blockId != -1) {
                                                            block = BlockRegistry.getBlock(blockId);

                                                            if (block.isSolid()) {
                                                                RESULT.facing = Facing.TOP;
                                                                break correction;
                                                            }
                                                        }
                                                    }
                                                }

                                            } else if (nVelocityY > 0.0f && y > Math.floor(this.posY) + this.height && (chunk != null && !BlockRegistry.getBlock(chunk.getBlock(x, y - 1, z)).isSolid())) {
                                                // entity is jumping against ceiling, correction downwards has priority
                                                RESULT.facing = Facing.BOTTOM;
                                            }

                                            if (RESULT.facing == null) {
                                                if (Math.abs(nVelocityX) > Math.abs(nVelocityZ)) {
                                                    // velocity on x axis is greater, correcting on x axis has priority

                                                    if (nVelocityX < 0.0f)
                                                        RESULT.facing = Facing.EAST;
                                                    else
                                                        RESULT.facing = Facing.WEST;
                                                } else {
                                                    // velocity on z axis is greater, correcting on z axis has priority

                                                    if (nVelocityZ < 0.0f)
                                                        RESULT.facing = Facing.SOUTH;
                                                    else
                                                        RESULT.facing = Facing.NORTH;
                                                }
                                            }

                                            System.out.println("Correction Facing: " + RESULT.facing.name());

                                            OTHER.setPosition(x, y, z);
                                            OTHER.setSize(1.0f, block.getHeight(), 1.0f);
                                            collided = this.boundingBox.intersects(OTHER, RESULT);
                                        }

                                        if (collided) {
                                            if (y == Math.floor(this.boundingBox.getMinY()) && RESULT.facing != Facing.TOP) {
                                                // there is only collision at the feet -> stepup
                                                this.predictedY = Math.floor(this.predictedY) + block.getHeight();
                                            } else {
                                                this.applyCorrection();
                                            }
                                            break loop;
                                        }
                                    }
                                }
                            }
                        }

                        if (advancement >= length) {
                            break;
                        }

                        lengthLeft = Math.max(lengthLeft - 1.0f, 0.0f);
                        advancement = length - lengthLeft;
                    }
                }
            } else {
                this.predictedX = this.posX + this.velocityX;
                this.predictedY = this.posY + this.velocityY;
                this.predictedZ = this.posZ + this.velocityZ;
            }
        }
    }

Чтобы игрок не попадал сквозь стены при быстром переходеЯ разделил движение на единицы вдоль оси скорости, пока вся скорость не будет обработана.Первое положительное обнаружение столкновения вызовет исправление и отменит любые дальнейшие проверки.

Если найден блок, который является сплошным, статические AABB устанавливаются в координатах блока, и выполняется проверка столкновения по нему.На основе ранее определенных условий поправка для оси рассчитывается и сохраняется в переменной RESULT.Само пересечение и коррекция работают, как и ожидалось.

Я не уверен, почему именно, но из-за этого игрок приземляется на землю на один тик при приземлении после ходьбы в стену, как показано в этом видео: https://streamable.com/ogtjk

Есть ли другой способ определить, иду ли я к стене или падаю в землю?

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...