Universal Tween Engine / LibGDX: модели с потерей трансформации и вращения между подростками в последовательности
Я работаю над трехмерной игрой и пытаюсь правильно настроить движение персонажа. Взаимодействие простое, на экране есть одна модель, пользователь щелкает точку на экране, модель поворачивается лицом к точке, по которой щелкнули (сейчас модель - это маленький призрак с глазами, поэтому глаза поворачиваются лицом к нажатой точка), и, столкнувшись с точкой, модель переместится туда.
Я работал с каким-то кодом, который написал сам, но анимация не была плавной, и модель часто выходила за пределы / не соответствовала пункту назначения, а затем дергалась назад или вперед на место. Раньше я работал только с движком Universal Tween над 2D-анимацией, но решил попробовать. Это решило проблемы, связанные с рывками, и вращение и трансформация работают отлично, но я не могу заставить их работать вместе в определенной последовательности.
Если я включаю только код, который вращает модель, он работает нормально (ну, почти нормально, что-то становится странным с определением вращения по часовой стрелке или против часовой стрелки под определенными углами, но это еще одна проблема позже): я нажимаю на местоположение, модель поворачивается лицом к нему и сохраняет новое вращение до следующего щелчка, затем поворачивается лицом к этому месту. То же самое с включенным только переводом: я щелкаю точку, модель перемещается к ней и сохраняет свой новый перевод, пока я не нажму снова, затем она перемещается в это место и т. Д.
Когда оба перевода и поворота включены, я сталкиваюсь с проблемами. Модель сначала будет вращаться, чтобы повернуться лицом к нажатой точке, затем вернуться к исходному вращению, а затем переместиться к нажатой точке. При последующих щелчках модель сначала возвращается к своему первоначальному переводу, затем поворачивается в направлении точки нажатия (на самом деле это местоположение точки нажатия по отношению к повторно инициализированному положению), а затем перемещается к точке нажатия.
Я попытался переставить код, используя обратные вызовы, кэшировать новый перевод и ротацию для членов класса игровой модели, а затем с помощью set()
с их ценностями до начала подростка, но ничего не получалось.
Вот соответствующий код из моего TweenAccessor
:
public class GameModelTweenAccessor implements TweenAccessor<DynamicModel> {
public int getValues(DynamicModel target, int tweenType, float[] returnValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
returnValues[0] = trans.x;
returnValues[1] = trans.y;
returnValues[2] = trans.z;
return 3;
case ROTATION:
axisVec = new Vector3();
angle = target.model.transform.getRotation(new Quaternion()).getAxisAngle(axisVec) * axisVec.nor().y;
returnValues[0] = angle;
return 1;
...
}
}
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setToTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
target.model.transform.setToRotation(Vector3.Y, newValues[0]);
break;
...
}
}
}
А вот код, который запускает временную шкалу и анимацию (она находится в этом файле):
Timeline.createSequence()
.push(Tween.to(screen.ghost, GameModelTweenAccessor.ROTATION, Math.abs(angle - newRotation) / 200)
.target(newRotation)
.ease(TweenEquations.easeNone))
.push(Tween.to(screen.ghost, GameModelTweenAccessor.POSITION_XYZ, duration).
target(intersection.x, intersection.y, intersection.z)
.ease(TweenEquations.easeNone))
.start(screen.ghostManager);
Кто-нибудь может указать мне на решение?
РЕДАКТИРОВАТЬ: кажется, что преобразования, устанавливаемые по завершении каждой анимации, сбрасываются в 0 после завершения другой анимации. Это журналы этих значений из getValues
а также setValues
функции в TweenAccessor
:
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Get Trans: x: 0.0 y: 0.0 z: 0.0
Get Rot: 0.0
Set Rot: 9.598349
Set Rot: 9.814415
Set Rot: 10.052449
...
Set Rot: 39.99417
Set Rot: 43.397423
Set Rot: 46.62333
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.0012489144 y: 0.0 z: 0.001180494
Set Trans: x: 0.024489587 y: 0.0 z: 0.023147952
Set Trans: x: 0.04921494 y: 0.0 z: 0.04651875
...
Set Trans: x: 6.4197707 y: 0.0 z: 6.06807
Set Trans: x: 6.444479 y: 0.0 z: 6.091425
Set Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Get Trans: x: 6.453598 y: 0.0 z: 6.1000443
Get Rot: 0.0
Set Rot: 3.4318955
Set Rot: 6.795984
Set Rot: 10.0074415
...
Set Rot: 156.79567
Set Rot: 159.99591
Set Rot: 162.38742
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.03550978 y: 3.836017E-8 z: 0.021066409
Set Trans: x: 0.15527377 y: 1.6773768E-7 z: 0.092117175
...
Set Trans: x: 6.848614 y: 7.3983565E-6 z: 4.062985
Set Trans: x: 6.961268 y: 7.5200533E-6 z: 4.129818
Set Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Get Trans: x: 7.0624847 y: 7.6293945E-6 z: 4.189865
Get Rot: 0.0
Set Rot: -3.2620814
Set Rot: -6.8205137
Set Rot: -9.834579
...
Set Rot: -76.57533
Set Rot: -79.91388
Set Rot: -80.610855
Get Trans: x: 0.0 y: 0.0 z: 0.0
Set Trans: x: 0.01940876 y: 0.0 z: 0.033669088
Set Trans: x: 0.04174851 y: 0.0 z: 0.07242267
Set Trans: x: 0.06332677 y: 0.0 z: 0.109855264
...
Set Trans: x: 2.7853239 y: 0.0 z: 4.8318033
Set Trans: x: 2.808029 y: 0.0 z: 4.8711905
Set Trans: x: 2.827034 y: 0.0 z: 4.9041595
getValues
вызывается дважды для каждого типа анимации (перевод и вращение) до начала анимации. На этом этапе значения для перевода остаются такими же, какими они были установлены в конце последней анимации перевода. Значение для поворота было установлено обратно в 0 (хотя я предполагаю, что в результате следующей группы вызовов setValues
для вращения).
setValues
затем вызывается несколько раз для вращения, пока не достигнет цели вращения. Это (кроме сброса вращения) работает так, как я ожидал и надеюсь. После getRotation
хотя звонки перевод переводится обратно в 0,0,0, но не путем вызова setValues
(нет записи об этом). Затем перевод анимируется к цели с несколькими setValues
звонки.
После этого цикл перезапускается по другому клику и getValues
вызывается дважды для ротации и перевода. Перевод имеет значения, которые были установлены при последнем вызове setValues
напоследок Timeline
, но только до тех пор, пока вращение (вернувшееся в 0) не пройдет через анимацию движения.
4 звонка getValues
может быть что-то. Я не уверен, что в моей настройке вызовет Timeline
будет захватывать значения перевода до того, как начнется его анимация, если предполагается, что они будут выполняться последовательно, когда они помещаются в Timeline
, Я также не уверен, почему getValues
вызывается дважды для каждой анимации, но не исключено, что Tween Engine будет вызывать его дважды в течение анимации.
Я почти уверен, что функции перевода / вращения из LibGDX, которые я использую, хороши, они "прилипают" к моделям, и я использовал то же самое для перевода, что и с моей версией без Tween-Engine. Вращение похоже, но функция не та.
Изменение значений Y во время второго перевода - это то, на что стоит обратить внимание.
1 ответ
Таким образом, решение не имело ничего общего с Universal Tween Engine или тем, как оно было реализовано. Это было только мое плохое понимание функций матричного преобразования в LibGDX. Призыв к setToRotation
сбрасывает матрицу преобразования, а затем просто применяет вращение, таким образом обнуляя предыдущий перевод. setToTranslation
делает то же самое, что вызвало потерю вращения.
Для перевода я использовал setTranslation
вместо этого, и вращение не теряется. Я не думаю, что есть сопоставимая функция для вращения (используя rotate
вызывает все виды проблем), поэтому вместо этого я получаю новый Vector3
с сохранением в нем положения модели, затем примените setToRotation
, затем установите перевод на новый вектор. Вот обновленный код из TweenAccessor
:
public void setValues(DynamicModel target, int tweenType, float[] newValues) {
trans = target.model.transform.getTranslation(trans);
switch (tweenType) {
...
case POSITION_XYZ:
target.model.transform.setTranslation(newValues[0], newValues[1], newValues[2]);
break;
case ROTATION:
// store the position
Vector3 position = target.model.transform.getTranslation(new Vector3());
// then set the rotation and reset the translation
target.model.transform.setToRotation(Vector3.Y, newValues[0]).setTranslation(position);
break;
default:
assert false;
break;
}
}