HarmonyOS NEXT体验官#拼图小游戏

系统 OpenHarmony
自从我接触到鸿蒙系统,我就被它强大的性能和无限的可能性所吸引。作为一个热爱编程和游戏开发的我,决定尝试在这个全新的系统上开发一款手机拼图游戏。这个过程充满了挑战,但也让我收获颇丰。

想了解更多关于开源的内容,请访问:

51CTO 鸿蒙开发者社区

https://ost.51cto.com

用鸿蒙开发工具写一个拼图小游戏

想想就很激动,那就开工吧。

一、游戏开盘准备

大体思路,先开个游戏盘用来存放拼图,然后动态生成拼图块并且打乱它,再者编辑游戏逻辑,最后判断输赢,差不多了,真简单hh。

开局就是个"Hello World",真是亲切(ɔˆ ³(ˆ⌣ˆc)

生成游戏盘前,我要先准备点资料,一些配置参数,方便我掌控全局。

/**
   * 游戏配置
   */
  @State gameConfig:config_message = {
    w: 800, // 宽高参数,由图片的像素尺寸决定
    h: 800,
    rows: 5, // 行数
    cols: 5, // 列数
    isOver: false, // 游戏是否结束
    imgUrl: $r("app.media._0") // 图片路径
  };

紧接着是完成游戏逻辑的。

/游戏luoji
 ├── 设置拼图宽高
 │
 ├── 选择自己喜欢的图片
 │ 
 ├── 开始游戏               
 │    ├── 生成拼图          
 │    ├── 玩家扣件             
 │    └── 判断输赢
 │
 └── 结束游戏并重置

先是第一步设置宽高,我选择使用Textinput组件,因为这样可以让使用者自定义任何宽高,理论上可以达到无穷大,不知道会不会有大佬愿意拿显微镜玩拼图呢,哈哈哈~~

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);
    })
}

接下来就是选择图片了,我使用的是鸿蒙自带的选择框组件真的很高科技,我只要传个数组进去就完事了。

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) => {
       // 设置select为按下确定按钮时候的选中项index,这样当弹窗再次弹出时显示选中的是上一次确定的选项
       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))
     }
   })
 })

二、游戏开发环节

经过上面的充分准备,终于可以开始写最激动人心的游戏代码了,同时也最麻烦的~~~~~~~~~~

1.生成游戏环境

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 {
  // 准备数组
  this.initBlocksArray();
  // 数组洗牌
  this.shuffle();
}
initBlocksArray:Function = ()=>{

   for(let i = 0; i < this.gameConfig.rows; i ++ ){
     for(let j = 0; j < this.gameConfig.cols; j ++ ) {
       // i行号 j列号
       this.blocks.push({
         x: j * this.blockWidth * -1, // 当前背景图的横坐标
         y: i * this.blockHeight * -1, // 当前背景图的纵坐标
         currentX: j * this.blockWidth * -1, // 正确的背景图的横坐标
         currentY: i * this.blockHeight * -1, // 正确的背景图的纵坐标
         w: this.blockWidth,
         h: this.blockHeight,
         imgUrl: this.gameConfig.imgUrl,
         isVisible: (i === this.gameConfig.rows - 1 && j === this.gameConfig.cols - 1 ? false : true) // 拼图是否可见
       })
     }
   }
   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);
    }
    // 交换两个拼图块
    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.生成拼图块

为了更加好的描绘出拼图块的细节,我自定义了组件。

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(()=>{....}) // 存放着移动拼图块的逻辑

在做移动时,有着一个的细节处理,首先的浮点数比较大小,在计算过程中浮点数会产生精度问题,也就是除不尽的情况,这个时候比较相等时会比较不出来,为了避免这种情况可以用取整的方式进行处理,当然还其它很多方法我就不一一说明了。

/**
 * 移动拼图块
 */
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.定乾坤

我这使用了数组自带的方法来构建数组(filter)。

/**
 * 判断游戏输赢
 */
let wrongs = this.blocks.filter((item)=>{
  // 找到所有不在正确位置上的拼图块
  return !this.isCorrect(item);
})

if(wrongs.length === 0) {
  this.isShow = true;
}

这里写了两个辅助函数,辅助判断拼图块是否在正确的位置。

/**
 * 判断两值是否相等
 */
isEqual:Function = (x:number, y:number)=>{
  return Math.floor(x) === Math.floor(y);
}

/**
 * 判断当前拼图块是否在正确位置
 */
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;
}

到此游戏开发就结束了。

三、游戏测试

 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区

 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区

 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区 #HarmonyOS NEXT体验官#拼图小游戏-鸿蒙开发者社区

领域展开

空间移动

时间回溯

测试非常顺得,界面还算美观哈。

四、总结一下

自从我接触到鸿蒙系统,我就被它强大的性能和无限的可能性所吸引。作为一个热爱编程和游戏开发的我,决定尝试在这个全新的系统上开发一款手机拼图游戏。这个过程充满了挑战,但也让我收获颇丰。

在开始开发之前,我首先对鸿蒙系统进行了深入的研究,了解了它的架构、开发工具以及API等。我发现,鸿蒙系统为开发者提供了丰富的接口和工具,使得开发过程变得更加高效和便捷。同时,我也被它的分布式架构所吸引,这让我思考如何在游戏中实现跨设备的互动体验。

在开发过程中,我遇到了不少挑战。其中最大的挑战是如何实现拼图的平滑移动和旋转效果。为了实现这一效果,我深入研究了鸿蒙系统的图形渲染机制,并尝试了各种算法和优化方法。经过不断的尝试和调整,我终于找到了一个既流畅又美观的解决方案。

当游戏终于开发完成时,我感到无比的自豪和满足。

开源:free_D/鸿蒙开发拼图游戏 (gitee.com)

想了解更多关于开源的内容,请访问:

51CTO 鸿蒙开发者社区

https://ost.51cto.com

责任编辑:jianghua 来源: 51CTO 鸿蒙开发者社区
相关推荐

2022-02-17 20:18:27

JS鸿蒙操作系统

2024-07-26 16:17:22

2024-07-31 09:55:19

2020-12-07 16:20:53

Python 开发编程语言

2021-08-15 22:52:30

前端H5拼图

2023-08-07 15:18:29

游戏开发鸿蒙Arkts

2022-07-29 14:47:34

数独Sudoku鸿蒙

2022-10-19 15:27:36

数独Sudoku鸿蒙

2022-10-18 15:45:17

数独Sudoku鸿蒙

2022-10-19 15:19:53

数独Sudoku鸿蒙

2022-08-25 21:41:43

ArkUI鸿蒙

2022-11-01 15:17:48

JS鸿蒙小游戏

2021-01-15 12:15:36

鸿蒙HarmonyOS游戏

2021-01-12 12:16:55

鸿蒙HarmonyOS游戏

2022-02-11 14:02:09

游戏JS鸿蒙

2022-02-11 14:39:11

游戏JS鸿蒙

2022-10-28 16:20:10

JS鸿蒙小游戏

2021-08-23 11:03:54

鸿蒙HarmonyOS应用

2022-08-22 17:28:34

ArkUI鸿蒙
点赞
收藏

51CTO技术栈公众号