移动软件开发——推箱子游戏

2023年夏季《移动软件开发》实验报告

一、实验目标

1、综合应用所学的知识创建完整的推箱子游戏;2、熟练掌握和绘图API。

二、实验步骤

1.需求分析

1.1 首页功能需求

1.首页需要包含标题和关卡列表。

2.至少包括4个关卡选项,每个关卡显示预览图和第几关。

3.关卡列表点击可以跳转到对应的游戏画面。

1.2 游戏页功能需求

1.游戏页需要显示第几关,游戏画面,方向键和重新开始按钮。

2.点击方向键可以使游戏主角自行移动或推动箱子前进。

3.游戏画面为8x8小方块组成,包括地板,围墙,箱子,游戏主角小鸟和目的地。

4.点击重新开始按钮可以使游戏回归到初始位置并重新开始游戏。

2.页面配置

2.1 创建页面文件

2.2 删除和修改文件

2.3创建其他文件

3.视图设计

3.1 导航栏设计

"window": {
    "backgroundTextStyle": "light",
    "navigationBarBackgroundColor": "#E64340",
    "navigationBarTitleText": "推箱子游戏",
    "navigationBarTextStyle": "white"
  },

3.2 页面设计

(1)公共样式设计

app.wxss

/**app.wxss**/
/* 页面容器样式 */
.container {
  height: 100vh;
  color: #e64340;
  font-weight: bold;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: space-evenly;
}
/* 顶端标题样式 */
.title {
  font-size: 18pt;
}

(2)首页设计

index.wxml

<!--index.wxml-->
<!-- 整体容器 -->
<view class="container">
  <!-- 标题 -->
  <view class="title">游戏选关</view>

  <!-- 关卡列表 -->
  <!-- 关卡列表 -->
  <view class='levelBox'>
    <view class='box' wx:for='{{levels}}' wx:key='index'-level bindtap='chooseLevel' data-level='{{index}}'>
      <image src='/images/{{item}}'></image>
      <text>第{{index+1}}关</text>
    </view>
  </view>

</view>

index.wxss

/* 关卡列表区域 */
.levelBox {
  width: 100%;
}

/* 单个关卡区域 */
.box {
  width: 50%;
  /* 漂移从左到右来摆放 */
  float: left;    
  margin: 20rpx 0;
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* 选关图片 */
image {
  width: 300rpx;
  height: 300rpx;
}

(3)游戏页设计

game.wxml

<view class='container'>
  <!-- 关卡提示 -->
  <view class='title'>第{{level + 1}}关</view>
  <!-- 游戏画布 -->
  <canvas canvas-id='myCanvas'></canvas>
  <!-- 方向键 -->
  <view class='btnBox'>
    <button type='warn' bindtap='up' size="mini"></button>
    <view>
      <button type='warn' bindtap='left' size="mini"></button>
      <button type='warn' bindtap='down' size="mini"></button>
      <button type='warn' bindtap='right' size="mini"></button>
    </view>
  </view>
  <!-- 重新开始按钮 -->
  <button type='warn' bindtap='restartGame' size="mini" >重新开始</button>
</view>

game.wxss

/* 游戏画布样式 */
canvas {
  border: 1rpx solid;
  width: 320px;
  height: 320px;
}

/* 方向键按钮整体区域 */
.btnBox {
  display: flex;
  flex-direction: column;
  align-items: center;
}

/* 方向键按钮第二行 btnBox里面的view */
.btnBox view {
  display: flex;
  flex-direction: row;
}

/* 所有方向键按钮 */
.btnBox button {
  width: 90rpx;
  height: 70rpx;
}

/* 所有按钮样式 */
button {
  margin: 10rpx;
  /* background-color: red; */

}

4.逻辑实现

4.1 公共逻辑

data.js

//================================================
//地图数据map1-map4
//地图数据:1墙,2路,3终点,4箱子,5人物,0墙的外围
//================================================
//关卡1
var map1 = [
  [0, 1, 1, 1, 1, 1, 0, 0],
  [0, 1, 2, 2, 1, 1, 1, 0],
  [0, 1, 5, 4, 2, 2, 1, 0],
  [1, 1, 1, 2, 1, 2, 1, 1],
  [1, 3, 1, 2, 1, 2, 2, 1],
  [1, 3, 4, 2, 2, 1, 2, 1],
  [1, 3, 2, 2, 2, 4, 2, 1],
  [1, 1, 1, 1, 1, 1, 1, 1]
]
//关卡2
var map2 = [
  [0, 0, 1, 1, 1, 0, 0, 0],
  [0, 0, 1, 3, 1, 0, 0, 0],
  [0, 0, 1, 2, 1, 1, 1, 1],
  [1, 1, 1, 4, 2, 4, 3, 1],
  [1, 3, 2, 4, 5, 1, 1, 1],
  [1, 1, 1, 1, 4, 1, 0, 0],
  [0, 0, 0, 1, 3, 1, 0, 0],
  [0, 0, 0, 1, 1, 1, 0, 0]

]
//关卡3
var map3 = [
  [0, 0, 1, 1, 1, 1, 0, 0],
  [0, 0, 1, 3, 3, 1, 0, 0],
  [0, 1, 1, 2, 3, 1, 1, 0],
  [0, 1, 2, 2, 4, 3, 1, 0],
  [1, 1, 2, 2, 5, 4, 1, 1],
  [1, 2, 2, 1, 4, 4, 2, 1],
  [1, 2, 2, 2, 2, 2, 2, 1],
  [1, 1, 1, 1, 1, 1, 1, 1]
]
//关卡4
var map4 = [
  [0, 1, 1, 1, 1, 1, 1, 0],
  [0, 1, 3, 2, 3, 3, 1, 0],
  [0, 1, 3, 2, 4, 3, 1, 0],
  [1, 1, 1, 2, 2, 4, 1, 1],
  [1, 2, 4, 2, 2, 4, 2, 1],
  [1, 2, 1, 4, 1, 1, 2, 1],
  [1, 2, 2, 2, 5, 2, 2, 1],
  [1, 1, 1, 1, 1, 1, 1, 1]
]

module.exports = {
  maps: [map1, map2, map3, map4]
}

4.2 首页逻辑

index.js

Page({

  /**
   * 页面的初始数据
   */
  data: {
    levels : [
      'level01.png',   //图片的名称
      'level02.png',
      'level03.png',
      'level04.png'
    ]
  },

  /**
  * 自定义函数--游戏选关
  */
 chooseLevel: function (e) {
  let level = e.currentTarget.dataset.level
  wx.navigateTo({
    url: '../game/game?level=' + level
  })
},

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
    
  }
})

4.3 游戏页逻辑

game.js

var data = require('../../utils/data.js')
//地图图层数据 记录墙 道路 终点 外围用道路来填充
var map = [
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0]
]
//箱子图层数据
var box = [
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0],
  [0, 0, 0, 0, 0, 0, 0, 0]
]
//方块的宽度  px
var w = 40
//初始化小鸟的行与列
var row = 0
var col = 0

Page({

  /**
   * 页面的初始数据
   */
  data: {
    
  },

  /**
   * 自定义函数--初始化地图数据
   */
  initMap: function (level) {
    // 读取原始的游戏地图数据
    console.log("initMap===>>>:",level)
    let mapData = data.maps[level]
    //使用双重for循环记录地图数据
    for (var i = 0; i < 8; i++) {
      for (var j = 0; j < 8; j++) {
        box[i][j] = 0
        map[i][j] = mapData[i][j]

        if (mapData[i][j] == 4) {
          box[i][j] = 4
          map[i][j] = 2
        } else if (mapData[i][j] == 5) {
          map[i][j] = 2
          //记录小鸟的当前行和列
          row = i
          col = j
        }
      }
    }
  },

/**
  * 自定义函数--绘制地图
  */
  drawCanvas: function () {
    let ctx = this.ctx
    //清空画布
    ctx.clearRect(0, 0, 320, 320)
    //使用双重for循环绘制8x8的地图
    for (var i = 0; i < 8; i++) {
      for (var j = 0; j < 8; j++) {
        //默认是道路
        let img = 'ice'
        if (map[i][j] == 1) {
          img = 'stone'
        } else if (map[i][j] == 3) {
          img = 'pig'
        }

        //绘制地图
        ctx.drawImage('/images/icons/' + img + '.png', j * w, i * w, w, w)

        if (box[i][j] == 4) {
          //叠加绘制箱子
          ctx.drawImage('/images/icons/box.png', j * w, i * w, w, w)
        }
      }
    }

    //叠加绘制小鸟
    ctx.drawImage('/images/icons/bird.png', col * w, row * w, w, w)

    ctx.draw()
  },

  /**
   * 自定义函数--方向键:上
   */
  up: function () {
    //如果不在最顶端才考虑上移
    if (row > 0) {
      //如果上方不是墙或箱子,可以移动小鸟
      if (map[row - 1][col] != 1 && box[row - 1][col] != 4) {
        //更新当前小鸟坐标
        row = row - 1
      }
      //如果上方是箱子
      else if (box[row - 1][col] == 4) {
        //如果箱子不在最顶端才能考虑推动
        if (row - 1 > 0) {
          //如果箱子上方不是墙或箱子
          if (map[row - 2][col] != 1 && box[row - 2][col] != 4) {
            box[row - 2][col] = 4
            box[row - 1][col] = 0
            //更新当前小鸟坐标
            row = row - 1
          }
        }
      }
      //重新绘制地图
      this.drawCanvas()
      //检查游戏是否成功
      this.checkWin()
    }
  },
  /**
   * 自定义函数--方向键:下
   */
  down: function () {
    //如果不在最底端才考虑下移
    if (row < 7) {
      //如果下方不是墙或箱子,可以移动小鸟
      if (map[row + 1][col] != 1 && box[row + 1][col] != 4) {
        //更新当前小鸟坐标
        row = row + 1
      }
      //如果下方是箱子
      else if (box[row + 1][col] == 4) {
        //如果箱子不在最底端才能考虑推动
        if (row + 1 < 7) {
          //如果箱子下方不是墙或箱子
          if (map[row + 2][col] != 1 && box[row + 2][col] != 4) {
            box[row + 2][col] = 4
            box[row + 1][col] = 0
            //更新当前小鸟坐标
            row = row + 1
          }
        }
      }
      //重新绘制地图
      this.drawCanvas()
      //检查游戏是否成功
      this.checkWin()
    }
  },
  /**
   * 自定义函数--方向键:左
   */
  left: function () {
    //如果不在最左侧才考虑左移
    if (col > 0) {
      //如果左侧不是墙或箱子,可以移动小鸟
      if (map[row][col - 1] != 1 && box[row][col - 1] != 4) {
        //更新当前小鸟坐标
        col = col - 1
      }
      //如果左侧是箱子
      else if (box[row][col - 1] == 4) {
        //如果箱子不在最左侧才能考虑推动
        if (col - 1 > 0) {
          //如果箱子左侧不是墙或箱子
          if (map[row][col - 2] != 1 && box[row][col - 2] != 4) {
            box[row][col - 2] = 4
            box[row][col - 1] = 0
            //更新当前小鸟坐标
            col = col - 1
          }
        }
      }
      //重新绘制地图
      this.drawCanvas()
      //检查游戏是否成功
      this.checkWin()
    }


  },
  /**
   * 自定义函数--方向键:右
   */
  right: function () {
    //如果不在最右侧才考虑右移
    if (col < 7) {
      //如果右侧不是墙或箱子,可以移动小鸟
      if (map[row][col + 1] != 1 && box[row][col + 1] != 4) {
        //更新当前小鸟坐标
        col = col + 1
      }
      //如果右侧是箱子
      else if (box[row][col + 1] == 4) {
        //如果箱子不在最右侧才能考虑推动
        if (col + 1 < 7) {
          //如果箱子右侧不是墙或箱子
          if (map[row][col + 2] != 1 && box[row][col + 2] != 4) {
            box[row][col + 2] = 4
            box[row][col + 1] = 0
            //更新当前小鸟坐标
            col = col + 1
          }
        }
      }
      //重新绘制地图
      this.drawCanvas()
      //检查游戏是否成功
      this.checkWin()
    }
  },

  /**
   * 自定义函数--游戏成功处理
   */
  checkWin: function () {
    if (this.isWin()) {
      wx.showModal({
        title: '恭喜',
        content: '游戏成功!',
        showCancel: false
      })
    }
  },

  /**
   * 自定义函数--判断游戏成功
   */
  isWin: function () {
    //使用双重for循环遍历整个数组
    for (var i = 0; i < 8; i++) {
      for (var j = 0; j < 8; j++) {
        //如果有箱子没在终点
        if (box[i][j] == 4 && map[i][j] != 3) {
          //返回假,游戏尚未成功
          return false
        }
      }
    }
    //返回真,游戏成功
    return true
  },

  /**
   * 自定义函数--重新开始游戏
   */
  restartGame: function () {
    //初始化地图数据
    this.initMap(this.data.level )
    //绘制画布内容
    this.drawCanvas()
  },

  /**
   * 生命周期函数--监听页面加载
   */
  onLoad: function (options) {
     //获取关卡
     let level = options.level
     //更新页面关卡标题
     this.setData({
       level: parseInt(level) 
     })
     // console.log('onLoad setData', level,typeof(level))
     //创建画布上下文
     this.ctx = wx.createCanvasContext('myCanvas')
     //初始化地图数据
     this.initMap(level)
     //绘制画布内容
     this.drawCanvas()
  }
})

三、程序运行结果

首页:

在这里插入图片描述

点击第二关,游戏页面如下:
在这里插入图片描述

游戏成功:

在这里插入图片描述

游戏重新开始:
在这里插入图片描述

四、问题总结与体会

​ 此次实验我学会了如何使用组件以及小程序界面API中绘图相关的用法;对小游戏的开发有了深入的了解,同时学会了各种组件的使用。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: 好的,为了开发一个推箱子游戏,你需要了解一些基本的编程概念,如变量,循环,条件语句和函数。 首先,你需要定义游戏场景,即箱子和人物所在的空间。你可以使用二维数组表示游戏场景,每个元素都代表一个格子。 然后,你可以定义玩家的行动,如上下左右移动。当玩家移动时,需要判断该格子是否可以移动。如果是,则更新场景。 最后,你需要定义游戏的目标,如将所有箱子移动到指定位置。当游戏目标完成时,游戏结束。 以下是一个简单的代码示例: ``` def move(direction): if direction == "up": # code to move up elif direction == "down": # code to move down elif direction == "left": # code to move left elif direction == "right": # code to move right def play_game(): # code to initialize game while not game_over: direction = input("Enter a direction: up, down, left, or right") move(direction) # code to check game status and update game scene play_game() ``` 希望这个示例能帮助你开始编写代码。 ### 回答2: 推箱子是一款经典的益智类游戏,通过推动箱子,将所有的箱子推到指定位置,完成挑战。下面我将用300字介绍如何用Python开发一个推箱子游戏。 首先,需要导入Pygame库,它是一款专门用于游戏开发的库。接着,创建一个游戏窗口,并设置窗口大小和标题。 在游戏中,需要定义地图和角色的图像。使用数组来表示地图,并根据不同的值选择相应的图像,如墙、目标、箱子和人物。接着,将地图和角色渲染到游戏窗口中。 接下来,需要定义人物的移动操作。通过监听键盘事件,如果按下相应的键,则根据当前人物的位置和移动方向,判断是否可以进行移动操作。如果可以移动,则更新人物的位置,并相应地更新地图和角色的位置。 在判断箱子的移动时,需要考虑箱子不能穿过墙或其他箱子,只能推动。因此,需要在人物移动后判断人物是否与箱子相邻,若相邻则判断箱子是否可以移动。在确定箱子可以移动后,更新箱子的位置,并判断是否完成了挑战的目标。 在游戏中,可以根据需要添加背景音乐、背景图像和游戏难度等。 最后,添加一个游戏循环,用来更新游戏窗口的显示,并根据键盘事件进行相应的操作。 以上就是用Python开发推箱子游戏的大致步骤。通过学习和实践,可以进一步完善游戏的细节和功能,达到更好的游戏体验。 ### 回答3: 推箱子是一款经典的益智游戏,玩家需要将箱子推至指定位置。我们可以使用Python来开发一个简单的推箱子游戏。 首先,我们需要创建游戏的地图,可以使用一个二维列表来表示。列表中的元素可以用不同的字符来表示不同的物体,比如空地、墙、箱子和目标位置。我们可以用"_"表示空地,"#"表示墙,"@"表示玩家,"$"表示箱子,"X"表示目标位置。游戏地图可以按照游戏难度进行设计,可以使用数字代表不同难度级别,比如1代表简单、2代表中等、3代表困难。 接下来,我们需要实现一些基本的操作函数,比如移动箱子、玩家的移动、判断游戏是否胜利等。我们可以使用键盘上的方向键来控制玩家的移动,用"WASD"键来控制箱子的移动。当箱子被推到目标位置时,判断游戏是否胜利,如果所有的箱子都被成功推到目标位置则游戏获胜。 最后,我们可以使用Python的图形界面库来创建游戏界面,比如Pygame或Tkinter。在界面中显示游戏地图,并通过监听玩家的按键操作来更新地图的状态,从而实现游戏的进行。 通过以上的步骤,我们就可以使用Python开发一个简单的推箱子游戏。当然,开发一个完整的游戏还需要考虑更多的细节,比如关卡的设计、游戏的界面美化和声音效果等。但通过这个基础的框架,我们可以不断扩展和完善游戏,使其更加有趣和具有挑战性。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值