
??想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:??
??51CTO 開源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??
前言
Hello,各位好久不見!
非常抱歉,這段時(shí)間作者在忙于其他事情,所以一直沒有在社區(qū)更文。
時(shí)間過得很快,HarmonyOS與OpenHarmony也發(fā)展地很快,目前DevEco Studio的鴻蒙SDK也已經(jīng)更新到API8的版本了。對(duì)于鴻蒙操作系統(tǒng)的蒸蒸日上,我感到非常喜悅。
最近的一段時(shí)間,我重新閱讀了放在IDE中的數(shù)獨(dú)游戲項(xiàng)目的代碼,發(fā)現(xiàn)自己曾經(jīng)寫的代碼其實(shí)挺爛的。事實(shí)上,我是在接觸了鴻蒙的前端后,才開始嘗試去學(xué)習(xí)Java語(yǔ)言并利用Java創(chuàng)建UI的。所以在寫這個(gè)項(xiàng)目時(shí),我的Java仍處于比較菜的水平,權(quán)限修飾符用的很亂,并且有些代碼雖然能運(yùn)行,但也寫的不簡(jiǎn)潔或者不合理,所以近期我對(duì)這個(gè)項(xiàng)目的代碼進(jìn)行了許多優(yōu)化。實(shí)際上,作為一個(gè)學(xué)習(xí)者,任何人在編程的初期都不可避免地會(huì)寫一些爛代碼,以及踩一些坑。我們進(jìn)步的過程,不僅是學(xué)習(xí)一些未接觸過的新知識(shí),更重要的是在之前所寫過的代碼中不斷優(yōu)化,并在這個(gè)過程中不斷提煉新的計(jì)算機(jī)思維。
另外,因?yàn)镈evEco studo的SDK8版本已經(jīng)將Java移除了,這意味著在IDE中用Java代碼編譯的程序只能運(yùn)行在SDK7以前的華為機(jī)型,所以筆者在更新完關(guān)于數(shù)獨(dú)項(xiàng)目的文章后,就暫時(shí)不繼續(xù)分享有關(guān)Java的代碼了,而是先嘗試去學(xué)習(xí)與分享關(guān)于JavaScrip與ets的內(nèi)容。
上期的內(nèi)容回顧——>>https://ost.51cto.com/posts/15252。
正文
在本期的分享中,筆者將繼續(xù)完善項(xiàng)目的相關(guān)功能,并制作判定游戲是否通關(guān)的功能。
定義兩個(gè)重要的數(shù)組
打開GameAbilitySlice,并在合適的位置定義兩個(gè)二維數(shù)組——grids_win和grids_input。注意,這兩個(gè)數(shù)組是作為全局變量定義的,他們不能定義在生命周期函數(shù)或其他函數(shù)內(nèi)。
//定義兩個(gè)二維數(shù)組
int[][] grids_win=new int[6][];
int[][] grids_input=new int[6][];
......
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
.......
在這兩個(gè)新定義的數(shù)組變量,grids_win代表儲(chǔ)存了數(shù)獨(dú)答案的二維數(shù)組,而grids_input代表用戶在游戲交互過程中將要改變的數(shù)組。這兩個(gè)數(shù)組在項(xiàng)目中主要用于判定游戲的勝利與否,而判斷邏輯是這樣的:如果grid_input中每行每列的元素都等于grids_win中每行每列的元素,這就意味著用戶在數(shù)獨(dú)網(wǎng)格填入的數(shù)字與答案一致,此時(shí)游戲勝利;否則,游戲未成功。
在定義grids_win與grids_input前,我們?cè)谥暗奈恼轮幸呀?jīng)定義了grid_c0,而grid_c0用于生成網(wǎng)格區(qū)域中的數(shù)獨(dú)題目的。接下來,我們?cè)俣xgrid_c0數(shù)組所對(duì)應(yīng)的答案數(shù)組——grid_v0(注意,grid_c0與grid_v0均是定義在onstart函數(shù)內(nèi)的局部變量):
...
@Override
protected void onStart(Intent intent) {
super.onStart(intent);
grid_c0[0]=new int[]{0,0,0,0,1,0};
...
//定義grid_v0
int[][] grid_v0=new int[6][];
grid_v0[0]=new int[]{4,3,5,6,1,2};
grid_v0[1]=new int[]{1,5,2,4,6,3};
grid_v0[2]=new int[]{6,2,3,1,4,5};
grid_v0[3]=new int[]{5,4,1,2,3,6};
grid_v0[4]=new int[]{2,6,4,3,5,1};
grid_v0[5]=new int[]{3,1,6,5,2,4};
然后,我們?cè)龠M(jìn)行如下賦值操作(筆者也驚訝地發(fā)現(xiàn),Java的數(shù)組賦值竟然可以如此輕松快捷qwq):
grids_input=grid_c0;
grids_win=grid_v0;
這樣,grids_input和grids_win就被輸入對(duì)應(yīng)的數(shù)據(jù)了。
或許讀者會(huì)問,為什么不直接把grid_c0和grid_v0分別作為用戶交互的數(shù)組和答案數(shù)組來使用呢?原因是,grid_c0與grid_v0只是分別作為這個(gè)游戲的某一個(gè)一個(gè)關(guān)卡的題目與答案,在后文,筆者會(huì)導(dǎo)入更多的題目與答案,所以我們需要另外定義grid_input與grid_win作為兩個(gè)全局變量,當(dāng)關(guān)卡不同時(shí),這兩個(gè)變量也就分別負(fù)責(zé)儲(chǔ)存不同的題目與答案,以及參與判斷游戲是否勝利的邏輯判斷過程。
創(chuàng)建三個(gè)功能按鈕
我們先找到之前創(chuàng)建的6個(gè)圓形button對(duì)象(即用于在網(wǎng)格區(qū)域輸入數(shù)字的Button組件),并在這6個(gè)圓形button對(duì)象的監(jiān)聽器內(nèi)各加入一行指令(即下面代碼中“//”前的部分):
//按鍵區(qū)域
Button button_input1=new Button(this);
button_input1.setText("1");
button_input1.setTextColor(Color.BLACK);
button_input1.setTextSize(75);
button_input1.setBackground(element2);
button_input1.setComponentSize(150, 150);
button_input1.setPosition(70,1600);
button_input1.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setTextColor(Color.BLACK);
button_temp.setText("1");
grids_input[j1][k1]=1; //為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值1
}
});
layout1.addComponent(button_input1);
Button button_input2=new Button(this);
button_input2.setText("2");
button_input2.setTextColor(Color.BLACK);
button_input2.setTextSize(75);
button_input2.setBackground(element2);
button_input2.setComponentSize(150, 150);
button_input2.setPosition(70+160*1,1600);
button_input2.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setTextColor(Color.BLACK);
button_temp.setText("2");
grids_input[j1][k1]=2;//為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值2
}
});
layout1.addComponent(button_input2);
Button button_input3=new Button(this);
button_input3.setText("3");
button_input3.setTextColor(Color.BLACK);
button_input3.setTextSize(75);
button_input3.setBackground(element2);
button_input3.setComponentSize(150, 150);
button_input3.setPosition(70+160*2,1600);
button_input3.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setTextColor(Color.BLACK);
button_temp.setText("3");
grids_input[j1][k1]=3;//為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值3
}
});
layout1.addComponent(button_input3);
Button button_input4=new Button(this);
button_input4.setText("4");
button_input4.setTextColor(Color.BLACK);
button_input4.setTextSize(75);
button_input4.setBackground(element2);
button_input4.setComponentSize(150, 150);
button_input4.setPosition(70+160*3,1600);
button_input4.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setText("4");
grids_input[j1][k1]=4;//為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值4
}
});
layout1.addComponent(button_input4);
Button button_input5=new Button(this);
button_input5.setText("5");
button_input5.setTextColor(Color.BLACK);
button_input5.setTextSize(75);
button_input5.setBackground(element2);
button_input5.setComponentSize(150, 150);
button_input5.setPosition(70+160*4,1600);
button_input5.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setText("5");
grids_input[j1][k1]=5;//為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值5
}
});
layout1.addComponent(button_input5);
Button button_input6=new Button(this);
button_input6.setText("6");
button_input6.setTextColor(Color.BLACK);
button_input6.setTextSize(75);
button_input6.setBackground(element2);
button_input6.setComponentSize(150, 150);
button_input6.setPosition(70+160*5,1600);
button_input6.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setText("6");
grids_input[j1][k1]=6;//為網(wǎng)格區(qū)域?qū)?yīng)的題目矩陣的相應(yīng)位置賦值6
}
});
layout1.addComponent(button_input6);
setUIContent(layout1);
}
指令中的變量j1和變量k1,就是筆者在上期內(nèi)容中利用Text組件轉(zhuǎn)換所得的的每個(gè)網(wǎng)格的位置信息(詳細(xì)原理詳見上期內(nèi)容)。這樣以后,用戶每次在某個(gè)區(qū)域上輸入數(shù)字(1到6的整數(shù))時(shí),數(shù)組grids_input的對(duì)應(yīng)存儲(chǔ)位置也會(huì)被輸入相應(yīng)的數(shù)字元素。
完成上述操作后,我們開始另外創(chuàng)建三個(gè)新的按鈕,每個(gè)按鈕都負(fù)責(zé)不同的功能(如圖箭頭所指部分):

首先,我們?yōu)閷⒁獎(jiǎng)?chuàng)建的Button對(duì)象定義基本的背景元素:
//背景元素
...
ShapeElement element4=new ShapeElement();
element4.setRgbColor(new RgbColor(0,125,225)); //設(shè)置Rgb顏色
element4.setCornerRadius(50); //設(shè)置圓角
然后通過代碼布局創(chuàng)建這三個(gè)按鈕(需要寫入的Button對(duì)象的方法都差不多,只是一些參數(shù)不同):
//按鍵區(qū)—操作
Button button_clean=new Button(this);//“清除”按鈕
//基本UI屬性方法
button_clean.setText("清除");
button_clean.setTextColor(Color.WHITE);
button_clean.setTextSize(75);
button_clean.setBackground(element4);
button_clean.setComponentSize(280, 150);
button_clean.setPosition(50,1900);
//設(shè)置點(diǎn)擊監(jiān)聽器
button_clean.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
button_temp.setText("");
grids_input[j1][k1]=0;
}
});
//設(shè)置狀態(tài)改變監(jiān)聽器,讓按鈕擁有動(dòng)態(tài)效果
button_clean.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
@Override
public void onComponentStateChanged(Component component, int i) {
if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
button_clean.setComponentSize(290, 160); //組件處于被點(diǎn)擊態(tài)時(shí)尺寸放大
}else {
button_clean.setComponentSize(280, 150); //組件不處于被點(diǎn)擊態(tài)時(shí)尺寸恢復(fù)初始值
}
}
});
//將組件放入自定義布局
layout1.addComponent(button_clean);
Button button_replay=new Button(this);//"重新開始"按鈕
//基本UI屬性方法
button_replay.setText("重新開始");
button_replay.setTextColor(Color.WHITE);
button_replay.setTextSize(75);
button_replay.setBackground(element4);
button_replay.setComponentSize(370, 150);
button_replay.setPosition(50+307,1900);
//設(shè)置點(diǎn)擊監(jiān)聽器
button_replay.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
Intent intent_replay=new Intent();
present(new GameAbilitySlice(),intent_replay);
}
});
//設(shè)置狀態(tài)改變監(jiān)聽器,讓按鈕擁有動(dòng)態(tài)效果
button_replay.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
@Override
public void onComponentStateChanged(Component component, int i) {
if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
button_replay.setComponentSize(380,160);
}else {
button_replay.setComponentSize(370, 150);
}
}
});
//將組件放入自定義布局
layout1.addComponent(button_replay);
Button button_pr=new Button(this);//“提交“按鈕
//基本UI屬性方法
button_pr.setText("提交");
button_pr.setTextColor(Color.WHITE);
button_pr.setTextSize(75);
button_pr.setBackground(element4);
button_pr.setComponentSize(280, 150);
button_pr.setPosition(50+700,1900);
//設(shè)置點(diǎn)擊監(jiān)聽器
button_pr.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
}
});
//設(shè)置狀態(tài)改變監(jiān)聽器,讓按鈕擁有動(dòng)態(tài)效果
button_pr.setComponentStateChangedListener(new Component.ComponentStateChangedListener() {
@Override
public void onComponentStateChanged(Component component, int i) {
if (ComponentState.isStateMatched(ComponentState.COMPONENT_STATE_PRESSED,i)){
button_pr.setComponentSize(290, 160);
}else {
button_pr.setComponentSize(280, 150);
}
}
});
//將組件放入自定義布局
layout1.addComponent(button_pr);
這樣之后,我們就新創(chuàng)建了三個(gè)按鈕。其中,第一個(gè)”清除“按鈕用于在網(wǎng)格區(qū)域的獲焦網(wǎng)格上輸入空字符串,這意味著我們實(shí)現(xiàn)了清除白色網(wǎng)格內(nèi)已輸入數(shù)字的功能。在清除白色網(wǎng)格內(nèi)的數(shù)字的同時(shí),此按鈕還將grid_input(即在游戲過程會(huì)被用戶修改參數(shù)的矩陣)對(duì)應(yīng)位置的元素賦值為0,確保grid_input內(nèi)的對(duì)應(yīng)元素可以在清除功能被執(zhí)行時(shí)更新(否則游之后的游戲判斷功能會(huì)出bug)。
第二個(gè)”重新開始“按鈕用于讓應(yīng)用從當(dāng)前的GameAbilitySlice跳轉(zhuǎn)至另一個(gè)新生成的GameAbilitySlice,能夠?qū)崿F(xiàn)頁(yè)面刷新的功能。由于在之前的文章中,我們?cè)趏nBackground回調(diào)中加入了銷毀指令,所以系統(tǒng)由舊的GameAbilitySlice導(dǎo)航至新的GameAbilitySlice時(shí),舊的GameAbilitySlice由于進(jìn)入background態(tài)后會(huì)啟用onBackground回調(diào),進(jìn)而被銷毀。假如之前我們沒有添加銷毀指令,那么用戶每點(diǎn)擊一次”重新開始“的按鈕,AbilitySlice的實(shí)例棧就會(huì)堆放一個(gè)GameAbilitySlice,這是非常浪費(fèi)系統(tǒng)內(nèi)存資源的。
第三個(gè)按鈕是”提交“鍵,我們已經(jīng)寫入了基本的方法,但他的監(jiān)聽器內(nèi)是未添加任何代碼的。接下來我們就在這個(gè)button對(duì)象的監(jiān)聽器內(nèi)加入一些指令,以實(shí)現(xiàn)判斷游戲是否成功的功能。
制作判定游戲成功的函數(shù)
首先,我們?cè)诤线m的位置定義一個(gè)布爾型函數(shù):
import ......
public class GameAbilitySlice extends AbilitySlice {
......
private boolean Gamesuccess(){
int a,b;
for(a=0;a<6;a++){
for (b=0;b<6;b++){
if (grids_win[a][b]!=grids_input[a][b]){
return false;
}
}
}
return true;
}
......
@Override
protected void onStart(Intent intent) {
......
這個(gè)函數(shù)是返回布爾值的函數(shù),其封裝的代碼也非常簡(jiǎn)單,就是讓grid_win矩陣與grid_input矩陣的每個(gè)位置的元素進(jìn)行比較,如果經(jīng)過循環(huán)判斷為全部相等,那么這兩個(gè)矩陣就完全相等,即滿足了游戲勝利的條件。值得一提的是,由于此函數(shù)封裝的代碼包含的兩個(gè)變量——grids_win與grids_input是全局變量,所以筆者并沒有將執(zhí)行不同功能的代碼完全模塊化,這意味著如果這兩個(gè)全局變量在某些指令中被修改,Gamesuccess()是有幾率受到影響的,這是項(xiàng)目中不足的地方。
接著,我們通過代碼布局初始化兩個(gè)彈窗(彈窗必須定義在 button_pr之前):
//對(duì)話框
CommonDialog Dialog_win=new CommonDialog(getContext());
Dialog_win.setSize(800,400);
Dialog_win.setTitleText(" 游戲解答成功!");
Dialog_win.setButton(IDialog.BUTTON1,"返回主菜單",(iDialog, i) -> Dialog_win.destroy());
CommonDialog Dialog_fail=new CommonDialog(getContext());
Dialog_fail.setSize(800,400);
Dialog_fail.setTitleText(" 啊哦~沒有通過哦");
Dialog_fail.setContentText(" 游戲未成功解答");
Dialog_fail.setButton(IDialog.BUTTON1,"繼續(xù)游戲",(iDialog, i) -> Dialog_fail.destroy());
最后,我們?cè)凇疤峤弧卑粹o的監(jiān)聽器內(nèi)加入指令:
Button button_pr=new Button(this);
......
button_pr.setClickedListener(new Component.ClickedListener() {
@Override
public void onClick(Component component) {
//加入指令
if (Gamesuccess()){
Dialog_win.show();//成功
}else {
//加入指令
Dialog_fail.setContentText(" 游戲未完成或答案不正確" );
Dialog_fail.show();//失敗
}
}
});
......
layout1.addComponent(button_pr);
可以看到,如果Gamesuccess()函數(shù)返回的值是true,那么系統(tǒng)就會(huì)調(diào)用Dialog_win的show()方法,將Dialog_win展示在屏幕前以提示游戲成功;如果返回的值是false,系統(tǒng)則調(diào)用 Dialog_fail的show()方法,將Dialog_fail展示在屏幕前以提示游戲未成功。這樣一來,游戲成功與否的判斷邏輯便得以成功搭建。?
經(jīng)過上述的所有操作后,我們可以打開API為6的模擬機(jī)試玩,效果圖如下:


因?yàn)镾DK6版本的P40模擬機(jī)在近期被停用了,所以我只能用平板模擬機(jī)調(diào)試程序(/(ㄒoㄒ)/~~),圖片看起來可能有點(diǎn)怪怪的。
結(jié)語(yǔ)
本期的內(nèi)容就先分享到這里,更多關(guān)于數(shù)獨(dú)小游戲項(xiàng)目精彩的內(nèi)容我將在下期繼續(xù)為大家揭曉。
???想了解更多關(guān)于開源的內(nèi)容,請(qǐng)?jiān)L問:??
??51CTO 開源基礎(chǔ)軟件社區(qū)??
??https://ost.51cto.com??。