最近几天没有更新博客,是因为我这两天在学习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类:启动入口
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
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类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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类: 各种资源的读取
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
|
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类:主菜单界面
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
|
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类:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
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 ! 第一讲就到这里! 下一讲我们继续学习一下,屏幕之间的切换!
源码下载:http://down.51cto.com/data/893642