Libgdx之正交相机 OrthographicCamera
本翻译自Libgdx Wiki
本文主要介绍OrthographicCamera相机类和用法。OrthographicCamera是正交相机,用在2D游戏开发中,无论游戏物体放在游戏世界中的那个位置,用正交相机看到的物体都不会被缩放。
描述
正交相机的操作非常简单,就像我们在现实世界中操作相机一样,文章中主要介绍:
- 相机的移动和旋转
- 相机的放到和缩小
- 改变相机的视窗大小
在窗口(widow)坐标系和(世界)坐标系之间切换点的位置
使用正交相机可以在不必去操作矩阵的情况下非常方便的来移动游戏世界,所以的投影矩阵和视图矩阵都在后台实现。
下面的代码示例展示了怎样通过照相机在改变游戏世界
import com.badlogic.gdx.ApplicationListener;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.backends.lwjgl.LwjglApplication;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;public class OrthographicCameraExample implements ApplicationListener {static final int WORLD_WIDTH = 100;static final int WORLD_HEIGHT = 100;private OrthographicCamera cam;private SpriteBatch batch;private Sprite mapSprite;private float rotationSpeed;@Overridepublic void create() {rotationSpeed = 0.5f;mapSprite = new Sprite(new Texture(Gdx.files.internal("sc_map.png")));mapSprite.setPosition(0, 0);mapSprite.setSize(WORLD_WIDTH, WORLD_HEIGHT);float w = Gdx.graphics.getWidth();float h = Gdx.graphics.getHeight();// Constructs a new OrthographicCamera, using the given viewport width and height// Height is multiplied by aspect ratio.cam = new OrthographicCamera(30, 30 * (h / w));cam.position.set(cam.viewportWidth / 2f, cam.viewportHeight / 2f, 0);cam.update();batch = new SpriteBatch();}@Overridepublic void render() {handleInput();cam.update();batch.setProjectionMatrix(cam.combined);Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);batch.begin();mapSprite.draw(batch);batch.end();}private void handleInput() {if (Gdx.input.isKeyPressed(Input.Keys.A)) {cam.zoom += 0.02;}if (Gdx.input.isKeyPressed(Input.Keys.Q)) {cam.zoom -= 0.02;}if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {cam.translate(-3, 0, 0);}if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {cam.translate(3, 0, 0);}if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {cam.translate(0, -3, 0);}if (Gdx.input.isKeyPressed(Input.Keys.UP)) {cam.translate(0, 3, 0);}if (Gdx.input.isKeyPressed(Input.Keys.W)) {cam.rotate(-rotationSpeed, 0, 0, 1);}if (Gdx.input.isKeyPressed(Input.Keys.E)) {cam.rotate(rotationSpeed, 0, 0, 1);}cam.zoom = MathUtils.clamp(cam.zoom, 0.1f, 100/cam.viewportWidth);float effectiveViewportWidth = cam.viewportWidth * cam.zoom;float effectiveViewportHeight = cam.viewportHeight * cam.zoom;cam.position.x = MathUtils.clamp(cam.position.x, effectiveViewportWidth / 2f, 100 - effectiveViewportWidth / 2f);cam.position.y = MathUtils.clamp(cam.position.y, effectiveViewportHeight / 2f, 100 - effectiveViewportHeight / 2f);}@Overridepublic void resize(int width, int height) {cam.viewportWidth = 30f;cam.viewportHeight = 30f * height/width;cam.update();}@Overridepublic void resume() {}@Overridepublic void dispose() {mapSprite.getTexture().dispose();batch.dispose();}@Overridepublic void pause() {}public static void main(String[] args) {new LwjglApplication(new OrthographicCameraExample());}
}
上面代码展示了Libgdx程序如何通过正交相机来移动游戏世界。注意: 我们的游戏世界可以是任意单位,在此列中我们设置为100*100
当谈论到游戏世界时人们通常错误的认为游戏单位是像素,这也是不可避免发生的。如果有这种奇怪的每个单位中有多少像素的想法在你游戏代码中,这将会导致一些不必要的乘以或者除以常量,这种错误的理解方式是你困惑不解。当停止这种思考方式时许多其它问题将会得以避免
这些单位是什么?它们意味着什么?我怎样设计游戏对象的大小?在屏幕上应该展示多少个单位?我们很快就会学习到。
private OrthographicCamera cam; *1private SpriteBatch batch; *2private Sprite mapSprite; *3private float rotationSpeed; *4
*1 - OrthographicCamera的实例,我们控制它来观察游戏世界
*2 - SpriteBatch 渲染游戏世界
*3 - Sprite 在游戏世界中渲染的地图
*4 - 旋转相机的速度
@Override
public void create() {rotationSpeed = 0.5f; *1mapSprite = new Sprite(new Texture(Gdx.files.internal("sc_map.png"))); *2mapSprite.setPosition(0, 0); *3 mapSprite.setSize(WORLD_WIDTH, WORLD_HEIGHT); *4 float w = Gdx.graphics.getWidth(); *5float h = Gdx.graphics.getHeight(); *6cam = new OrthographicCamera(30, 30 * (h / w)); *7cam.position.set(cam.viewportWidth / 2f, cam.viewportHeight / 2f, 0); *8cam.update(); *9batch = new SpriteBatch(); *10
}
当我们创建ApplicationListener的实例的时候会调用create方法,在此处我们初始化变量。
*1 - 设置当前旋转速度为 0.5 弧度
*2 - 初始化Sprite,并且赋给它纹理:sc_map.png,点击此处下载纹理,并且复制到 assets 目录下面
*3 - 设置mapSprite的位置为(0, 0)。(并不需要严格按照代码来,因为Sprite的默认位置也是0,0)
*4 - 设置mapSprite的大小为世界的大小,sprite有一个100*100的维度,也就是世界大小
*5 - 创建局部变量 width 表示应用显示屏的宽度
*6 - 创建局部变量height 表示应用显示屏的高度
*7 - 创建正交相机,2个参数规定了视窗的高宽,也觉得了在各个轴上能看到的游戏世界的大小。
在示例代码中,设置视窗宽为30, 视窗高为 30 * (h/w)。视窗宽度没啥好说的,即在x轴能看到游戏世界为30个单位。视窗高度我们设置为30 * 屏幕纵横比。这也是游戏能画出来正确比例。想象一下,如果我们忽略纵横比,设置视窗的高宽为30 * 30,当渲染一个30*30的物体时,在屏幕上渲染的物体会发生变形,成为压扁的正方形,除非屏幕也是一个正方形,但这基本不可能。30*30的物体为什么会发生变形,因为我们设置了视窗的高宽为30 * 30,这不符合屏幕的纵横比
- 如果创建的相机视窗高宽为100*100(new OrthographicCamera(100, 100)),并且相机居中,我们能看到整张地图。
- 如果创建的相机视窗高宽为100*50(new OrthographicCamera(100, 50)),并且相机居中,我们能看到半张地图。
- 如果创建的相机视窗高宽为50*50(new OrthographicCamera(50, 50)),并且相机居中,我们能看到1/4张地图。
*8 - 设置相机初始位置位于地图左下角,但是…相机位置是位于相机中心,所以我们移动相机位置,相机位置.x + half viewport width, 相机位置.y + half viewport height,位置移动之后,相机位于0,0
*9 - 更新相机,无论任何时候操作相机之后到要 update 来更新相机里的矩阵
*10 - 创建SpriteBatch实例
创建完成了,开始渲染和操作相机吧!
@Override
public void render() {handleInput(); *1cam.update(); *2 batch.setProjectionMatrix(cam.combined); *3Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); *4batch.begin(); *5mapSprite.draw(batch); *6batch.end(); *7
}
*1 - 根据不同的按键操作相机的位置,缩放,旋转
*2 - 更新相机
*3 - 根据相机的视图和投影矩阵更新SpriteBatch实例
*4 - 清屏
*5 *6 *7 - 渲染地图
深入handleInput()方法,看如何来操作相机
private void handleInput() {if (Gdx.input.isKeyPressed(Input.Keys.A)) {cam.zoom += 0.02;//If the A Key is pressed, add 0.02 to the Camera's Zoom}if (Gdx.input.isKeyPressed(Input.Keys.Q)) {cam.zoom -= 0.02;//If the Q Key is pressed, subtract 0.02 from the Camera's Zoom}if (Gdx.input.isKeyPressed(Input.Keys.LEFT)) {cam.translate(-3, 0, 0);//If the LEFT Key is pressed, translate the camera -3 units in the X-Axis}if (Gdx.input.isKeyPressed(Input.Keys.RIGHT)) {cam.translate(3, 0, 0);//If the RIGHT Key is pressed, translate the camera 3 units in the X-Axis}if (Gdx.input.isKeyPressed(Input.Keys.DOWN)) {cam.translate(0, -3, 0);//If the DOWN Key is pressed, translate the camera -3 units in the Y-Axis}if (Gdx.input.isKeyPressed(Input.Keys.UP)) {cam.translate(0, 3, 0);//If the UP Key is pressed, translate the camera 3 units in the Y-Axis}if (Gdx.input.isKeyPressed(Input.Keys.W)) {cam.rotate(-rotationSpeed, 0, 0, 1);//If the W Key is pressed, rotate the camera by -rotationSpeed around the Z-Axis}if (Gdx.input.isKeyPressed(Input.Keys.E)) {cam.rotate(rotationSpeed, 0, 0, 1);//If the E Key is pressed, rotate the camera by rotationSpeed around the Z-Axis}cam.zoom = MathUtils.clamp(cam.zoom, 0.1f, 100/cam.viewportWidth);float effectiveViewportWidth = cam.viewportWidth * cam.zoom;float effectiveViewportHeight = cam.viewportHeight * cam.zoom;cam.position.x = MathUtils.clamp(cam.position.x, effectiveViewportWidth / 2f, 100 - effectiveViewportWidth / 2f);cam.position.y = MathUtils.clamp(cam.position.y, effectiveViewportHeight / 2f, 100 - effectiveViewportHeight / 2f);}
从代码可以看到会主动查询Key事件,相机会做相应的操作,当具体按键按下时。
最后5行确保相机不跑出游戏世界。
需要确保相机的zomm不能增长或者缩小的值使我们的世界翻转,也不能过多的显示游戏世界。所以我们要计算 effectiveViewportWidth 和 effectiveViewportHeight ,仅仅需要viewportWidth/Height * zoom,然后使用clamp 取我们需要的值。
最后2行需要确保相机不会translate出游戏世界 0 < x < 100
当游戏的屏幕大小发生变化应该怎么做?当处理不同屏幕比时我们应该实现怎样的策略。下面是作者整理的基本思路。
如果想要简单的测试可以参考Wiki的Viewport篇。
下面的resize策略确保无论屏幕分辨率怎样变化,总是在x-轴看到30个单元
@Overridepublic void resize(int width, int height) {cam.viewportWidth = 30f; // Viewport of 30 units!cam.viewportHeight = 30f * height/width; // Lets keep things in proportion.cam.update();}
下面的resize策略将会根据分辨率多少会将游戏世界显示不全
@Overridepublic void resize(int width, int height) {cam.viewportWidth = width/32f; //We will see width/32f units!cam.viewportHeight = cam.viewportWidth * height/width;cam.update();}
启动实现的主要方法如下:
public static void main(String[] args) {new LwjglApplication(new OrthographicCameraExample());}
最终效果如下:
Libgdx之正交相机 OrthographicCamera相关推荐
- 正交相机OrthographicCamera在GDX中的使用
这里写自定义目录标题 OrthographicCamera简述 学习资料 主要函数 属性的设置 绘制.显示 OrthographicCamera 源码实例 OrthographicCamera简述 C ...
- Unity 3D 正交相机(Orthographic)
1. Camera.aspect 表示摄像机显示区域的纵横比.宽高比,摄像机初始化的时候会默认设置成当前屏幕的宽高比,可以更改,也可以通过 Camera.ResetAspect 来重置. 2. Cam ...
- Unity正交相机适配
unity的正交相机,在适配的时候默认情况下是高度不变,而宽度会随着屏幕大小进行改变,之前还以为这个要自己去处理,但是测试之后才发现,都已经做好了. 如果我们的项目需要高度改变,宽度不变,那么添加代码 ...
- three.js正投影相机OrthographicCamera
<!-- https://threejs.org/docs/index.html#api/zh/cameras/OrthographicCamera --> <!DOCTYPE ht ...
- 正交相机下实现滚轮按钮拖动,滚动滚轮缩放的功能
实现了一个功能,鼠标滚轮键按下可以拖动视野内的物体全体(其实是相机自己在移动),滚动滚轮可以缩放内容(其实是改变相机视野大小) 效果如下 代码奉上 1 using UnityEngine; 2 usi ...
- 【ThreeJS基础教程-初识Threejs】1.5 选择合适的相机与相机切换
选择合适的相机 学习ThreeJS的捷径 两种常用相机 案例分析 创建两种相机 透视相机 PerspectiveCamera 正交相机OrthographicCamera 切换相机 掌控相机 学习Th ...
- 09 Controls相机控制器
我们在第七节的时候讲解过了相机的相关,也制作了一个简易的相机控制器. 但是,在正常的项目当中,大家的需求都是不一样的,又或者碰上中途需求的改变,对相机的操作需求也不可能和我们做的简易版的相机控制器就可 ...
- Three 之 three.js (webgl)透视视角和正交视角,以及透视转正交的视角切换
Three 之 three.js (webgl)透视视角和正交视角,以及透视转正交的视角切换 目录 Three 之 three.js (webgl)透视视角和正交视角,以及透视转正交的视角切换 一.简 ...
- 从Maya中把模型搬运至网页的过程
虽然利用threejs来在网页中渲染3d模型不是第一次折腾了,但是还是遇到了各种问题.总结下我所遇到的问题,希望能给正在使用threejs的小伙伴一个帮助. 一.所使用的软件与开发环境 Maya201 ...
最新文章
- 运维基础-文件权限管理
- OpenCV中直方图反向投影算法详解与实现
- 4.6.2 软件测试的步骤
- spark-sql建表语句限制_SparkSQL
- 第二章:变量和运算符
- java弱口令生成1001无标题,教你批量生成自动发卡平台需要的卡密数据
- 《图解HTTP》-读
- Item-Based Collaborative Recommender System
- hashmap 允许key重复吗_搞懂 HashMap,这一篇就够了
- 如何搭建企业数据平台
- sql-查询不同状态下的数据之和
- sql注入python编程_Python编写SQL注入工具(2)
- 使用ps 批处理图片(gif 转 png)
- matlab一个m文件定义多个函数,matlab怎么在一个m文件中写多个函数?
- 数学建模-多元线性回归
- 我们为什么要推广经方?
- 湖北汽车工业学院校园导游咨询与最短路径
- C#中的Obsolete特性
- iOS 中设置下划线失效不显示
- 「领域驱动设计」DDD,六边形架构,洋葱架构,整洁架构,CQRS的整合架构