- 浏览: 230308 次
- 性别:
- 来自: 杭州
文章分类
最新评论
-
mzl_2011:
Private Declare Function Delete ...
VB-改变文件只读属性 -
kdkler:
批量修改数据库表名(sqlserver) -
kdkler:
这个用的是存储过程?有点看不明白,能不能说明一下呢?
批量修改数据库表名(sqlserver) -
zjfgf:
写的太好了,学习了!
Java接口和Java抽象类 -
aotostar:
很好!
Java接口和Java抽象类
File -> New Project
填上名字:
Name:随便
目录:
Directory:随便
点Next.
然后选择JDK,点 ...,
如果是JBuilder 2005选择 J2ME Wireless Toolkit 2.1
JBuilder 2006选择 J2ME Wireless Toolkit 2.2。
点Next.
点Finish.
第一步完成。
----------------------------------------------------------------------------------
第二步,
File -> New...
选Micro, 然后选 MIDP MIDlet,点ok.
填Class
name:按照我的惯例,填GameMidlet,注意Java中的大小写敏感,类名使用大写开头。
填Package:删了,啥也不填。一般j2me
game程序就那几个类,不用按package分的那么细。
可以理解为这个类,是j2me应用程序的入门儿类。手机运行会找这个类,以后再说。
点Next.
现在填这个类是画布类,我们的游戏代码主要在这里面编,
它叫做Canvas,其实继承Displayable(意思是一个可拿来显示的咚咚),Midlet有个方法,就是给手机添加一个Displayable对象。
因此,我们编好这个Canvas,用Midlet的API把它加到手机上,就完了。
手机就可以按照我们的逻辑来画东西啦。具体以后再谈。
填Class Name: 按照我的惯例,填Game就行了。
Tile:什么鸟玩意儿,直接删除。
填Displayable type: 这里就选择javax.microedition.lcdui.Canvas
(xx.xx.xx的意思是,javax是个包,microedition是他下面的包,如此类推,最后一个是类)
这里一选,就说明这个类是canvas了。
Command Handling:默认不变,就是Interface implementation.
他的意思是,JBuider给你生成这个类没有实现的接口。简而言之,就是他自动给你加几个空方发体,这几个空方法是你需要实现的。
以后再说了。
点Next,
他那意思是给这个程序单独搞一个运行参数的配置。不管,没啥用。
点Finish。
ok,成功了。
看看左边的导航栏,点开[+]Project
Source,就有两个文件了,
一个叫Game.java
一个叫GameMidlet.java
现在稍微讲解一下这程序是怎么个运行法儿:
----------------------------------------------------------------------------------
双击GameMidlet.java
发现有个构造函数 public GameMidlet()
有个startApp()
有个pauseApp()
有个destroyApp()
有个public static void quitApp()
第一个和最后一个是JBuilder自动生成的。
中间三个***App是你需要实现的,JBuilder只给了空方法。
运行一个MIDlet的流程是这样的:
两步:
先是调用一个MIDlet的构造函数(GameMidlet)。
然后调用startApp。
怎么调不用你管,反正自动按这个顺序。只要明白就行了。
看看他做了什么事,
在构造函数中,把自己赋给了一个对象instance,这个玩意以后在Game类中还用得到。以后再说。
在startApp中,
使用这句话
Display.getDisplay(this).setCurrent(displayable);
就把咱们写得Game类,一个画布,给加载了。
在模拟其中运行,就点工具栏上面那个三角符号。
或者按F9.
至此,这项目的建立,运行的步骤就说完了。
但是现在这程序还不会动。
下一次讲Game.java这个Canvas是怎么回事,
怎么让手机屏幕无限循环的一次一次画下去。
j2me游戏教程(2)什么是状态机,怎样在上次的代码中建立状态机,什么是FPS?
在讲述如何构建一个无限循环,来反复不停的往屏幕上绘图的程序之前,
先说一下什么是状态机。
(都是按我自己的理解来的,和实际可能有出入!)
首先需要一台不断提供能量的机器。
一个while循环就可以做到这一点。
while (true) {
做事。
}
那么不管这事是什么,都可以一直做下去。
机器就有了。
那么状态是咋回事?
就是你把好多事放到这个机器里,一会叫做这个,一会叫做那个。
如果增添一套比较成熟的管理方式,那么就是一个非常完整的状态机了.
举个例子:
while (true) {
第一, 画一个三角。
第二,画一个方块。
}
程序先画三角,再画方块,每一次循环中,两件事同时做。
加个控制变量把他们分开。
state = 第一
while (true) {
if (state == 第一) {
画三角。
} else if (state == 第二) {
画方块。
}
}
(Java里,用=号来赋值,用==来做比较)
现在程序总是在画三角,因为每一次循环总是在画三角。
如果在画了三角有5分钟左右,就想让画方块,代码这样写:
state = 第一
while (true) {
if (state == 第一) {
画三角。
if (画了五分钟) {
state = 第二
}
} else if (state == 第二) {
画方块。
}
}
这样,程序会先画5分钟三角,再一直画方块.
大概清楚了么?
一个while循环,然后使用"状态"来控制做不同事,就构成了状态机。
整个游戏都是一个状态机。
比如,游戏一开始画logo
按了5键,转到画菜单。
再按5,转到画场景。
还有所谓的AI,也是一个状态机的方式,以后再说了。
--------------------------------------
现在先看一下再JBuilder的那个项目中,如果构造这样一个状态机,要这么这么这么:
加个方法,
public void run() {
while (true) {
System.out.println("xx");//这句话的意思是在控制台打印xx
//以后可以自在这里面可以写自己的事情.
}
}
然后在类名处,写成这样一个:
以前是这样
public class Game extends Canvas implements CommandListener {
现在变成这样:
public class Game extends Canvas implements Runnable, CommandListener {
多加了一个Runnable接口.
然后在
GameMidlet这一句
Display.getDisplay(this).setCurrent(displayable);
后面添上
new Thread(displayable).start();
就行了.
现在运行一下,然后按模拟器上的Select按钮。
如果看到控制台不断有xx打印出来,就是成功了。
-------------------------------------
刚才这几个步骤的意思是,
通过implements Runnable 把Game这个Canvas变成一个线程。
然后通过
new Thread(displayable).start();
使这个线程运行。
这个线程运行之后,就会自动调用run方法。
而我们在run里面搞了一个无限的循环,这就构成了一个状态机了。
等于说,我们的状态机,是运行在另一个线程之中。
而不会干扰系统的方法调用。
如果你用一个普通的方法构建状态机,然后调用,类似这样:
在class Game不实现Runnable接口
只在里面加一个gogogo()方法:
public void gogogo() {
while (true) {
}
然后在GameMidlet这一句
Display.getDisplay(this).setCurrent(displayable);
后面添上
displayable.gogogo();
这就完蛋了.因为gogogo里面有个无限循环,系统永远退不出
startApp()这个方法了。
如果使用线程,就不会产生这种情况。无限循环是在另一个线程独立运行的。
(java怎么实现的,估计还要看点儿线程之类的书了,可以不用搞那么深入。)
这样系统该掉其他方法,还会掉其他方法。
先动手做一下,再往下看。
------------------------------------------------
现在可以说说桢的概念了。英文叫做Frame.
可以这样理解:
while (true) {
开始
如果 (***) 干这干那
否则如果 (yyy) 干那干这
结束
这从 开始,到 结束
的一个过程,也就是一次循环,叫做一桢。
每一桢用的时间,叫做SPF (Second per frame)
倒过来,每秒运行的桢数,叫FPS (Frame per second)
打CS的时候,有的版本可以看到屏幕上画着 FPS:
后面一个数字,不停的变。
这个东西越高越好,表示游戏速度越快。
怎么计算FPS:
在开始的时候记录一下系统时间,在结束的时候又记录一下系统时间,
两个时间一减,就知道这一桢用的时间了(SPF)。 1/SPF
就是FPS.
限桢的概念:
有时候一个循环奇快,有时候一个循环奇慢.
打游戏的时候, 你看着画面时快时慢, 肯定不中.
就要限制一下.
先规定好一个FPS,假如是每秒20桢,那么一桢用的时间就是50毫秒。
在 结束
的地方,判断,如果这一桢用的时间小于50毫秒,记录下还差多少时间到50毫秒,
然后就让程序睡觉,睡够这个差值,使这一桢整体使用的时间达到50毫秒。
这样平均下来游戏的FPS就是20,很稳定。
如果一桢的时间超出了50毫秒,那就没办法了。这就是游戏运行慢的原因,这时要想办法看看是做了什么事,导致程序运行缓慢。
魔兽肯定采用了比较高级的控制FPS的方法,如果卡,他就使用很低的FPS,不卡了之后,他就用很快的FPS补上。
这样看起来游戏
在网络速度不好的情况下,很迟钝的样子,然后再网络速度好了以后,刷刷刷运行的飞快。
神奇的是,他们能计算出桢的速度变化。
都是我的猜测而已了。
大概明白了没有?没有就说。
作业来了,试着写一下计算FPS的代码,把FPS打印到控制台。
有啥问题问哦。
下次,讲一下绘图的系统方法是怎么个调用流程。如何绘制图形到屏幕上。怎么察看相关的API文档。
加个方法,
public void run() {
while (true) {
System.out.println("xx");//这句话的意思是在控制台打印xx
//以后可以自在这里面可以写自己的事情.
}
}
的意思是在Game.java里加。
j2me游戏教程(3)如何使用上次构建的状态机框架,一直不停的绘制图形到屏幕上!
Paint方法:
protected void paint(Graphics g) {
/** @todo Add paint codes */
}
这个方法是Game这个Canvas的。
我们在这里只是继承,并需要重写。
它由系统调用,每当我们调用repaint()方法,系统就会去调用paint方法。
那个Graphics g 也是系统传入的。
逻辑上是这样的:
Graphics g,这个图像处理参数,具有一些绘图的API,
我们使用它在paint中写好绘制图像的方法之后,
一旦系统调用paint方法,就相当于开始进行绘制了。
比如:
在run这个方法中,添加repaint()方法。
public void run() {
while (true) {
System.out.println("xx");
repaint(); //意味着提醒系统去调用paint
}
}
不断的调用repaint,就会使系统不断的调用paint方法。
如果我们在paint方法中添加一个绘制图形的语句:
protected void paint(Graphics g) {
g.setColor(0); //设置黑色
g.fillRect(0, 0, 128, 128); //填充一个矩形区域
}
然后运行,按模拟器的select,然后屏幕就一直有个128x128的方块处于被刷黑状态。
现在可以试试状态机:
------------------------------
先定义一个static Graphics ourGraphics;
//我们用的Graphics.默认值是null(空)
然后,在paint中,把系统的g赋给我们的ourGraphics.
这样做在于,以后我们也有了一个系统的Graphics了,可以用ourGraphics在别的方法里面绘制,而不是只能跑到paint(Graphics
g)中使用g进行绘制。
protected void paint(Graphics g) {
if (ourGraphics == null) {
ourGraphics = g; //赋值一次就够了
}
}
------------------------------
在Game.java中,加个变量表示游戏状态:
static int gameState = 0;
static int temp;
然后在run方法的 wile(true)里面加上 switch语句:
while (true) {
switch (gameState) {
case 0:
repaint(); // 得到ourGraphics变量。
gameState = 1; //
改变gameState,让他变成1,下一次循环就会到 case 1
的状态。
break;
case 1:
if (ourGraphics == null) {
return;
//如果这个变量还没有被赋值,啥也不干。有的时候调用repaint之后,系统调用paint会迟一些,所以ourGraphics仍然可能处于null(空)的状态。
}
ourGraphics.setColor(0); //设置黑色
ourGraphics.fillRect(0, 0, 128, 128);
//填充一个矩形区域
ourGraphics.setColor(0x00ff00);
//设置绿色,画一个小绿框。下面有讲颜色。
ourGraphics.fillRect(20, 20, 88, 88);
temp++;
if (temp == 1000) {
gameState = 2;
//可以给gameState的改变加些条件,例如,case
1运行了1000次以后,才把它变成 case 2
}
break;
case 2:
ourGraphics.setColor(0);
ourGraphics.fillRect(0, 0, 128, 128);
ourGraphics.setColor(0xffffff);
//设置为白色。Java中0x开头表示16进制。0xffffff中的 ff
ff ff分别表示R,G,B三种颜色,
//在photoshop的前景或背景的颜色选择器中选一种颜色,下面就有这种颜色的16进制值。
//例如: 0xff0000 代表红, 0x00ff00代表绿,
0x0000ff代表蓝
ourGraphics.drawString("Fuck Man! It's New Game!", 128
//画一个字符串。具体参数以后再讲,
//如果在这个状态中不改变gameState这个变量,程序就会一直处于这个状态,画字符串。
break; //记住加break
}
repaint(); //
为什么在这儿还加repaint()?因为使用Graphics画东西还不够,还必须调用repaint,让系统调用其他方法之Graphic画的东西刷新在屏幕上。
//现在流行的方法是把状态机放在paint()中,下面有示例,和这个一摸一样,只不过是把switch移动一下。
}
把状态机放在paint()中的例子:
-------------------------------------
while (true) {
repaint();//就留这一句。
-------------------------------------
protected void paint(Graphics g) {
if (ourGraphics == null) {
ourGraphics = g;
}
switch (gameState) {
case 0:
repaint();
gameState = 1;
break;
case 1:
if (ourGraphics == null) {
return;
}
ourGraphics.setColor(0);
ourGraphics.fillRect(0, 0, 128, 128);
ourGraphics.setColor(0x00ff00);
ourGraphics.fillRect(20, 20, 88, 88);
temp++;
if (temp == 1000) {
gameState = 2;
}
break;
case 2:
ourGraphics.setColor(0);
ourGraphics.fillRect(0, 0, 128, 128);
ourGraphics.setColor(0xffffff);
ourGraphics.drawString("Fuck Man! It's New Game!", 128
break;
}
-------------------------------------
如果获得API帮助??
-------------------------------------
例如,我想知道Graphics里面有哪些画图或者画字符串的方法,参数的意义是什么,怎么办?
在JBuilder里,把鼠标移到Graphics
g上,左键点一下,然后按F1,
Graphics这个类的说明就有了。
在Method
Summary区域,可以看到他的所有方法。比如,fillRect,点进取就有详细说明。
JBuilder的帮助做的窗口很小,看起来不方面。
直接到JBuilder的j2mewtk的安装目录:
例如:E:\JBuilder2005\j2mewtk2.1\docs\api\midp
然后打开index.html,然后点上面一个index(在help旁边)
如果要查Graphics,就点字母G,然后按字母排序找就行了.比较麻烦.
可以Ctrl+ F 输入Graphics,
只要找到一个连接,点进去之后就可以看说明了.
目前,大概知道有查API这回事即可.
按键事件的处理。
所谓按键处理,就是在游戏中,玩家按了一个键。
你把它记录下来,然后在程序中判断,按了什么键,要完成什么相应的功能。
这个按键处理系统也是十分巧妙,实现的方式不一样,照顾的方面也不一样。
我现在只能说些简单的例子。
一个简单的键盘处理系统,可能会经过很长期的完善才能变得成熟。
因为这个东西实现起来很复杂。(对我来说^^)
在Game.java中添加如下代码:
public void keyPressed(int keyCode) {
//
系统事件,当键盘被按下,系统会调用这个方法,并把你按得键值keyCode传进来。
// 注意,这个 keyCode键值不是数值,就是说你按
number
1,不一定是1。在手机上,每个键都有一个独特的keyCode,这和生产厂商有关。
//
不同的手机,keyCode可能不相同。模拟器的keyCode和真实手机的也可能不相同.
:)
}
public void keyReleased(int keyCode) {
//
同上,当一个键被松开,系统会调用这个方法。
}
public void keyRepeated(int keyCode) {
//
同上,当一个键被持续按着,系统会调用这个方法。
//
大多数手机不支持这个方法,反而支持keyPressed方法。
//
就是说,如果你一直按着某个键,keyPressed方法会被持续调用,而不是这个方法。
}
所以我们就只看
public void keyPressed(int keyCode) {
}
public void keyReleased(int keyCode) {
}
这两个方法就够了。一个好的设计完全可以用这两个方法满足所有需要。
在上次的Game.java中,第二个状态是这样:
case 2:
ourGraphics.setColor(0);
ourGraphics.fillRect(0, 0, 128, 128);
ourGraphics.setColor(0xffffff);
ourGraphics.drawString("Fuck Man! It's New Game!", 128
>> 1, 128 >> 1, Graphics.TOP | Graphics.HCENTER);
// 我们在这里添上键盘处理件:
/**
if (keyValue == NUM6) {
gameState = 3; // 如果按了6键,就跑到状态3里面.
}
*/
break;
然后我们再添一个状态3,
case 3:
ourGraphics.setColor(0);
ourGraphics.fillRect(0, 0, 128, 128);
ourGraphics.setColor(0xffffff);
ourGraphics.drawString("Haha, dao 3 le!", 128 >> 1, 128
>> 1, Graphics.TOP | Graphics.HCENTER);
// 我们在这里添上键盘处理事件:
/**
if (keyValue == NUM4) {
gameState = 2; // 如果按了4键,又跑回状态2里面.
}
*/
break;
上面的代码先就那样把注释加着, /** */
定义一个变量,
int keyValue;
//然后在这里面
public void keyPressed(int keyCode) {
//加上一句:
System.out.println("The keycode of the key you pressed is " +
keyCode);
//这样你每按一个键,控制台就会把这个键对应的键值打印出来. //把这个键值赋给这个变量,作为以后判断按键事件的依据。
keyValue = keyCode;
}
现在可以试试这个程序,按一下4,或6,看看他们的键值是多少,然后定义以下两个变量:
final static int NUM4 = 52; //在这里写按4时得到的键值
final static int NUM6 = 54; //在这里写按6时得到的键值
// final 就意味不可改变。static final就是一个常量。
一般来说,这些键值都事先定义好。手机不同,只需要改变这些常量的值就可以了。
现在可以把case 2 和 case 3的注释去掉。运行程序:
这样 在到达状态2以后,按6键,游戏就会跑到状态3。
然后再按4,就会返回到状态2。
为了避免keyValue这个值一直保存,在
case 2的里面加上:
/**
if (keyValue == NUM6) {
gameState = 3; // 如果按了6键,就跑到状态3里面.
keyValue = -1; //
随便搞个负数给它,免得这个值在其它状态还可能起作用.意思就是判断完了就不要了.
}
*/
一个简单的键盘处理的例子就完成了。:)
但是这个键盘处理的方法非常不完善。
case 2运行的时候,如果很快的按6再按4,
这时keyValue变成NUM4,然后在 case
2的判断中,就判断不到你按过6。
看来现在的问题就是用一个keyValue变量存储按过的键,只能存储一个。
有个办法:
假设 keyValue 用2进制表示,他就有很多位可以利用。
int keyValue = 0; // 0000 00000 0000.....
假设第四位表示按键4,第六位表示按键6......依此类推。
那么如果按了 NUM6, 就把
keyValue的第6位表示成1,表示按下。
那么如果按了 NUM4, 就把
keyValue的第4位表示成1,表示按下。
只要存储的位置不同,那么一个整数就可以同时存储多个按键的状态了。
现在定义两个变量,表示某个键在这个整形中的位置:
static final int GAME_NUM4 = 1 << 4; // 意思是二进制的10000,
把1向右移4位。
static final int GAME_NUM6 = 1 << 6; // 意思是二进制的1000000,
把1向右移6位。
修改 这个函数。
public void keyPressed(int keyCode) {
switch (keyCode) {
case NUM4:
keyValue |= GAME_NUM4; // 如果二进制keyValue的值是 0,
那么|(或)了之后,keyValue = 10000.
// 如果 keyValue = 0010
0000,那么|(或)了之后,keyValue = 0011
0000.就是右数第5位改变了,其他位不变。
break;
case NUM6:
keyValue |= GAME_NUM6;
break;
// case
....完整的游戏代码里,后面还有很多case,要把所有手机上的按键都给写全了。java的整型是32位的。应该能存32个键。足够了。
}
}
存储按键值得方法变了,判断的方法也要变。
修改case 2中的
if (keyValue == NUM6) {
gameState = 3;
keyValue = -1;
}
变成
if ((keyValue & GAME_NUM6) != 0 ) { //
如果按了6键那一位是1,就表示6被按下了。
// 0 & 1 = 0, 1 & 1
= 1
//
那么keyValue的每一位和GAME_NUM6的每一位 &
// 1111 1010
keyValue (随便写的值)
// 0100 0000
GAME_NUM6
//
这样看来那么keyValue &
GAME_NUM6的值就决定在第7位。其他位&的结果肯定是0。
//
如果keyValue的第7位是1,就意味着keyValue & GAME_NUM6 != 0.
gameState = 3;
keyValue &= ~GAME_NUM6; //
使用完之后,把这一个按键位清空,其他位保留。
// ~符号的意思是取反.
GAME_NUM6 的二进制是 0100 0000, 取反之后等于1011 1111
// 再跟keyValue
&,就相当于其他为保持不变,第7位变成0了。
}
可以试试看了,现在的话,很快的按6再按4,6仍然是起作用的。
一般在
keyReleased方法中,也要把释放的键位给清空,也用上面的方法。
恩,我举的例子似乎不是很好。没能表现key系统的处理。
按键可能随时发生,所以keyValue的值可能会随时改变。这样的话,直接用keyValue来进行判断就非常不稳定了。
现在的解决办法是:
使用keyValue来记录按键的状态。
按下-->打开相应的位, 释放-->清空相应的位
然后再搞一个变量专门进行判断。
假设这个变量是frameKey.
在每一桢开始时,frameKey = keyValue.
那么对这一桢来说,frameKey是不变的!不管你怎么按键,变得都是keyValue.
在这一桢里,我们始终用frameKey来进行按键的判断。因此,按键的状态变得稳定了。
然后在下一桢,又把变过的keyValue赋给frameKey,再使用frameKey进行判断。
呵呵。这样的判断也可能漏掉某些按键。
其实我很长时间都没有去认真研究代码,去理解代码了。
成天忙于改bug,对游戏开发本身的知识简直蜕变为0了。
只有以后动手做。如果碰到具体的不能满足的需要,就再去研究。
就讲这么多。
j2me游戏教程(5)动画系统
播放动画为什么还要有个系统待我慢慢讲来。
状态机大家都知道了,就是无限循环的东西。
动画是由一桢一桢组成的,这个桢和游戏的桢概念不同。但含义都差不多。
每一桢就是组成动画的一幅画,上一桢和下一桢可能相同也可能不同,连续播放就可看到动画的效果 比如一个动画由12桢组成:(假设 | \—/都是小棍形状)
|, |, |, \, \, \, —, —, —, /, /, /
那么连续播放的话,就是播完从头再拨,就可以看到一个小棍儿在不停的转。
那么在一个不断循环的系统中播放动画,播完一遍需要12次循环,也就是12桢。
为什么相同的桢要播放三次,那是因为你要控制它播放的速度。
如果每桢播一次,机器又很快,就是FPS很快,你就会看到一个小棍儿在疯转。
相同的一个动画,如果你的机器快,他的机器慢,在上面看起来效果就不一样。
但是之前我们讲过FPS,讲过限桢的概念。
就是把一桢的时间控制在相同的范围。
如果程序限了桢,不管在哪个机器上,绘制一桢的时间都是确定的。
那么这个动画在哪看起来速度都一样。
那么现在看看代码怎么写呢?
一个动画有12桢意味着要播12次。
int frameIndex = 0; //用来记录一个动画的当前桢。
while (true) {
if (frameIndex == 0) {
// 画 |
} else if (frameIndex == 1) {
// 画 |
} else if (frameIndex == 2) {
// 画 |
} else if (frameIndex == 3) {
// 画 \
} else ...........
....................
frameIndex ++; //上面的代码确保每次只画一桢,这句话在画完了之后把frame往下推一个。
}
这样的代码很麻烦,也肯定不是一个成熟的游戏需要的。
更简单些,这样:
while (true) {
playAnimation();
这个playAnimation() 这样写:
int frameIndex = 0;
Image[] animationFrames;
//定义一个图像数组来存储一个动画的每一桢图像。假设已经被装载好了。假设是上面那幅动画,这个数组里面就存了12幅画。
(实际不是这样,还要更仔细一些,例如只存二进制数据而不是一个图像,到了播放时才即时创建一幅出来。而且相同的桢数据不会重复放在里面。
现在只是举个比上面的代码好点儿的例子。)
public void playAnimation() {
Image currentFrameImage = animationFrames[frameIndex]; //把当前桢的那幅图给取出来
if (playAnimation) { //现在假设没有这个变量,往后看。
drawImag(currentFrameImage,...);//画到屏幕上。(代码没写全,drawImage还有很多参数)
frameIndex ++; // 桢往后推个1。下次画就是画下一桢了。
}
if (frameIndex > animationFrames.length) {// 如果播完了12桢怎么办?自己决定,是重头画还是停止。
//如果重头画
//frameIndex = 0;
//如果让这个动画画一遍就停下.
// playAnimation = false;
//现在知道有什么用了吧?虽然playAnimation还在被一直调用,但已经没有东西画出来了。
}
恩, 动画的播放原理差不多就这样。
然后再说说详细说说动画的组成。
动画是由一桢一桢的图像组成。
而每一桢的图像又是一块一块的小图拼成。
为什么要这样做?
一方面,在j2me程序中,程序+资源包的大小十分受限。
假如做一个小人儿的动画,就算10桢,就需要10幅画。
如果再多做几个小人儿动画,就是10xN幅画,太大了。
这样,就可以把各种各样小人儿的头,不同形状,不同动作,专门做成一整套,一个一个的,或者说看起来一块一块的,拼到一个大图上。
小人儿们的腿,身子,胳膊等等,也都按不同形状,不同动作等分别做成一整套。
这样,就可以从这些胳膊腿儿里面挑一个形状,挑一个动作,组成一个小人,这就形成了一桢。
不同的形状,不同的动作的组合可以做很多个不同的动画,但是资源大小并没有改变。(不能贴图,google应该改进.)
另外一方面,碰撞检测。
在播放动画的时候,往往需要检测一个动画和另一个动画是否碰撞在一起。
例如,街霸。
那么检测碰撞,不是检测整个一桢的碰撞,而是这一桢中,人的胳膊腿儿等的碰撞。
这样,把一大块桢分开成小的模块,也更合理。
这个组成桢的基本单位,术语称作"Module"。
在这里不多说,其实我也说不出啥东西。还是要自己做,自己研究。
还有动画的应用。
例如我们要播放一个片头动画,这很常见。展示我们的logo之类的。
就这样。
while (true) {
playAnimation(mylogo);
我们要让主角行走。
while (true) {
player.setAction(walk); //把动画封装在一个player类中。
player.update(); // 之前玩家已经把动作设置成为walk了。现在来更新这个动作。其实就是动画的播放
public class Player {
int action;
public void update() {
switch (action) {
case walk:
playAnimation(walkAnimation); //呵呵,怎么样?这就差不多是AI了,只不过这个AI只是会播放动画
//再随便瞎写几句
if (keyPressed(Num4)) {
//如果按了四键,就让玩家的位置发生变化。相应的,playAnimation里绘制桢的坐标就发生了变化。
//这样看起来,主角就是一边做walk的动作一边移动了。呵呵。像真实世界中的。
posX -= 20; //往左移动。
}
break;
}
}
相关推荐
编写并运行第一个简单的MIDlet程序——“HelloWorld”。 **2.2.5 编译并运行程序** 使用KToolbar编译并运行MIDlet程序。 **2.2.6 MIDP程序打包以及混淆** 将MIDlet程序打包成.jar文件,并使用混淆工具优化代码。 ...
尽管最初的项目目标并未实现,但Java作为一种面向对象的编程语言,因其简洁、安全和跨平台的特性,于1995年发布了第一个版本JDK1.0,迅速获得了广泛关注。 Java不仅是一种语言,也是一种软件平台。Java平台的核心是...
Java语言起源于1995年,由Sun公司发布,最初并非专为互联网设计,而是从一个名为"Green"的项目发展而来,旨在为家用电器创建分布式代码系统。起初,项目团队考虑使用C++,但由于其复杂性和安全性问题,他们最终基于...
- 创建工程:在Eclipse中新建项目,定义项目名称和位置。 - 创建源文件:在工程中添加Java源代码文件(.java),设置包和类结构。 - 编写代码:使用Eclipse的代码编辑器编写程序逻辑。 - 编译运行:通过Eclipse...
《Java深度历险》这本书中的第一章《深入Java 2 SDK》旨在探讨Sun Microsystems发布的Java开发工具——Java 2 SDK的重要性和特点。该章节从Java开发工具的历史演变出发,深入介绍了Java 2 SDK的特性及其对Java开发者...
第1章 Java图形用户界面编程 1 实例1 布局管理 2 实例2 设计软件启动界面 9 实例3 实现多色窗口 11 实例4 切分窗口 13 实例5 丰富多彩的按钮 15 实例6 在窗口中显示背景图 16 实例7 在窗体中绘制图形 18 实例8 利用...
- **Java 1.0**:第一个公开发布的版本,包含8个包,212个类。 - **Java 1.1**:已过时。 - **Java 2 (1.2)**:改名后的版本,包含59个包,1520个类。 - **Java 1.3, 1.3.1**:主要是对Java 2的一些bug进行修复。 - ...
这里边包括:第1章 Java图形用户界面编程 1 实例1 布局管理 2 实例2 设计软件启动界面 9 实例3 实现多色窗口 11 实例4 切分窗口 13 实例5 丰富多彩的按钮 15 实例6 在窗口中显示背景图 16 实例...