Camera 如何在 Libgdx 和 Viewport 中工作
如果您使用 LibGdx,您很快就会来到相机和视口.如果您第一次使用相机和视口,您会遇到一些关于它如何工作以及如何使用它的问题.所以:
- 如何在 LibGdx 中使用相机?视口的宽度和高度是多少?
- 什么是视口,如何使用它以及它如何与相机配合使用?
如何在 LibGdx 中使用相机?什么是视口宽度和高度?
首先,重要的是您要知道相机使用的是世界单位而不是像素.世界单位不是常规单位.您可以自己定义一个世界单位是多少.以后再说吧.
首先,我们创建一个OrthographicCamera
、一个SpriteBatch
和一个Texture
:
private OrthographicCamera 相机;私有 SpriteBatch 批处理;私人纹理img;@覆盖公共无效创建(){//我们创建一个OrthographicCamera,通过它我们可以看到50x50的世界单位相机 = 新的 OrthographicCamera(50,50);批处理 = 新 SpriteBatch();img = new Texture("badlogic.jpg");}
我们创建一个 OrthographicCamera
并在构造函数中定义如果我们通过这个相机观察我们的世界,我们会看到多少个世界单位.在我们的示例中,50 x 50 世界单位是视口宽度和高度.所以我们创建了一个视口宽度和高度为 50 的相机.
在 render() 方法中我们渲染我们的图像:
@Override公共无效渲染(){//清屏(1)Gdx.gl.glClearColor(1, 1, 1, 1);Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);//设置SpriteBatch的ProjectionMatrix(2)batch.setProjectionMatrix(camera.combined);批处理.开始();//在位置 0, 0 上绘制图像,宽度为 25,高度为 25 (3)batch.draw(img, 0, 0, 25, 25);批处理.end();}
(1) 清除屏幕,如果我们不这样做,每个纹理都会覆盖另一个,如果我们绘制动画,我们将看到旧的帧.
(2) batch
是我们的抽屉,他绘制我们的图像、动画等.默认情况下,他绘制了一个世界,其中有很多世界单位,例如屏幕有像素,所以在这种情况下 1 个世界单位 =1 像素.但现在我们将看到 50 x 50 世界单位,不管屏幕有多大.要说 Batch 应该画出我们通过相机看到的东西,我们必须调用:batch.setProjectionMatrix(camera.combined);
(3) 现在我们在位置 0,0 上绘制 img
但是 0, 0 并不意味着在 Pixel 位置 0,0 上,这意味着图像将在 World 位置 0,0 上绘制宽度和高度也不是像素,而是世界单位,因此 img
将在位置 0,0 25x25 世界单位大上绘制.因此,在 50x50 的视口上,图像占据了整个屏幕的四分之一.
图像完全按预期填充了整个屏幕的四分之一.但是为什么它在右上角而不是在左下角呢?
问题是相机点的中心在位置 0,0
所以我们的图像被绘制在位置 0,0 他填充右上角.我们必须设置相机的位置,使 0,0 在左下角:
camera = new OrthographicCamera(50,50);camera.position.set(camera.viewportWidth/2, camera.viewportHeight/2, 0);
在 render() 方法中,我们必须添加 camera.update()
,因为每次我们更改相机的位置或比例或其他内容时,我们都必须更新相机.
现在图像在左下角.
像素在哪里?
我们总是谈论世界单位,但像素在哪里?像素还在.如果我们的屏幕尺寸为 200 x 200 像素,则批处理将始终绘制 200 x 200 像素.使用方法 batch.setProjectionMatrix(camera.combined);
我们只说批次有多少世界单位是一个像素.
如果我们有一个 200 x 200 像素的屏幕并且我们创建一个具有 50 x 50 世界单位的视口的相机,那么 SpriteBatch 知道 1 WorldUnit = 4 像素.现在我们绘制一个 25 x 25 World Units 大的图像,SpriteBatch 知道他必须绘制 25 * 4 = 100 像素大的图像.
所以像素仍然存在,但在世界单位中更容易思考.如果还不够清楚,这里有一点更详细的描述:
图像的比率为 1,因为 50(宽度)/50(高度)= 1,因此图像将始终具有相同的宽度和高度.侧面的条在我们的视口之外,将以您在此处定义的颜色绘制:Gdx.gl.glClearColor(1, 1, 1, 1);
扩展视口
也许我们不会在旁边设置栏,然后我们可以使用 ExtendViewport.ExtendViewport 通过在一个方向上扩展世界来保持世界纵横比没有条.意味着在宽高比较大的屏幕上,您会看到更多的世界.
在 400x200 纵横比 = (400/200 = 2) 的屏幕上,您将看到比在 300x200 (300/200 = 1.5) 的屏幕上看到的更多;
为了显示这一点,创建一个 ExtendViewport 并绘制大于视口的图像和第二个小图像:
camera = new OrthographicCamera(50, 50);camera.position.set(camera.viewportWidth/2, camera.viewportHeight/2, 0);viewport = new ExtendViewport(camera.viewportWidth, camera.viewportHeight, camera);//在 render() 方法中批处理.开始();batch.draw(img, 0, 0, 100, 50);batch.draw(img, -20, 0, 20, 20);批处理.end();
如果我们现在以 200x200 的屏幕尺寸启动我们的程序,我们会看到:
如果我们在 x 轴上调整屏幕的大小来使屏幕更宽:
现在我们可以看到第一张图片和第二张图片的更多信息,但比例始终相同.图像被拉伸只是因为我们将其绘制为 100x50,而不是因为调整大小.
如果您将了解更多信息、阅读并查看一些教程并阅读 LibGdx wiki,我希望这将解决一些关于相机和视口的问题:https://github.com/libgdx/libgdx/wiki
If you work with LibGdx it goes not long until you come to Camera and viewport. If you work the first time with camera and Viewport you get some questions about how it works and how to use it. So:
- How can I use a Camera in LibGdx? What's viewport width and height?
- What is a Viewport, how can I use it and how it works together with the Camera?
How can I use a Camera in LibGdx? What's viewport width and height?
Firstly it's important that you know the Camera works with World units not with Pixels. World units are not a regular Unit. You self can define how much one World Unit is. Later more.
First, we create an OrthographicCamera
a SpriteBatch
and a Texture
:
private OrthographicCamera camera;
private SpriteBatch batch;
private Texture img;
@Override
public void create () {
//We create a OrthographicCamera through which we see 50x50 World Units
camera = new OrthographicCamera(50,50);
batch = new SpriteBatch();
img = new Texture("badlogic.jpg");
}
We create a OrthographicCamera
and in the Constructor we define how many World Units we see if we look through this camera into our world. In our example 50 x 50 World Units these are the viewport width and height.
So we have created a Camera with a viewport width and height of 50.
In the render() method we render our image:
@Override
public void render () {
//Clear the screen (1)
Gdx.gl.glClearColor(1, 1, 1, 1);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
//Set ProjectionMatrix of SpriteBatch (2)
batch.setProjectionMatrix(camera.combined);
batch.begin();
//Draw image on position 0, 0 with width 25 and height 25 (3)
batch.draw(img, 0, 0, 25, 25);
batch.end();
}
(1) Clear the Screen, if we don't do that every Texture will draw over the other and if we draw a Animation we will see the old Frames.
(2) The batch
is our Drawer he draws our Images, Animations etc. Default he draws a World which has so many World Units like the Screen has Pixels so in this case 1 World Unit = 1 Pixel. But now we will see 50 x 50 World Units doesn't matter how big the screen is. To say the Batch that he should draw what we see through our camera we must call: batch.setProjectionMatrix(camera.combined);
(3) Now we draw our img
on Position 0,0 But 0, 0 doesn't mean on Pixel position 0,0 it means the Image will be drawn on World position 0,0 also width and height are not in Pixels they are in World units so the img
will be drawn on Position 0,0 25x25 World Units big. So on a 50x50 viewport, the image fills one-quarter of the whole screen.
The Image fill one-quarter of the whole screen exactly as expected. But why it is on the right top and not on the bottom left?
The Problem is that the center of the Camera point on the position 0,0
So our Image is drawn on position 0,0 he fills the top right corner. We must set the position of the camera so 0,0 is in the bottom left corner:
camera = new OrthographicCamera(50,50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
In the render() method we must add camera.update()
because every time we change the position or the scale or what else of the camera we must update the camera.
Now the Image is in the bottom left.
Where are the Pixels?
We always speak about World units but where are the Pixels? The pixels are still there. If we have a Screen size of 200 x 200 pixels the batch will always draw 200 x 200 pixels. With the method batch.setProjectionMatrix(camera.combined);
we only say the batch how much World Units are one Pixel.
If we have a Screen with 200 x 200 pixels and we create a Camera with a viewport of 50 x 50 world units the SpriteBatch know 1 WorldUnit = 4 Pixels. Now we draw a Image which is 25 x 25 World Units big the SpriteBatch knows he must draw the image 25 * 4 = 100 pixel big.
So the pixels still there but it's easier to think in World Units. If it's not clear enough here is a little bit more detailed description: Libgdx's World Units
Box2d
It's also very important to think in World Units if you use Box2d because Box2d works with Meters. So if you create a Body with a Force off 5 on the x axis, the Body is 5 m/s fast.
And now it's very cool to work with World Units because you can say 1 World Unit = 1 Meter so you can create a object with a width of 10 and you know after one second the Body will be in the Center of the Object. If you work with Pixels you will have a Problem if you have a different Screensize.
What is a Viewport, how can I use it and how it works together with the Camera?
Now we have the big Problem about different Screen sizes. Suddenly we have a Screen size of 350 x 200 pixels, now the Image will be stretched and don't look so nice as before.
For this Problem we use Viewports a few Viewports are StretchViewport
, FitViewport
and ExtendViewport
. All viewports you can find here: https://github.com/libgdx/libgdx/wiki/Viewports.
Firstly what is a Viewport.
Imagine the camera is a speaker who speaks English. Different Screen Sizes are other People who speak German, French, Chinese etc. and the Viewport is the translator. The Translator doesn't change the sense of that what the English Speaker says but he adapts it so the others can understand it. Same are camera and Viewport. Viewport doesn't say or change what you can see on your screen if you run the program. He only handles that you always see the same on different Screen sizes. A Camera can life without Viewport. A Viewport not without Camera.
Add a viewport Object:
private Viewport viewport;
and the resize() method:
@Override
public void resize (int width, int height) {
viewport.update(width, height);
}
StretchViewport
Create a StretchViewport:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new StretchViewport(camera.viewportWidth, camera.viewportHeight, camera);
In the StretchViewport Constructor, we define the viewport width and height and the Camera.
Now we get the same result as before if we have different Screensizes the Images will be stretched.
FitViewport
Maybe we won't stretch our Images we will matter about the ratio of x and y.
The ratio of x and y means: is an Object 2 width and 1 height he will always twice as wide as high for example 200x100, 30x15 but not 20x15.
Create a FitViewport:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new FitViewport(camera.viewportWidth, camera.viewportHeight, camera);
Now the Image will always be a square. To see the Bars on the Side lets draw the Image as big as our viewport:
batch.draw(img, 0, 0, 50, 50);
The Image has a Ratio of 1 because of 50(width)/50(height) = 1 so the Image will always have the same width and height. The Bars on the side are outside of our Viewport and will be drawn in the color you define here: Gdx.gl.glClearColor(1, 1, 1, 1);
ExtendViewport
Maybe we won't Bars on the Side then we can take a ExtendViewport. The ExtendViewport keeps the world aspect ratio without bars by extending the world in one direction. Means on a screen where the aspect ratio between width and height are bigger you will see more of the world.
On a screen 400x200 aspect ration = (400/200 = 2) you will see more than on a screen of 300x200 (300/200 = 1.5);
To show this create a ExtendViewport and draw the Image bigger than the viewport and a second small Image:
camera = new OrthographicCamera(50, 50);
camera.position.set(camera.viewportWidth / 2, camera.viewportHeight / 2, 0);
viewport = new ExtendViewport(camera.viewportWidth, camera.viewportHeight, camera);
// in render() method
batch.begin();
batch.draw(img, 0, 0, 100, 50);
batch.draw(img, -20, 0, 20, 20);
batch.end();
If we now start our Program with a Screen size of 200x200 we see:
And if we resize the Screen on x axis To make the screen wider:
Now we can see more off the first Image and additinal the Second image but the ratio will always be the same. The Image is only stretched because we draw it 100x50 not because of resizing.
I hope this will clear some Questions about Camera and Viewport if you will learn more, read and look some tutorials and read the LibGdx wiki: https://github.com/libgdx/libgdx/wiki
相关文章