HarmonyOS NEXT體驗(yàn)官#拼圖小游戲
想了解更多關(guān)于開(kāi)源的內(nèi)容,請(qǐng)?jiān)L問(wèn):
用鴻蒙開(kāi)發(fā)工具寫(xiě)一個(gè)拼圖小游戲
想想就很激動(dòng),那就開(kāi)工吧。
一、游戲開(kāi)盤準(zhǔn)備
大體思路,先開(kāi)個(gè)游戲盤用來(lái)存放拼圖,然后動(dòng)態(tài)生成拼圖塊并且打亂它,再者編輯游戲邏輯,最后判斷輸贏,差不多了,真簡(jiǎn)單hh。
開(kāi)局就是個(gè)"Hello World",真是親切(?? 3(???c)
生成游戲盤前,我要先準(zhǔn)備點(diǎn)資料,一些配置參數(shù),方便我掌控全局。
/**
* 游戲配置
*/
@State gameConfig:config_message = {
w: 800, // 寬高參數(shù),由圖片的像素尺寸決定
h: 800,
rows: 5, // 行數(shù)
cols: 5, // 列數(shù)
isOver: false, // 游戲是否結(jié)束
imgUrl: $r("app.media._0") // 圖片路徑
};
緊接著是完成游戲邏輯的。
/游戲luoji
├── 設(shè)置拼圖寬高
│
├── 選擇自己喜歡的圖片
│
├── 開(kāi)始游戲
│ ├── 生成拼圖
│ ├── 玩家扣件
│ └── 判斷輸贏
│
└── 結(jié)束游戲并重置
先是第一步設(shè)置寬高,我選擇使用Textinput組件,因?yàn)檫@樣可以讓使用者自定義任何寬高,理論上可以達(dá)到無(wú)窮大,不知道會(huì)不會(huì)有大佬愿意拿顯微鏡玩拼圖呢,哈哈哈~~
Row(){
Text("拼圖的尺寸:")
TextInput()
.width("50vp")
.height("40vp")
.backgroundColor(Color.Grey)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.maxLength(2)
.onChange((val)=>{
this.gameConfig.rows = parseInt(val);
})
Text("X")
TextInput()
.width("50vp")
.height("40vp")
.backgroundColor(Color.Grey)
.fontColor(Color.White)
.textAlign(TextAlign.Center)
.maxLength(2)
.onChange((val)=>{
this.gameConfig.rows = parseInt(val);
})
}
接下來(lái)就是選擇圖片了,我使用的是鴻蒙自帶的選擇框組件真的很高科技,我只要傳個(gè)數(shù)組進(jìn)去就完事了。
Button("選擇圖片")
.width("100vp")
.onClick(() => {
TextPickerDialog.show({
// 文檔:https://developer.huawei.com/consumer/cn/doc/harmonyos-references-V5/ts-methods-textpicker-dialog-V5#%E7%A4%BA%E4%BE%8B1
range: this.imgName,
selected: this.select,
disappearTextStyle: {color: Color.Red, font: {size: 15, weight: FontWeight.Lighter}},
textStyle: {color: Color.Black, font: {size: 20, weight: FontWeight.Normal}},
selectedTextStyle: {color: Color.Blue, font: {size: 30, weight: FontWeight.Bolder}},
onAccept: (value: TextPickerResult) => {
// 設(shè)置select為按下確定按鈕時(shí)候的選中項(xiàng)index,這樣當(dāng)彈窗再次彈出時(shí)顯示選中的是上一次確定的選項(xiàng)
this.select = value.index;
this.gameConfig.imgUrl = $r(`app.media._${this.select}`);
console.log("我的配置:" + JSON.stringify(this.gameConfig.imgUrl));
console.log(this.select + '')
console.info("TextPickerDialog:onAccept()" + JSON.stringify(value))
}
})
})
二、游戲開(kāi)發(fā)環(huán)節(jié)
經(jīng)過(guò)上面的充分準(zhǔn)備,終于可以開(kāi)始寫(xiě)最激動(dòng)人心的游戲代碼了,同時(shí)也最麻煩的~~~~~~~~~~
1.生成游戲環(huán)境
Column(){
/**
* 初始化游戲面板
*/
Flex({wrap:FlexWrap.Wrap}){
/**
* 渲染拼圖
*/
ForEach(this.isShuffle?this.blocks: this.blocks, (item: Block, idx:number) => {
setPiece({block: item, blocks: this.blocks, idx: idx, isShuffle: this.isShuffle, isShow: this.isWin})
})
/**
* 贏的組件
*/
Text("Win")
.fontSize(80)
.fontColor(Color.White)
.position({x:70, y: 80})
.visibility(this.isWin?Visibility.Visible:Visibility.Hidden)
}
.width(this.gameConfig.w + 4 + "px") // 4px的邊框
.height(this.gameConfig.h + 4 + "px")
.borderWidth("2px")
.borderColor("#ccc")
2.初始化游戲的信息
/**
* 初始化生成信息
*/
aboutToAppear(): void {
// 準(zhǔn)備數(shù)組
this.initBlocksArray();
// 數(shù)組洗牌
this.shuffle();
}
initBlocksArray:Function = ()=>{
for(let i = 0; i < this.gameConfig.rows; i ++ ){
for(let j = 0; j < this.gameConfig.cols; j ++ ) {
// i行號(hào) j列號(hào)
this.blocks.push({
x: j * this.blockWidth * -1, // 當(dāng)前背景圖的橫坐標(biāo)
y: i * this.blockHeight * -1, // 當(dāng)前背景圖的縱坐標(biāo)
currentX: j * this.blockWidth * -1, // 正確的背景圖的橫坐標(biāo)
currentY: i * this.blockHeight * -1, // 正確的背景圖的縱坐標(biāo)
w: this.blockWidth,
h: this.blockHeight,
imgUrl: this.gameConfig.imgUrl,
isVisible: (i === this.gameConfig.rows - 1 && j === this.gameConfig.cols - 1 ? false : true) // 拼圖是否可見(jiàn)
})
}
}
return ;
}
/**
* 打亂拼圖
*/
shuffle:Function = ()=>{
let len = this.blocks.length;
for(let i = 0; i < len - 1; i ++ ) {
let idx = Math.floor(Math.random() * len) % (len - 1);
while(idx == i){
idx = Math.floor(Math.random() * len) % (len - 1);
}
// 交換兩個(gè)拼圖塊
this.blocks[i].x += this.blocks[idx].x;
this.blocks[i].y += this.blocks[idx].y;
this.blocks[idx].x = this.blocks[i].x - this.blocks[idx].x;
this.blocks[idx].y = this.blocks[i].y - this.blocks[idx].y;
this.blocks[i].x = this.blocks[i].x - this.blocks[idx].x;
this.blocks[i].y = this.blocks[i].y - this.blocks[idx].y;
}
}
3.生成拼圖塊
為了更加好的描繪出拼圖塊的細(xì)節(jié),我自定義了組件。
Flex()
.width(this.block.w + "px") // 每塊拼圖的寬度
.height(this.block.h + "px") // 每塊拼圖的高度
.borderWidth("1px")
.borderColor(Color.White)
.backgroundImage(this.block.imgUrl)
.backgroundImagePosition({x: this.block.x, y: this.block.y})
.visibility(this.block.isVisible ? Visibility.Visible: Visibility.Hidden)
.onClick(()=>{....}) // 存放著移動(dòng)拼圖塊的邏輯
在做移動(dòng)時(shí),有著一個(gè)的細(xì)節(jié)處理,首先的浮點(diǎn)數(shù)比較大小,在計(jì)算過(guò)程中浮點(diǎn)數(shù)會(huì)產(chǎn)生精度問(wèn)題,也就是除不盡的情況,這個(gè)時(shí)候比較相等時(shí)會(huì)比較不出來(lái),為了避免這種情況可以用取整的方式進(jìn)行處理,當(dāng)然還其它很多方法我就不一一說(shuō)明了。
/**
* 移動(dòng)拼圖塊
*/
if(!this.isShow && ((Math.floor(y1) === Math.floor(y2) && Math.floor(Math.abs(x1 - x2)) === w) || (Math.floor(x1) === Math.floor(x2) && Math.floor(Math.abs(y1 - y2)) === h))){
this.blocks[this.idx].x += inVisibleBlock.x;
this.blocks[this.idx].y += inVisibleBlock.y;
inVisibleBlock.x = this.blocks[this.idx].x - inVisibleBlock.x;
inVisibleBlock.y = this.blocks[this.idx].y - inVisibleBlock.y;
this.blocks[this.idx].x = this.blocks[this.idx].x - inVisibleBlock.x;
this.blocks[this.idx].y = this.blocks[this.idx].y - inVisibleBlock.y;
let temp = this.blocks[this.idx].isVisible;
this.blocks[this.idx].isVisible = inVisibleBlock.isVisible;
inVisibleBlock.isVisible = temp;
this.isShuffle = !this.isShuffle;
}
4.定乾坤
我這使用了數(shù)組自帶的方法來(lái)構(gòu)建數(shù)組(filter)。
/**
* 判斷游戲輸贏
*/
let wrongs = this.blocks.filter((item)=>{
// 找到所有不在正確位置上的拼圖塊
return !this.isCorrect(item);
})
if(wrongs.length === 0) {
this.isShow = true;
}
這里寫(xiě)了兩個(gè)輔助函數(shù),輔助判斷拼圖塊是否在正確的位置。
/**
* 判斷兩值是否相等
*/
isEqual:Function = (x:number, y:number)=>{
return Math.floor(x) === Math.floor(y);
}
/**
* 判斷當(dāng)前拼圖塊是否在正確位置
*/
isCorrect:Function = (block: Block)=>{
let flag1:boolean = this.isEqual(block.x, block.currentX);
let flag2:boolean = this.isEqual(block.y, block.currentY);
return flag1 && flag2;
}
到此游戲開(kāi)發(fā)就結(jié)束了。
三、游戲測(cè)試
|
|
|
領(lǐng)域展開(kāi) | 空間移動(dòng) | 時(shí)間回溯 |
測(cè)試非常順得,界面還算美觀哈。
四、總結(jié)一下
自從我接觸到鴻蒙系統(tǒng),我就被它強(qiáng)大的性能和無(wú)限的可能性所吸引。作為一個(gè)熱愛(ài)編程和游戲開(kāi)發(fā)的我,決定嘗試在這個(gè)全新的系統(tǒng)上開(kāi)發(fā)一款手機(jī)拼圖游戲。這個(gè)過(guò)程充滿了挑戰(zhàn),但也讓我收獲頗豐。
在開(kāi)始開(kāi)發(fā)之前,我首先對(duì)鴻蒙系統(tǒng)進(jìn)行了深入的研究,了解了它的架構(gòu)、開(kāi)發(fā)工具以及API等。我發(fā)現(xiàn),鴻蒙系統(tǒng)為開(kāi)發(fā)者提供了豐富的接口和工具,使得開(kāi)發(fā)過(guò)程變得更加高效和便捷。同時(shí),我也被它的分布式架構(gòu)所吸引,這讓我思考如何在游戲中實(shí)現(xiàn)跨設(shè)備的互動(dòng)體驗(yàn)。
在開(kāi)發(fā)過(guò)程中,我遇到了不少挑戰(zhàn)。其中最大的挑戰(zhàn)是如何實(shí)現(xiàn)拼圖的平滑移動(dòng)和旋轉(zhuǎn)效果。為了實(shí)現(xiàn)這一效果,我深入研究了鴻蒙系統(tǒng)的圖形渲染機(jī)制,并嘗試了各種算法和優(yōu)化方法。經(jīng)過(guò)不斷的嘗試和調(diào)整,我終于找到了一個(gè)既流暢又美觀的解決方案。
當(dāng)游戲終于開(kāi)發(fā)完成時(shí),我感到無(wú)比的自豪和滿足。
開(kāi)源:free_D/鴻蒙開(kāi)發(fā)拼圖游戲 (gitee.com)