Joined: 17 Oct 2009, 19:57 Posts: 4102 Location: Киров
Немного странным выглядит, что с некоторых высот игрок не может свернуть в бок, пролетая дальше вниз. А стоит поднять такую дыру выше или ниже, и заворот возможен. Вот пример таких прыжков:
Joined: 18 Oct 2009, 04:01 Posts: 7206 Location: Владивосток
Это кстати ровно того же рода особенность, что и способность думера проходить над узкими проёмами определённого размера в полу, если не останавливаться. Так что точно wontfix. Почему-то лишь сейчас дошло.
Я когда-то сталкивался с похожей особенностью в своих играх. Не обязательно подымать UPS, надо просто просчитывать промежуточное движение объектов. Что-то типа for i := 1 to abs(vely) do move(velx, sign(vely)); вместо y := y + vely; x := x + velx; Правда не уверен насколько это применяемо к физонию д2д/дф и насколько сломается его совместимость с оригиналом.
Joined: 18 Oct 2009, 04:01 Posts: 7206 Location: Владивосток
DeaDDooMER wrote:
Не обязательно подымать UPS, надо просто просчитывать промежуточное движение объектов. Что-то типа for i := 1 to abs(vely) do move(velx, sign(vely)); вместо y := y + vely; x := x + velx;
Это же буквально continuous collision detection, разве нет? Надо Кетмара спросить. И да, даже в таком случае шаг цикла должен быть больше 1, мне кажется. Иначе летать в шахте на doom2d.wad:MAP08 будет слишком легко, например.
DeaDDooMER wrote:
Правда не уверен насколько это применяемо к физонию д2д/дф и насколько сломается его совместимость с оригиналом.
Сломаться не должна, потому что ну а где бы? Думер разве что проваливаться в узкие щели начнёт, это да. Но можно делать такое лишь для вертикальной скорости. Зато вот работать будет по идее интереснее, хотя и требовательнее к скорости станет. Можно из этого реквест соорудить так-то, разве что в "Наркомании".
Joined: 18 Oct 2009, 04:01 Posts: 7206 Location: Владивосток
Комментарий от Кетмара к моему предположению про CCD:
ketmar» эм... и? blackdoomer» так это оно или не оно? ketmar» там давно есть tracebox, хоть и черезжопный ketmar» ну да, ccd. только физоний поменяется blackdoomer» вот я тебя и прошу прояснить и заодно дать совет, как описанный в теме косяк поправить %-) blackdoomer» только лучше не здесь, а на форуме же ketmar» никак blackdoomer» тогда проясни это деддумеру там, будь ласка ketmar» он последним предложением всё сказал ketmar» нельзя ничего чинить, надо сидеть и молиться. на совместимость и оригинальный физоний ketmar» ничего не поделаешь ketmar» там, кстати, и дак нечто похожее blackdoomer» ну так дорогой, когда я тебе кидаю ссылко на форум, я хочу, чтобы это ты не мне сказал, а всем %-) blackdoomer» я не хочу за тебя чревовещать ketmar» если б я хотел сам -- я бы написал сам. мне лениво, у меня болит голова, жопа, хвост. пиши от себя как умный ketmar» впрочем, там вам "пролетая над гнездом кукушки" никакой ццд не поможет ketmar» я прочитал таки тему, лол ketmar» поняш всё правильно сказал ketmar» дельта движения же. хоть обмажься весь ццд -- на одном кадре думер ещё не, а на другом уже всё. а между этим неважно, насколько всё плавно, потому что опрос кнопочек покадровый blackdoomer» так ведь думер УЖЕ в сторону идёт, просто упирается же, разве нет? ketmar» ну и поняш показал решение. а ццд тут не при чём: ццд точно так же сразу упрётся в стену ketmar» я давно предлагал сделать coyote time, которое и это решает blackdoomer» можно я этот кусок переписки туда запощу? ketmar» можно репостить всё, что я пишу, если я явно не запрещал %-)
Joined: 18 Oct 2009, 04:01 Posts: 7206 Location: Владивосток
DeaDDooMER wrote:
Не обязательно подымать UPS, надо просто просчитывать промежуточное движение объектов. Что-то типа for i := 1 to abs(vely) do move(velx, sign(vely)); вместо y := y + vely; x := x + velx;
for i := 1 to dx do if not movex() then break; for i := 1 to dy do if not movey() then break;
Косяк здесь в том, что сначала объект перемещается сугубо по оси X, а затем так же целиком по оси Y. И вместо диагонального движения (точнее, векторного) мы получаем дифференцированное.
Однако если попробовать сходу починить наивным способом, то он не особо помогает:
Code:
MinAxis := Min(dx, dy);
// main motion for i := 1 to MinAxis do begin movey(); // vertical movement is less common, so it goes first movex(); end;
// remaining movement // only one of both lines will be executed at a time, so their order doesn't matter for i := 1 to dy-MinAxis do if not movey() then break; for i := 1 to dx-MinAxis do if not movex() then break;
Как и вот такой вариант:
Code:
MinAxis := Min(dx, dy); xgo := True; ygo := True;
// main motion for i := 1 to MinAxis do begin // vertical movement is less common, so it goes first if ygo then ygo := movey() else if not xgo then break; if xgo then xgo := movex() else if not ygo then break; end;
// remaining movement // only one of both next lines will be executed at a time, so their order here doesn't matter for i := 1 to dy-MinAxis do if not ygo then break else ygo := movey(); for i := 1 to dx-MinAxis do if not xgo then break else xgo := movex();
А вот это - работает и позволяет игроку залетать в любые проёмы вне зависимости от скорости. Причём как в вертикальные, так и в горизонтальные.
Code:
h := Abs(dx); v := Abs(dy); // TODO: why it's always 1 for an idle player?
// main motion repeat // h/v will not go negative here, as we already operate on a minimal quantity MinAxis := Min(h, v); for i := 1 to MinAxis do begin xfree := movex(); yfree := movey();
// leave early if no movement were made and thus we became static if not xfree and not yfree then Exit(state);
// Ord() is used to avoid explicit conditionals which are expensive because we're in a loop h -= Ord(xfree); v -= Ord(yfree); end; until MinAxis = 0;
// Remaining movement. At this point either 'h' or 'v' must be zero. // Therefore, only one of both these loops will be executed, so their order doesn't matter. // But horizontal movement is more common, so it goes first to avoid the excessive check for 'v'. for i := 1 to h do if not movex() then Exit(state); for i := 1 to v do if not movey() then Exit(state);
В чём суть. Вызовы movex() можно считать синонимами проверок, залетаем ли мы в вертикальное отверстие. То же самое верно и относительно movey() для горизонтальных, но это мы пока опустим. Отсюда следует, что чисто умозрительно пролёт может происходить в следующих обстоятельствах:
если горизонтальное движение оказалось небольшим и потому исчерпалось в основном цикле (main motion) сильно раньше того, как иссякло вертикальное;
если вертикальное движение оказалось по модулю намного больше горизонтального и потому долго крутило доводящий цикл (remaining movement), где нет movex().
Мой код учитывает оба этих случая. Но тут уже я сам не уверен в правильности содеянного, потому что некоторые карты вроде бы полагались на данную особенность физики для трюков. Ар, Джа и прочие неравнодушные - опишите, как должно быть. В теме сейчас этого нет.
Кстати, в этой же функции ещё есть вполне конкретный баг: шаг по оси применяется к объекту сразу же, поэтому следующая за этим проверка по противоположной оси осуществляется уже в новых координатах. При нынешнем дифференцированном движении это незаметно, но иначе физика начинает зависеть от того, какая из осей для проверки выбирается первой. Я это исправил, но не уверен, что оно так не было и в оригинале. DeaDDooMER, можешь глянуть, пожалуйста? Ты лучше нас всех знаком с тамошним кодом. Кроме того, если исправлять эту проблему для наклонных поверхностей, то игрок начинает намертво застревать вот здесь на Anthill:
Attachment:
screenshot-2024-11-02-02-55-20.png [ 74.28 KiB | Viewed 11090 times ]
Поэтому для них пока впилил хак, оставляющий старое поведение. Хотя не знаю, может так наоборот правильно стало бы. Надо смотреть точно размер проёма на карте.
Ар - посмотри, пожалуйста, и потестируй. Как вертикальные проёмы, так и горизонтальные. А также наклонные поверхности. Заодно прилагаю новую проверочную карту на основе твоей, где сделал побольше вертикальных проёмов.
Оффтоп:
Джа - теперь наклонные поверхности можно попробовать отладить. Я вроде въехал, как они работают.
Joined: 18 Oct 2009, 04:01 Posts: 7206 Location: Владивосток
DeaDDooMER wrote:
стоит потом переписать без exit. бряки фу кака.
У меня там ещё и Goto будет. А как бы ты переписал? Просто я вот наоборот считаю, что не стоит пытаться вытанцовывать парадигму на языке, не соответствующему ей в полной мере. Потому что язык в конечном счёте оказывается всегда сильнее. Особенно если сам код был перенесён с другого языка (в нашем случае - Си).
DeaDDooMER wrote:
Ты преувеличиваешь моё познание и понимаение кода %)
Это не отменяет моего утверждения, что исходники 1.35 более-менее глубоко исследовали только falcon, Rembo, ketmar и ты. Сам понимаешь, кого из этой цепочки мне сподручнее попросить.
DeaDDooMER wrote:
Взаимодействие игрока с миром смотреть в PL_act(), а движение в Z_moveobj().
Угу, спасибо. Похоже, что это действительно косяк, и оригинал так не делает. Потому что p->x=x;p->y=y; (это 1.35, но в 1.30 и 1.40 то же самое, я посмотрел). По этой причине осмелился закоммитить: https://repo.or.cz/d2df-sdl.git/commitdiff/866d84306232261c7be9e5a3ea47222562abe9d4 На всякий случай повторю: это исправление не для описанного в первом сообщении изъяна, а лишь связанного с ним.
h := Abs(dx); v := Abs(dy); while (h > 0) and (v > 0) do begin if movex() then Dec(h) else h := 0; if movey() then Dec(v) else v := 0; end; while (h > 0) and movex() do Dec(h); while (v > 0) and movey() do Dec(v);
Чёрный Думер wrote:
Просто я вот наоборот считаю, что не стоит пытаться вытанцовывать парадигму на языке, не соответствующему ей в полной мере.
for надо воспринимать как цикл с фиксированным счётом. То что в него можно вставить бряк не значит что это хорошая идея, потому что это нарушает инвариант i = MaxAxis+1 после цикла. В данном случае этот инвариант тут вообще не нужен ни для чего и цикл можно объеденить с внешним.
Алсо, надо попробовать двигаться по алгоритму Брезенхэма, а после упирания в стенку - по отдельным осям как сейчас. А ещё лучше делать через трассировку в гриде, что бы не попуксельно прыгать, а сразу по тайлам.
Free Pascal always calculates the upper bound exactly once before initializing the counter variable with the initial value. It is not allowed to change (i. e. assign a value to) the value of a loop variable inside the loop.
А вообще жаль, что нет элементарной конструкции repeat N, как в GML.
DeaDDooMER wrote:
То что в него можно вставить бряк не значит что это хорошая идея, потому что это нарушает инвариант i = MaxAxis+1 после цикла.
А вот это уже неправда. Такого инварианта во FreePascal нет - и как раз именно потому, что в нём есть Break, Exit, Fail (sic!) и прочие конструкции, вышибающие порядок исполнения. Не говоря уже о том, что переменная счётчика может быть и глобальной. Смотрим документацию по предыдущей ссылке:
Quote:
The value of the loop variable is undefined after a loop has completed or if a loop is not executed at all. However, if the loop was terminated prematurely with an exception or a break or goto statement, the loop variable retains the value it had when the loop was exited.
After the for statement terminates (provided this was not forced by a Break or an Exit procedure), the value of counter is undefined.
Я же ведь недаром говорю, что не стоит писать на Pascal'е как на Oberon'е. При этом сугубо теоретически я согласен с твоим обоснованием, но это тот самый случай, когда язык побеждает парадигму. С этим ничего поделать нельзя, кроме как смириться.
DeaDDooMER wrote:
В данном случае этот инвариант тут вообще не нужен ни для чего и цикл можно объеденить с внешним.
Увы, нельзя - иначе игрок будет пролетать проёмы, см. выше. Другой вопрос, что нам может быть необходимо, чтобы он их пролетал. Поэтому я и попросил Ара и Джа подумать, как должно быть. Но с нынешним вариантом точно надо что-то делать, потому что он попросту непредсказуем ни для игрока, ни для картодела. А свой код я привёл лишь в качестве наглядного примера, чтобы чётко граничные случаи физики понимать. Он не претендует на оптимальность, к тому же при замерах встроенным xprofiler оказывается несколько медленнее нынешнего и твоего. Хотя тут ещё надо убедиться, что профилировщик не привирает - порой значения в нём начинает зашкаливать, и я не понимаю, по какой причине.
Вообще, неплохо было бы посмотреть поведение в 1.30 и 1.40 для справки. Ар или Джа - это тоже к вам просьба.
DeaDDooMER wrote:
Алсо, надо попробовать двигаться по алгоритму Брезенхэма, а после упирания в стенку - по отдельным осям как сейчас.
Joined: 17 Oct 2009, 19:57 Posts: 4102 Location: Киров
Чёрный Думер wrote:
Вообще, неплохо было бы посмотреть поведение в 1.30 и 1.40 для справки. Ар или Джа - это тоже к вам просьба.
В оригинале с аэроконтролем туго обстоят дела. Тот же случай с пролётом дыры с зелёным ключом, на уровне с тюрьмой и соплемётом в оригинальной кампании. В дф этот ключ брался без проблем при тупом падении вниз с поворотом. Из-за этого в 1.666 и были внесены исправления в этом месте. Поэтому ориентироваться на неповоротливую физику д2д явно не стоит. В дф она уже изначально делалась другой. И делать сейчас как в д2д – портить старые карты под дф. Другое дело, что сейчас возникают вот такие неприятные вещи, как пример из первого поста: просто падая, игрок не может завернуть; а вот подпрыгнув и падая (то есть высота падения увеличится ~56px) – заворот вдруг становится возможен. То есть такие вещи даже в голове не просчитываются у игрока. И это плохо.
_________________ Давай, картечью демонов Размажем по стене. Давай, берсерком выпустим Весь ливер сатане! Сделайте нормальный огнемёт!
Users browsing this forum: No registered users and 0 guests
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot post attachments in this forum