Алгоритм попадания пули

Программирование на Юнити.

Алгоритм попадания пули

Сообщение Jagiello 29 мар 2018, 00:11

Не могу найти в инете нормальное решение попадания пули(является точкой) в части тела без физики(чтобы сервер мог проверять и обрабатывать).
Написал следующее: Для каждой части тела(части тела статичны, представляют из себя прямоугольники) находятся глобальные координаты каждой вершины прямоугольника. После из него строится многоугольник, где пуля может находится с учетом скорости и направления выстрела. И наконец проверяется входит ли пуля-точка в этот многоугольник.
Изображение
Изображение
Но из-за костылей (предположительно, в постройке многоугольника) некоторые случаи не обрабатываются.

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

Синтаксис:
Используется csharp
public static class CollisionController
    {
        #region Public methods
        public static int CheckCollision(Vector2 bulletPos, Vector3 gunpointPos, BodyPart[] bodyParts, out int partId)
        {
            for (int i = 0; i < bodyParts.Length; i++)
            {
                var bodyPart = bodyParts[i];

                Vector2[] points = GetRealPoints(bodyPart);

                var min = float.MaxValue;
                var max = float.MinValue;
                var minId = 0;
                var maxId = 0;

                for (var j = 0; j < points.Length; j++)
                {
                    var dist = Vector2.Distance(points[j], bulletPos);
                    if (dist < min)
                    {
                        min = dist;
                        minId = j;
                    }
                    else if (dist > max)
                    {
                        max = dist;
                        maxId = j;
                    }
                }

                Vector2[] possiblePoints = GetPossiblePoints(points, minId, maxId, gunpointPos);

                if (IsBelongToHeroPart(bulletPos, possiblePoints))
                {
                    partId = i;
                    return bodyPart.Damage;
                }
            }

            partId = -1;
            return -1;
        }
        #endregion

        #region Private methods
        private static Vector2[] GetRealPoints(BodyPart bodyPart)
        {
            var pos = bodyPart.Transform.position;

            return new[]
                {
                    new Vector2(pos.x - bodyPart.Width, pos.y - bodyPart.Height),
                    new Vector2(pos.x + bodyPart.Width, pos.y - bodyPart.Height),
                    new Vector2(pos.x + bodyPart.Width, pos.y + bodyPart.Height),
                    new Vector2(pos.x - bodyPart.Width, pos.y + bodyPart.Height),
                };
        }


        private static Vector2[] GetPossiblePoints(Vector2[] realPoints, int minId, int maxId, Vector2 gunpoint)
        {
            var points = new Vector2[6];
            var isRevert = true;
            for (int i = 0, j = 0; j < 6; i++, j++)
            {
                var direction = realPoints[i] - gunpoint;
                var offset = direction.normalized * Bullet.BULLET_SPEED;
                if (i == minId)
                {
                    points[j] = realPoints[i];
                    continue;
                }
                if (i == maxId)
                {
                    points[j] = realPoints[i] + offset;
                    continue;
                }

                if (isRevert)
                {
                    points[j] = realPoints[i];
                    points[j + 1] = realPoints[i] + offset;
                    isRevert = false;
                    j++;
                }
                else
                {
                    points[j] = realPoints[i] + offset;
                    points[j + 1] = realPoints[i];
                    j++;
                }
            }
            return points;
        }


        //https://ru.wikibooks.org/wiki/Реализации_алгоритмов/Задача_о_принадлежности_точки_многоугольнику
        private static bool IsBelongToHeroPart(Vector2 point, Vector2[] p) //p is the points
        {
            var previus = p.Length - 1;
            var isBelong = false;

            for (var current = 0; current < p.Length; current++)
            {
                if (p[current].y < point.y && p[previus].y >= point.y || p[previus].y < point.y && p[current].y >= point.y)
                {
                    if (p[current].x + (point.y - p[current].y) / (p[previus].y - p[current].y) * (p[previus].x - p[current].x) < point.x)
                    {
                        isBelong = !isBelong;
                    }
                }
                previus = current;
            }
            return isBelong;
        }
        #endregion
    }
Jagiello
UNец
 
Сообщения: 18
Зарегистрирован: 11 окт 2017, 22:58
Откуда: Беларусь, Минск

Re: Алгоритм попадания пули

Сообщение 1max1 29 мар 2018, 00:45

Наверное проблема в том, что в следующем кадре пуля может перескочить прямоугольник и в коде вы это как-то не правильно проверяете(код не смотрел, много буковок:D). Предлагаю не мудрить с расчетами, повесить ригидбоди на пулькии поставить Collision Detection - Continuous. Ну, а если без физики, думаю нужно копать в сторону пересечения прямых.
Аватара пользователя
1max1
Адепт
 
Сообщения: 5505
Зарегистрирован: 28 июн 2017, 10:51

Re: Алгоритм попадания пули

Сообщение Jagiello 10 апр 2018, 08:54

1max1 писал(а):Наверное проблема в том, что в следующем кадре пуля может перескочить прямоугольник и в коде вы это как-то не правильно проверяете(код не смотрел, много буковок:D). Предлагаю не мудрить с расчетами, повесить ригидбоди на пулькии поставить Collision Detection - Continuous. Ну, а если без физики, думаю нужно копать в сторону пересечения прямых.

Если не мудрить, появляются проблемы на сервере, плюс так интересней :)
Получилось сделать через проверки пересечения отрезков, если кому надо, то:
Синтаксис:
Используется csharp
 public static class CollisionController
    {
        #region Public methods
        public static int GetCollidedBodyPartId(Vector2 bulletPos, Vector2 bulletStartPoint, Vector3 gunpointPos, BodyPart[] bodyParts)
        {
            bool success = false;
            for (int id = 0; id < bodyParts.Length; id++)
            {
                Vector2[] points = GetRealPoints(bodyParts[id]);

                success = Intersect(bulletStartPoint, bulletPos, points[0], points[1]);
                if (!success)
                {
                    success = Intersect(bulletStartPoint, bulletPos, points[1], points[2]);
                }
                else if (!success)
                {
                    success = Intersect(bulletStartPoint, bulletPos, points[2], points[3]);
                }
                else if (!success)
                {
                    success = Intersect(bulletStartPoint, bulletPos, points[3], points[0]);
                }

                if (success)
                {
                    return id;
                }
            }
            return -1;
        }
        #endregion
        #region Private methods
        private static Vector2[] GetRealPoints(BodyPart bodyPart)
        {
            var pos = bodyPart.Transform.position;

            return new[]
                {
                    new Vector2(pos.x - bodyPart.HalfWidth, pos.y - bodyPart.HalfHeight),
                    new Vector2(pos.x + bodyPart.HalfWidth, pos.y - bodyPart.HalfHeight),
                    new Vector2(pos.x + bodyPart.HalfWidth, pos.y + bodyPart.HalfHeight),
                    new Vector2(pos.x - bodyPart.HalfWidth, pos.y + bodyPart.HalfHeight),
                };
        }


        private static bool Intersect(Vector2 a, Vector2 b, Vector2 c, Vector2 d)
        {
            if (!Intersect(a.x, b.x, c.x, d.x) || !Intersect(a.y, b.y, c.y, d.y))
            {
                return false;
            }

            Vector3 m = GetLine(a, b);
            Vector3 n = GetLine(c, d);

            float zn = Det(m.x, m.y, n.x, n.y);
            if (Mathf.Abs(zn) < Mathf.Epsilon)
            {
                if (Mathf.Abs(Dist(m, c)) > Mathf.Epsilon || Mathf.Abs(Dist(n, a)) > Mathf.Epsilon)
                {
                    return false;
                }

                if (CompareVector2(b, a))
                {
                    Swap(ref a, ref b);
                }

                if (CompareVector2(d, c))
                {
                    Swap(ref c, ref d);
                }

                return true;
            }
            else
            {
                Vector2 line;
                line.x = -Det(m.z, m.y, n.z, n.y) / zn;
                line.y = -Det(m.x, m.z, n.x, n.z) / zn;
                return IsBetweenPoints(a.x, b.x, line.x)
                    && IsBetweenPoints(a.y, b.y, line.y)
                    && IsBetweenPoints(c.x, d.x, line.x)
                    && IsBetweenPoints(c.y, d.y, line.y);
            }
        }

        private static Vector3 GetLine(Vector2 p, Vector2 q)
        {
            var a = p.y - q.y;
            var b = q.x - p.x;
            var c = (-a * p.x) - (b * p.y);

            float z = Mathf.Sqrt(a * a + b * b);
            if (Mathf.Abs(z) > Mathf.Epsilon)
            {
                a /= z;
                b /= z;
                c /= z;
            }

            return new Vector3(a, b, c);
        }

        private static float Dist(Vector3 line, Vector2 point)
        {
            return line.x * point.x + line.y * point.y + line.z;
        }

        private static bool IsBetweenPoints(float l, float r, float x)
        {
            return Mathf.Min(l, r) <= x + Mathf.Epsilon && x <= Mathf.Max(l, r) + Mathf.Epsilon;
        }

        private static bool Intersect(float a, float b, float c, float d)
        {
            if (a > b) Swap(ref a, ref b);
            if (c > d) Swap(ref c, ref d);
            return Mathf.Max(a, c) <= Mathf.Min(b, d) + Mathf.Epsilon;
        }

        private static void Swap(ref float a, ref float b)
        {
            var temp = a;
            a = b;
            b = temp;
        }

        private static void Swap(ref Vector2 a, ref Vector2 b)
        {
            var temp = a;
            a = b;
            b = temp;
        }

        private static float Det(float a, float b, float c, float d)
        {
            return (a * d - b * c);
        }

        private static bool CompareVector2(Vector2 a, Vector2 b)
        {
            return a.x < b.x - Mathf.Epsilon || Mathf.Abs(a.x - b.x) < Mathf.Epsilon && a.y < b.y - Mathf.Epsilon;
        }
        #endregion
    }
 

P.S: лучше строить отрезок пули не начиная с точки спавна пули, а с позиции пули в предыдущем кадре, но в моем случае и так работает.
Jagiello
UNец
 
Сообщения: 18
Зарегистрирован: 11 окт 2017, 22:58
Откуда: Беларусь, Минск


Вернуться в Скрипты

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 9