【Go项目】实现推箱子游戏

  1. 地图构建
  2. 地图显示
  3. 接收键盘事件
  4. 逻辑判断
  5. 打印处理
  6. 通关判断
  7. 总结

相信大家都玩过推箱子这个游戏,本文我们就将用go语言实现一个推箱子游戏。

地图构建

首先我随便找了一个推箱子的关卡,并且用坐标的方式展现出来。

我们可以看到其实这个地图就是个二维数组。因此我们先声明一个二维数组。

    const (
        W = 10
        H = 7
    )
 var GameMap = [H][W]int{}

我们用二维数组存储的内容来表示不同的物体,用0 表示空地,用1表示墙,用2表示小人,用3表示箱子,用4表示箱子推放的地点。现在我们来对刚刚到数组初始化下。

    var GameMap = [H][W]int{
        {0, 0, 0, 1, 1, 1, 1, 1, 1, 0},
        {0, 1, 1, 1, 0, 0, 0, 0, 1, 0},
        {1, 1, 4, 0, 3, 1, 1, 0, 1, 1},
        {1, 4, 4, 3, 0, 3, 0, 0, 2, 1},
        {1, 4, 4, 0, 3, 0, 3, 0, 1, 1},
        {1, 1, 1, 1, 1, 1, 0, 0, 1, 0},
        {0, 0, 0, 0, 0, 1, 1, 1, 1, 0},
    }

地图显示

地图构建好了,我们现在再来设置地图的显示,我们通过二维数组遍历的方法,来打印地图。

    func initMap() {

        for i := 0; i < H; i++ {
            for j := 0; j < W; j++ {

                switch (GameMap[i][j]) {
                case 0:
                    fmt.Printf(" ")//空地
                case 1:
                    fmt.Printf("▒") //墙
                case 2:
                    fmt.Printf("♘") //人
                case 3:
                    fmt.Printf("✩") //箱子
                case 4:
                    fmt.Printf("⊙") //箱子推放的终点
                case 6:
                    fmt.Printf("♞") //人物走到箱子存放点显示
                case 7:
                    fmt.Printf("✭") //箱子推到存放点显示
                }
            }
            fmt.Println()
        }
    }

1 2 3 4 的内容没什么好说的,我们具体的说下6和7, 就是当人物和箱子和4(箱子存放点)重叠的时候,起到变色效果。

接收键盘事件

因为go语言没有像c语言一样,给我们提供无缓冲输入getch。所以我们需要借助一个开源包来帮我实现键盘接收功能。
使用下面的命令安装库:

go get github.com/nsf/termbox-go

有一点需要注意一下,使用go run 直接在控制台中会编译错误。最好使用go build 打包后运行。

我们需要在main函数中添加这个初始化代码

        for true {
            err := termbox.Init()
            if err != nil {
                panic(err)
            }
            defer termbox.Close()
func control() {

        /******键盘事件代码*******/
        switch ev := termbox.PollEvent(); ev.Type {
        case termbox.EventKey:
            switch ev.Key {
            case termbox.KeyArrowUp: //小键盘向上

            case termbox.KeyArrowDown://小键盘向下

            case termbox.KeyArrowLeft://小键盘向左

            case termbox.KeyArrowRight://小键盘向右

            case termbox.KeyF5://F5 用来重置地图

            }
        }

逻辑判断

我们首先设置小人向上移动,我们之前说过,地图其实就是个二维数组,我们想要向上移动,只需要把小人的纵坐标减一个单位就行。
我们的地图只有小人和箱子可以移动,但是箱子的移动又是依靠小人的推动。所以我们的给小人的位置设置成2个变量。

var col, row int // col row 是人物所在坐标变量

我们简单说下人物向上的逻辑:

  • 想要人物向上移动,就需要先判断人物上一个坐标是不是空地,能不能走;
  • 人物向上移动的时候,我们需要把人物原来所在地位置变为空地,然后把上面的空地变成人物;
  • 如果人物的上面是箱子,并且箱子的上面是空地,那么我们就以人物现在的位置为参照点,把人物移动一个单位长度,把箱子移动2个单位长度;
  • 箱子跟人物一样都想要重新设置位置,把原来箱子的位置设置为小人,然后把箱子上面的空地重新设置为箱子;
  • 当我走错想要重新开始的时候,只需要把GameMap重新初始化就行

然后其他的键盘事件,如向下、向左、向右事件都是类似的,都是参考小人的当前坐标来的。

  • 小人向下 纵坐标加1
  • 小人向左 横坐标减1
  • 小人向右 横坐标加1
func control() {
        var col, row int // col row 是人物所在坐标变量
        for i := 0; i < H; i++ {
            for j := 0; j < W; j++ {
                if GameMap[i][j] == 2 || GameMap[i][j] == 6 {
                    col = i
                    row = j
                }
            }
        }
        //ch, _ := getch.Getch()
        switch ev := termbox.PollEvent(); ev.Type {
        case termbox.EventKey:
            switch ev.Key {
            case termbox.KeyArrowUp:
                if GameMap[col-1][row] == 0 || GameMap[col-1][row] == 4 { //col-1 就是 Y坐标-1 就是往上
                    GameMap[col][row] -= 2;   //因为要移动人物,所以我们要在移动人物的时候 把人物原来的位置重新绘图
                    GameMap[col-1][row] += 2; // 让上一个坐标的位置 等于2  绘制出 人物
                    //  如果往上是箱子
                } else if GameMap[col-1][row] == 3 || GameMap[col-1][row] == 7 {
                    if GameMap[col-2][row] == 0 || GameMap[col-2][row] == 4 { //因为人要推箱子,所以Y坐标-2,如果箱子上面是空地
                        GameMap[col][row] -= 2;   // 重新绘图人物 col =1  row =5
                        GameMap[col-1][row] -= 1; // 往上再减1 就是 1-1 =0  5-1 =4   设置箱子原来位置为空地
                        GameMap[col-2][row] += 3; // 重新设置箱子的位置
                    }
                }

            case termbox.KeyArrowDown:
                if GameMap[col+1][row] == 0 || GameMap[col+1][row] == 4 {
                    GameMap[col][row] -= 2;
                    GameMap[col+1][row] += 2;
                } else if GameMap[col+2][row] == 0 || GameMap[col+2][row] == 4 {
                    if GameMap[col+1][row] == 3 || GameMap[col+1][row] == 7 {
                        GameMap[col][row] -= 2;
                        GameMap[col+1][row] -= 1;
                        GameMap[col+2][row] += 3;
                    }
                }
            case termbox.KeyArrowLeft:
                if (GameMap[col][row-1] == 0 || GameMap[col][row-1] == 4) {
                    GameMap[col][row] -= 2;
                    GameMap[col][row-1] += 2;
                } else if (GameMap[col][row-2] == 0 || GameMap[col][row-2] == 4) {
                    if (GameMap[col][row-1] == 3 || GameMap[col][row-1] == 7) {
                        GameMap[col][row] -= 2;
                        GameMap[col][row-1] -= 1;
                        GameMap[col][row-2] += 3;
                    }
                }
            case termbox.KeyArrowRight:
                if (GameMap[col][row+1] == 0 || GameMap[col][row+1] == 4) {
                    GameMap[col][row] -= 2;
                    GameMap[col][row+1] += 2;
                } else if (GameMap[col][row+2] == 0 || GameMap[col][row+2] == 4) {
                    if (GameMap[col][row+1] == 3 || GameMap[col][row+1] == 7) {
                        GameMap[col][row] -= 2;
                        GameMap[col][row+1] -= 1;
                        GameMap[col][row+2] += 3;
                    }
                }
            case termbox.KeyF5:
            GameMap = [H][W]int{
                    {0, 0, 0, 1, 1, 1, 1, 1, 1, 0},
                    {0, 1, 1, 1, 0, 0, 0, 0, 1, 0},
                    {1, 1, 4, 0, 3, 1, 1, 0, 1, 1},
                    {1, 4, 4, 3, 0, 3, 0, 0, 2, 1},
                    {1, 4, 4, 0, 3, 0, 3, 0, 1, 1},
                    {1, 1, 1, 1, 1, 1, 0, 0, 1, 0},
                    {0, 0, 0, 0, 0, 1, 1, 1, 1, 0},
                }
            }
        }

    }

有一点需要注意,需要设置小人坐标越界处理。 比如说小人一直向左走,横坐标会一直减-1,如果超出二维数组的范围,就会产生数组越界。

打印处理

因为我们的游戏是通过控制台循环打印来实现的,这种方法如果不做一些处理,屏幕会不断的下滚,打印N次,因此我们需要每次打印前都清空下控制台。使用控制台打印还存在一个问题,如果我们不设置下打印时间等待,就会造成游戏一直闪烁。

    func main() {

        for true {
            err := termbox.Init()
            if err != nil {
                panic(err)
            }
            defer termbox.Close()
         //清空控制台。
            cmd := exec.Command("clear") //mac的清屏命令
            //exec.Command("cmd", "/c", "cls") //如果是win使用该调命令
            cmd.Stdout = os.Stdout
            cmd.Run()
            initMap()
            control()
            time.Sleep(100)

        }

    }

通关判断

没有推到存放点的箱子,我们是通过二维数组值为3来定义的,存放倒终点的箱子是通过7来定义的。我们可以遍历数组,当二维数组中的值没有3,那说明箱子已经全部存放倒终点,说明过关。

总结

我们已经实现的推箱子的基本功能,你可以再次基础上添加背景音乐,多重关卡,步数显示等功能。

本文转自 https://blog.csdn.net/yang731227/article/details/88914718,如有侵权,请联系删除。


部分资料来源于网络,版权属其原著者所有,只供学习交流之用。如有侵犯您的权益,请联系【公众号:码农印象】删除,可在下方评论,亦可邮件至ysluckly.520@qq.com。互动交流时请遵守宽容、换位思考的原则。

×

喜欢就点赞,疼爱就打赏

(function(){ var bp = document.createElement('script'); bp.src = '//push.zhanzhang.baidu.com/push.js'; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(bp, s); })();
休闲小游戏