Рассчитать правильное изображение направления спрайта в игре с высоты птичьего полета? (Математика здесь может быть вектор скорости в градусах угла?) - PullRequest
2 голосов
/ 08 сентября 2010

Справочная информация: у меня есть 8 изображений для каждого спрайта в игре JavaScript с видом моей птицы, представляющей верхнюю, верхнюю правую, правую, правую нижнюю части и т. Д., В зависимости от скорости космического корабля игрока.Учитывая значения sprite.speed.x и sprite.speed.y (например, 4 и -2,5 или 2 и 0), как мне получить правильный угол в градусах?Учитывая этот угол, я мог бы найти, какое значение градуса представляет какое изображение спрайта.Или, возможно, есть еще более простой способ.(В настоящее время я просто использую что-то вроде «если x ниже нуля, используйте левое изображение» и т. Д., Что приведет к тому, что диагональные изображения будут использоваться почти все время.)

При поиске я обнаружил ...

angle = Math.atan2(speed.y, speed.x);

... но почему-то я все еще что-то упускаю.

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

Большое спасибо за любую помощь!

Ответы [ 3 ]

10 голосов
/ 08 сентября 2010

Хороший вопрос!Мне понравился ответ tom10 (на отметке +1), но я подумал, можно ли это сделать без особой тригонометрии.Вот короткое решение с последующим объяснением.

// slope is a constant, 0.414...; calculate it just once
var slope = Math.tan(Math.PI/8);

// do this for each x,y point
var s1 = x * slope + y > 0 ? 0 : 1;
var s2 = y * slope + x > 0 ? 0 : 1;
var s3 = y * slope - x < 0 ? 0 : 1;
var s4 = x * slope - y > 0 ? 0 : 1;

var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);

Это устанавливает значение segment между 0 и 7. Вот пример с 2000 случайных точек (полный исходный код в конце ответа).Используя значения x, y скорости спрайта, вы можете использовать значение сегмента, чтобы подобрать соответствующее изображение спрайта.

alt text

Tadaa!

Так как же это работает? Наше сегментное выражение выглядит немного загадочным.

Замечание один : мы хотим разбить круг вокруг точки на8 сегментов одинакового углового размера.360/8 = 45 градусов на сегмент.Четыре из 8 сегментов центрированы на одной из двух сторон осей x и y, нарезанных под углом 45/2 = 22,5 градуса каждая.

alt text

Наблюдение два: Уравнение прямой на плоскости, a*x + b*y + c = 0, когда превращено в неравенство, a*x + b*y + c > 0 может использоваться для проверки, на какой стороне линии расположена точка.Все наши четыре линии пересекают начало координат (x = 0, y = 0), и, следовательно, сила c = 0.Кроме того, все они находятся под углом 22,5 градуса от оси x или y.Это дает нам четыре линейных уравнения:

y = x * tan (22,5);у = -х * загар (22,5);x = y * tan (22,5);x = -y * tan (22,5)

Превратившись в неравенства, получим:

x * tan (22,5) - y> 0;x * tan (22,5) + y> 0;у * загар (22,5) - х> 0;y * tan (22,5) + x> 0

Проверка неравенств для данной точки позволяет нам узнать с каждой стороны каждой линии, что она лежит: alt text alt text

alt textalt text

Наблюдение три : мы можем объединить результаты теста, чтобы получить желаемый шаблон номера сегмента.Вот визуальная разбивка:

В последовательности: 4 * s4, 2 * (s2 ^ s4) и сумма 4 * s4 + 2 * (s2 ^ s4) alt text alt text alt text

(Символ ^ - это Javascript XORоператор.)

А вот s1 ^ s2 ^ s3 ^ s4, сначала сам по себе, а затем добавлен к 4 * s4 + 2 * (s2 ^ s4) alt text alt text

Дополнительный кредит: банкамы подправили вычисления, чтобы использовать только целочисленную арифметику?Да - если известно, что x и y являются целыми числами, мы могли бы умножить обе части неравенства на некоторую константу (и округлить), что привело бы к целочисленной математике.(Однако это будет потеряно в Javascript, чьи числа всегда с плавающей запятой двойной точности.):

var s1 = x * 414 + y * 1000 > 0 ? 0 : 1;
var s2 = y * 414 + x * 1000 > 0 ? 0 : 1;
var s3 = y * 414 - x * 1000 < 0 ? 0 : 1;
var s4 = x * 414 - y * 1000 > 0 ? 0 : 1;

Полный исходный код для нашего примера выше: (просто поместите его вновый html-файл и откройте его в любом браузере)

(см. демонстрационный пример на jsbin)

<html>
    <head>
        <style type="text/css">
            .dot { position: absolute; font: 10px Arial }
            .d0 { color: #FF0000; }
            .d1 { color: #FFBF00; }
            .d2 { color: #7fcc00; }
            .d3 { color: #00FF7F; }
            .d4 { color: #00FFFF; }
            .d5 { color: #5555FF; }
            .d6 { color: #aF00FF; }
            .d7 { color: #FF00BF; }
        </style>
        <script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.4.2/jquery.min.js"></script>
        <script type="text/javascript">
            $(function() {
                var $canvas = $("#canvas");
                var canvasSize = 300;
                var count = 2000;
                var slope = Math.tan(Math.PI/8);

                $canvas.css({ width: canvasSize, height: canvasSize });
                for (var i = 0; i < count; ++i) {

                    // generate a random point
                    var x = Math.random() - 0.5;
                    var y = Math.random() - 0.5;

                    // draw our point
                    var $point = $("<div class='dot'></div>")
                        .css({
                            left: Math.floor((x + 0.5) * canvasSize) - 3,
                            top:  Math.floor((y + 0.5) * canvasSize) - 6 })
                        .appendTo($canvas);

                    // figure out in what segment our point lies
                    var s1 = x * slope + y > 0 ? 0 : 1;
                    var s2 = y * slope + x > 0 ? 0 : 1;
                    var s3 = y * slope - x < 0 ? 0 : 1;
                    var s4 = x * slope - y > 0 ? 0 : 1;
                    var segment = 4 * s4 + 2 * (s2 ^ s4) + (s1 ^ s2 ^ s3 ^ s4);

                    // modify the point's html content and color
                    // (via its CSS class) to indicate its segment
                    $point
                        .text(segment)
                        .addClass("d" + segment);
                }
            });
        </script>
    </head>
    <body>
        <div id="canvas" style="position: absolute; border: 1px solid blue">
        </div>
    </body>
</html>
3 голосов
/ 08 сентября 2010

То, что вы предлагаете, совершенно верно!Обратите внимание, что результат Math.atan2 находится в радианах, и вы, вероятно, больше знакомы с градусами;вы можете конвертировать, используя angle_degrees = angle*(180./pi).

(Обратите внимание также, что вам не нужно нормализовать, как предложено RCIX, хотя вы можете, если хотите, то, что у вас есть, angle = Math.atan2(speed.y, speed.x);, должно работать нормально.)

0 голосов
/ 08 сентября 2010

Вы были на правильном пути. Нормализуйте вектор скорости (сначала проверьте, чтобы оба компонента были равны 0), вызовите для него atan2, а затем преобразуйте полученное значение радиан в какое-то дружественное перечисление направления или что-то, что вы можете использовать, чтобы выбрать правильный спрайт.

...