??xml version="1.0" encoding="utf-8" standalone="yes"?> 最q在|上不断的看到有人问如何用BCB的TServerSocket和TClientSocketq行~程的问题,所以决定把我的一些编E经验告诉给大家,让大家能够尽快的掌握他们的用法?BR> 译Q张三丰 语嫣 所有的不^衡性最l归lؓ没有选择性。只要记住这个原则,容易区分可校正的不q性及Ҏ的不q性?BR> 复杂性控?BR> 一旦游戏已受宏观调控,游戏的^衡必要q入l节调校。如果游戏至达到有点乐可aQ且不存在明昄问题Q则已基本上完成宏观调控q可开始{向微细节。微观调控是游戏{划Zq一步完^衡性而实施的手术。一个小手术一般被定义为:变化值相对于一个“全球”数|影响许多其它的游戏要素)要少?0%Q相对于一个“地Ҏ”数|一个单一游戏要素Q则应少?0-40%?BR> ȝ 介绍一个游戏开始的画面?BR>先徏两个c?MenuScreen.java SimpleCustomMenuWithBGFont.java用于试 首先说下SimpleCustomMenuWithBGFont.java import javax.microedition.midlet.*; public class SimpleCustomMenuWithBGFont extends MIDlet implements CommandListener { Display display; public SimpleCustomMenuWithBGFont() { protected void startApp() throws MIDletStateChangeException { protected void pauseApp() { } public void commandAction (Command cmd, Displayable dis) { } 主要来介l一下MenuScreen.java public class MenuScreen extends Canvas implements Runnable { // 讄字体 // 讄颜色 static int startHeight; // 菜单开始的高度 static final int spacing = highFont.getHeight()/2; // 菜单w的距?BR> // 菜单?BR> static final String[] mainMenu = {"New Game","High Score","Settings","Help","About"}; thread = new Thread(this); } Zq里介绍一下J2ME中的字体: 字体风格由Font cM的静态常量进行定义,字体风格是可以多选的Q可能的取gؓQ?BR>STYLE_BOLD : 加粗 字体由Font cM的静态常量进行定义,可能的取gؓQ?BR>SIZE_LARGE: 大号字体 创徏字体时ƈ不是通过Font cȝ构造方法来创徏Q而是利用Fontcȝ静态方?BR>static Font getFont(int face,int style,int size)来创建字体。或者利?BR>static font getDefauleFont()来创建系l默认字体?BR>在MIDP v2.0中,为Fontcd加了新的一个方法用于创建字体,?static Font
首先要讲一下他们的一些设|(属性)Q?BR>TServerSocket 的几个属?BR>Active
是否是工作状态,可以把它讄为ture或false来启动和停止侦听?BR>Port
本机侦听的端口,可以讄Z大于1024的数Q?BR>ServerType
服务端的工作方式Q有两个选择Q一个是stNonBlocking 非阻塞方式,一个是stThreadBlockingU程d方式Q默认是非阻塞方式。用非阻塞方式编E比较简单(我个为)Q用d方式Ҏ个连接必自qU程来控制收发?BR>ThreadCacheSize
~冲的线E个?当ServerType是stThreadBlocking时有效?BR>TClientSocket 的几个属?BR>Active
是否是工作状态,可以把它讄为ture或false来同服务端徏立或断开q接?BR>Host
服务器(ServerSocketQ的计算机名Q是一个字W串
Address
服务器(ServerSocketQ的IP地址
ClientType
客户端的工作方式一U是ctNonBlocking非阻塞方式,一U是ctBlockingd方式Q默认是非阻塞方?BR>Port
同服务端(ServerSocket)q接的端口,q个端口是ServerSocket的侦?nbsp; 端口
在设|ClientSocket的(HostQ主机名和设|(AddressQIP地址是等效的Q但设主机名需要网l具有DNSQ域名解析服务)服务器,而且设主机名要比设主机的IP地址q接的速度慢一些。徏议用Address来同ServerSocketq行q接?BR>
讄好所有的属性后QServerSocket控g可以通过它的Active属性置为true来进行侦听了。ClientSocket可以通过讄它的Active属性来同ServerSocketq行q接。连接成功就可以q行通讯了。在q个q程中会产生一些事Ӟ下面说说他们各自的事件?BR>
TServerSocket的几个事?BR>OnAccept
当一个客户同服务端连接成功后产生q个事gQ生这个事件后q个q接可用了Q可以对q个客户q行发送和接收数据?BR>OnClientConnect
当一个客h在同服务端徏立连接的时候生此事gQ在q里你可以决定是否接受这个连接?BR>OnClientDisconnect
当一个客户同服务端的q接断开的时候生此事gQ你需要在q里q行一些处理,如从q接列表中清除次q接释放内存{?BR>OnClientError
当客户同服务端出现错误时产生此事Ӟ在此事g中你可以通过讄ErrorCode Q?0来屏蔽系l的错误提示。这样就可以避免讨厌的英文错误了。根据ErrorEvent的不同的值来得知发生了什么错误,它有一下几中错误类?BR>eeGeneral 未知错误
eeSend 发送数据出现错?BR>eeReceive 接收数据出现错误
eeConnect 客户hq接时出现错?BR>eeDisconnect 客户断开q接时出现错?BR>eeAccept 接受一个客h发生错误
一般来Ԍ当发生错误的时候这个客Lq接已l不可用了,要对q个客户q行q接p|处理?BR>OnClientRead
当服务端收到客户端发来的数据的时候生此事g。接收客L的数据都在这里进行?BR>OnClientWrite
当服务端发送数据的时候生此事g?BR>
TClientSocket的几个事?BR>
OnConnect
同服务端QServerSocketQ连接成功后生此事gQ生此事g后才说明q个q接可用了,q时才可以向ServerSocket发送数据?BR>OnConnecting
正在同服务端q行q接是生此事g?BR>OnDisconnect
同服务端的连接断开后生此事gQ生此事g后ClientSocket的Active属性就为false了,q时q个q接׃可用了,必须重新q行q接才能向服务器发送数据?BR>onError
当Socket发生错误时生此事gQ这个事件的意义和ServerSocket的Error事g完全一P只是它没有eeAccept错误?BR>OnRead
当接收到服务端发来的数据后生此事g?BR>OnWrite
当向服务端发送数据的时候生此事g?BR>
上面介绍了一下这两个控g的基本属性和事gQ在掌握一些方法就可以用这两个控gq行~写通讯E序了,今天写的有写累了Q改天在写它的方法ƈ具体使用q两个控件编写一个通讯E序Q有两个选择Q一个是局域网的聊天程序,一个是局域网内的控制E序Q大家喜Ƣ那一个呢Q给点徏议吧.
]]>
作者:Tom Cadwell
一个伟大的设计和一个杰出的游戏之间往往只有一个缺乏游戏^衡性的区别。多数游戏策划要通过反复试验才学会游戏^衡的基本原理。如果他们幸q的话,也许可以得到同事传授的一两个窍门。精通游戏^衡的人往往警惕C守着自己的秘密,或者无心与人分享。结果是虽然有关游戏q性的信息实存在Q但是可得到的却很少。这文章试NqC个获得游戏^衡性的Ҏ?BR>
什么是游戏qQ?BR>
Sid Meier 曄说过Q“一个游戏是很多有趣的选择的集合。”因此得出的是如果游戏失d^衡,׃减少q些选择而媄响游戏性。一个理想的游戏应该l过一pd的选择Q最后以胜利或其它完成的条gl束。有时一些选择明显成ؓ唯一的选择Q或明显是无效的。如果在某一阶段Q游戏出C有唯一的选择Q而游戏却没有l束Q就说明游戏的^衡性有了问题?BR>
几乎所有通常所谓的不^衡都来自选择权的减少。例如,在一个策略游戏里Q如果某一U部队的作用和费用相比过于划,׃造成其它的部队几乎或完全没有作用。这U情况不仅只留给玩家一个选择Q无从选择Q,而且使玩家受到很多不相关的干扰。这些干扰实际上让游戏变得比较迷乱,减损了游戏性,而且让玩家感到灰心?BR>
游戏大富?Monopoly)中就有很好的游戏不^衡性的例子。在游戏的后期,玩家们L量拖长呆在监狱里的旉。显然地Q玩家在游戏后期的最好的{略是q监p且不付钱出来,希望别hq入自己的领土而破产。在玩大富翁的最后阶D,无需再作选择Q游戏基本结束了。没有h再选择是否购买财Q也很少有机会再Ҏ游戏规则新的财Q因为房子已l被用完Q,而且因ؓ资已经被几个h集中Q所以也不再有交易可
做。一旦生这U情形,游戏变成每个玩家有一定的机率莯而基本上l束了。此时玩家可以做的很,除非靠运气得胜。这情景与游戏前期及中期大相径庭Q那时玩家往往忙于大施战术、y妙夺取利益、陷宛_手或谨慎购买“重量”黄艌Ӏ绿色或p色的C?BR>
q只是一个不q性的举例说明。在游戏中存在许多不同种cȝ不^衡性。所有的不^衡性都与没有选择性或~Z选择性有兟?BR>
?太昂贵却用处不大和便宜而且有效Q游戏选择通常与游戏代L联系Q不是牺牲其它的选择、游戏金钱或其它的商品。当一个选择太昂贵以致用处不大,或者太便宜而成为明昄选择Ӟ游戏的不q性就出现了,因ؓ有一些游戏选择无效了。虽然此cMq性最为普遍,但是通常l过单地调整q些选择的h格或者是效果可以纠正过来?BR>
?玩家旉的不qQ大多数游戏q性对比的基础是以玩家Z个选择而必L弃其它的各种不同选择的代h衡量。我们很Ҏ忽视玩家必须消耗时间执行每一个选择。在一个即时游戏里Q玩家在游戏里没有无限的旉Q所以时间不仅是一个资源,而且是一个有限的资源。在一个非x游戏里,游戏旉不受限制Q但是玩家的旉是受限制的。这U不q性基本上是另一U太昂贵或太廉h不^衡性的表现Q只是这里这些游戏的代h不是有Ş的。游戏星际争?Starcraft)里的虫族(Zerg)是一个很好的q种不^衡的例子。虽然虫族从h上与其它族类是^衡的Q但是就玩家的时间而言他们很容易被刉及使用。主要由于这个特点,虫族在游戏星际争霸发行之后大U长?个月中,在联赛与竞赛中一直是最受欢q的U族?BR>
?技术水q的不^衡性:随着玩家的游戏技能不断提高,不同的游戏选择的相Ҏ效性也会改变。如果一U选择Ҏ操作Q而另一U极难操作,则结论是一个资q家和一个新玩家的对q两U选择的相Ҏ效性的判断是完全不同的。这是游戏开发者的一个常见的陷阱Q因Z们一般比较接q“高U玩家”的水^Q所以经常看不见新玩家所要面对的问题。但是从另一斚w看,随着操作水^的提高,而游戏性也同时“进化”,通常被认为是一件好事。所以注意到q种q性很重要Q但是也要认识到上面说的现象也很普遍?BR>
?强制的劣势或优势Q在一个对战的游戏里,一些操作的l合使得某一Ҏh优越性。这样不仅是典型的不q性(因ؓ有一个选择明显最好)Q这U状况还是不公^的。在一个多人游戏中Q最好避免不公^的情况出玎ͼq也是保证游戏^衡的重要一招?BR>
如何辑ֈ可^衡?BR>
游戏q性通常被认为是alpha或beta试的事情,但事实上像M工程Q好的准备工作是实现良好游戏q的关键。优U的游戏设计具有极大的可^衡性,也就是指游戏pȝ可以较容易地调整到^衡的状态。如果系l没有可q性,费尽周折也不可能游戏调整到q?BR>
一个游戏是一个系l,在设计初期应用良好的pȝ设计方式带来较好的可^衡性。好的系l设计方式可以分成三个重要步骤:游戏要素的模块性,q诏的设计宗旨及对复杂性的控制与调节。在设计的早期就采用q些Ҏؓ设计师在游戏试的alpha和beta阶段节省大量的时间?BR>
游戏要素的模块?BR>
游戏要素的模块性归l于每个游戏要素只ؓ了一个特别目的存在,如果可能的话Q尽量做到只有一个单一的目的。只要诏彻这个原则,调整一个游戏要素只会改变游戏的某个斚w而不是许多方面?BR>
有一个很好的例子Q说明游戏要素缺乏模块性会造成游戏开发h不必要的ȝ。在星际争霸的beta试中,暴雪QBlizzardQ星际争霸的开发hQ有一套相当清晰的伤害pȝQ其中每一늧各有三种伤害方式Q爆炸性的Q标准型的或冲击性的。每U伤x式都有一个根据外型大而不同的伤害pL——爆炸性伤宛_大型目标最有效Q冲L伤宛_型目标最有效Q而标准型伤害可用于Q何目标。其中一个兵U——飞龙(MutaliskQ,不断l^衡性带来问题,因ؓ功能性上看,不可以被分ؓ大、中或小型中的一U。如果将飞龙设ؓ中型늧Q则它对于爆炸性武器类型的늧来说抉|力太强;如将其设为大型,则其相对爆炸性武器类型的늧Q这U兵U一般是飞龙的天敌)又过于脆弱。暴雪(BlizzardQ不能仅仅修改爆炸性相对于大型늧或爆炸性相对于中型늧的伤害系敎ͼ因ؓq样做的话就会媄响一大批其它늧的设|。也无法修改爆炸性武器兵U的d|因ؓq样会媄响其它的很多的设|?BR>
更让人困惑的是飞龙有两个重要角色——防I军与防步兵Q陆战兵U没有空中攻击能力)Qƈh相同的基本伤宛_Q而其他类似的兵团Q侦察机QScout、幽灉|机-WraithQ却有不同的武器pȝQ可以根据具体角色进行调整?BR>
因ؓ在伤害系l和飞龙的设计上~Z模块性的原因Q暴雪直到游戏上市后五个月才佉K龙兵U达到^衡。这q不是因Z正是不可能做刎ͼ而是因ؓ~Zpȝ模块性而修正非常困难。飞龙在星际争霸里具有一定独特的用途,如果暴雪它的^衡参C其它不相关的늧分开设计Q^衡将大ؓҎ。最单的Ҏ是为飞龙(及其它类似兵团)d一个独立的cdQƈl予它一个针对各U伤害的自己的防御系数。如果设计师飞
龙的I军与地面攻d分开来,调节q也会变得单?BR>
当然Q星际争霸的多数设计都有相当E度的模块性。施法者(SpellcasterQ兵U具有清晰的用途和相对Ҏ的角色就是一个很好的例子。事实上许多法QSpellsQ,包括寄生虫(BroodlingQ和EMP振荡波(EMP BlastQ,h非常Ҏ的作用,使调整这些兵U的q性就Ҏ得多?BR>
良好的系l模块性不仅是游戏q性的前提Q它q是朝着解决的方向走q一步。有一个良好的模块性可以设计师针对各U特D问题轻松进行调_而不会媄响到其它pȝ?BR>
q诏的设计宗?BR>
q诏的设计宗旨可能是在初始设计阶D要遵守的最重要的原则,但是往往Ҏ因ؓ政策问题、疏忽大意或~Z良好沟通而被忽视。连贯设计宗旨的定义是如果游戏要素没有根据游戏的大局q行同步设计Q最好的l果是它会玩家偏移主要的游戏感觉,最坏的可能性是它会损害主要的游戏感觉。这U情况存在于~Z中心控制或开发时间很长的游戏中?BR>
较有名的多用Ll游戏(MUDQDurisQLand of BloodlustQ是Everquest—“无的d”的原型Sojourn的姐妹版Q就因此带来太多问题。其中一个例子是Q某个程序设计h自行~入一个他自己感兴的角色cd。虽然这个角色类型本w很有意思,但是它其它几个cd变得无用或大失威力。这个角色类型拥有了其它U族专有的技能,而正是这些技能的专有性才使得q些U族实用而且好玩。这个程序员q带来很多类似的?BR>戏^衡性问题。他的主要目的是创造一个他感兴的cd。这与多用户|络游戏开发h惌创造有、独创的角色q与整个pȝ相吻合的愿望相冲H。他的类型非但不独特Q因为是从其它各cd中各取一部分特点)Q还与游戏的其它部分格格不入?BR>
复杂性控制应概括为:“保持简单、易懂”。过于复杂的游戏pȝ让h费解Q因此,也更隑ց到^衡。一个过于复杂的pȝ通常是因为最初的设计太糟p和无休止的d补丁Q理Zq些补丁是合理,但实际上是不q诏的一团糟Q,或者是太常见的“太多厨师呆在一个厨戉K”的现象Q这通常也说明缺乏设计宗旨一致性的问题。复杂性控制的另外一个优点就是它避免了一些潜在的游戏性的问题。尤其是Q正如复杂的游戏pȝ让h费解也因此不好^衡,也更难让玩家理解Q甚至从某一E度开始玩家很隑ֆ享受游戏。一个很常见的设计错误是Z游戏复杂化而牺牲游戏深度,那将Ҏ戏^衡调整造成极大的困难,q成Ҏ戏性的困惑和费解?BR>
基本游戏qq程
除了基本的规则和技巧之外,q程是非帔R要的。游戏的qq程有几个步骤,每个步骤都有各种各样的技巧?BR>
首先要考虑的是让游戏进入一个有及可玩的境界,q就需要宏观调控,或者说让游戏中的大部分要素臛_辑ֈ基本上^衡,而且不存在Q何要素过分地不^衡。只要达到这个状态,可以l细调游戏要素的具体部分Q如RTS游戏里的U族或派pR?BR>
当然在游戏alpha试阶段之前通常应已q行了宏观调_所以可能随着新功能的增加要重新进行调整。家园(HomeworldQ的ȝ划Erin Daly提出Q应相关的功能在同一旉加入Q然后做一个宏观调控,基本上这是在整个开发过E中保持游戏可玩性的最有效的方法?BR>
一旦实现最后的宏观调整Q最好在alpha试阶段的后期,可以对游戏q行微观调控1使游戏^衡达到完的E度?BR>
宏观调控
提供一个可q的游戏系l显然只是达到游戏^衡的W一个步骤。即便是最完美的设计也需要变成现实,而在实施的过E中错误׃出现Q在初期设计中就l常会出现小错误。许多游戏h值在整个游戏实现之后才能被清楚认识到。在q些情况下,设计者必dalpha试阶段之前及测试期间运用宏观调控技巧校正^衡倹{?BR>
宏观调控应在微观调开始之前结束;如果游戏的基q在不断改变Ӟ较小的^衡性的改变变得没有效果而无用。在q行宏观调时Q目的是“找到”在设计案中描述的游戏性目标。当Ӟ在你q不清楚如何表明核心游戏性时是不可能q行游戏l节的调整?BR>
Z瞄准核心游戏性,明确地说明核心游戏性及其如何体现是很重要的。只要做到这一步,可以徏立一定的基线Q也是Ensemble Studios1所谓的“定锚”。D例说明,你也许设立游戏速度的基Uؓ“大U?0分钟长的游戏”,或者设立角色韧性的基线为“被一个危险怪兽d3ơ是致命的”。一旦你为每个游戏因素(一个地图、一个角色类型,一D对话等{)都找到满意的基线Q就可以利用q些游戏要素基线为根据扩展游戏?BR>
q性数?BR>
一旦完成某一特定要素的宏观调控,在有些情况下可应用^衡性数学将l果复制到类似的要素中去。虽然用q性数学改善系l的功效q不定Q原因是很难计算一些微妙的l节Q但是它对确定不同游戏要素中的基U还是有效的。公认的、几乎对每一个游戏都有帮助的一个公式是成本效率方程式?BR>
成本效率公式说明Q针对一个成分来_
游戏威力×耐久?效力
而且Q?BR>qx根(游戏威力×耐力÷成本的^方)=成本效力
游戏威力有可能是火力Q伤x×发速度Q或Ҏ。耐力可以是用次数或被击Ҏ。成本代表游戏资源,通常是金子、钱币或回合Q如赛中走一步的真正成本是一个回合)。另一个有用的方程式主要适用于策略游戏和其它“战斗”场景的是分解式方程式。“分解式”反应了战斗场景中众多小体ȝ有效性虽然与几个大群体的战斗力相{,但效力却不相同的特点。众多小体通常效力,当然q是假设不存在其它微妙的因素Q如大团体攻d늧Ӟ杀伤力太大而造成费Q。这是因为群体在个体逐渐d之后逐渐丧失威力Q而一个大늧可以支撑较长旉Q所以不会因逐渐损失而失L力。据此,相对有效性的公式被定为:
有效性的减损Q相对于较小的个体)=
0.5+0.5×[较大个体的数量ï个体的数量Q相同的hQ]
所得到数字的倒数则是较大个体有效性的增加?BR>
q些公式和其它“^衡性数学”对于初步的q性特别有用。最好避免从数学上实现完的q性,除非是相当简单的游戏pȝ。比如说Q因为游戏规则简单,q游戏Riskq不是特别困难,且玩家的选择可以作到相当的量化。^衡游戏大富翁QMonopolyQ是可能的,但是会比游戏Risk困难Q因为随机因素(如滚骰子Q相对于Risk来说可造成更普遍的影响Q而且也因为大富翁有更多数量的特别游戏因素Q运气牌、抵D则,监狱
{等Q。譬如在一个现代的RTS中,能够在这h多复杂性的情况下得到完的数学q性,q当于完成了博士论文?BR>
微观调控最大的挑战是找到问题。一旦找到问题,可以开始稍微调整数|但要注意不要因此再生出新的问题。良好的要素模块和预先计划在q一阶段很有效果——没有它们,可能做不到在一个合理时间范围内完成游戏的^衡?BR>
辨识较小的不q?BR>
{划有几个技巧可辨识较小的不q性。其中最明显的做法是大量地测试游戏,L一贯受惠或占优势的ҎQ或L从不被用的Ҏ。另一个常用方法是与一个试验h或另一个策划讨论假讄情景或与其对战,扑ֈ一个一致认Z产生的结果,然后在游戏中试是否会发生同Ll果?BR>
如果{划用第一U方法,只是L占优势(或从不被使用Q的ҎQ确定这U情况生的实际原因是很重要的,q确认事情是否应该这样发展。对不^衡性进行分c,q尝试将其归cd典型的不q性中Q有助于理解问题。基本上是了解不^衡性的cd及特征就能够调整它?BR>
q年来,愈加行的方法是U密记录Q不告诉玩家Q游戏成果及l计数据。游戏世U帝国(Age of EmpiresQ,雪乐山(SierraQ发表的几款游戏及斗争阴影(StrifeshadowQ都受益于这个技巧。有时这些统计数据具开导性,有时也极兯导性?BR>
Ҏ有的数据都应有所保留。有时一个不成熟的测试h会带来很不正确的结果,只因Z们不熟悉游戏Q而且没有Z全面试Q或只是试最Ҏ的部分)。同LQ一个过于成熟的试人群也有可能忽视其它{略的潜力,或被困在一个很高却较模糊的不q点,而这些不q与其它更明显的不q点相比显得不那么紧迫。Ethermoon׃公司在游戏斗争阴影里应用的一个极为有效的技巧就是夸大在beta阶段的补丁中的游
戏^衡性变化,来怂恿玩家试新的战略Q而不再l“抵抗”新的变化?BR>
发现不^衡性的W二个方法有时被UCؓ“追逐不q性”,也就是一个假定的情景被定义后Q而由此生的各种可能的行动及l果都应是符合设计的。例如,一个坦克部队的冲锋应该被认为打败一个轻型R队的q攻Q但同时也应该受到轻度伤宻I而面寚w坦克步兵团的反攻则应受到重创。如果在实际的游戏中Q一个坦克部队的冲锋可完全歼灭一个轻型R队ƈ可以与防坦克步兵团不分胜负,此时坦克部队的过于强大就造成了不q
性。追逐不q性是十分重要的,如果严格地执行,很容易就可以发现75%以上的较不q性问题。游戏往往不按照策划所愿望的某U特别方式发展,特别是在一个对抗型的多人游戏中Q一个“地区性”^衡h值的微小变化可以造成游戏的^衡与不^衡?BR>
要紧记的一条是无论何时q行不^衡性的搜烦Q在游戏早期所讄的游戏要素,往往比后期的游戏要素要敏感得多。仅仅因Z个早期游戏要素的不^衡性会影响在它之后讄的所有东西,而后期游戏要素能够制造麻烦的旉有限。正如有必要在做微观调控之前先做好游戏的宏观调控Q也有必要先q早期的游戏因素?BR>
修整较小的不q?BR>
一旦L识ƈ证明了不q性,是否Ҏq行修正呢?是的Q如果游戏已被设计ؓҎ调整Q?一个非常可调的游戏具备的素质能让设计师在不间接影响其它游戏因素的情况下Q专门对付某一不^衡性?BR>
校正的重要一Ҏ保持l调的水准(往的斚w惻IQ尤其是升游戏的时候。一个过于强大的游戏要素Ҏ使其它要素失L力,而一个过于无力的游戏要素则会被忽略而毫无效果?BR>
q很重要的一Ҏl调时不要媄响到其它的游戏数|譬如Q在角色扮演游戏里考虑一个叫做“火球”的W咒Q它是火pȝ咒的一U。如果火球威力过大,{划可做的就是全面降低火p魔法的威力Q或火球降U。很明显你应该做的是选择“地Ҏ”的解决ҎQ在l调全面火系法之前火球降U。这是一个很单的例子Q在大多情况下,游戏要素之间都存在一定程度的互相依赖。}慎地考虑一个改变将会带来的冲击力,试
使用专门解决问题而不影响其它游戏要素的方法?BR>
最后,避免“过度解决”不q性。当{划在同一旉q用多重不同的细调方法来解决一个特定问题时׃产生“过度解决”的情Ş。这样就很难军_变化所带来的效果,因ؓ你应用了多重独立的可变性来影响一个不独立的可变性。“过度解决”也有可能因意外地媄响其他游戏要素而带来麻烦?BR>
开发游戏过E当中,在面对许多细节所带来的庞大冲L很容易偶忽视最l目标。保持真实地对待所惛_到的游戏性,从根本上贯彻游戏q的原则是很辛苦的Q但只有q样才可以保证高质量的游戏^衡,q且避免beta试拖得太久。多人游戏日渐受到青睐,游戏q应尽可能地做到最好。太多充满希望的多h游戏都因为^庸的游戏q而黯然失艌Ӏ?
]]>
2.面向对象E序设计?个重要原则是什么?
装、多态性和l承?BR>
3.什么是变量Q?BR>变量是一U命名的内存地址Q变量的内容可以在程序运行时修改?BR>
4.什么样的变量名无效Q?BR>变量名只可以?字母QA/aQ、下划线Q_Q及圆W号Q?Q开_数字做开头的变量无效?BR>
5.如何创徏单行注释与多行注释?
单行注释以?/开始”,在行束。多行注释以?*”开始,以?/”结束?BR>
6.语句在一行中的放|位|有限制吗?
没有限制。Java是一UŞ式自q语言?BR>
7.JavaZ么要严格指定其简单类型的取D围和行ؓQ?BR>Java严格指定其简单类型的取D围和行ؓ是ؓ了确保跨q_的可UL性?BR>
8.Java的字W类型是什么?它与其他大多数程序设计语a的字W类型的不同之处是什么?
Java的字W类型是char。Java字符采用Unicode~码格式而不是ASCII格式Q后者是大多数其他计机语言采用的格式?BR>
9.因ؓM?值都为trueQ所以boolean值可以取M你想要的|对吗Q?BR>不对Qboolean值只能是true或者false?BR>
10.解释自增q算W的前缀形式与后~形式的不同?
当自增运符在其操作C前,Java会先执行对应的操作Q然后获得操作数的值用语表辑ּ的其他部分。如果运符位于操作C后,Java 会在自增之前获得操作数的倹{?BR>
11.在表辑ּ中,byte和short升Z么类型?
在表辑ּ中,byte和short升?int cd?BR>
12.总述什么时候需要强制{换?
当在不兼容的cd之间转换Ӟ或者窄域{换发生时Q需要用强制{换?img src ="http://m.tkk7.com/yangsail/aggbug/3046.html" width = "1" height = "1" />
]]>
import javax.microedition.lcdui.*;
//import java.util.*;
Display pauseDisplay;
boolean isSplash = true;
MenuScreen menuScreen;
MenuScreen menuScreen = new MenuScreen();
display = Display.getDisplay(this);
display.setCurrent(menuScreen);
}
}
protected void destroyApp (boolean flag) throws MIDletStateChangeException {}
}
q个cd?是作ؓ了测试用的?/P>
import javax.microedition.lcdui.*;
static final Font lowFont = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_PLAIN, Font.SIZE_SMALL);
static final Font highFont = Font.getFont (Font.FACE_MONOSPACE, Font.STYLE_BOLD, Font.SIZE_MEDIUM);
static final int NEW_GAME = 0;
static final int HIGH_SCORE = 1;
static final int SETTINGS = 2;
static final int HELP = 3;
static final int ABOUT = 4;
static final int MENU_ITEM_COUNT = 5;
static final int lowColor = 0x0000FF00; // Not Highlighted
static final int highColor = 0x000000FF; // Highlighted
static final int highBGColor = 0x00CCCCCC; // Highlighted Background
static int width; //屏幕?BR> static int height; // 屏幕?/P>
// 当前高亮昄的烦引号
static int menuIdx;
Thread thread;
// 背景?BR> Image bgImage;
//构?BR> public MenuScreen() {
width = getWidth();
height = getHeight();
// 计算菜单开始的高度
startHeight = (highFont.getHeight() * mainMenu.length) + ((mainMenu.length-1) * spacing);
startHeight = (height - startHeight) / 2;
// 默认所选ؓ菜单的第一?BR> menuIdx = 0;
try {
bgImage = Image.createImage("/res/bg.png");
} catch (Exception e) {}
thread.start();
}
public void run() {
while(true) {
repaint();
}
}
public void paint(Graphics g) {
//清屏
g.setColor(0x00000000);
g.fillRect(0,0,width,height);
// 背景
g.drawImage(bgImage,(width - bgImage.getWidth()) / 2, (height - bgImage.getHeight())/2,20);
for (int i=0; i<mainMenu.length; i++) {
if (i==menuIdx) {
//g.setColor(highBGColor);
//g.fillRect(0,startHeight + (i*highFont.getHeight()) + spacing,width,highFont.getHeight());
g.setFont(highFont);
g.setColor(highColor);
g.drawString(mainMenu,
(width - highFont.stringWidth(mainMenu)) / 2,
startHeight + (i*highFont.getHeight()) + spacing,
20
);
} else {
g.setFont(lowFont);
g.setColor(lowColor);
g.drawString(mainMenu,
(width - lowFont.stringWidth(mainMenu) ) / 2,
startHeight + (i*highFont.getHeight()) + spacing,
20
);
}
}
}
protected void keyPressed (int code) {
if (getGameAction(code) == Canvas.UP && menuIdx - 1 >= 0) {
menuIdx--;
} else if (getGameAction(code) == Canvas.DOWN && menuIdx + 1 < mainMenu.length) {
menuIdx++;
}else if (getGameAction(code) == Canvas.FIRE)
switch(menuIdx) {
case NEW_GAME: System.out.println("Start New Game"); break;
case HIGH_SCORE: System.out.println("Display High Score"); break;
case SETTINGS: System.out.println("Display Settings"); break;
case HELP: System.out.println("Display Help"); break;
case ABOUT: System.out.println("Display About Info."); break;
}
}
字体的属性由Q字体类型,风格和字体大构成,h意颜色ƈ不是字体的属性。字?BR>cd由FormcM的静态常量进行定义,可能的取?
FACE_MONOSPACE: {宽字体
FACE_PROPORTIONAL: 比例字体Q非常宽
FACE_SYSTEM: pȝ字体
STYLE_ITALIC Q斜?BR>STYLE_PLAIN Q常?BR>STYLE_UNDERLINEDQ带下划U字?/P>
SIZE_MEDIUM: 中号字体
SIZE_SMALL: 号字体
getFont(int fontSpecifier),参数fontSpecifier的取D围被定义为Fontc?
的静态常量,目前只能׃U取|
FONT_STATIC_TEXT:静态文本字?定义了屏q显C时讑֤采用的字?BR>FONT_INPUT_TEXT:输入文体,定义了在用户输入时设备采用的字体
在显C文字时,如果文字q长而超q当前屏q的宽度,那么多余的部分将无法昄,
所以ؓ了有效地昄文字可以用检字W或者字W串的宽度的Ҏ,q序判断每
行输出的字符?来达到更好的昄效果.
]]>
囄资源乃是游戏的外,直接影响一个游戏是否看上去很美。在J2ME游戏开发中Q由于受到容量和内存的两重限Ӟ囄使用受到极大的限制。在q种环境中,处理好图片的使用问题显得更加重要?BR> 本文从容量和内存两个斚w谈谈J2ME游戏囄处理的基本方法?/P>
一 减少囄定w
Ҏ1Q将多张png囄集成C张图片上?BR> q是最基本也是最有效的减png囄定w的办法了。比如你?0张png囄Q每?0×15Q现在你可以把它集成C?00×15或?0×150或者X×X的图片上厅R这张大png囄的容量比10张png囄的d量小很多。这是因为省M9张图片的文g_文gl束数据块等{,而且合ƈ了调色板Q如?0张图片的调色板恰好相同,则省M9张图片的调色板所占的定wQ这是个不小的数字)
Ҏ2Q减图片的颜色?BR> 减少颜色也算是一个方法?我想说的是什么时候减Q谁d。如果游戏完成后发现定w出Q此时在用优化工具减颜Ԍ虽然能降低图片容量,但图片效果可能就不让你满意了。所以,在美工作图时p定使用的颜色数Q手机游戏用的是象素图Q即一个象素一个象素点出来的图像,所以预先规定调色板颜色数量是可以办到的。不q,最l用优化工具也是有用的Q有时候相差一两种颜色Q但效果差别q不大,定w却可以变一些。呵呵,减少颜色实可以是一U方法?/P>
Ҏ3Q尽可能使用旋{和翻?BR> q点不用解释?nbsp;
Ҏ4Q用换调色板技术和自定义图片格?BR> 如果前两U方法还不能满你对定w的要求,而你的游戏中恰好使用了很多仅颜色不同的怪物Q那么可以试试换调色板技术。J2ME规范中规定手可以支持png格式的图片,每张png都带有调色板数据Q如果两张图片除了颜色不同而其他(包括颜色敎ͼ完全相同Q则只要保存一张图片和其他囄的调色板Q这相对于保存多张图片来说节省了不少定w。不q这个方法挺ȝQ你得了解png文g格式Q然后做一个工h取出调色板数据和调色板数据块在png文g中的偏移。内存中保存囑փ仍用Image,如果要换调色板,则将png文gdC个字节数l中Q根据调色板数据块在png中的偏移Q用新的调色板代替原来的调色板数据,然后用这个字节数l创建出换色后的Image。也怽觉得保存一张png和n份调色板数据的方法有Ҏ贏V至多保存?份调色板数据啊!如果直接图像数据提取出来,在加上n份调色板数据Q岂不是更节省容量。但是用上面的ҎQ我们还可以用drawImage渲染。如果这栯定义了图片格式,那只有自己写个渲染函CQ这倒还可以Q只不过put pixel的速度在某些机器上非常慢。或者自己构造png格式数据,再用Image.如果你真得决定这么做Q我q有个小Q不要对囑փ数据q行压羃Qzip压羃大多数时候比你写得压~算法好(参见J2ME Game开发笔讎ͼ压羃q是不压~?。论坛上有位朋友提过使用bmp格式代替png格式Qjar中图片容量更,也是一个道理?/P>
?减少囄所占内?/P>
1 囄所占内存的计算
png囄所占用的内存ƈ不对应于囄定w。图片占用的内存的计ؓQwidth*height*bpp。bpp即ؓpȝ内置的颜色位数。以Nokia 6600ZQ象素格式ؓ565?6位。所以一?00*100的图片占?00*100*(16/8)=20000字节Q约?9.5k的内存。象素格式是固定的无法改变,所以只有减图片的宽和高才能降低其消耗的内存?/P>
2 减少Image对象数量可节U大量内?BR> 减少Image对象数量不等于减图片数量。我的意思是_一张集成图保存在一个Image对象中,通过setClip的方法从q个Iamge对象中选取你需要的囑փ渲染。不q这个方法牺牲了一炚w度Q每帧都从集成图Image中减切图像的速度比无减切的渲染慢。但对于数目不多的渲染,比如_Q用这个方法没问题。这个方法还有一个问题就是不能释N成图中不需要的囄Q这p看你集成的程度了。从囄定w和内存管理的角度l合考虑Q我一般用二ơ集成的Ҏ。比如有n个精?先将各精灉|有的囄集成C张集成图中,得到n张集成图Q然后将qn张集成图再次集成C张更大的集成图中。这样在jar中只存在一张集成图。用时Q先大集成囑ֈ割蝲入到n个Image对象中即可。这样各个精늚囄可以单独理了?/P>
3 使用旋{和翻?BR> 只保存一个原始的Image,需要时再旋转或{
q是一张连q看的地图,假设??的部分是两张相同的牌?BR> 在数l矩阵中Q?表示没有牌,大于1表示有牌。至于是什么牌Q那是随机的了?BR>不要告诉我,你说的“布局法”是指怎么把牌刚刚好放上去Q那个无所谓什?BR>法Q你只要首先在地图数l中准备好偶C1Q在布牌时保证每U牌是偶C
Q不同种cȝ牌用大于1的数来表C)Q相应地攑օ每个1的位|上可以了?/FONT>
一、计地图上q两张牌能不能连?当然能了Q哈哈)?/FONT>
q是q连看寻路算法的W一步?BR>先定义一下两张牌能连的充分条Ӟ
1.两张牌是同一U?BR>2.两张牌之间有一条全?的\可以q通?BR>3.q一条\不能有两个以上的拐角(corner)
满q三个条Ӟ可以认两张牌是可以q的?/FONT>
首先Q我们依据前两个条g来完成一个基本的寻\法?BR>我们的目的是??扑և一条可以连通的路来?BR>那么很明显从8?的第一步一其有四个方向可以选择Q分别是东,南,西,?BR>Qe, s, w, n以中国地图方向ؓ标准Q四个方向,在第一步中我们首先假设?BR>个方面没有Q何优劣,那么我可以Q意选择一个方向移动,那就是东面吧?BR>图二Q?BR>0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 8, Q?, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 9, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
我从8向东Ud了一步,所以到达了-8的位|,我之所以可以移?8位置Q很明显Q?BR>是因?8的位|上原来是一?Q表C没有牌L?BR>那么现在寻\的问题就变成了,如何从-8找连?的\了!
说到q里应该明白了吧Q好多费话,有点像娘们在说话?/FONT>
所以目前的寻\法归结Z个递归法的基本问题?BR>先从8到找C一个结点-8Q再用同L规则Q从Q?扑ֈ下一个结点,比如Q?8。。?BR>图三Q?BR>0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 8, Q?, Q?8, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
0, 0, 0, 0, 0, 0, 0, 0 , 9, 0
0, 0, 0, 0, 0, 0, 0, 0 , 0, 0
如果一直都能OKQ没有阻的话,最后找C9Q就成功以Q如要有一步不能走下去了,
再退回上个结点,向别的方向发展,都不行,再退回上U结点,再向别的方向发展Q?BR>q里的逻辑是递归的思想了?/FONT>
用这LҎ写出来的法已经能在最优的情Ş下用了,比如?Q到Q?8Q哈哈?BR>但在E微复杂的情况下Q会产生奇多的递归l点。P4Z跑不动啊。我试过Q哈哈?/FONT>
那么W二步就是ؓQe,s,w,nQ四个方向加权,也就是让它们之间有一个优先权Q说白了?BR>是先试哪一条\。决定的法则应该有好几个吧,比如?L位置来看Q它处于8L东南面,
那试路时当然应当优先选择东面和南面,再比?号如果牌8L正东面,那当然是选择正东了?BR>再比如,当走刎ͼ8的位|时Q明昑֏能再C个方向,因ؓ它是不能回头的?/FONT>
l过q样的处理,递归法生成的结Ҏ会明昑֏,会更快的扑ֈ成功的\。但性能在最坏情?BR>下没有本质改变?/FONT>
接下来,W三步,我们把第三个充分条g加进来,来解x本问题?BR>3.q一条\不能有两个以上的拐角(corner)
按照面向对象的思想Q很自然的,我给每个在递归法中生成的位置l点加上了个corner的属性,
来记录这条\到目前ؓ止拐了几个角?BR>q样一下子好办了啊。如果发现这个结点已l拐了两个弯Ӟ如果要再拐弯Q或者到?之前注定
要再增加cornorӞ果断overQ返回上U结炏V?/FONT>
注意Q要把二、三两步的条件综合v来详l规划一个个可能性,可能提早让不可能的l点OVERQ?BR>q就是提高性能的关键吧。算法预见性越强,性能p高吧?/FONT>
我们的算法在赛扬500Q?56M的机器上Q?0万次q_l果是一ơ运花时不过0.1毫秒,得q不
_Q速度实很快Q因为在很坏的情形下Q生的最大结Ҏ?90几个Q这样必然会很快?BR>Q详l的数据已经C清了?/FONT>
说了q么多了Q应当明白第一步连通算法的思\了吧Q我所知道的,都已l尽可能的讲出来了?BR>q个法完全是自己按照连q看的特点,度n定做的。因为是一步步test出来的,所以我个h
觉得是很自然的,没有M高深的地方,完成后,性能也很好,q才觉得是如此的单。相信大?BR>仔细看看p写出自己的算法来的吧?/FONT>
二、整个地图有没有解?Q?可以q通的ȝ敎ͼ
q是一个问题?BR> 解决q个问题之前Q我们先来解xC功?hintQ就是ؓ玩家提供提示Q哪一对牌可以q?BR>通?BR> 我的做法是,先把整个地图中相同牌归到一P用数l也好,链表也好?BR> 像这P
4,4,4,4
5,5,5,5
6,6,6,6
......
然后计算比如4?之间有哪两对可以q,至于如何判断能不能连Q第一步算法已l实C吧,哈哈?BR> 一发现有可以连的牌退出来Q告诉玩家这两张牌可以连啊!
q就OK了?BR> q完全是建立在第一步算法的基础上实现的?/FONT>
好的Qhint功能可以了,
那么Q整个地图没有解的算法是不是出来了?
是的Q如果找不到可以hint的牌Q那当然是没有解了Q?BR> 把所有可以hint的对数记下来Q就是ȝ可以q通数了?/FONT>
xQ与q连看所有算法有关的问题解决完毕?BR>W二步算法的实现Q明昑ּ销要大很多Q最坏情况应当会是单ơ连通算法计量的大U?0倍以?BR>Q与牌的L量和摆的位置有关Q还好在一般的服务器上单次q通计花的时间实在太了Q?BR>实际q行中完全可以很畅。以上数据都是我估计的理论|因ؓ实际试时一直没有问题,
我也懒得计算出真正比较可靠的均g?BR>
q一部分也是我认为可以有所改进的部分,公开出来Q也希望大家能提供一些更好,更y?BR>的方法,我觉得我计算q通数和有无解的方法是比较W的ҎQ几乎是仗着基本的算法快Q一
个一个算出来的。有没有更好的方法呢Q期待中...
1?nbsp; 高用户界面
高用户界面是指J2ME~程中用到的Form(H体)、TextBox(文本?、List(列表?和Alert(提示信息?{的使用Q以及其中的一些控件的使用Q当然也包括相应的事件处理。应用在一般的如登陆窗体、关于窗体和提示{?2?nbsp; 低用户界面
低用户界面指CanvascdGraphicscȝ的用,以及相应的事件处理,应用与游戏编E以及特D界面的l制{?/P>
3?nbsp; 记录存储pȝ
记录存储pȝ是手Z支持的用于数据永久保存的技术。因为手Z没有数据文g的概念,所以一般需要保存的数据只能以记录的形式保存?/P>
4?nbsp; 声音处理pȝ
MIDP1.0不支持声韛_理,但是很多手机厂商如Nokia、Siemens{都支持Q所以播攑֣音也是一w要掌握的技术。该技术用的API多和手机厂商相关。但是MIDP2.0提供了通用的支持?/P>
5?nbsp; |络~程
|络~程指在手机中通过GPRS或者CDMA|络以HTTP协议或者SOCKET的Ş式连接网l。现在的手机支持HTTP|络~程的占大多敎ͼ支持SOCKET的相对很。所以网l编E暂时也是使用HTTP协议q行~程?/P>
6?nbsp; 多线E?/SPAN>
多线E是J2ME应用中比较核心的技术之一Q因行网l编E和低用户界面~程是ؓ了响应迅速,都需要处理成多线E。所以也必须熟练掌握?/P>
7?nbsp; 短信息编E?/SPAN>
很多手机都提供了用于发送短信息的APIQ如NOKIA、SIEMENS、SAMSUNG{,所以在J2ME中发送短信息也是一个比较常用的技术?/P>
8?nbsp; 其他
当然Ҏ厂商的不同还提供了其他的一些技术,如Siemens的API中支持文ӞNokia的API支持dpȝ通讯录等{?/P>
|
|
|
|
|
|
M动画的最基本的前提,是要在够快的时间内昄和更换一张张的图片,让h的眼睛看到动的画面效果。图片必L照顺序画出来。从一张图片到下一张图片之间的变化小Q效果会好?
首先要做的,是用你的图片处理YӞ比如ps或者fireworkQ创Zpd相同大小的图片来l成动画。每张图片代表动M帧?BR>
你需要制作一定数量的-Q越多的帧会让你的动ȝ上去^滑。制作好的图片一定要保存成PNG(Portable Network Graphics) 格式QMIDP唯一支持的图片格式;Q有两个办法让你刚做好的囄在MIDlet上变成动甅R第一Q把囄都放C个web服务器上Q让MIDlet下蝲他们QMIDP内置的HTTP支持。第二个办法更简单,把图片用MIDlet打包成jar文g。如果你使用的是J2ME开发工P把PNG文g攑֜你的目文g里面可以了?
动画的过E其实更像帐本记录:昄当前帧,然后适当地更换到下一帧。那么用一个类来完成这个工作应该是很恰当的Q那好,我们先定义一个AnimatedImagec:
import java.util.*;
import javax.microedition.lcdui.*;
// 定义了一个动画,该动d实只是一pd相同大小的图?
// 轮流昄Q然后模拟出的动?
public class AnimatedImage extends TimerTask {;
private Canvas canvas;
private Image[] images;
private int[][] clipList;
private int current;
private int x;
private int y;
private int w;
private int h;
// Construct an animation with no canvas.
public AnimatedImage( Image[] images ){;
this( null, images, null );
};
// Construct an animation with a null clip list.
public AnimatedImage( Canvas canvas, Image[]
images ){; this( canvas, images, null );
};
// Construct an animation. The canvas can be null,
// but if not null then a repaint will be triggered
// on it each time the image changes due to a timer
// event. If a clip list is specified, the image is
// drawn multiple times, each time with a different
// clip rectangle, to simulate transparent parts.
public AnimatedImage( Canvas canvas, Image[] images,
int[][] clipList ){;
this.canvas = canvas;
this.images = images;
this.clipList = clipList;
if( images != null && clipList != null ){;
if( clipList.length < images.length ){;
throw new IllegalArgumentException();
};
};
if( images != null && images.length > 0 ){;
w = images[0].getWidth();
h = images[0].getHeight();
};
};
// Move to the next frame, wrapping if necessary.
public void advance( boolean repaint ){;
if( ++current >= images.length ){;
current = 0;
};
if( repaint && canvas != null && canvas.isShown()
){;
canvas.repaint( x, y, w, h );
canvas.serviceRepaints();
};
};
// Draw the current image in the animation. If
// no clip list, just a simple copy, otherwise
// set the clipping rectangle accordingly and
// draw the image multiple times.
public void draw( Graphics g ){;
if( w == 0 || h == 0 ) return;
int which = current;
if( clipList == null || clipList[which] == null
){;
g.drawImage( images[which], x, y,
g.TOP | g.LEFT );
}; else {;
int cx = g.getClipX();
int cy = g.getClipY();
int cw = g.getClipWidth();
int ch = g.getClipHeight();
int[] list = clipList[which];
for( int i = 0; i + 3 <= list.length; i +=
4 ){;
g.setClip( x + list[0], y + list[1],
list[2], list[3] );
g.drawImage( images[which], x, y,
g.TOP | g.LEFT );
};
g.setClip( cx, cy, cw, ch );
};
};
// Moves the animation's top left corner.
public void move( int x, int y ){;
this.x = x;
this.y = y;
};
// Invoked by the timer. Advances to the next frame
// and causes a repaint if a canvas is specified.
public void run(){;
if( w == 0 || h == 0 ) return;
advance( true );
};
};
此时再用你所能想到的MҎQ在不明昑֤真的前提下进行颜色压~,最后把兰色LQ就得到成品啦。哈哈啊?/FONT>
用另外一个女生的囄做的?BR>同样的方法,可以取得同样良好的结果?BR>
反复q行不同背静的测试,
发现锯问题解决得很好,兰色的背静幕布在抠图q后能与其他颜色很好的融合?BR>
前面的朋友说了动ȝ制作,一个动L由很多l在?,您说的方法把一帧的动d成一q幅的图?再一q幅地蝲?q样做的效率不高.应该把各q员的动L在一?做成一q大的图,加蝲一q大图比加蝲多幅图效率要高,一q大图也比多q小囑֮易管理得?那么怎样做呢?
做法和前面朋友说的差不多,关键是这个方?
drawImage(Image img,dx1,dy1,dx2,dy2,sx1,sy1,sx1,sy1,this)
其中Image img是来源图?
dx1是目的区域的===左上===点?==X====坐标;
dy1是目的区域的===左上===点?==Y====坐标;
dx2是目的区域的===右下===点?==X====坐标;
dy2是目的区域的===右下===点?==Y====坐标;
sx1是来源区域的===左上===点?==X====坐标;
sy1是来源区域的===左上===点?==Y====坐标;
sx2是来源区域的===右下===点?==X====坐标;
sy2是来源区域的===右下===点?==Y====坐标.
================================================================
下面是一个动ȝE序代码.
import java.awt.*;
import java.applet.*;
public class Seriallmage4 extends Applet implements Runnable
{
int SPF; //用来表示U程暂停的时?BR> int sx,sy;
/******************************************************************
sx,sy用来表示来源区域的左上顶?因ؓ每一帧我们都做成了长?0?7的图,再把它们攑֜一起做成一q大的图,
所以来源区域的右下点的坐标等于左上顶点坐标加上长和高.
式子?sx+80;sy+87
******************************************************************/
Image Animation; //来源图象
MediaTracker MT; //囑փq踪?用来查图像是否完全加?BR> Thread newThread; //新的U程.
Image OffScreen; //后台囑փ;
Graphics drawOffScreen; //l制后台囑փ的实?实例).
//======================init()函数===========================
public void init()
{
SPF=100; //U程暂停的时间是100豪秒,
MT=new MediaTracker(this); //实体化图像追t器.
Animation=getImage(getDocumentBase(),"lunisEx.gif");//加蝲囑փ.
//======================//q踪囑փ=============================
MT.addImage(Animation,0);
try
{
showStatus("图象加蝲?..."); //在状态栏中显CZ?BR> MT.waitForAll(); //{待加蝲
}
catch(InterruptedException E){}
//============================================================
OffScreen=createImage(300,300); //建立后台画面的实?BR> drawOffScreen=OffScreen.getGraphics(); //取得后台画面的绘制类
}
//===========================================================
public void start() //start函数
{
newThread=new Thread(this); //建立U程
newThread.start(); //启动U程
}
public void stop() //stop函数
{
newThread=null; //U程设ؓnull
}
//==========================================================
public void paint(Graphics g)
{
drawOffScreen.setColor(Color.green); //讄后台画面的背景色,
drawOffScreen.fillRect(0,0,300,300); //Mơ背?作用是清除后台画?如果背景是一q画
//则要用这q图来画.
drawOffScreen.drawImage(Animation,20,20,250,250,sx,sy,sx+80,sy+87,this);//在后台画面绘?BR>//的图?
g.drawImage(OffScreen,0,0,300,300,this);//把后台画面画到前台画?BR> }
public void update(Graphics g)
{
paint(g);
}
//====================新动d@环在?=======================50
public void run()
{
while(newThread !=null) //如果U程不等于null
{
repaint(); // 重画一?
try
{
Thread.sleep(SPF); //让线E暂?BR> sx+=80; //M一ơ后,改变来源区域,使其C一?
if(sx==800) //我的囑փ是一q?00*170?有两?每行十,如果sx走到?BR>//800p明播攑֮W一行了,
{
sy+=85; //播放完第一行后,改变来源区域的左上顶点其跑到第二行.
sx=0;
if(sy==170) //如果播放完第二行?又让它回到第一?
{
sy=0;
}
}
}
catch(InterruptedException E){}
}
}
}
动画每秒播放多少帧好?
电媄的是每秒24?q样的速度不仅能生视觉停?而且让h感觉到画面非常流?那么是不是我们做游戏也要用这个速度?{案是否定的,实际上每U播?0帧就_产生视觉停留,如果讄高了会消耗更多的资源.臛_的是,有一些手每秒10帖都做不?
一、序a
昨天在网上闲逛,发现一讲解用delphi实现华容道游戏的文章Q颇受启发.于是Q生了华定w游戏UL到手Zȝ冲动Q现在手机游戏琳琅满目,不一而Q华定w的实现版本也很多Q正巧不久前W者对J2ME下了一番功夫,正想借这个机会小试牛刀。选用J2ME的原因还有一个就是目前Java开发大行其刎ͼ无线增殖业务q猛发展QJ2ME的应用日渐活跃v来,也希望我的这文章能够ؓJ2ME知识的普及和开发团队的壮大推L助澜。由于长期受ISO规范的媄响,q次试牛刀我也打算늅软g工程的要求,q取瀑布式的开发模式来规划目Q也希望借此Z向各位没有机会参与正式项目开发的读者介l一下Y件开发的程?/FONT>
q里我们先定义项目组的h员体?其实只有我一个h)Q技术调研、需求分析、概要设计、详l设计、编码、测试均有笔者一人担任;工q里我找了个捷径Q盗用网上现成的囄Q然后用ACDSee把它由BMP转换成PNG格式(我出于讲座的目的Q未做商业应用,应该不算侉|?Q至于发布工作,׃~少OTA服务器,此项工作不做(但是我会介绍q步如何??BR>
接下来,我们规划一下项目实现的旉表,以我个hl验Q设惛_下:技术调研用2?q部分解决项目的可行性和重大技术问题,旉会长一?Q需求分析用半天(毕竟有现成的东东可以参照Q只要理清思\p了,况且q有很多以前用过的设计模式和写好的代?Q概要设计再用半?有了需求,概要只不够是照方抓药)Q详l设计要??q一步要把所有的问题x楚,q要可能的准确描述出来)Q编码用2?其实1天就够了Q技术已l不是问题,多计划出一天来应付H发事g)Q测试用2?试应该臛_占全部项目的四分之一Q不q这个项目只是一个DemoQ也太简单了)Q发布也要用上半?管我们不去实际发布它,但是q要q旉搞清楚应该如何做)Q最后就是项目ȝ和开庆功?旉待定)?BR>
?利其?/B>
“公Ʋ善其事Q必先利其器”,做项目之前第一步是前期调研Q我们要做的华容道这个东东随处可见,我们要调研的是两个方面:
1、游戏的内容Q游戏本w很单,是有几个格子,Ҏ占据其中一个较大的格子Q然后被几个格子包围Q这些格子Ş状不一定相同,但是挡住了曹操移动的方向Q游戏者需要挪动这些格子最l把ҎUdC个指定的位置才算是过养I更具体的分析我们攑֜后面需求分析和概要设计中讨论?BR>
2、技术储备:谈到技术,q里单介l一下J2ME.Java有三个版本,分别是J2MEQ微型版Q?J2SEQ标准版Q?J2EEQ企业版Q.J2ME是一个标准,采用Q层l构设计Q最低层是配|层QConfigurationQ也是讑֤层,其上是简表层QProfileQ?再上是应用层QApplicationQ?MIDP是Ud信息讑֤表,目前L手机支持MIDP1.0Q最新的是MIDP2.0,它比前一个版本增加了Ҏ戏的支持Q在javax.microedition.lcdui.game包中提供了一些类来处理游戏中的技术,比如我们后面会用到的Spritec,它是用来{囄?权衡再三Q笔者决定用MIDP2.0来做开发.首先需要安装一个J2ME的模拟器Q我们就用Sun公司的WTK2.0Q我觉得Sun的东西最权威Q当然你也可以用Nokia.Siemens或是Motolora{其他模拟器Q但是他们的JDK不尽相同Q写出来的程序移植是比较ȝ的.Sun公司的WTK2.0可以刎ͼA href="http://here/?Qhttp://here/下</AQ蝲Q当然要x功下载的前提是你要先注册成ؓSun的会员(其实q样对你是有好处的)Q当下来之后是按照提示一步一步的安装Q安装好了之后,我们用一?Hello World"E序开始你的J2ME之旅Q我们启动WTK2.0工具集中的KToolBarQ然后点击New Project按钮Q在弹出的输入框中输入Project Name为HelloWorld,MIDlet Class Name为Hello,然后点击Create ProjectQ开始生成项目,工具会弹出MIDP配置表,q里接受生成的默认|以后q可以修改)点击OKQ工hC我们把写好的Java源程序放到[WTK_HOME]\apps\HelloWorld\src目录之下Q我们编辑如下代码,q保存在上述目录之下Q文件名为Hello.java?BR>
import javax.microedition.midlet.*; import javax.microedition.lcdui.*; public class Hello extends MIDlet { private Display display; public Hello(){ display =Display.getDisplay(this); } public void startApp(){ TextBox t = new TextBox("Hello","Hello",256,0); display.setCurrent(t); } public void pauseApp(){ } public void destroyApp(boolean unconditional){ } } |
保存好了之后Q点击Build按钮Q工具会Z~译E序Q如无意外再点击Run按钮Q会弹出一个手机界面,剩下的就不用我教了吧Q用鼠标Ҏ机按键一狂点)。呵呵,你的W一个J2MEE序已经OK?什么?你还一炚w没懂呢(真是厉害Q不懂都能写出J2MEE序来,果然是高手)Q我q里主要是介lWTK2.0工具的用,E序q不是目的,不懂的话后面q会有详l的解说Q这里只是带你上路.什么?你不懂JavaQ那也没有关p,后面我再讲得l一炏V?BR>
跌J2MEQ我们先来讲Ҏ戏的理论Q具体到华容道这个游戏,主要有三个方面,贴图Q游戏操作.逻辑判断Q这里讲讲脓图,其他两方面放在概要设计和详细设计里讲Q所谓的贴图Q其实就是画图,是在要昄囑Ş的位|上输出一副图片,Q要是牵扯到动画pȝ一些,可以使用TimerTask.Thread或Rannable之类的技?Q这副图片可以是事先准备好的也可以是临时处理的.在J2ME中有一个Imagec?专门用于理囄Q它有createImage()ҎQ可以直接读取图片文ӞJ2ME只支持PNG格式的图片)Q也可以截取已有的图片的一部分Q这h们可以把很多囄攑֜一P然后一张一张的截下来,好处是节省存储空间和文gd旉Q对于手两者都是性能的瓶颈)QJ2MEq有一个Graphicsc,专门用于l图Q它有drawImage()ҎQ可以把一副图片在指定的位|上昄出来Q它q有drawRect()Ҏ和setColor()ҎQ这两个Ҏ在后面我们进行游戏操作时׃用到Q这里先交代一下.有了囄和绘囄ҎQ还需要知道把囄到谁w上QJ2ME提供了一个Canvasc,字面意思就是画布,它有一个paint()Ҏ用于h面Q还有一个repaint()Ҏ用于调用paint()ҎQ听着有些p涂是吧Q不要紧Q我来结合具体程序讲解一下.Z今后~程的方便,我们创徏两个cImages和Draw,Images用于保存一些常量值和囄QDraw主要是用于画图,q两个类的源代码如下?BR>
Imagescȝ源代码如下:
package huarongroad;
import javax.microedition.lcdui.*; public class Images {//保存帔R public Images() {//构造函?BR>} public static boolean init() {//初始化游戏中用到的图?BR>try { return true; |
Drawcȝ源代码如下:
package huarongroad; import javax.microedition.lcdui.*; public class Draw { public static boolean paint(Graphics g, byte img, int x, int y) { public static boolean paint(Graphics g, byte img, int x, int y, int unit) { |
其中Imagescd的是l图位置帔RQ也是在画图时每个格子的长度和相对坐标原点位置要进行的调整Q、地图位|常量(地图的长、宽Q,地图标记帔RQh物对应的记号Q,地图l合标记帔RQ后面会l说Q,囄帔RQ存放h物的囄Q;DrawcM要负责在制定的位|画Zh物图片。下面我来说说ImagescM的地图标记常量和地图l合标记帔R。ؓ了能够灵zȝ安排各个关面的布局Q我们决定把游戏布局的信息存储在外部文g中,然后E序启动后把它读q来。这h们制定了一套存储图片的代码Q这是地图标记帔RQ如上面ImagescM定义的Caocao(Ҏ)用a字符来表C,当程序读到a字符时就能将它{化成Ҏ对应的图片,q在da字符的位|上q行昄。但是从实际观察中我们发现所有的囄q不是统一大小的,有的?个格子,有的?个格子,q有的占1个格子,而且即便同是占两个格子的囄q有横、竖之分。有鉴于此,我们引入了地囄合标记常量,是说在遇到占有多个格子的时候,?(也就是Images.LEFT)表示它的左边是一个真正的地图标记Q?(也就是Images.UP)表示它的上边是一个真正的地图标记Q?(也就是Images.LEFTUP)表示它的左上Ҏ一个真正的地图标记。地囄合标记常量其实就是用来占位置的,与实际显C无养I当后面我们将到移动时q会再来分析l合标记的用?BR>
DrawcM要是用来在画布上d囑ŞQ它有两个paintҎQ这是很常见的函数重载。但是程序中实际上只用到?个参数的paintҎQ它直接获得要画囄的相对坐标位|信息,然后调用5个参数的paintҎ?个参数的paintҎ相对坐标位|信息{换成l对位置Qƈ实际调用Graphics.drawImage()ҎQ将Images中的囄M出来。这U实现方法的好处是灵zd便于扩展Q但你需要画囄位置q不能够对应到格子中的相对坐标位|时Q你可以直接调?个参数的paintҎQ而不必再M改这各类Q但你添加新的图片时Q只要在Images中增加对应的帔RQ然后向Draw?个参数的paintҎd一条处理就可以了?BR>写到q里Q两天的旉刚好用完?BR>
三、需求分?/B>
q部分叫做需求分析,听v来挺吓h的,其实是搞清楚我们要做什么,做成什么样Q那些不做。下面我引领着大家共同来完成这一步骤。首先,我们要做一个华定w的游戏,华容道的故事q里不再赘述了,但其中的人物在这里限定一下,如上面Imagesc里的定义,我们q个版本只提供曹?Caocao)、关?Guanyu)、张?Zhangfei)、n?Zhaoyun)、黄?Huangzhong)、马?Machao)和卒(Zu)。我们这里也限定一下游戏的操作ҎQ首先要通过方向键选择一个要Ud的区?是一张图?Q被选择的区域用黑色Ҏ框住Q选好后按Fire?是定?这块区域选中Q被选中的区域用l色Ҏ框住Q然后选择要移动到的区域,此时用红色方框框住被选择的区域;选好要移动到的区域之后按Fire键将要移动的区域(囄)Ud要移动到的区域,q去掉绿色和U色的方框。这里需要强调的概念有选择的区域、选中的区域、要Ud的区域和要移动到的区域,q四个概念请读者注意区分,当然也应当把q一部分记入数据字典之中。ؓ了文章的重点突?介绍如何制作一个J2ME的收集游?Q我们这里限定一些与本主题无关的内容暂不d玎ͼq关之后的动?实现时要用到TimerTask或Threadc,后箋的系列文章中我会详细介绍动画斚w的知?、关面之间的切换(其实很简单,当完成Q务之后重新再做一?、暂停和保存{操?q部分的内容介绍的资料很多,我也写不Z么新的东东来Q难免抄袭,故此免掉)?BR>
需求分析基本完成,M午还有一D|_马上动手用ACDSee把从|上找来的BMP文gQ调整其大小?71*177(我的q个囄是两个部分合在一P所以比手机实际屏幕大了)Q另存ؓPNG格式。半天时间刚刚好Q不但搞清楚了要做的东东Q还把要用的囄准备好了?BR>
四、概要设?/B>
概要设计是从需求分析过渡到详细设计的桥梁和U带Q这一部分中我们确定项目的实现Ҏ和模块的划分。我们决定将整个目分成五个部分Q分别是前面介绍的Images、DrawQ还有Map和Displayable1和MIDlet1。Images和Drawcd能简单、结构固定,因此很多目我们都用这两各c,q里直接拿来Ҏp用了Q前面已l介l过q里不再赘述。MapcL用来从外部文件读入地图,然后保存在一个数l之中,q部分的内容是我们在本阶D讨论的重点。Displayable1是一个承了CanvascȝdQ它用来处理E序的主要控刉辑和一部分控制逻辑所需的辅助函敎ͼ主要函数应该包括用来l图的paint()函数、用来控制操作的keyPressed()函数、用来控刉择区域的setRange()函数、用来控刉择要移动到区域的setMoveRange()函数、用来移动选中区域的Move()函数和判断是否完成Q务的win()函数Q更具体的分析,我们攑ֈ详细设计中去l化。MIDlet1实际上就是一个控制整个J2ME应用的控制程序,其实也没有什么可特别的,它和我们前面介绍?Hello World"E序大同异Q这里就不展开来说了,后面会脓出它的全部代码?BR>
MapcM要应该有一个Grid[][]的二l数l,用来存放华容道的地图Q还应该有一个read_map()函数用来从外部文件读取地囑ֆ容填充Grid数据l构Q再是要有一个draw_map()函数用来把Grid数据l构中的地图内容转换成图片显C出?当然要调用DrawcȝpaintҎ)。说到读取外部文ӞW者知道有两种ҎQ一U是传统的定义一个InputStream对象Q然后用getClass().getResourceAsStream()Ҏ取得输入,然后再从输入中取得外部文g的内容,例如
InputStream is = getClass().getResourceAsStream("/filename"); if (is != null) { byte a = (byte) is.read(); } |
q里h意文件名中的根\径是相对于便以后的class文g攄的位|,而不是源文g(java)。第二种Ҏ是用onnector.openInputStreamҎQ然后打开的协议是ResourceQ但是这U方法笔者反复尝试都没能调通,报告的错误是~少Resource协议Q估计第二种Ҏ用到J2ME的某些扩展类包,此处不再q。由于以前已l做q一些类似华定wq样的地图,q里直接l出Mapcȝ代码Q后面就不再详细解释MapcMQ以便于我们可以集中_֊处理Displayable1中的逻辑。Mapcȝ代码如下Q?BR>
package huarongroad; import java.io.InputStream; public class Map { public byte Grid[][];//存放地图数据 public Map() {//构造函敎ͼ负责初始化地图数据的存储l构 public int[] read_map(int i) { public boolean draw_map(Graphics g) { |
对于像华定wq样的小型地囑֏以直接用手工来绘制地囄内容Q比如:
fa1c
2232
bd1e
2gg2
gihg
但是Q如果遇到像坦克大战或超U玛莉那L地图Q就必须另外开发一个地囄辑器?我会在后l的文章中介l用vb来开发一个地囄辑器)?/FONT>
五、详l设?/B>
详细设计是程序开发过E中臛_重要的一个环节,好在我们在前面的各个阶段中已l搭建好了项目所需的一些工P现在q个阶段中我们只需集中_֊设计好Displayable1中的逻辑?两天的时间当然不只干q点z,q要把其他几个类的设计修改一?
Displayable1q个c负责处理程序的控制逻辑。首先,它需要有表示当前关面的变量level、表C当前光标位|的变量loc、表CUd区域的变量SelectArea、表CUd到的区域的变量MoveArea、表C是否已有区域被选中而准备移动的变量Selected和Mapcȝ实例MyMap。然后,我们Ҏ用户按不同的键来处理不同的消息,我们要实现keyPressed()函数Q在函数中我们处理按键的上下左右和选中(Fire)Q这里的处理需要我展开来讲一Ԍ后面我很快会把这一部分详细展开?BR>
接下来,是实现paint()函数Q我们打在q一部分中反复的重画背景、地囑֒选择区域Q这个函数必d理好区域被选中之后的画W颜色的切换Q具体讲是在没有选中M区域时要用黑色画W,当选重要移动的区域时用绿色画W,当选择要移动到的区域时改用U色ȝ(当然附加一张流E图是必不可的)?BR>
再下面要实现的setRange()函数和setMoveRange()函数Q这两个函数用来讄要移动的区域和要Ud到的区域Q我的思\是利用前面在ImagescM介绍q的地图l合标记帔RQ当Ud到地囄合标记常量时Q根据该点地图中的值做逆向变换扑ֈ相应的地图标记常量,然后讄相应的loc、SelectArea和MoveArea,其中setMoveRange()函数q用C一个辅助函数isInRange(),isInRange()函数是用来判断给定的Ҏ否在已选中的要Ud的区域之?如果isInRange()的返回值是假ƈ且该点处的g是空白就表明要移动到的区域R犯了其他以被占用的区域。有了setRange()和setMoveRange()函数QMove()函数水到渠成了,Move()函数要Ud的区域移动到要移动到的区?在移动过E中分ؓ三步q行:
W一.复制要移动的区域Q?BR>
W二.复制出的要Ud区域复制到要Ud到的区域(q两步分开q行的目的是防止在复制过E中覆盖掉要Ud的区?Q?BR>
W三.用isInRange2()判断l定的点是否在要Ud到的区域?不在要Ud到的区域内的点设|成I白?BR>
下面我们详细的分析一下keyPressed()函数的实现方?首先,keyPressed()函数要处理按键的上下左右和选中(Fire),在处理时需要用CanvascȝgetGameAction函数来将按键的键D{换成游戏的方?q样可以提高游戏的兼Ҏ?因ؓ不同的J2ME实现,其方向键的键g一定是相同??BR>
接下?分别处理四个方向和选中.当按下向上时,先判断是否已l选定了要Ud的区?即this.selected是否为真),如果没有选中要移动区域则让光标向上移动一?然后调用setRange()函数讄选择要移动的区域,再调用repaint()函数h屏幕,否则如果已经选中了要Ud的区?p光标向上Ud一?然后调用setMoveRange()函数判断是否能够向上Ud已选中的区?如果能移动就调用repaint()函数h屏幕,如果不能Udp光标向下退回到原来的位|?BR>
当按下向下时,先判断是否已l选定了要Ud的区?如果没有选中要移动的区域则判断当前所处的区域是否Z个格?如果是两个格高则向下Ud两格,如果是一个格高则向下Ud一?接着再调用setRange()函数讄选择要移动的区域,而后调用repaint()函数h屏幕,否则如果已经选中了要Ud的区?p光标向下Ud一?然后调用setMoveRange()函数判断是否能够向下Ud已选中的区?如果能移动就调用repaint()函数h屏幕,如果不能Udp光标向上退回到原来的位|?按下向左时情况完全类似向上的情况,按下向右时情况完全类似向下的情况,因此q里不再赘述,详细情况请参见程序的源代码?BR>
当按下选中键时,先判断是否已l选中了要Ud的区?如果已经选中了要Ud的区域就调用Move()函数完成pUd的区域到要移动到的区域的Udq程,接着调用repaint()函数h屏幕,然后已选择标记|成false,l箋调用win()函数判断是否完成了Q?否则如果q没有选定要移动的区域则再判断当前选中区域是否为空?如果不是I白将选中标记|成true,然后h屏幕.q里介绍一个技?在开发程序遇到复杂的逻辑的时?可以构造一格打印函数来所兛_的数据结构打印出来以利调?q里我们构造一个PrintGrid()函数,q个函数Ua是ؓ了调试之?效果q得不错.x我们完成了编码前的全部工作?/FONT>
?~码
整个目共有五个c?有四个类的代码前面已l介l过?而且是在其他目中用过的相Ҏ熟的代码.现在只需全力d现Displayable1c?Displayable1cȝ代码如下:
package huarongroad; import javax.microedition.lcdui.*; public class Displayable1 extends Canvas implements CommandListener { private int[] loc = new int[2]; QA href="file://?Qfile://光</AQ标的当前位|,0是水q位|,1是竖直位|?BR>private int[] SelectArea = new int[4];//被选定的区域,卌Ud的区?BR>private int[] MoveArea = new int[4];//要移动到的区?BR>private Map MyMap = new Map();//地图c?BR>private boolean selected;//是否已经选中要移动区域的标志 public void commandAction(Command command, Displayable displayable) { protected void paint(Graphics g) { private void setRange() { private boolean setMoveRange() { private boolean isInRange(int x, int y) { private boolean isInRange2(int x, int y) { protected void keyPressed(int keyCode) { private boolean win(){ private void PrintGrid(String a) { private void Move() { |
代码的相兛_?在详l设计阶D已l讲q?代码中有比较相近的注?误者自行研d?全部的代码写好,用wtk2.0自带的Ktoolbar工具建立一个工E?接下来把M源文件放到正位|下,然后点击build,再点run,完成了E序的编?当然如果有错误还要修改和调试.
七、测?BR>
作ؓ一个真正的产品要经q单体测试、结合测试和pȝ试。由于项目本w简?而且大部分代码已l是相对成熟?我们跌单体试Q又׃W者的实际环境所?无法搞到Java手机,无法架设OTA服务?因此我们也只能放弃系l测试。那么就让我们开始结合测试吧。测试之前要先出一个测试式样书,也就是测试的计划。我们将它简化一?只测试如下几U情?W一、对各种形状的区域的选择和移?W二、͘q边界区域的选择和移?W三、同一区域的反复选择和反复移?W四、非法选择和非法移动。有了测试的目标,接下来的工作是用wtk2.0自带的Run MIDP Application工具q行试。打开q个工具,加蝲huarongRoad的jad文g,E序׃自动q行,选择launch上MIDlet1q个E序,华容道游戏就会跃然屏q之?接下来的工作是左三?右三?拇指扭扭,来做试。测试过E中发现M的问?立刻发一个bug给自己,然后又是痛苦的调试和修正bug,如此如此?BR>
?发布
谈到发布,其实是个关键,再好的品不能很好的发布出去也只是个产品而已,变不成商品也得不到回报.׃W者的条g所?q里只能是纸上谈?不过q是希望能够使读者对q一q程有所了解(|上的资料也很多)?BR>
J2ME的程序发布一般都是通过OTA(Over The Air),你只需要一台有公网IP的主机和一个普通的web Server可以了(管要求很低,但笔者还是没?Q这里我们以apacheZ介绍一下OTA服务的配|,首先是安装好了apache服务器,然后在conf目录下找到mime.types文gQ在该文件中加入如下两行
application/java-archive jar
text/vnd.sun.j2me.app-descriptor jad
然后重vapache服务器就可以了。接下来的工作就是修改jad文g中MIDlet-Jar-URL:后面的参敎ͼ它改ؓURL的绝对\径,卻IA href="http://***/"Qhttp://***/Q?AQhuarongroad.jar(其中***是你的域名或IP地址)。在下面是用java手机下蝲jad文gQ它会自动部|相应的jar文gq加载它。剩下的工作和在模拟器上操作是一L了?BR>
九、项目ȝ
xQ我们已l完成了一个J2ME游戏的全部开发过E,E序中涉及到了调研、分析、设计、编码、测试和发布{方面的问题Q其实在实际的工作中q有很多更ؓ具体的问题,毕竟技术只在Y件开发过E中占据很有限的一部分Q这里限于篇q的限制无法一一具体展开。今后,W者计划再写一用J2ME开发手机屏保的文章Q借此Z向读者展CJ2ME动画技术;然后再写一J2ME|络应用的文章,做一个类似开心辞兔RL知识问答游戏Q以便向读者展CJ2ME的网l技术;待这两方面的技术交待清楚之后,我将引领读者制作一个稍大一些的游戏?/FONT>
现在Q让我们假设怽正在设计一ƾMMO ARPG中的怪物行动模式?/FONT>
在主的韩国pMMO ARPG中,q些问题一般被如下处理Q?BR>1. 敌h是否会主动攻ȝ家h物?如何判断Q?BR>分ؓdd和被动攻MU类型。前者当玩家人物q入自n周围一定范围之后激发攻击行为。后者当自n被攻dȀ发攻击行为?/FONT>
2. 当怪物面对多个玩家的时候,如何选择自己的攻d象?
L的处理方式是dW一个攻击自q玩家?/FONT>
3. 战斗q程中,怪物能够改变自己的攻ȝ标吗Q如何判断?
基本上不改变?/FONT>
4. 当处于不利的情况下,怪物会作Z么行动?
L到底Q有些游戏中有逃跑的设定?/FONT>
5. 怪物之间能否协作Q?BR>一拥而上?/FONT>
在欧经典MMORPG EverQuest中,是这样处理的Q?BR>1. 敌h是否会主动攻ȝ家h物?如何判断Q?BR>通过一个关p表来决定,如果玩家和自w阵营关pL劣,那么dd。否则是被动d。(q个关系表是动态的Q会Ҏ玩家的行改变)
2. 当怪物面对多个玩家的时候,如何选择自己的攻d象?
d首先q入自己视野或者攻击自q玩家?/FONT>
3. 战斗q程中,怪物能够改变自己的攻ȝ标吗Q如何判断?
战斗中,怪物有一个隐藏的仇恨列表。怪物会攻d于自׃恨列表顶端的玩家Q而玩家会增加仇恨的动作包括攻凅R挑衅、医疗同伴、施N法……等{。换a之,如果一个法师疯狂的施放d法术Q那么一定会被怪物首先料理。同LQ如果牧师频J的ȝ同伴Q也会被怪物Ҏ照顾。(正确的战斗方式是让肉盄角色保证自己永远处于仇恨列表最端Q当然这很困难,但是却很有必要,也很有趣Q?/FONT>
4. 当处于不利的情况下,怪物会作Z么行动?
逃跑Q而且速度相当快。鉴于怪物之间能够呼唤Q所以让怪物成功跑掉往往比较可怕。有一些法师系的敌Z用施放传送门的方式逃跑?/FONT>
5. 怪物之间能否协作Q?BR>怪物之间的仇恨可以传递。如果两个怪物距离很近Q那么很可能一个发现到了你而两个都冲过来。法师系的怪物也会l同伴加血和一些防御性法术?/FONT>
单的_EQ在“h工智能”的范畴上ƈ没有走多q,但是在游戏上却做的很。特别是“仇恨列表”,q不复杂的规则衍生出了很多的变化Q堪U天才的设计。当你体会到因ؓq个限制而衍生出的精彩刺Ȁ的游戏过E,便能理解到玩家想要的其实q不仅仅是经验D已?/FONT>
试试看将“仇恨列表”导入你的游戏,你觉得会发生什么?
1. 使用Runnable和创建线E的d@?/SPAN>
一般主体的做法是?/SPAN>Displayableq个cd?/SPAN>Runnableq个接口Q然后在其构造函C创徏一个线E,启动?/SPAN>run()函数,?/SPAN>run函数里面包含了游戏的主循环。下面是我在仙剑里面的片断代码?/SPAN>
public class GameMIDlet extends MIDlet {
static GameMIDlet instance;
Display display;
GameDisplayable displayable = null;
public GameMIDlet() {
instance = this;
display = Display.getDisplay(this);
displayable = new GameDisplayable();
}
public void startApp() {
display.setCurrent(displayable);
}
public void pauseApp() {
}
public void destroyApp(boolean unconditional) {
displayable.running = false;
}
public static void quitApp() {
instance.destroyApp(true);
instance.notifyDestroyed();
instance = null;
}
}
public class GameDisplayable extends FullCanvas implements Runnable {
/** L制线E?/SPAN> */
Thread MainThread = null;
/** 游戏旉间隔 毫秒为单?/SPAN> */
public static long timeinterval = 20;
public static boolean Isstable = true;
/* 用于游戏旉的变?/SPAN> */
public static long timeold = 0;
public static long timenow = 0;
public long interval = 0;
public static long frames_per_second = 0;
int count = 0;
long second = 0;
public static boolean running = true;
public GameDisplayable() {
// 开始主U程
Thread MainThread = new Thread(this);
MainThread.start();
}
public void run() {
while (running) {
timenow = System.currentTimeMillis();
interval = timenow - timeold;
if (interval >= timeinterval) {
timeold = timenow;
Game_Process();
if (second != (System.currentTimeMillis() / 1000)) {
second = System.currentTimeMillis() / 1000;
frames_per_second = count;
count = 1;
}
else
count++;
}
lib.sleep(30);
}
}
其中关于控制d@环速度的代码可以不要,但是lib.sleep(30)必须保留Q因为在Nokia 60的手ZQ如果去除了sleep(30),那么游戏无法切换回来。同Ӟ在游戏中M一个内部@环中Q也必须加入sleep(30)q个{待Q才能让游戏可以切换回来Q至于ؓ什么这样做Q我暂时q不清楚?/SPAN>30ms是我试q没有问题的数|可能?/SPAN>30msq小的g是没有问题的?/SPAN>
同时Q在MOTO的手ZQ必d游戏的主循环攑֜一个线E中Q游戏才能切换回来,不过可以不加上面说的sleep(30)延时?/SPAN>
2. 不用线E的d@环办?/SPAN>
q个办法只能?/SPAN>Nokia的^C实现Q而我只徏议在Nokia 40的^C做,q样不需要线E,道理上来说节U了一些内存,如果不是内存很紧张的机型Q那么最好还是用上一U办法?/SPAN>
游戏的主循环攑֜MIDlet?/SPAN>class里面Q具体做法如下:
public class GameMIDlet extends MIDlet {
GameDisplayable displayable = null;
/** 游戏旉间隔 毫秒为单?/SPAN> */
public static long timeinterval = 0;
//用于游戏旉的变?/SPAN>
public static long timeold = 0;
public static long timenow = 0;
public long interval = 0;
public static long frames_per_second=0;
int count=0;
long second =0;
public static boolean running = false;
static boolean exitApp =false;
public GameMIDlet() {
displayable = new GameDisplayable();
running =true;
}
public void startApp() {
running =true;
Display.getDisplay(this).setCurrent(displayable);
while(running) {
timenow = System.currentTimeMillis();
interval = timenow - timeold;
if (interval >= timeinterval) {
timeold = timenow;
displayable.Game_Process();
if(second != (System.currentTimeMillis() /1000)){
second = System.currentTimeMillis()/1000;
frames_per_second = count;
count = 1;
}else
count ++;
}
}
if(exitApp) {
destroyApp(true);
notifyDestroyed();
}
}
public void pauseApp() {
running =false;
}
public void destroyApp(boolean unconditional) {
running = false;
}
public static void quitApp() {
running =false;
exitApp =true;
}
}
角色扮演游戏(RPG)是深受广大游戏迷们喜q一U游? 它以独特的互动性和故事性吸引了无数的玩家。它向h们提供了出现实生活的广阔的虚拟世界QZ能够试扮演不同的角Ԍȝ历和体验各种不同的h生旅E或奇l历。这些体验都是在现实生活中无法实现的。在玩过许多游戏后,许多玩家都不再仅仅满于一个游戏玩家的w䆾Q而会思考游戏是如何制作的,q且打算制作一个自q游戏Q网上的各种游戏制作组更是如雨后春W般涌现。下面我q大家介绍一下角色扮演游戏引擎的原理与制作,希望能对游戏制作爱好者有所帮助?
一 游戏引擎的原?/FONT>
说到引擎Q游戏迷们都很熟悉。游戏引擎是一个ؓq行某一cL戏的机器设计的能够被机器识别的代码(指oQ集合。它象一个发动机Q控制着游戏的运行。一个游戏作品可以分为游戏引擎和游戏资源两大部分。游戏资源包括图象,声音Q动ȝ部分Q列一个公式就是:游戏=引擎Q程序代码)+资源Q图象,声音Q动ȝQ。游戏引擎则是按游戏设计的要求顺序的调用q些资源?/FONT>
?nbsp;角色扮演游戏的制?/FONT>
一个完整的角色扮演游戏的制作从大的分工来说可以分ؓQ策划,E序设计Q美工,音乐制作以及目理Q后期的试{?BR> {划主要d是设计游戏的剧情Q类型以及模式等Qƈ分析游戏的复杂性有多大Q内Ҏ多少Q策划的q度要多快等因素?BR> E序设计的Q务是用某U编E语a来完成游戏的设计Qƈ与策划配合,辑ֈ预期的目的?BR> 工主要是根据游戏的时代背景与主题设计游戏的场景及各U角色的图象?BR> 音乐制作是根据游戏的剧情和背景制作游戏的音乐与音效?BR> 目理主要是控制游戏制作的q程Q充分利用现有的资源Qh员,资金Q设备等Q,以达到用量的资金实现最大的收益?BR> 后期的测试也是非帔R要的一个环节,对于一个几十hp几个月甚x几年旉制作的游戏,试往往能找到许多问题,只有改进E序才能保游戏的安全发行?BR> ׃文章主要是讲解游戏程序的制作的,所以策划,工Q音乐制作等斚w误者参考其它文章,下面我就讲讲游戏E序的设计?/FONT>
(一) 开发工具与主要技?/FONT>
1Qg开发工?/FONT>
游戏E序开发工h很多Q在不同游戏q_上有不同的开发工兗在个h计算ZQ可以用目前性的软g开发工P比如QCQC++QVC++QDelphiQC++ Builder{。由于Windows操作pȝ的普及和其强大的多媒体功能,来多的游戏支持Windows操作pȝ。由于VC是微软的产品Q用它来~写WindowsE序有强大的E序接口和丰富的开发资源的支持Q加之VC严}的内存管理,在堆栈上良好的分配处理,生成代码的体U小Q稳定性高的优点,所以VC++成为目前游戏的L开发工兗?/FONT>
2QDirectXlg的知?/FONT>
谈到Windowspȝ下的游戏开发,我们p说一下微软的DirectX SDK?BR> Windowspȝ有一个主要优Ҏ应用E序和设备之间的独立性。然而应用程序的讑֤无关性是通过牺牲部分速度和效率的到的QWindows在硬件和软g间添加了中间抽象层,通过q些中间层我们的应用E序才能在不同的g上游刃有余。但是,我们因此而不能完全利用硬件的特征来获取最大限度的q算和显C速度。这一点在~写Windows游戏时是致命的,DirectX便是册个问题而设计的。DirectX由快速的底层库组成ƈ且没有给游戏设计dq多的约束。微软的DirectX软g开发工具包QSDKQ提供了一套优U的应用程序编E接口(APIsQ,q个~程接口可以提供l你开发高质量、实时的应用E序所需要的各种资源?BR> DirectX?个组件分别是Q?BR> DirectDrawQ?nbsp;使用面切换的方法实现动画,它不仅可以访问系l内存,q可以访问显C内存?BR> Direct3DQ?nbsp;提供?Dg接口?BR> DirectSoundQ?nbsp;立体声和3D声音效果Q同时管理声卡的内存?BR> DirectPlayQ?nbsp;支持开发多人网l游戏,q能处理游戏中网l之间的通信问题?BR> DirectInputQ?nbsp;为大量的讑֤提供输入支持?BR> DirectSetupQ?nbsp;自动安装DirectX驱动E序?BR> 随着DirectX版本的提高,q增加了音乐播放的DirectMusic?/FONT>
3QAlphaBlend 技?/FONT>
现在许多游戏Z辑ֈ光媄或图象的透明效果都会采用AlphaBlend 技术。所谓AlphaBlend技术,其实是按照"Alpha"混合向量的值来混合源像素和目标像素Q一般用来处理半透明效果。在计算Z的图象可以用R(U色)QG(l色)QB(蓝色)三原色来表示。假设一q图象是AQ另一q透明的图象是BQ那么透过BȝAQ看上去的图象C是B和A的合图象,设B图象的透明度ؓalpha(取gؓ0-1Q?为完全透明Q?为完全不透明)QAlpha混合公式如下Q?BR> R(C)=alpha*R(B)+(1-alpha)*R(A)
G(C)=alpha*G(B)+(1-alpha)*G(A)
B(C)=alpha*B(B)+(1-alpha)*B(A)
R(x)、G(x)、B(x)分别指颜色x的RGB分量原色倹{从上面的公式可以知道,Alpha其实是一个决定合透明度的数倹{应用Alpha混合技术,可以实现游戏中的许多ҎQ比如火光、烟雾、阴影、动态光源等半透明效果?/FONT>
4QA*法
在许多游戏中要用鼠标控制人物q动Q而且让h物从目前的位|走到目标位|应该走最短的路径。这p用到最短\径搜索算法即A*法了?BR> A*法实际是一U启发式搜烦Q所谓启发式搜烦Q就是利用一个估价函数评估每ơ的的决{的价|军_先尝试哪一U方案。如果一个估价函数可以找出最短的路径Q我们称之ؓ可采Ux。A*法是一个可采纳的最好优先算法。A*法的估价函数可表示为:
f(n) = g(n) + h(n)
q里Qf(n)是节点n的估价函敎ͼg(n)是v点到l点的最短\径|h(n)是n到目标的最断\l的启发倹{由于A*法比较复杂Q限于篇q,在此单介l一下,具体理论朋友们可以看人工斚w的书c了解详l的情况?/FONT>
其它技术还有粒子系l,音频与视频的调用Q图象文件的格式与信息存储等Q大家可以在学好DirectX的基上逐渐学习更多的技术?/FONT>
Q二Q游戏的具体制作
1Q地囄辑器的制?/FONT>
RPG游戏往往要有大量的场景,场景中根据需要可以有草地Q湖泊,树木Q房屋,家具{道俱,׃一个游戏需要很多场景且地图来大Qؓ了节省空_提高图象文g的可重用性,RPG游戏的画面采用很多重复的单元Q可以叫做“图块”)所构成的,q就要用到地囄辑器了。我们在制作游戏引擎前,要完成地囄辑器的制作。在 RPG游戏里,场景的构成,是图块排列顺序的记录。首先制定一个场景构成文件的格式Q在q个文g里记录构成场景所需要的囑֝的排列顺序,因ؓ我们已经为每个图块徏立了索引Q所以只需要记录这些烦引就可以了。一个场景的构成Q是分成几层来完成的Q地面,建筑和植物,家具摆设Q和在场景中zd的h物或者物体(比如飘扬的旗帜)Q按照一定的序把它们依ơ显C到屏幕上,Ş成了一个丰富多采的场景。我们可以用数组来表C地囑֜景的生成q程?/FONT>
MapData[X][Y]Q?nbsp;//地图数据QX表示地图宽度QY表示地图高度
Picture[num]Q?nbsp;//道具的图片,num表示道具的L
void MakeBackGround() //生成场景函数
{
int n;
for( int i=0; i<Y; i++) //共Y?BR> for( int j=0; j<X; j++) //共X?BR> {
n=MapData[ i ][ j ]; //取得该位|的道具~号
Draw( j*32, i*32, Picture[n]); //在此位置(j*32,i*32)画道?BR> }
}
2Q游戏的模块的划?/FONT>
游戏按功能分为:消息处理pȝ、场景显C及行走pȝ、打斗系l三大主要部分。其中又以消息处理系lؓ核心模块Q其余部分紧紧围l它q行?/FONT>
一Q消息处理系l?/FONT>
消息处理pȝ是游戏的核心部分。游戏用到的消息处理pȝ先等待消息,然后Ҏ收到的消息{到相应的函数q行处理。比如:主角到敌h后,我们pE序产生‘打斗消息’,消息处理pȝ收到q个消息后就会马上{到打斗模块中厅R消息处理的大体框架如下Q?/FONT>
//定义E序中要用到的变?BR>DWORD Message; //消息变量
WinMain() //q入E序
{
初始化主H口;
初始化DirectDraw环境Qƈ调入E序需要的囑Ş、地图数据;
while( 1 ) //消息循环
{
switch( Message )
{
case 行走消息: 行走模块();
case 打斗消息Q?nbsp;打斗模块();
case 事g消息Q?nbsp;事g模块();
}
}
}
二:场景昄及行走系l?/FONT>
作ؓRPG游戏Q其所有事件的发生几乎都是和场景有养I例如Q不同的地方会碰C同的敌h、与不同的h对话得知不同的事情等。鉴于这部分的重要性,我们可再它划分为:背景昄、行?nbsp;?nbsp;事g发生 三个子模块,分别处理各自的功能。下面进行具体分析?/FONT>
Q一Q背景显C?BR> E序q行后,先读取前面地囄辑器制作的场景所需要的囑֝的排列顺序,按照排列序图象拼成一个完整的场景Q一般做法是Q在内存中开辟一C个屏q缓存区Q事先把卛_昄的图象数据准备在~存区内Q然后一ơ性搬Ӟ把它们传送到真正的屏q缓冲区内?BR> 游戏用到的图片则事先制作好ƈ存于另外的图形文件中。地囄辑器制作的场景文件仅仅是对应的数据,而不是真正的囄。在游戏中生成场景就是地囄辑的逆过E,一个是Ҏ场景生成数据Q而另一个是Ҏ数据生成场景?/FONT>
Q二Q行?BR> 要让主角在场景中行走Q至要有上、下、左、右四个行走方向Q每个方?q图Q站立、迈左腿、迈双、迈左腿Q,如图Q游戏中一定要图片的背景设ؓ透明Q这样在Mh物的时候就不会覆盖上背景色了(q一技术DirectDraw中只要用SetColorKeyQ)函数原囄背景色过滤掉p了)。我们让主角位置不动Q而场景UdQ即采用滚屏技术来实现角色在场景上Ud。这栯色一直保持在屏幕的正中间Q需要做的工作只是根据行走方向和步伐不停变换囄而已。行走时的障物判断也是每一个场景中必定要有的,有一些道具如树木、房屋等是不可跨的。对此我主要用一个二l数l来对应一个场景,每一个数lg表场景的一格(见图3)。有障碍的地方,该数l的对应gؓ1Q可通过的地方的gؓ0?/FONT>
Q三Q事件发?BR> 事g发生原理是把相应事件的序号存储在地囄某些格子中,当主角一t入q个格子׃触发对应事g。例如:在游戏开始时Q主角是在他的家里。他要是惛_ȝ话,需要执行场景切换这个处理函数。我们假定该事g的编号ؓ001Q那么在地图上把安外\口处的格子D?01。这样主角走到\口时Q编号ؓ001的场景切换函数就会被触发Q于是主角便C下一个场景中。程序具体如下:
void MessageLoop( int Msg ) //消息循环
{
switch( Msg )
{
char AddressName[16]; //数组AddressName[16]用来存储主角所在地点的名称
case ADDRESS == 001: // 由ADDRESS的值决定场景|出门Q?BR> ScreenX=12; ScreenY=0; //初始化游戏背景位|?BR> Hero.x=360; Hero.y=80;//主角坐标
Move();//主角Ud函数
//以下E序用来昄主角所在地?BR> sprintf(AddressName,"下一q游戏场景的名称");
PrintText(lpDDSPrimary, 280, 330,AddressName , RGB(255,255,255));//在屏q上昄出场景的名称
break;
}
}
三:打斗pȝ
l大多数的RPG都是有战斗存在的Q因此,打斗pȝ成为RPGpȝ中很重要的一环。有不少RPG游戏采用回合制打斗方式,因ؓ实现h较ؓ单。和打斗紧密相关的是升Q通常在一场战斗结束后Q主角的l验值都会增加。而当l验值到达一定程度时Q角色就升了?BR> 上面我简要的介绍了角色扮演游戏的制作Q由于写q篇文章的目的是让读者对角色扮演游戏的制作有一个基本的了解Q所以读者朋友们可以研究相关资料?/FONT>