再读J2ME游戏编程(2013.11.01)
决定再读一遍J2ME游戏编程。这本书是我2005年购自黄岛新华书店,那是第一次听说J2ME可以开发手机上的游戏,立刻就惊呆了,久久不能离去,再书店的角落捧着此书贪婪的看着,因为就这一本,可笑的怕被别人先买走,当天花了RMB79购入,这是近十天的生活费了。
重读此书,作为对作者Martin.J.Wells的崇拜之情,也因为此书是自己游戏开发的启蒙读物。虽然,从现在来讲,J2ME技术本身已经过时,但是此书中包含的游戏开发的思想,流程,步骤,术语,以及技术要点,都值得细细回味。
Java的成功秘诀,简单来说就一句话:Write Once,Run Anywhere。在我看来,Java的前身,star seven的电子设备,在当时应该是有一定的前瞻性的。只不过生不逢时,这就同2004年NOKIA的S80,S90系列一样,其他环境达不到,出现这么一个东西,没有无线信号,没有互联网,这样的设备,以现在的标准,确实不应该称之为下一代计算机技术。
oak比Java更好听,James Gosling为啥不继续使用这一个名字呢?Java的成功在于跨平台,在不同平台上实现了各自的虚拟机,然后上层统一使用一种语言。WebRunner搭上了当时的互联网的车票,具体到现在的ios,android,winphone平台,为什么不继续使用这一个技术?android自己实现的Dalvik虚拟机与KVM差别有多大?
Java的三大分支J2SE,J2EE,J2ME,目前J2ME已经没落,除了部分低端山寨机的MTK平台还继续使用之外,好像已经不多见。J2SE也在磕磕绊绊的发展,脚本化。只有J2EE在企业级开发上有足够的底蕴和积累,目前还是主流。
Java抛弃的多重继承,模板,运算符重载,确实是大部分程序员所厌恶的,也只因为此,Java让程序员成了一个大众化的,门槛较低的职业,也变成了人们眼中所谓的码农。
最后,到处都是微型设备,前景依旧光明,便携式设备室真是一个令人兴奋的产业。比如,NOKIA S30,S40,S60,S80,S90...SONY ERICSSON MOTOROLA ...由此而来的,是设备的分散化,碎片化,所以J2ME平台在设计之初,根据类别来设计,设计为“配置”和“简表”。
配置:对一组相似性设备的抽象底层描述。CDC AND CLDC
简表:更倾向于上层UI界面的一种抽象描述。MIDP
在不同的配置上,JAVA为它们设计了不同的虚拟机。比如,Hotspot VM,CVM,KVM。设计不同的虚拟机是一个正确的选择,我们不能兼容性限制再最低的硬件设备上,这对高端设备是一个不公平的折中。
J2ME安全控制在虚拟机安全(预校验)和应用程序安全(沙箱)。
MIDlet:JAR AND JAD,注意JAR和JAD的关系,以及为什么需要这么做?
最后,我们真的可以用这些有限的资源来开发一流的游戏,而且一个很棒的事情是不用把灵魂出卖给出版商获取¥200万美元的预算去开发一个大型游戏--如果果可以的话,创建一个成功的J2ME游戏只需要志同道合的几个朋友,最好都有点疯狂,当项目完成后,会有可靠的方法来实现盈利。
再读J2ME游戏编程之二(2013.12.09)
本内容是J2ME游戏编程书籍附录的Java2回顾,虽然比较简单,但是可以基本了解Java2的全貌。
一 Java概述
二 Java程序运行机制
三 对象
1.对象描述(状态和行为)/ Java对象描述
数据描述-->数据行为-->渲染层
Car:{int x,int y}-->move:{run x,y}-->paint:{setPosition}
2.实例化 构造器 域 方法
3.多个构造器
4.对象和内存
引用和对象的关系
没有什么途径可以释放内存
通过引用是否还在有用判断是否需要回收对象
四 基本语法
1.注释
2.基本类型boolean byte char short int long float double,没有unsigned
3.常用类型
数字:十进制=12 八进制=012 十六进制=0X12 长整型=12L 单浮点=12F 双浮点=12D
转义字符:
数值赋值:
4.运算符
赋值 算术 位操作(>> << >>>) 条件运算
5.语句
顺序 条件 循环
特例:break label
6.字符串String
判断字符串是否相等的问题== vs equals
String.valueof()//数字转字符串
7.数组
int array[];
int array[]={1,2,3,4,5,6,7};
array.length;//数组长度
五 面向对象编程
1.继承
extends
super()//父类构造器
Vector()//数据类型Java1.2
instanceof//判断实例
object.getClass().getName().equals("...");
2.重载和重写
3.abstract
虚拟类没法被实例化虚
虚拟方法子类必须被实例化
4.接口
5.访问保护
6.内部类
内部类可以不受限制的访问封装类的所有数据,封装类不能访问任何内部类的数据。
7.析构器
void finalize();//垃圾回收之前
8.this
不能用在static方法中
9.static
类共用属性
10.final
final修饰的属性和方法不能被改变
final修饰的方法不能被重载
六 异常
try{}
catch(Exception e){}
finall
七 其他
package i
再读J2ME游戏编程之三(2013.12.22)
本书的第二部分是介绍开发方式和基本API的使用。
虽然可视化集成开发环境更加友好方便,但是命令行方式更能够让我们理解运行应用程序的原理。在J2ME中,共分三步:
1.javac编译
2.preverify预校验
3.midp运行
J2ME在的API在设计上分为Application,Timer,Network,RMS,UI五大部分。
应用程序方面,一个MIDLET是一个应用程序,一个应用程序由JAM控制。MIDLET的API功能主要分为2个方面,即JAM告诉MIDLET一些事情,MIDLET告诉JAM一些事情。
定时器方面,分为Timer和TimerTask。这里没有什么特别的东西,和J2SE的里面是一致性。
网络方面,采用的时一套名为GCF的框架,采用了工厂模式。
数据保存上,采用了一套简单的RMS系统,都比较简单,没有什么可以多说的...
接下来是大头的一块,UI。
UI分为高级UI(Screen)和低级UI(Canvas)组成。Display上显示的唯一的一个Displayable。在Screen中,List,TextBox,Alert,Form.Form是一个比较特殊的控件,是其他控件的集合容器。在Canvas中,我们使用Graphics在Canvas上绘制Image.
这里面提到过两个问题比较有意思,
一个是何谓可变图像和不可变图像?不能修改的图像就是不可变的,否则是可变的。
另外一个是何谓双缓冲?先不要直接在屏幕上使用Graphics工具绘图,而是先创建一张图片,在这上面得到图片的Graphics,然后在这上面绘图,最终,把这个图片一次性绘制到屏幕上。
虽然这里主要以MIDP1.0进行讲解,但是,相信我,再MIDP1.0上积累的开发经验不会白扔的,在MIDP2.0上大多可以继续使用,并且拥有更好的特性。
事实上确实如此。
再读J2ME游戏编程之四(2016.01.04)
下面的内容作者以一个小游戏实例,讲解了一个游戏的基本框架和J2ME常用的API的使用,这个游戏就是去年非常流行的天天过马路的初级版。
一个游戏的核心,其实就是一个游戏循环,这个循环的每一次之中,把所有的任务都做一遍,要足够快,使得对玩家看上去有一种真实感,就像播放电影一样,播放电影是每一秒24帧,就是每一秒24个循环,游戏可能需要更快,不管什么游戏,基本结构如下:
void run()
{
input();
logic();
paint();
//计算FPS
if(curtime-lasttime>1000)
{
lasttime=curtime;
out("FPS");
FPS=0;
}
else
{
FPS++;
}
//通过FPS可以得到PFS,就是每一帧的时间,然后通过时间运算,可以发现是否有剩余的时间,有的话可以休眠线程。
FPS=24;
pfs=1000/FPS;
if(curtime-lasttime<pfs)
{
sleep(pfs-(curtime-lasttime));
}
//注意,以上两个FPS和PFS是两种不同的情况
}
在logic循环中,这里有一个问题,如何解决没有浮点数的情况下的游戏循环移动逻辑?本书的做法是:以100ms为一个渲染单位,假设20ms左右为一个游戏循环,每一个循环,都把这个循环的dtime相加,当到达100的时候,开始一次渲染。然后如果时间有剩余,积攒记录之。直到积攒到下一个100ms。
上面讲的是游戏的通用结构,说白了就是一个大循环。最后,让我们来熟悉一下游戏渲染的通用结构。
1.首先,要有一个应用程序管理器,在J2ME里面是JAM,MIDLET,这里面有应用下程序的入口和出口。
APPManager.java
start()/pause()/exit()....
2.应该有一个游戏菜单Menu.java,这里放置的是游戏界面的各个入口。
3.游戏场景GameScene.java,游戏的场景,游戏主循环(即本文开头的循环)在这里。游戏的主循环看需求是否要展开独立的线程。
void run()
{
while(true)
{
...
}
}
这里面包含所有演员的逻辑处理和渲染,使用接口。注意,这里要用双缓冲。
void paint()
{
Vector...paint();
ArrayList...paint();
}
4.Actor.java。演员,也就是平常我们所说的精灵。演员有自己的逻辑运算和渲染。
void ticker(float dtime)
{
}
void render()
{
}
5.碰撞检测:矩形碰撞
再读J2ME游戏编程之五(2016.01.21)
在本书的第三部分,作者使用J2ME提供的API做了一个详细的游戏,名Star Assault,中文名就叫空间大战吧。这里虽然是一个游戏实例,但是从另外一个角度来看,它有两个功能。第一是作者用MIDP1.0的API实现了MIDP2.0的游戏库API。第二,从整体来看,这个项目实现了一个简单的2D游戏引擎,至少具有初级的引擎模型。从这部分开始,通过再次阅读这个项目,来复习一下如何制作一个简单的游戏引擎。
首先,就是我们要说的精灵的实现。精灵是一个游戏内容的主体表现者,是不可或缺的一部分,它是通过如下代码结构实现的。
1.图片打包,杂凑和简凹。
这个在cocos引擎的时代已经非常常见了,即把许多散图打包在一张图片上,不过在J2ME时代要简陋的多,至少在本项目中,这一切都是有序的,不需要plist描述文件。
2.加载图像,这里,图像以左上角为基准原点。
void loadImage(String filename);
2.图像裁剪,用到一个概念,之前讲过的可变图像和不可变图像,我们把不可变图像绘制在可变图像上,完成了图像裁剪。
Image getImageRegion(Image source,int x,int y,int width,int height);
取得source图片的一部分,从x,y点开始去width,height的大小,并返回新的图片。
3.分离帧,既然上面的方法可以取出图片的一部分,我们可认为每次取出一帧,然后保存起来。
Image[] extractFrames(Image source,int x,int y,int wide,int high,int width,int height);
参数含义同上,只不过加了x,y方向上分别有多少帧的数字。
4.图像集,其实就是把多状态的精灵归集,每一个状态都有对应的Image[]
ImageSet.java
numStates;//状态数
stateFrameWidth[numStates];//状态帧宽度
stateFrameHeight[numStates];//状态帧高度
stateFrames[numStates][];//状态和帧数 ?复习一维数组和二维数组
addState(Image frames[],int aniTime);//添加状态及时间,实际上这个也是这一步中最关键的方法
void draw(Graphics g,int state,int frame,int x,int y);//绘制帧
5.精灵,精灵和图像集为啥要分开,作者并没有详谈,但是把图像加载和实体渲染分开,好像还是必要的。
Sprite.java,最注重的是动画处理,这里用到了我们之前说过的cycle(float dt),通过时间间隔决定是否换帧
curState;//当前状态
curFrame;//当前帧
Sprite(ImageSet set,int state,int frame);//初始化
setState(int state,bool force);//状态切换,是否强制
6.可能的改进
动画反向播放
状态自切换
事件触发器
图像集管理
资源数据化