寶石碰碰:HTML5開發(fā)Android本地化App游戲案例
首先來開發(fā)一個(gè)類似于對(duì)對(duì)碰的游戲,游戲界面如下。

游戲操作:上下左右劃動(dòng)屏幕,來操作寶石向不同的方向移動(dòng)。
游戲規(guī)則:當(dāng)有三個(gè)一樣的寶石相鄰則消除,被消除過一次的寶石會(huì)變成半透明,當(dāng)所有寶石都被消除一次后,則進(jìn)入下一關(guān)。
游戲測(cè)試連接:http://lufylegend.com/demo/GemGem
制作開始
一,準(zhǔn)備
首先,需要下載lufylegend.js引擎,下載地址:http://lufylegend.com/lufylegend
二,游戲開發(fā)
引擎lufylegend1.7.0中擴(kuò)展了LLoadManage靜態(tài)類,可以讀取圖片,js文件以及文本文件,本次游戲開發(fā)就來體驗(yàn)一下這個(gè)新功能,首先看下面數(shù)組:
- var loadData = [
 - {path:"../jquery.js",type:"js"},
 - {path:"./js/share.js",type:"js"},
 - {path:"./js/Social.js",type:"js"},
 - {path:"./js/GameRanking.js",type:"js"},
 - {path:"./js/GameLogo.js",type:"js"},
 - {path:"./js/GameClear.js",type:"js"},
 - {path:"./js/Gem.js",type:"js"},
 - {path:"./js/Stage.js",type:"js"},
 - {path:"./js/Clock.js",type:"js"},
 - {path:"./js/Point.js",type:"js"},
 - {path:"./js/GetPoint.js",type:"js"},
 - {path:"./js/Bullet.js",type:"js"},
 - {path:"./js/Event.js",type:"js"},
 - {path:"./js/function.js",type:"js"},
 - {path:"./js/GameBody.js",type:"js"},
 - {name:"num.+",path:"./images/plus.png"},
 - {name:"num.0",path:"./images/0.png"},
 - {name:"num.1",path:"./images/1.png"},
 - {name:"num.2",path:"./images/2.png"},
 - {name:"num.3",path:"./images/3.png"},
 - {name:"num.4",path:"./images/4.png"},
 - {name:"num.5",path:"./images/5.png"},
 - {name:"num.6",path:"./images/6.png"},
 - {name:"num.7",path:"./images/7.png"},
 - {name:"num.8",path:"./images/8.png"},
 - {name:"num.9",path:"./images/9.png"},
 - {name:"back",path:"./images/back.png"},
 - {name:"line",path:"./images/line.png"},
 - {name:"clear",path:"./images/clear.png"},
 - {name:"gem01",path:"./images/gem01.png"},
 - {name:"gem02",path:"./images/gem02.png"},
 - {name:"gem03",path:"./images/gem03.png"},
 - {name:"gem04",path:"./images/gem04.png"},
 - {name:"gem05",path:"./images/gem05.png"},
 - {name:"gem06",path:"./images/gem06.png"},
 - {name:"gem07",path:"./images/gem07.png"},
 - {name:"gem08",path:"./images/gem08.png"},
 - {name:"gem09",path:"./images/gem09.png"},
 - {name:"ico_sina",path:"./images/ico_sina.gif"},
 - {name:"ico_qq",path:"./images/ico_qq.gif"},
 - {name:"ico_facebook",path:"./images/ico_facebook.png"},
 - {name:"ico_twitter",path:"./images/ico_twitter.png"}
 - ];
 
將需要的js文件和圖片文件都加到數(shù)組內(nèi),如果需要加載文件為js文件時(shí),需要指定type為js,如果加載的文件為圖片,則type可以不設(shè)定。
讀取過程與之前用法完全一樣:
- function main(){
 - loadingLayer = new LoadingSample3();
 - addChild(loadingLayer);
 - LLoadManage.load(
 - loadData,
 - function(progress){
 - loadingLayer.setProgress(progress);
 - },
 - function(result){
 - LGlobal.setDebug(true);
 - datalist = result;
 - removeChild(loadingLayer);
 - loadingLayer = null;
 - gameInit();
 - }
 - );
 - }
 
下面來向游戲中添加8行8列64塊寶石,具體做法如下:
- function addGem(){
 - stage.setStage(stage.num + 1);
 - gemLayer.removeAllChild();
 - list = [];
 - //添加寶石
 - for(i=0;i<8;i++){
 - list.push([]);
 - for(var j=0;j<8;j++){
 - num = (Math.random()*9 >>> 0)+1;
 - g = new Gem(num);
 - g.x = j*60;
 - g.y = i*60+120;
 - gemLayer.addChild(g);
 - list[i].push(g);
 - }
 - }
 - //檢驗(yàn)可消除寶石
 - do{
 - clearList = checkClear();
 - if(clearList.length > 0){
 - for(i=0;i<clearList.length;i++){
 - g = clearList[i];
 - num = (Math.random()*9 >>> 0)+1;
 - g.change(num);
 - }
 - }
 - }while(clearList.length > 0);
 - }
 
上面代碼中的Gem對(duì)象是一個(gè)寶石類,完整代碼如下:
- function Gem(num){
 - var self = this;
 - base(self,LSprite,[]);
 - self.num = num;
 - self.bitmap = new LBitmap(new LBitmapData(datalist["gem0"+num]));
 - self.bitmap.x=self.bitmap.y=10;
 - self.addChild(self.bitmap);
 - }
 - Gem.prototype.change = function (num){
 - var self = this;
 - self.num = num;
 - self.bitmap.bitmapData = new LBitmapData(datalist["gem0"+num]);
 - }
 
Gem類繼承自LSprite,內(nèi)部包含一個(gè)LBitmap對(duì)象來顯示寶石圖片。
代碼清單3中調(diào)用了checkClear函數(shù),來檢驗(yàn)是否有可消除寶石,檢測(cè)方法為先進(jìn)行橫向檢索,然后進(jìn)行縱向檢索。
- clearList = [];
 - //橫向檢索
 - for(i=0;i<8;i++){
 - checkList = [list[i][0]];
 - for(j=1;j<8;j++){
 - if(checkList[checkList.length - 1].num == list[i][j].num){
 - checkList.push(list[i][j]);
 - }else{
 - clearList = addClearList(clearList,checkList);
 - checkList = [list[i][j]];
 - }
 - }
 - clearList = addClearList(clearList,checkList);
 - }
 - //縱向檢索
 - for(i=0;i<8;i++){
 - checkList = [list[0][i]];
 - for(j=1;j<8;j++){
 - if(checkList[checkList.length - 1].num == list[j][i].num){
 - checkList.push(list[j][i]);
 - }else{
 - clearList = addClearList(clearList,checkList);
 - checkList = [list[j][i]];
 - }
 - }
 - clearList = addClearList(clearList,checkList);
 - }
 
addClearList函數(shù)作用是將可消除寶石壓入clearList數(shù)組,做法如下:
- function addClearList(clearList,checkList){
 - if(checkList.length >= 3){
 - clearList = clearList.concat(checkList)
 - }
 - return clearList;
 - }
 
游戲操作需要?jiǎng)潉?dòng)屏幕,但是在lufylegend.js引擎中,是沒有劃動(dòng)屏幕的事件的,所以我通過下面MOUSE_DOWN,MOUSE_UP獲取點(diǎn)擊時(shí)和點(diǎn)擊后的位置,來模擬一下劃動(dòng)事件。
- backLayer.addEventListener(LMouseEvent.MOUSE_DOWN,onDown);
 - backLayer.addEventListener(LMouseEvent.MOUSE_UP,onUp);
 
再來看看具體做法,先是onDown函數(shù)。
- function onDown(e){
 - if(mouse_down_obj.isMouseDown)return;
 - continuous = 0;
 - mouse_down_obj.x = e.offsetX;
 - mouse_down_obj.y = e.offsetY;
 - mouse_down_obj.time = new Date().getTime();
 - mouse_down_obj.cx = e.offsetX/60 >>> 0;
 - mouse_down_obj.cy = (e.offsetY - 120)/60 >>> 0;
 - mouse_down_obj.isMouseDown = true;
 - list[mouse_down_obj.cy][mouse_down_obj.cx].graphics.drawRect(1,"black",[0, 0, 60, 60],true,"#000000");
 - }
 
通過e.offsetX和e.offsetY來獲取點(diǎn)擊位置,通過getTime()來獲取點(diǎn)擊時(shí)刻的時(shí)間。
再來看看onUp函數(shù)。
- function onUp(e){
 - list[mouse_down_obj.cy][mouse_down_obj.cx].graphics.clear();
 - if(new Date().getTime() - mouse_down_obj.time > 500){
 - mouse_down_obj.isMouseDown = false;
 - return;
 - }
 - var mx = e.offsetX - mouse_down_obj.x;
 - var my = e.offsetY - mouse_down_obj.y;
 - if(Math.abs(mx) > Math.abs(my)){
 - if(mx > 50){
 - move("right");
 - return;
 - }else if(mx < -50){
 - move("left");
 - return;
 - }
 - }else{
 - if(my > 50){
 - move("down");
 - return;
 - }else if(my < -50){
 - move("up");
 - return;
 - }
 - }
 - mouse_down_obj.isMouseDown = false;
 - }
 
函數(shù)中通過同樣的方法得到點(diǎn)擊結(jié)束時(shí)的位置和時(shí)間,然后與點(diǎn)擊時(shí)刻做比較,最后計(jì)算劃動(dòng)的方向,然后根據(jù)劃動(dòng)的方向來調(diào)用move函數(shù),讓寶石移動(dòng)。
move函數(shù)如下:
- function move(dir){
 - direction = dir;
 - var m = moveGem(dir,8);
 - var mx = m[0],my = m[1];
 - var obj,fun;
 - for(var i=0;i<8;i++){
 - if(mx == 0){
 - obj = list[i][mouse_down_obj.cx];
 - }else{
 - obj = list[mouse_down_obj.cy][i];
 - }
 - if(i < 7){
 - fun = null;
 - }else{
 - fun = function(){
 - hiddenObj.visible = true;
 - checkClear();
 - };
 - }
 - LTweenLite.to(obj,0.3,
 - {
 - x:obj.x+mx,
 - y:obj.y+my,
 - onComplete:fun,
 - ease:Strong.easeOut
 - });
 - }
 - }
 
下面以向右移動(dòng)為例來說明一下move函數(shù)的處理過程,如下:

先將最左邊的一個(gè)寶石H移到最左邊,然后再利用LTweenLite緩動(dòng)類將整個(gè)一行8個(gè)寶石,向右緩動(dòng)一個(gè)單位。向左的話正好相反,向上向下也是同樣的原理。
每次緩動(dòng)結(jié)束,要調(diào)用一次checkClear函數(shù),來判斷一下是否有可消除的寶石,如果有則開始消除寶石,如何來消除寶石呢?
我依然以向右劃動(dòng)來舉例說明,看下面圖片,假設(shè)D1,D2,D3可消除,E4,F4,G4可消除:

那么首先將D1,D2,D3移到左邊邊界外,E4,F4,G4也移到邊界外,表示被消除, 之后對(duì)每一行的寶石進(jìn)行位置判定,如每行的第一個(gè)寶石的x坐標(biāo)應(yīng)該是60,第二個(gè)為120,以此類推。如果他們不在自己的相應(yīng)位置上,那么將其向左移動(dòng)到 規(guī)定位置就可以了,寫成代碼的話,如下:
- function moveList(){
 - var gem,time,maxTime,mx,my,fun;
 - maxTime = 0;
 - switch(direction){
 - case "left":
 - for(i=0;i<8;i++){
 - for(j=0;j<8;j++){
 - gem = list[i][j];
 - mx = 60*j;
 - if(gem.x > mx){
 - time = 0.3*((gem.x-mx) / 60 >>> 0);
 - if(maxTime < time)maxTime = time;
 - fun = null;
 - if(gem.x > 420){
 - fun = function(gem){
 - if(gem.x <= 420)gem.visible = true;
 - }
 - }
 - LTweenLite.to(gem,time,
 - {
 - x:mx,
 - onUpdate:fun,
 - onComplete:fun,
 - ease:Strong.easeOut
 - });
 - }
 - }
 - }
 - break;
 - case "right":
 - for(i=0;i<8;i++){
 - for(j=0;j<8;j++){
 - gem = list[i][j];
 - mx = 60*j;
 - if(gem.x < mx){
 - time = 0.3*((mx-gem.x) / 60 >>> 0);
 - if(maxTime < time)maxTime = time;
 - fun = null;
 - if(gem.x < 0){
 - fun = function(gem){
 - if(gem.x >= 0)gem.visible = true;
 - }
 - }
 - LTweenLite.to(gem,time,
 - {
 - x:mx,
 - onUpdate:fun,
 - onComplete:fun,
 - ease:Strong.easeOut
 - });
 - }
 - }
 - }
 - break;
 - case "up":
 - for(i=0;i<8;i++){
 - for(j=0;j<8;j++){
 - gem = list[j][i];
 - my = 120+60*j;
 - if(gem.y > my){
 - time = 0.3*((gem.y-my) / 60 >>> 0);
 - if(maxTime < time)maxTime = time;
 - fun = null;
 - if(gem.y > 560){
 - fun = function(gem){
 - if(gem.y <= 560)gem.visible = true;
 - }
 - }
 - LTweenLite.to(gem,time,
 - {
 - y:my,
 - onUpdate:fun,
 - onComplete:fun,
 - ease:Strong.easeOut
 - });
 - }
 - }
 - }
 - break;
 - case "down":
 - for(i=0;i<8;i++){
 - for(j=0;j<8;j++){
 - gem = list[j][i];
 - my = 120+60*j;
 - if(gem.y < my){
 - time = 0.3*((my-gem.y) / 60 >>> 0);
 - if(maxTime < time)maxTime = time;
 - fun = null;
 - if(gem.y < 120){
 - fun = function(gem){
 - if(gem.y >= 120)gem.visible = true;
 - }
 - }
 - LTweenLite.to(gem,time,
 - {
 - y:my,
 - onUpdate:fun,
 - onComplete:fun,
 - ease:Strong.easeOut
 - });
 - }
 - }
 - }
 - break;
 - }
 - LTweenLite.to({},maxTime*1.5,
 - {
 - onComplete:checkStageClear,
 - ease:Strong.easeOut
 - });
 - }
 
當(dāng)然,游戲是有時(shí)間限制的,看下面的Clock類:
- function Clock(){
 - var self = this;
 - base(self,LSprite,[]);
 - self.timer = 0;
 - self.addTimer = 0.05;
 - self.graphics.drawArc(5,"#333333",[0,0,70,0,2*Math.PI]);
 - }
 - Clock.prototype.onframe = function (){
 - var self = this;
 - self.timer += self.addTimer;
 - self.graphics.clear();
 - self.graphics.drawArc(10,"#333333",[0,0,70,0,2*Math.PI]);
 - self.graphics.drawArc(5,"#ffffff",[0,0,70,-Math.PI*0.5,Math.PI*self.timer/180-Math.PI*0.5]);
 - }
 
首先將Clock加載到游戲中,然后再利用ENTER_FRAME時(shí)間軸事件,來不斷調(diào)用Clock的onframe不斷的繪制圓弧,當(dāng)timer的數(shù)值大于等于360的時(shí)候代表畫完了整個(gè)圓弧,那么游戲結(jié)束。
以上,游戲的主要原理都介紹完了,下面看看如何來把游戲轉(zhuǎn)化為本地App
三,發(fā)布本地化App
首先,用Eclipse新建一個(gè)Android Project;
注:如何搭建Android環(huán)境,我就不說了,網(wǎng)上教程多得是,隨便百度一下吧。
然后,填寫項(xiàng)目名稱,并選擇相應(yīng)的sdk版本,這里我選了2.2;
接著是填寫相應(yīng)數(shù)據(jù),這個(gè)隨自己心情就可以了。
接著,重點(diǎn)來了,在工程下的assets文件夾下,簡(jiǎn)歷一個(gè)www文件夾(名字自己隨意),然后把剛才開發(fā)好的游戲復(fù)制到這個(gè)文件夾下,當(dāng)然,lufylegend引擎也必須復(fù)制過來。

接著修改res/layout/main.xml文件,添加webView,如下:
- <?xml version="1.0" encoding="utf-8"?>
 - <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
 - android:layout_width="fill_parent"
 - android:layout_height="fill_parent"
 - android:orientation="vertical" >
 - <WebView
 - android:id="@+id/webView1"
 - android:layout_width="match_parent"
 - android:layout_height="match_parent" />
 - </LinearLayout>
 
最后,修改Main.java文件,利用webView來顯示html網(wǎng)頁,如下:
- public class Main extends Activity {
 - /** Called when the activity is first created. */
 - @Override
 - public void onCreate(Bundle savedInstanceState) {
 - super.onCreate(savedInstanceState);
 - setContentView(R.layout.main);
 - WebView webview = (WebView )findViewById(R.id.webView1);
 - webview.getSettings().setJavaScriptEnabled(true);
 - webview.setVerticalScrollbarOverlay(true);
 - webview.loadUrl("file:///android_asset/www/index.html");
 - }
 - }
 
好了,運(yùn)行程序吧。畫面如下:

最后,想要發(fā)布游戲?yàn)?apk文件的話,build一下就好了。
結(jié)束了,簡(jiǎn)單吧?
四,源碼
最后給出本次游戲的源代碼:
http://lufylegend.com/lufylegend_download/GemGem.rar
注:只含游戲源碼,lufylegend.js引擎請(qǐng)自己到官網(wǎng)下載















 
 
 



 
 
 
 