【開發者說欄目】HarmonyOS開發者公眾號一直堅持海納百創的態度,致力於從不同角度,不同方面幫助開發者們更好更快地熟練HarmonyOS相關開發知識,同時為開發者們提供更好的展現自己、充分發揮自己特長的平臺,【開發者說】因此而生。
我們面向所有開發者們徵集HarmonyOS相關的技術洞察或見解,開發過程的心得和開發成果,同時也為大家展示開發者們的開發成果,經驗分享和心得。
本文作者:張詔添,深圳大學-信息與計算科學專業在讀學生
本期我們為大家帶來的是由開發者張詔添投稿的童年經典遊戲在HarmonyOS手機上的開發過程,簡單易上手的小遊戲demo,希望給你的HarmonyOS開發之旅多一點啟發。
俄羅斯方塊,一款經典的小遊戲,相信是很多人童年的回憶。
當然作為開發者而言,不僅要會玩遊戲,同樣要會做遊戲,秉承著這樣的開發理念,張詔添總結了一些開發俄羅斯方塊遊戲的要點訣竅,並在一步步開發中最終實現。
首先讓我們先來了解一下俄羅斯方塊遊戲開發的要點
繪製一些基礎組件:比如遊戲的方陣格局,左右移動,旋轉改變方塊的按鈕,重新開始的按鈕等等;設計不同方塊形狀:設定有7類不同的方塊,形狀如下:
還需根據方塊形狀不同設計不同的方塊變化樣式,並且這些方塊在遊戲中的出現是隨機的;
方塊的下落: 實現方塊的自動向下移動且再次任意一種方塊,當中涉及到時間的設置、方塊下移判斷及判斷方塊不能下移之後的新方塊產生;方塊左右移動:點擊左右移動鍵向相應方向移動一格,當左右有其他方塊或達到邊界時不能進行移動;改變方塊形狀;方塊消除:當有任一行全部方塊填滿時該行消除,該行上述的所有方塊均會向下移動一格;遊戲結束與重新開始:遊戲結束情況判定及頁面設計,重新開始清零啟動。
下面我們就正式進入項目開發環節,來看一下如何逐一攻破以上要點:
創建項目
成功下載安裝DevEco Studio之後,在創建新工程時,選擇Phone選項,選擇默認的模板(java版),將文件命名為不帶中文或特殊字符的項目(如此處的MyPhoneGame2),最後點擊Finish。
左右滑動查看更多
為了保障應用能以「俄羅斯方塊」的名字呈現,我們需要config.json文件中做一些小改動,找到代碼中的「label」:「MyPhoneGame2」,將其修改成"label": "俄羅斯方塊",這樣就可以實現將應用名稱修改為俄羅斯方塊了。
同時,在最下方"launchType":"standard"的後面添加以下代碼,可以實現去掉應用上方的標籤欄:
entry>src>main>config.json "launchType": "standard", "metaData": { "customizeData": [ { "name": "hwc-theme", "value": "androidhwext:style/Theme.Emui.Light.NoTitleBar", "extra": "" } ] } 左右滑動查看更多
繪製基礎組件
在這個要點中我們要繪製一個15*10的方陣和「←」按鈕、「→」按鈕、「變」按鈕、「重新開始」按鈕,方陣和按鈕的代碼在entry>src>main>java>com.example.myphoneapplication>slice>MainAbilitySlice中編寫:
方塊繪製實現
定義方格的邊長length為常量100,方格的間距interval為常量2,,定義一個位置布局layout和一個表示方格顏色的二維數組grids,創建函數initializeinitialize()分別對其初始化,布局layout初始化為線性布局DirectionalLayout,二維數組grids全部賦值為0,在onStart函數中調用函數initializeinitialize(),具體代碼為:
public class MainAbilitySlice extends AbilitySlice { private DirectionalLayout layout; private static final int length=100; private static final int interval=2; private int[][] grids; public void onStart(Intent intent) { super.onStart(intent); initialize(); } public void initialize(){ layout = new DirectionalLayout(this); grids = new int[15][10]; for(int row = 0; row < 15; row++) for(int column = 0; column < 10; column++) grids[row][column] = 0; } 左右滑動查看更多
然後創建函數drawGrids(int[][]grids)用於繪製15*10的方陣:
public void drawGrids(){ layout.setLayoutConfig((new ComponentContainer.LayoutConfig(ComponentContainer.LayoutConfig.MATCH_PARENT,ComponentContainer.LayoutConfig.MATCH_PARENT))); Component.DrawTask task=new Component.DrawTask() { @Override public void onDraw(Component component, Canvas canvas) { Paint paint = new Paint(); paint.setColor(Color.BLACK); RectFloat rect=new RectFloat(30-20,250-20,length*10+interval*9+30+20,length*15+interval*14+250+20); canvas.drawRect(rect,paint); 左右滑動查看更多
因為有七種顏色的方塊,所以分別用0到7代表一種顏色,顏色可以有不同選擇,這裡僅提供一類色系作為參考:
for(int row = 0; row < 15; row++){//0表示灰色,1代表紅色,2代表綠色,3代表藍綠色,4代表品紅色,5代表藍色,6代表白色,7代表黃色 for(int column = 0; column < 10; column++){ if(grids[row][column] == 0) paint.setColor(Color.GRAY); else if(grids[row][column] == 1) paint.setColor(Color.RED); else if(grids[row][column] == 7) paint.setColor(Color.YELLOW); 左右滑動查看更多
四個按鈕創建
創建完方陣後要開始繪製上面的四個按鈕,這裡創建函數drawButton():
public void drawButton(){ ShapeElement background = new ShapeElement(); background.setRgbColor(new RgbColor(174, 158, 143));//按鈕的背景色 background.setCornerRadius(100); 左右滑動查看更多
繪製按鈕涉及創建按鈕變量,按鈕包含的文字信息,文字位置,文字顏色,字號,按鈕在屏幕中顯示的位置,點擊呈現的效果,代碼結構基本一致,只是參數有所變化,這裡就只以向左的「←」按鈕的繪製為例,其餘表示向右的「→」,表示旋轉變化的「變」,表示「重新開始」的按鈕創建方式大家可以在完整代碼中獲取:以向左的「←」按鈕的繪製:
Button button1 = new Button(this); button1.setText("←"); button1.setTextAlignment(TextAlignment.CENTER); button1.setTextColor(Color.WHITE); button1.setTextSize(100); button1.setMarginTop(1800); button1.setMarginLeft(160); button1.setPadding(10,0,10,0); button1.setBackground(background); button1.setClickedListener(new Component.ClickedListener() { @Override public void onClick(Component component) { leftShift(); } }); layout.addComponent(button1); 左右滑動查看更多
最後實現的效果為:
最後在initialize()函數中調用drawButton()函數和drawGrids()函數
至此我們完成基本的方陣和按鈕繪製,接下來進入方塊的生成。
隨機產生方塊
這個部分我們要攻破兩個點,一個是如何形成方塊,另一個是如何實現隨機。
在這個應用中,我們將採取常量二維數組來儲存不同顏色的不同形狀所在的位置,比如{{0,3},{0,4},{0,5},{0,6}};代表:
其中{0,3}就表示該方塊的第一個方格在grids[0][3]的位置,{0,4}就表示該方塊的第二個方格在grids[0][4]的位置,以此類推,這樣連起來就可以得到一種顏色的一種形狀的方塊了。
由於這段代碼數量眾多,大家可以在完整代碼中找到,這裡主要講述設置方法。
然後定義各種表示方塊的常量二維數組,定義方塊所佔方格的數量grids_number為常量4,二維數組NowGrids表示當前方塊的形狀,row_number表示方塊的總行數,column_number表示方塊的總列數,Grids表示方塊的顏色,column_start表示方塊第一個方格所在二維數組grids的列數:
private static final int grids_number=4; private int[][] NowGrids; private int row_number; private int column_number; private int Grids; private int column_start; 左右滑動查看更多
接著需要創建函數「create+Color+Grids」為各種顏色各種形狀的方塊賦予對應的NowGrids、row_number、column_numbr、Grids、column_start的值,下面以
為例。
public void createRedGrids1(){ NowGrids=RedGrids1; row_number=2; column_number=3; Grids=1; column_start=3;
接下來解決另一個問題,隨機生成的問題,創建函數createGrids()隨機調用「create+Color+Grids」函數,再將存儲不同顏色的不同形狀的方塊所在的位置賦予對應的Grids值。
最後在initialize()函數中直接調用createGrids()函數。
方塊自動下落
這個部分我們要實現方塊能自動向下移動並且再次產生一種形狀的方塊。
首先需要定義一個時間變量timer,緊接著定義當前下落的行數Nowrow,當前左右移動的列數Nowcolumn,在函數createGrids()中對Nowrow和Nowcolumn賦值為0。
創建函數down()判斷方塊能否再次下移,判斷方法為當方塊下移到下邊界時或方塊下方有其他方塊時,則不能繼續下移了,返回false,否則返回true:
public boolean down(){ boolean k; if(Nowrow + row_number == 15){ return false; } for(int row = 0; row < grids_number; row++){ k = true; for(int i = 0; i < grids_number; i++){ if(NowGrids[row][0] + 1 == NowGrids[i][0] && NowGrids[row][1] == NowGrids[i][1]){ k = false; }} if(k){ if(grids[NowGrids[row][0] + Nowrow + 1][NowGrids[row][1] + Nowcolumn] != 0) return false; } } return true; } 左右滑動查看更多
創建函數run(),初始化timer,增加時間事件,判斷當方塊能繼續下移時則清除當前方塊,Nowrow加1,再在下一格的位置繪製剛才的方塊,實現方塊的下移,當方塊不能下移時則產生新的方塊。
public void run(){ timer=new Timer(); timer.schedule(new TimerTask() { @Override public void run() { getUITaskDispatcher().asyncDispatch(()->{ if(down()){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } Nowrow++; for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } else{ createGrids(); } drawGrids(); }); } },0,750); } 左右滑動查看更多
最後在函數onStart(Intent intent)中調用函數run()
最終實現效果如下:
方塊左右移動
通過點擊 「←」或「→」方塊來控制方向朝相應方向移動的效果。
由於向左向右的代碼結構類似,這裡我們都以左移作為示例:
首先我們需要創建函數left()判斷方塊能否再次左移,判斷方法為當方塊左移到左邊界時或方塊左方有其他方塊時,則不能繼續左移了,返回false,否則返回true
然後創建函數leftShift(),判斷當方塊能繼續左移時則清除當前方塊,Nowcolumn減1,再在左一格的位置繪製剛才的方塊,實現方塊的左移,可以參考以下代碼:
public void leftShift(){ if(left()){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = 0; } Nowcolumn--; for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } drawGrids(); } 左右滑動查看更多
最後在函數drawButton()中的"←"按鈕和"→"按鈕增加點擊事件,分別調用上述的函數。
改變方塊形狀
這個部分主要實現點擊「變」將會切換成該方塊其他形狀的效果。
首先創建函數"chang+Color+Grids"用於調用新方塊的"create+Color+Grids"函數,實現在同一種顏色的方塊中變換到其他形狀的方塊,小正方形的方塊不需要實現這種變化。
然後創建函數changGrids()用於判斷當前方塊的顏色,接著調用對應的改變方塊形狀的"chang+Color+Grids"函數
最後在函數drawButton()中的"變"按鈕增加點擊事件,調用函數changGrids()。
方塊消除
當有任一行全部填滿方塊時該行便會消除,該行上述的所有方塊均會向下移動一格
首先創建函數eliminateGrids()用於判斷是否有任一行全部填滿方塊,當存在時則消除該行,並且該行上述的所有方塊均會向下移動一格:
public void eliminateGrids() { boolean k; for (int row = 14; row >= 0; row--) { k = true; for (int column = 0; column < 10; column++) { if (grids[row][column] == 0) k = false; } if (k) { for (int i = row - 1; i >= 0; i--) { for (int j = 0; j < 10; j++) { grids[i + 1][j] = grids[i][j]; } } for (int n = 0; n < 10; n++) { grids[0][n] = 0; } } } drawGrids(); } 左右滑動查看更多
最後在函數createGrids()中調用函數eliminateGrids()
實現效果如下:
遊戲結束與重新開始
關於遊戲結束其實需要操刀兩部分事情,一個是遊戲結束的文本,一個是如何判定遊戲結束。
創建函數drawText()用於繪製遊戲結束文本,這個部分比較簡單,只需在頁面上創建文本即可。
那麼進入下一步,如何判斷遊戲是否結束,答案是判斷能否再次產生新的方塊。
因此我們需要創建函數gameover()用於判斷能否再次產生新的方塊,判斷方法為當產生新的方塊原有的位置存在不為0的數字則無法產生新的方塊,返回true
public boolean gameover(){ for(int row = 0; row < grids_number; row++){ if(grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] != 0){ return true; } } return false; } 左右滑動查看更多
再在函數createGrids()中增加判斷,當遊戲未有結束時繼續產生新的方塊,當遊戲結束時停止時間和調用函數drawText()用於顯示遊戲結束文本:
public void createGrids(){//部分代碼沒有貼出,歡迎自行下載附件查看原始碼 if(gameover() == false){ for(int row = 0; row < grids_number; row++){ grids[NowGrids[row][0] + Nowrow][NowGrids[row][1] + Nowcolumn] = Grids; } } else{ timer.cancel(); drawText(); } } 左右滑動查看更多
實現如下效果:
最後在函數drawButton()中的"重新開始"按鈕增加點擊事件,調用函數initialize()和函數run()。
到此,我們已經完成了俄羅斯方塊手機應用的開發啦!
回顧整個開發過程,難點在於用於遊戲的不同俄羅斯方塊的繪製生成,系統如何隨機產生方塊,如何判定方塊下落的位置,實現消除等幾個方面,但實現並不困難。