Пиксельно-совершенные столкновения в Monogame с плавающими позициями

Немного другая проблема, которая вызывает очень похожие сообщения компилятора, зависит от времени жизни объекта, а не для хранения явной ссылки. Примером этого является библиотека ssh2 . При разработке чего-то большего, чем тестовый проект, возникает соблазн попытаться помещать Session и Channel, полученные из этого сеанса рядом друг с другом, в структуру, скрывая детали реализации от пользователя. Однако обратите внимание, что определение Channel имеет время жизни 'sess в аннотации типа, а Session - нет.

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

Один из способов его решения очень простым образом - объявить Session снаружи в вызывающем, а затем для аннотации ссылки в структуре со временем жизни, подобный ответ в , этот пост Форума пользователя Rust говорит об одной и той же проблеме при инкапсуляции SFTP. Это не будет выглядеть элегантно и может не всегда применяться - потому что теперь у вас есть два объекта для работы, а не тот, который вам нужен!

Выключает арендованный ящик или owning_ref crate из другого ответа также являются решениями для этой проблемы. Рассмотрим owning_ref, который имеет специальный объект для этой цели: OwningHandle . Чтобы избежать перемещения основного объекта, мы выделяем его в куче, используя Box, что дает нам следующее возможное решение:

use ssh2::{Channel, Error, Session};
use std::net::TcpStream;

use owning_ref::OwningHandle;

struct DeviceSSHConnection {
    tcp: TcpStream,
    channel: OwningHandle, Box>>,
}

impl DeviceSSHConnection {
    fn new(targ: &str, c_user: &str, c_pass: &str) -> Self {
        use std::net::TcpStream;
        let mut session = Session::new().unwrap();
        let mut tcp = TcpStream::connect(targ).unwrap();

        session.handshake(&tcp).unwrap();
        session.set_timeout(5000);
        session.userauth_password(c_user, c_pass).unwrap();

        let mut sess = Box::new(session);
        let mut oref = OwningHandle::new_with_fn(
            sess,
            unsafe { |x| Box::new((*x).channel_session().unwrap()) },
        );

        oref.shell().unwrap();
        let ret = DeviceSSHConnection {
            tcp: tcp,
            channel: oref,
        };
        ret
    }
}

. Результатом этого кода является то, что мы не можем использовать Session, но он сохраняется вместе с Channel, который мы будем использовать. Поскольку OwningHandle вызывает раздел Box, который разделяет Channel, когда он хранится в структуре, мы называем его как таковой. ПРИМЕЧАНИЕ. Это только мое понимание. У меня есть подозрение, что это может быть неверно, поскольку оно, по-видимому, очень близко к обсуждению OwningHandle небезопасности .

. Любопытная деталь здесь заключается в том, что Session логически имеет аналогичную связь с TcpStream, поскольку Channel имеет значение Session, но его собственность не принимается и нет аннотаций типа вокруг так. Вместо этого пользователь должен позаботиться об этом, поскольку в документации к методу рукопожатия говорится:

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

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

blockquote>

Таким образом, использование TcpStream полностью зависит от программиста, чтобы обеспечить правильность кода. С OwningHandle внимание к тому, где происходит «опасная магия», выполняется с помощью блока unsafe {}.

Дальнейшее и более высокоуровневое обсуждение этой проблемы находится в этом Rust User's Forum - который включает в себя другой пример и его решение с использованием ящика для аренды, который не содержит небезопасных блоков.

1
задан Cool Cascade 16 January 2019 в 01:21
поделиться

1 ответ

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

Основная причина, по которой столкновения не выглядят корректно, - это масштабирование. Цвета для новых пикселей между диагоналями получают свои цвета путем усреднения * окружающих пикселей. Эффект заставляет изображение выглядеть больше, чем оригинал, особенно на диагонали.

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

Единственное прямое (идеальное по пикселям) решение - сравнивать фактический результат после масштабирования. Это требует рендеринга всего экрана дважды, а масштабный коэффициент требует большего количества вычислений. (не рекомендуется)

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

Другая проблема - скорость движения. Если вы перемещаетесь быстрее, чем один пиксель на Update (), обнаружения столкновений на пиксель недостаточно, если движение должно быть ограничено препятствием. Вы должны разрешить столкновение.

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

Простой алгоритм разрешения (математическое решение см. Ниже) состоит в том, чтобы разматывать движение наполовину, проверять на столкновение. Если он все еще сталкивается, размотайте движение на четверть, иначе продвиньте его на четверть и проверьте на столкновение. Повторяйте, пока движение не станет менее 1 пикселя. Это запускает журнал скорости.

Что касается верхней стены, которая не сталкивается идеально: если начальное значение Y не кратно скорости вертикального движения, вы не попадете идеально на ноль. Я предпочитаю решить эту проблему, установив Y = 0, когда Y отрицательно. То же самое для X, а также когда X и Y> границы экрана - начало координат, для нижней и правой частей экрана.

Я предпочитаю использовать математические решения для разрешения столкновений. На ваших примерах изображений вы видите коробку, сталкивающуюся с ромбом, форма ромба математически представлена ​​как расстояние Манхэттена (Math.Abs(x1-x2) + Math.Abs(y1-y2)). Из этого факта легко напрямую рассчитать разрешение до столкновения.

Об оптимизации:

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

Как вы уже сказали, удалите все Math.Floor с, так как приведение достаточно. все вычисления внутри циклов не зависят от переменной цикла вне цикла.

(int)a.Bounds.Y * a.Texture.Width и (int)b.Bounds.Y * b.Texture.Width не зависят от переменных x или y и должны быть вычислены и сохранены перед циклами. Вычитания 'y- [вышеуказанная переменная] `должны храниться в цикле" y ".

Я бы рекомендовал использовать битборд (1 бит на 8 на 8 квадратов) для коллизий. Это уменьшает широкий (8x8 ) проверки столкновений на O (1). Для разрешения 144x144 все пространство поиска становится 18x18.

0
ответ дан Strom 16 January 2019 в 01:21
поделиться
Другие вопросы по тегам:

Похожие вопросы: