最近几天没有更新博客,是因为我这两天在学习Libgdx的一个游戏源码,毕竟再怎么研究libgdx游戏引擎的原理,如果不去实践一下,掌握起来还是比较费劲的。(我个人对于一个新的东西的掌握,都是先从HelloWorld开始,然后开始写一些例子,从各个方位去了解他,其中自然有很多底层原理性的东西,不懂是肯定的!但我不会去深究它,因为这样太浪费时间,而且很容易走偏,我会在学完基本上如何使用和了解了它的大体以后,再去慢慢深入其原理,这样的一来一回,学习效率会倍增!)
好了不多说了,在以后的几篇博客里我会逐步分版本的讲解一下SuperJumper这款游戏,让大家(当然还有我,毕竟我也是初学者嘛! 嘿嘿!)逐渐掌握libgdx这款游戏引擎(框架)的使用方法。
1.游戏介绍:
这是一款跳跃型的游戏,主人物会一直往上跳,我们只需控制左右移动让其踩在适当的跳台上即可继续的往上跳,最终加到的金币越多, 到达城堡就胜利了。(貌似有点无聊哈,不过我们是来学习它的框架和使用方法的,相信大家学习完之后,自己也能做一个更好玩的游戏哦!)
上个图:
这里游戏源码我们可以从官网提供的SVN上下载(http://libgdx.googlecode.com/svn),我试过了可以的!连接成功后直接检出即可
当然,这里蜗牛已经将superjumper检出,同时也为大家配置好环境,直接使用即可。下载地址:http://down.51cto.com/data/893457
这里,我想说一下,因为本人也是初学者,第一次看到源码后不知道从何下手,所以每讲我会将每个版本的源码放出来,方便初学者循序渐进的学习它,相信这样的效率会更高!
好了不多说了,我们一步一步的开始吧!
2.项目创建
2.1为了方便测试起见,我们整个项目都在desktop上开发运行,大家也可以在android模拟器上试试,真机上就不行了(因为这款游戏需要左右按键的哦)
步骤:1.点击libgdx文件夹中的gdx-setup-ui.jar
2.在弹出的窗体中我们来新建项目:
3.下一步,点击launch即可,
4.这样我们的项目已经建立成功,接下来就是找到刚才我们建立项目的文件目录下,用eclipse将其导入到工程下。
导入成功!(第一个:源代码; 第二个:Android版本;第三个: 桌面版本)
ok! 项目已经创建成功!现在我们点击desktop版本右击运行 Java Application 进行测试,弹出一个窗体说明框架正常!以后我们的代码都在第一个MySuperJumper中编写,在desktop中进行测试!!
3.游戏代码框架搭建
SuperJumper类:启动入口
package com.zhf.mylibgdx;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.graphics.FPSLogger;
/**
* 启动入口
* @author ZHF
*
*/
public class MySuperJumper extends Game{
boolean firstTimeCreate = true; //是否是第一次创建
FPSLogger fps; //帧频
@Override
public void create () {
Settings.load();
Assets.load();
setScreen(new MainMenuScreen(this));
fps = new FPSLogger();
}
@Override
public void render() {
super.render();
fps.log();
}
@Override
public void dispose () {
super.dispose();
getScreen().dispose(); //销毁
}
}
Settings类:
package com.zhf.mylibgdx;
/**
* 设置类:三个方法: 1.load()读取声音开关和最高分. 2.save()保存配置 3.addScore()最高分排行榜,对数组赋值。
* @author ZHF
*
*/
public class Settings {
//记录声音开起与关闭
public static boolean soundEnabled = true;
/**加载配置文件**/
public static void load (){
}
}
Assets类: 各种资源的读取
package com.zhf.mylibgdx;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.audio.Music;
import com.badlogic.gdx.audio.Sound;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.TextureRegion;
/**
* 各种资源的读取(这里TextureRegion的用法可以学习,还有Music类,Sound类的使用方法)
* @author ZHF
*
*/
public class Assets {
public static Texture background;
public static TextureRegion backgroundRegion; //背景
public static Texture items; //一系列的图片
public static TextureRegion mainMenu; //主菜单
public static TextureRegion logo;
public static TextureRegion soundOn; //声音按钮
public static TextureRegion soundOff;
public static Sound clickSound; //按下音效
public static Music music; //背景音乐
/**通过资源名获取资源**/
public static Texture loadTexture (String file) {
return new Texture(Gdx.files.internal(file));
}
/**加载各种资源**/
public static void load () {
//背景
loadTexture("data /background.png");
backgroundRegion = new TextureRegion(background, 0, 0, 320, 480);
//主画面中的UI控件
items = loadTexture("data/items.png");
logo = new TextureRegion(items, 0, 352, 274, 142);
mainMenu = new TextureRegion(items, 0, 224, 300, 110);
soundOff = new TextureRegion(items, 0, 0, 64, 64);
soundOn = new TextureRegion(items, 64, 0, 64, 64);
//点击音效
clickSound = Gdx.audio.newSound(Gdx.files.internal("data/click.ogg"));
//背景音乐
music = Gdx.audio.newMusic(Gdx.files.internal("data/music.mp3"));
music.setLooping(true); //循环
music.setVolume(0.5f); //大小
if (Settings.soundEnabled) music.play();
}
/**播放游戏音效**/
public static void playSound (Sound sound) {
if (Settings.soundEnabled) sound.play(1);
}
}
MainMenuScreen类:主菜单界面
package com.zhf.mylibgdx;
import com.badlogic.gdx.Game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Screen;
import com.badlogic.gdx.graphics.GL10;
import com.badlogic.gdx.graphics.GLCommon;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.Rectangle;
import com.badlogic.gdx.math.Vector3;
/**
* 主菜单界面
* @author ZHF
*
*/
public class MainMenuScreen implements Screen{
Game game;
OrthographicCamera guiCam;
SpriteBatch batch;
Rectangle soundBounds;
Rectangle playBounds;
Rectangle highscoresBounds;
Rectangle helpBounds;
Vector3 touchPoint;
public MainMenuScreen(Game game) {
// TODO Auto-generated constructor stub
//得到Game对象,以便能调用Game切换到下一个画面,目前无用
this.game = game;
//相机,大小是320*480像素,这里作者把像素都按屏幕分辨率320*480写死了,等下会介绍如何适配到不同像素
guiCam = new OrthographicCamera(320, 480);
//相机位置
guiCam.position.set(320 / 2, 480 / 2, 0);
//渲染器
batch = new SpriteBatch();
//喇叭图标
soundBounds = new Rectangle(0, 0, 64, 64);
playBounds = new Rectangle(160 - 150, 200 + 18, 300, 36);
highscoresBounds = new Rectangle(160 - 150, 200 - 18, 300, 36);
helpBounds = new Rectangle(160 - 150, 200 - 18 - 36, 300, 36);
//点击点向量(就是用于记录用户点击的位置)
touchPoint = new Vector3();
}
/**刷新**/
public void update (float deltaTime) {
//如果屏幕有被点击
if (Gdx.input.justTouched()) {
//此句是难点,重点分析
//touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0)是把得到的点击坐标弄成touchPoint向量
//unproject函数查看源码,得出两部分信息,
//第一,把得到的点击坐标,由左上为(0,0)的坐标系--》左下为(0,0)的坐标系。
//(呃,还是详细说下吧,真实的设备的坐标起点都是左上角,而本游戏的矩形框是以左下角为起点弄的坐标)
//第二调用了vec.prj(invProjectionView);这么一个语句
//invProjectionView这个参数的意思是结合了“视图”和“投影”矩阵的逆矩阵,vec就是touchPoint向量
//大家其实可以结合刚才draw中重点将的语句来理解,
//理想像素(320*480)---经过矩阵(“视图”和“投影”矩阵)----实际像素(x*x)
//实际像素(x*x)-----经过矩阵(“视图”和“投影”逆矩阵)----理想像素(320*480)
//这样拉伸和压缩的变换以后便能适应大多数设备
guiCam.unproject(touchPoint.set(Gdx.input.getX(), Gdx.input.getY(), 0));
//调用辅助类OverlapTester,检测已经转换成理想像素的touchPoint向量是否在理想的playBounds矩形框内
if (OverlapTester.pointInRectangle(playBounds, touchPoint.x, touchPoint.y)) {
//播放点击音效
Assets.playSound(Assets.clickSound);
//game.setScreen(new GameScreen(game));
return;
}
if (OverlapTester.pointInRectangle(highscoresBounds, touchPoint.x, touchPoint.y)) {
Assets.playSound(Assets.clickSound);
//game.setScreen(new HighscoresScreen(game));
return;
}
if (OverlapTester.pointInRectangle(helpBounds, touchPoint.x, touchPoint.y)) {
Assets.playSound(Assets.clickSound);
//game.setScreen(new HelpScreen(game));
return;
}
if (OverlapTester.pointInRectangle(soundBounds, touchPoint.x, touchPoint.y)) {
Assets.playSound(Assets.clickSound);
//依据Settings类中的成员变量决定声音的开关
Settings.soundEnabled = !Settings.soundEnabled;
if (Settings.soundEnabled)
Assets.music.play();
else
Assets.music.pause();
}
}
}
public void draw (float deltaTime) {
//清空画面
GLCommon gl = Gdx.gl;
gl.glClearColor(1, 0, 0, 1);
gl.glClear(GL10.GL_COLOR_BUFFER_BIT);
//更新照相机位置(此处多余,因为这个照相机位置压根没动过)
guiCam.update();
//此句是难点,详细分析
//guiCam.combined参数是一个矩阵,兼具“投影”和“视图”矩阵的功能
//“投影”矩阵的作用是改变照相机的大小
//“视图”矩阵的作用是改变照相机的位置
//setProjectionMatrix函数的作用是把矩阵送给batcher计算
//这整个语句可以理解为,batcher这个本来是320*480像素的渲染范围的渲染器,
//在经过矩阵的计算后可以batcher的渲染范围可以适应当前像素
batch.setProjectionMatrix(guiCam.combined);
//关闭混合(这个貌似是作者为了节约GPU多加的一句,只在渲染背景的时候加)
batch.disableBlending();
batch.begin();
batch.draw(Assets.backgroundRegion, 0, 0, 320, 480);
batch.end();
//关闭混合
batch.enableBlending();
batch.begin();
batch.draw(Assets.logo, 160 - 274 / 2, 480 - 10 - 142, 274, 142);
//这里注意是将3个选项(“play”“HighscoresScreen”"help")只用一个图片表达
batch.draw(Assets.mainMenu, 10, 200 - 110 / 2, 300, 110);
batch.draw(Settings.soundEnabled ? Assets.soundOn : Assets.soundOff, 0, 0, 64, 64);
batch.end();
}
@Override
public void render(float delta) {
//这里系统会开一个线程不断地调用此方法的
update(delta);
draw(delta);
}
@Override
public void resize(int width, int height) {
// TODO Auto-generated method stub
}
@Override
public void show() {
// TODO Auto-generated method stub
}
@Override
public void hide() {
// TODO Auto-generated method stub
}
@Override
public void pause() {
// TODO Auto-generated method stub
}
@Override
public void resume() {
// TODO Auto-generated method stub
}
@Override
public void dispose() {
// TODO Auto-generated method stub
}
}
OverlapTester类:
package com.zhf.mylibgdx;
import com.badlogic.gdx.math.Rectangle;
/**
* 工具类:检测各种碰撞
* @author ZHF
*
*/
public class OverlapTester {
/**检测输入的X,Y是否在输入的矩形框r内**/
public static boolean pointInRectangle(Rectangle r, float x, float y) {
return r.x <= x && r.x + r.width >= x && r.y <= y
&& r.y + r.height >= y;
}
}
恩,注解都写得很清楚,我想可能还是有初学者还是不太明白其中的细节,其实我有的也不太懂,还是第一段的话,不影响大家整体思路的情况下,我们继续前进,有些东西到后面你就会有整体的把握!
经过上面的代码框架的搭建,运行起来的的效果:
点击“play”“HighscoresScreen”"help"会有音效,点击喇叭会关闭和开启背景音乐
ok ! 第一讲就到这里! 下一讲我们继续学习一下,屏幕之间的切换!