Заставьте объект упасть, когда он близок к краю, используя контроллер персонажа - PullRequest
1 голос
/ 10 мая 2019

Я использую инструмент контроля персонажа Unity.

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

Вот как это выглядит: enter image description here

Я использую собственную гравитацию так же, как это:

private void ApplyGravity() {
        if (!characterController.isGrounded) {
            gravity += Physics.gravity * Time.deltaTime * gravityFactor;
            if (gravity.y > 0 && !Input.GetButton(jumpButton))
                gravity += Physics.gravity * Time.deltaTime * gravityFactor * (lowJumpMultiplier - 1);
        }
        else if (!isJumping) gravity = Vector3.down;

        CheckCollisionFlags();
        movement += gravity;
    }

Я не нашел способа изменить свойства контроллера персонажа, чтобы применить скольжение или добавить новый коллайдер для столкновения.

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

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

1 Ответ

0 голосов
/ 12 мая 2019

Мне наконец-то удалось решить эту проблему.

В первую очередь я проверял, заземлена ли середина персонажа или нет. Я добился этого, запустив луч прямо в положение объекта. Затем я хотел, чтобы двое знали, какая сторона не сталкивается с поверхностью. Я выпустил еще четыре луча, по одному в каждой точке кардинала вокруг капсульного коллайдера. Я извлек каждую точку, которая не была заземлена, и рассчитал разницу с текущей позицией персонажа. Это дает мне координату, которую я добавил к движению объекта, который будет увеличиваться, пока не упадет.

Давайте посмотрим:

Так я создал каждый луч. Мне нужно было два значения (x, z), чтобы изменить точку кардинала, в которую должен быть помещен каждый луч. Затем я просто установил y в землю, вычтя половину высоты капсульного коллайдера.

private Ray CreateEdgeCheckRay(float x, float z) {
        Vector3 rayPos = transform.position;
        rayPos.y -= characterController.height / 2;
        rayPos.x += x;
        rayPos.z += z;
        return new Ray(rayPos, Vector3.down);
    }

Я использовал этот метод только для создания каждого луча одновременно:

private Ray[] CreateSideRays() {
        Ray[] sideRays = new Ray[4];
        sideRays[0] = CreateEdgeCheckRay(0.5f, 0);
        sideRays[1] = CreateEdgeCheckRay(-0.5f, 0);
        sideRays[2] = CreateEdgeCheckRay(0, 0.5f);
        sideRays[3] = CreateEdgeCheckRay(0, -0.5f);

        return sideRays;
    }

Я использовал Physics.RaycastNonAlloc, получая результаты в массиве длины 1 для повышения производительности.

private bool RayGrounded(Ray ray) {
        return Physics.RaycastNonAlloc(ray, rayResults, groundRayLength) < 1;
    }

Наконец, я объединил все вместе. Мне не нужно никаких вертикальных движений, так как ранее я применил гравитацию.

private void CheckGroundRays() {
        Ray middleRay = CreateEdgeCheckRay(0, 0);
        if (RayGrounded(middleRay)) {
            Vector3 inEdgeMovement = Vector3.zero;
            foreach(Ray ray in CreateSideRays()) {
                if(RayGrounded(ray))
                    inEdgeMovement += (ray.origin - transform.position);
            }
            inEdgeMovement.y = 0;
            movement += inEdgeMovement;
        }
    }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...