Вы не вошли.
Страницы 1
Здравствуйте. Как что бы объект ходил по сфере.
И по кубу по всем сторонам, а не только с верху?
Offline
В общем случае нужно получить нормаль поверхности под ногами персонажа и присвоить ее вектору Up:
ux = ObjectGetAbsoluteUp(character, 0);
uy = ObjectGetAbsoluteUp(character, 1);
uz = ObjectGetAbsoluteUp(character, 2);
if (ObjectRaycast(characterPointerDown, planet))
{
ux = ObjectGetCollisionNormal(0);
uy = ObjectGetCollisionNormal(1);
uz = ObjectGetCollisionNormal(2);
}
ObjectSetAbsoluteUp(character, ux, uy, uz);
characterPointerDown в данном случае - это даммикуб, привязанный к персонажу и повернутый вниз на 90 градусов:
characterPointerDown = DummycubeCreate(character);
ObjectPitch(characterPointerDown, -90);
Для сферы это будет работать хорошо, как и для любых гладких тел, но вот с кубами сложнее. Самое простое, думаю, использовать куб со скругленными краями (чем больше радиус скругления, тем лучше).
Также в случае с кубами скорее всего нужно будет как-то смягчать резкие переходы нормали, тут все зависит от того, как реализуется механика движения. Если есть гравитация, и все построено на физике, то возможно и не надо будет смягчать.
Offline
Спасибо получилось со сферой. Но высоту от центра не соблюдает. А на кубе не переходит на другие стороны.
И возник вопрос, Вы говорите:
...
Если есть гравитация, и все построено на физике, то возможно и не надо будет смягчать.
То есть можно указать центр гравитации сам объект, а точнее его центр?
Редактировался Nic (2020-06-02 14:59:04)
Offline
Чтобы высота соблюдалась, как раз и должна быть гравитация)
То есть можно указать центр гравитации сам объект, а точнее его центр?
В принципе да. Но в случае с кубами, видимо, гравитация должна быть противоположна нормали, иначе вблизи от краев персонаж будет вести себя как на склоне, т.е. скатываться вниз (из-за того, что вектор к центру и ориентация поверхности не совпадают). Полагаю, надо как-то хитро вычислять или переключать вектор гравитации в зависимости от типа поверхности.
Ну и конечно, тут не обойтись без физического движка, так как надо обрабатывать столкновения с планетой. ODE самый простой вариант, хотя теоретически можно сделать на любом. Правда, я сам такую механику не пробовал делать.
Offline
Кстати, вспомнил, что была статья на тему механики как в Mario Galaxy: https://www.gamasutra.com/view/feature/ … mario_.php
Там даже демка есть для Blitz, можно посмотреть, как в ней сделано для несферических планет.
Offline
Сделал пример с физикой: http://xtreme3d.ru/files/examples/planet_gravity.zip
Заметно, что переходы слишком резкие без смягчения нормалей, как я и ожидал.
Offline
Сделал пример с физикой: http://xtreme3d.ru/files/examples/planet_gravity.zip
Заметно, что переходы слишком резкие без смягчения нормалей, как я и ожидал.
Спасибо за полный пример, сразу отпало много вопросов. Вы мне даже настроение подняли на полдня.
Возник вопрос в основном с манекенами, почему их три.
character - сам персонаж
character_caster - ноги персонажа для определения низа
character_model - ??? что-то персонажа
Я взял смелость прокомментировать Ваш код у себя в платформе разработки DevelNext, и прошу поправить меня где я не совсем правильно это сделал или вообще пропустил так как не понял что там происходить. Особенно это связано с вычислениями.
#<!--Получаем размер окна-->
$screen = UXScreen::getPrimary();#Получаем парамметры данного окна
$screenW = $screen->bounds['width'];#Получаем длину окна
$screenH = $screen->bounds['height'];#Получаем высоту окна
$x = new xtreme3d; #Инцилизация обекта движка
$x->EngineCreate();#Запуск 3D-движка
$matlib = $x->MaterialLibraryCreate();#Создание библиотеки матерялов
$x->MaterialLibraryActivate($matlib);#Активация библиотеки матерялов
#<!--Окно-->
$handle = $x->FindWindow($this->title);#Находим данное окно где будет вид игры
$view1 = $x->ViewerCreate(0, 0, $this->width, $this->height, $handle);#создаем первый вид, в котором будет отображаться пространство в 3D;
$x->ViewerSetBackgroundColor($view1, $x->MakeColorRGB(127,127,127));#выбираем цвет фона для вида
$x->ViewerSetLighting($view1, true);#включаем освещение
$x->ViewerEnableFog($view1, false);#Включить туман для вида
$x->ViewerSetFogColor($view1, $x->MakeColorRGB(127,127,127));#цвет тумана
$x->ViewerSetFogDistance($view1, 8, 50);#дальность тумана мини, макс
$x->ViewerSetAntiAliasing($view1, 0); #Сглаживание
#<!--Окно-->
$back = $x->DummycubeCreate(0);#Задний небесный купол
$scene = $x->DummycubeCreate(0);#Сцена
$front = $x->DummycubeCreate(0);#Передний текстовой план
#<!--Источника света-->
$light = $x->LightCreate('lsOmni', $scene);#Создаем источник света
$x->LightSetAmbientColor($light, $x->MakeColorRGB(127,127,127), 1.0);#Назвачаем цвеи тени
$x->LightSetDiffuseColor($light, $x->MakeColorRGB(255,255,255), 1.0);#Назначаем цвет материала
$x->LightSetSpecularColor($light, $x->MakeColorRGB(255,255,255), 1.0);#Назначаем цвет блика
$x->ObjectSetPosition($light, 10, 10, 10);#Даем координаты позиции
#<!--/Создание источника света-->
$x->OdeManagerCreate();#Создание движка физики
$x->OdeManagerSetGravity(0, 0, 0);#Установка сили и направления гравитации
$planetoid = $x->FreeformCreate("planetoid.obj", $matlib, $matlib, $scene);#Добавляем объект из файла (планетоид)
$x->FreeformBuildOctree($planetoid);#Строит октарное дерево для объекта свободной формы для проверки столкновений
$x->MaterialCreate("planetoid", "");#Созжаем материал
$x->MaterialSetAmbientColor("planetoid", $x->MakeColorRGB(0,255,0), 1.0);#Назвачаем цвеи бликака
$x->MaterialSetDiffuseColor("planetoid", $x->MakeColorRGB(0,255,0), 1.0);#Назначаем цвет материала
$x->ObjectSetMaterial($planetoid, "planetoid");#Назначаем материал на объект
$x->OdeStaticCreate($planetoid);#говорим физдвижку что планетоид статический объект (т.е. на планетоид)
for ($i = 0; $i < $x->FreeformMeshObjectsCount($planetoid); $i = $i + 1)#Считаем меши объекта для добавление телу полигональной сетки
{
$x->OdeAddTriMesh($planetoid, $i);#Добавляет телу геометрию полигональной сетки (меш)
}
$character = $x->DummycubeCreate($scene);#Создаем манекен для кубика (персонаж)
$x->ObjectSetPosition($character, 0, 1.5, 0);#Даем координат
$x->OdeDynamicCreate($character);#говорим физдвижку что кубил динамический объект
$x->OdeAddSphere($character, 0, 0, 0, 0.2);#назначаем коллайдер виде сферы на кубик
$x->OdeSurfaceSetMu($character, 0);#Устанавливает коэффициент трения
$x->OdeSurfaceSetMu2($character, 0);#Устанавливает μ2 - необязательный коэффициент трения для второго направления трения поверхности тела.
$character_model = $x->CubeCreate(0.4, 0.4, 0.4, $scene);#Создаем куб
$x->MaterialCreate("character", "");#Созжаем материал
$x->MaterialSetAmbientColor("character", $x->MakeColorRGB(255,0,0), 1.0);#Назвачаем цвеи бликака
$x->MaterialSetDiffuseColor("character", $x->MakeColorRGB(255,0,0), 1.0);#Назначаем цвет материала
$x->ObjectSetMaterial($character_model, "character");#Назначаем материал на объект (т.е. на куб)
$character_caster = $x->DummycubeCreate($character_model);#Манекен для определения низа у персонажа
$x->ObjectPitch($character_caster, -90);#Поворачиваем его
$camPos = $x->DummycubeCreate($character_model);#Создаем манекен для позиции камеры
$x->ObjectSetPosition($camPos,0,0,0);#Определяем его локальные коордмнаты
$camera = $x->CameraCreate($camPos);#Создаем камеру
$x->ObjectPitch($camera, -20);#Поворачиваем камеру чуть вниз
$x->ObjectSetPosition($camPos, 0, 1, 2);#Определяем её координаты как от 3-го лица
$x->CameraSetViewDepth($camera, 500);#Задам дальность обзора камеры
$x->CameraSetFocal($camera, 60);#Задаем угол зрения камеры
$x->CameraSetNearPlaneBias($camera, 0.2);#Задаем коэффициент ближней плоскости отсечения камеры.
$x->ViewerSetCamera($view1, $camera);#Определяем камеру, которую должен использовать вид для отрисовки проекции на сцену.
$mx = ($screenW) / 2;#Высчитываем центр окноа по горизонту
$my = ($screenH) / 2;#Высчитываем центр окноа по вертикали
$x->MouseSetPosition($mx, $my);#Устанавливаем курсор в эту точку
#Устанавливаем таймер-цикл с параметрами которые буду изменяться в цикле
$this->timer = new UXAnimationTimer(function() use (
$x, $zm, $view1, $camPos, $mx, $my, $camera, $light, $character, $planetoid, $character_caster, $character_model){
$x->OdeDynamicSetRotationQuaternion($character, 0, 0, 0, 1);#Задает поворот динамического тела, выраженный кватернионом
#Получаем абсолутный вектор направление модели персонажа
$dirX = $x->ObjectGetAbsoluteDirection($character_model, 0);#X
$dirY = $x->ObjectGetAbsoluteDirection($character_model, 1);#Y
$dirZ = $x->ObjectGetAbsoluteDirection($character_model, 2);#Z
#Получаем абсолутный вектор "права" направление персонажа
$rightX = $x->ObjectGetAbsoluteRight($character_model, 0);#X
$rightY = $x->ObjectGetAbsoluteRight($character_model, 1);#Y
$rightZ = $x->ObjectGetAbsoluteRight($character_model, 2);#Z
#Высчитываем
$movX = $dirX * 2;
$movY = $dirY * 2;
$movZ = $dirZ * 2;
#Высчитываем
$strX = $rightX * 2;
$strY = $rightY * 2;
$strZ = $rightZ * 2;
#Сбрасываем переменные для правильного указания скорость объекта (Персонажа)
$vx = 0;
$vy = 0;
$vz = 0;
#Указываем скорость направления при нужно нажатой клавишой
if ($x->KeyIsPressed(38) || $x->KeyIsPressed(87)) # Клавиша верх или "W"
{
$vx = $vx + $movX;
$vy = $vy + $movY;
$vz = $vz + $movZ;
}
else if ($x->KeyIsPressed(40) || $x->KeyIsPressed(83)) # Клавиша вниз или "S"
{
$vx = $vx - $movX;
$vy = $vy - $movY;
$vz = $vz - $movZ;
}
if ($x->KeyIsPressed(39) || $x->KeyIsPressed(68))#Клавиша вправо или "D"
{
$vx = $vx + $strX;
$vy = $vy + $strY;
$vz = $vz + $strZ;
}
else if ($x->KeyIsPressed(37) || $x->KeyIsPressed(65))#Клавиша влево или "A"
{
$vx = $vx - $strX;
$vy = $vy - $strY;
$vz = $vz - $strZ;
}
$x->OdeDynamicSetVelocity($character, $vx, $vy, $vz);#Назначение скорости объекта в нужном направлении (персонажа)
$x->ObjectPointToObject($character_caster, $planetoid);#Направлять объект в сторону дургого объекта (Ноги персонажа к планетоиду)
#Получаем абсолутный вектор "верха" персонажа
$ux = $x->ObjectGetAbsoluteUp($character, 0);#X
$uy = $x->ObjectGetAbsoluteUp($character, 1);#Y
$uz = $x->ObjectGetAbsoluteUp($character, 2);#Z
#Если есть пересечения (соприкосновения) Ног персонажа с планетоидом
if ($x->ObjectRaycast($character_caster, $planetoid))
{
#То получить координаты нормали полигона последнего пересечения
$ux = $x->ObjectGetCollisionNormal(0);#X
$uy = $x->ObjectGetCollisionNormal(1);#Y
$uz = $x->ObjectGetCollisionNormal(2);#Z
}
$x->ObjectSetPositionOfObject($character_model, $character);#Синхронизируем абсолютные координаты объекта 1(Модели персонажа) с абсолютными координатами объекта 2(персонажем).
$x->ObjectSetAbsoluteUp($character_model, $ux, $uy, $uz);#Назначить абсолютный единичный вектор Up объекта(Модели персонажа)
$x->OdeDynamicAddForce($character, -$ux * 15, -$uy * 15, -$uz * 15);#Прикладывает силу в абсолютных координатах к центру массы динамического тела(персонажа с 15).
$x->OdeManagerStep(0.01);#вычисления новых состояний тел ODE.
$x->Update(0.01);#Обновление объектов сцены
$x->ViewerRender($view1);#отрисовка указаного вида (экрана)
});#конец цикла-таймера
$this->timer->start(); // запускаем таймер
}
Ну и собственно Ваш перенесенный проект в DevelNext. Скачать>>>
Я это сделал в основном для себя что бы лучше понять Ваш код. И очень Вас прошу дописать, мною пропушенные, комментарии в коде.
Заранее благодарен.
Редактировался Nic (2020-06-05 08:35:45)
Offline
Разделение на character и character_model потому что физическую модель (character) нельзя использовать для поворотов, она используется только для вычисления позиции. Поворот персонажа (ObjectTurn) надо задавать через графическую модель (character_model), которая привязана к позиции character, но вращается независимо. К сожалению, в X3D сильно ограничена обратная связь между объектами и их ODE-телами - объект с динамическим телом контролируется только через ODE и начисто игнорирует ObjectTurn и пр. В противном случае можно было бы упростить механику и обойтись только одним объектом.
Конкретно в примере поворот не используется, но его легко добавить.
И очень Вас прошу дописать, мною пропушенные, комментарии в коде.
Код почитал, комменты вроде везде правильные. По поводу этой части:
#Высчитываем
$movX = $dirX * 2;
$movY = $dirY * 2;
$movZ = $dirZ * 2;
#Высчитываем
$strX = $rightX * 2;
$strY = $rightY * 2;
$strZ = $rightZ * 2;
Это просто компоненты скорости в двух перпендикулярных направлениях, которые в зависимости от нажатой клавиши добавляются или отнимаются от vx, vy, vz. Вместо 2 можно поставить любой желаемый модуль скорости.
Offline
Страницы 1