Храните камеру libgdx внутри границ при панорамировании и масштабировании
Я разрабатываю игру для Android с помощью LibGDX. Я добавил масштабирование и панорамирование. Моя проблема заключается в том, как избежать выхода из игровой площадки. Как бы то ни было, вы можете панорамировать вне игровой зоны в черноту. Когда я полностью вычеркнул, я знаю, как с этим бороться, я только что сказал:
if(camera.zoom == 1.0f) ;
else {
}
Но, если увеличить масштаб, как это сделать. Я знаю, что это не так сложно, я просто не могу понять. После создания я установил камеру в середину экрана. Я знаю, как кастрюлю, я использую camera.translate(-input.deltaX, -input.deltaY, 0), мне просто нужно проверить перед этим вызовом, чтобы увидеть, находится ли позиция вне игровой зоны. Когда я увеличен, как я могу проверить, находится ли я на краю экрана?
Ответы
Ответ 1
Вы можете использовать один из
camera.frustum.boundsInFrustum(BoundingBox box)
camera.frustum.pointInFrustum(Vector3 point)
camera.frustum.sphereInFrustum(Vector3 point, float radius)
чтобы проверить, находится ли точка/коробка/сфера в пределах вашей камеры.
То, что я обычно делаю, - это определить 4 коробки вокруг моего мира, где игроку не разрешается видеть. Если камера перемещена и одно из ячеек находится в усеченном состоянии, я верну камеру назад в предыдущее положение.
Изменить: AAvering внедрил это в коде ниже.
Ответ 2
Здесь код, который я вызываю после положения камеры, обновляется из-за панорамирования или масштабирования в моей 2D-игре с использованием орфографической камеры. Он корректирует положение камеры, чтобы он не показывал ничего за пределами области воспроизведения.
float camX = camera.position.x;
float camY = camera.position.y;
Vector2 camMin = new Vector2(camera.viewportWidth, camera.viewportHeight);
camMin.scl(camera.zoom/2); //bring to center and scale by the zoom level
Vector2 camMax = new Vector2(borderWidth, borderHeight);
camMax.sub(camMin); //bring to center
//keep camera within borders
camX = Math.min(camMax.x, Math.max(camX, camMin.x));
camY = Math.min(camMax.y, Math.max(camY, camMin.y));
camera.position.set(camX, camY, camera.position.z);
camMin
- самый низкий левый угол, который камера может не показывать ничего за пределами области воспроизведения, а также смещение от угла камеры к центру.
camMax
- это самый высокий правый угол, в котором может находиться камера.
Ключевая часть, которую я предполагаю, что вам не хватает, - это масштабирование размера камеры с помощью уровня масштабирования.
Ответ 3
Кредит идет к Matsemann за идею, вот реализация, которую я использовал.
Создайте собственный класс MyCamera, расширяющий OrthographicCamera, и добавьте следующий код:
BoundingBox left, right, top, bottom = null;
public void setWorldBounds(int left, int bottom, int width, int height) {
int top = bottom + height;
int right = left + width;
this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
}
Vector3 lastPosition = new Vector3();
@Override
public void translate(float x, float y) {
lastPosition.set(position.x, position.y, 0);
super.translate(x, y);
}
public void translateSafe(float x, float y) {
translate(x, y);
update();
ensureBounds();
update();
}
public void ensureBounds() {
if (frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom)) {
position.set(lastPosition);
}
}
Теперь, в вашем обычном sceene или в каком бы режиме вы ни использовали (в моем случае это был пользовательский класс Board):
camera.setWorldBounds()
и в вашем методе GestureListener.pan вы можете вызвать
camera.translateSafe(x, y);
он должен держать вашу камеру в границах
Ответ 4
Здесь мое решение:
float minCameraX = camera.zoom * (camera.viewportWidth / 2);
float maxCameraX = worldSize.x - minCameraX;
float minCameraY = camera.zoom * (camera.viewportHeight / 2);
float maxCameraY = worldSize.y - minCameraY;
camera.position.set(Math.min(maxCameraX, Math.max(targetX, minCameraX)),
Math.min(maxCameraY, Math.max(targetY, minCameraY)),
0);
Где:
-
targetX
и targetY
- это мировые координаты, где находится ваша цель.
-
worldSize
является Vector2
размера мира.
Ответ 5
У меня недостаточно репутации для написания комментариев, поэтому я укажу на некоторые предыдущие ответы.
Решение AAverin с ограничивающей рамкой, созданное с идеей Мацемана, не очень хорошо, потому что оно досадно замедляется, когда вы находитесь рядом с одним краем (границей) и пытаетесь перевести по диагонали, и в этом случае вы панорамируете в одну сторону за пределы и другие в правильном направлении.
Я настоятельно рекомендую вам попробовать решение из нижней части метода handleInput, представленного в
https://github.com/libgdx/libgdx/wiki/Orthographic-camera
Это работает плавно, и некоторые из предыдущих ответов выглядят так, но этот использует MathUtils.clamp wihch - прямолинейный и намного более чистый.
Ответ 6
Указанный класс CustomCamera работает не очень хорошо. Я использовал его, чтобы отобразить жест щепотки в zoomSafe, и камера будет постоянно отскакивать/мигать слева направо, когда она находится на краю границ. Камера также не работает должным образом с панорамированием. Если вы попытаетесь переместиться по краю границ, он не будет качаться в любом месте, как если бы края были "липкими". Это происходит потому, что он просто переводится в последнюю позицию вместо того, чтобы просто регулировать координату за пределами границ.
Ответ 7
Идеальный класс для этого (частично благодаря AAverin)
Этот класс не только прилипает к границам, он также привязывается к границам при масштабировании.
Вызовите их для настройки границ и перемещения камеры.
camera.setWorldBounds()
camera.translateSafe(x, y);
При масштабировании вызова
camera.attemptZoom();
И вот класс:
public class CustomCamera extends OrthographicCamera
{
public CustomCamera() {}
public CustomCamera(float viewportWidth, float viewportHeight)
{
super(viewportWidth, viewportHeight);
}
BoundingBox left, right, top, bottom = null;
public void setWorldBounds(int left, int bottom, int width, int height) {
int top = bottom + height;
int right = left + width;
this.left = new BoundingBox(new Vector3(left - 2, 0, 0), new Vector3(left -1, top, 0));
this.right = new BoundingBox(new Vector3(right + 1, 0, 0), new Vector3(right + 2, top, 0));
this.top = new BoundingBox(new Vector3(0, top + 1, 0), new Vector3(right, top + 2, 0));
this.bottom = new BoundingBox(new Vector3(0, bottom - 1, 0), new Vector3(right, bottom - 2, 0));
}
Vector3 lastPosition;
@Override
public void translate(float x, float y) {
lastPosition = new Vector3(position);
super.translate(x, y);
}
public void translateSafe(float x, float y) {
translate(x, y);
update();
ensureBounds();
update();
}
public void ensureBounds()
{
if(isInsideBounds())
{
position.set(lastPosition);
}
}
private boolean isInsideBounds()
{
if(frustum.boundsInFrustum(left) || frustum.boundsInFrustum(right) || frustum.boundsInFrustum(top) || frustum.boundsInFrustum(bottom))
{
return true;
}
return false;
}
public void attemptZoom(float newZoom)
{
this.zoom = newZoom;
this.snapCameraInView();
}
private void snapCameraInView()
{
float halfOfCurrentViewportWidth = ((viewportWidth * zoom) / 2f);
float halfOfCurrentViewportHeight = ((viewportHeight * zoom) / 2f);
//Check the vertical camera.
if(position.x - halfOfCurrentViewportWidth < 0f) //Is going off the left side.
{
//Snap back.
float amountGoneOver = position.x - halfOfCurrentViewportWidth;
position.x += Math.abs(amountGoneOver);
}
else if(position.x + halfOfCurrentViewportWidth > viewportWidth)
{
//Snap back.
float amountGoneOver = (viewportWidth - (position.x + halfOfCurrentViewportWidth));
position.x -= Math.abs(amountGoneOver);
}
//Check the horizontal camera.
if(position.y + halfOfCurrentViewportHeight > viewportHeight)
{
float amountGoneOver = (position.y + halfOfCurrentViewportHeight) - viewportHeight;
position.y -= Math.abs(amountGoneOver);
}
else if(position.y - halfOfCurrentViewportHeight < 0f)
{
float amountGoneOver = (position.y - halfOfCurrentViewportHeight);
position.y += Math.abs(amountGoneOver);
}
}
}