title: go语言实现推箱子游戏
tags: go
相信大家都玩过推箱子这个游戏,本文我们就将用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 是人物所在坐标变量
我们简单说下人物向上的逻辑:
然后其他的键盘事件,如向下、向左、向右事件都是类似的,都是参考小人的当前坐标来的。
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,那说明箱子已经全部存放倒终点,说明过关。
我们已经实现的推箱子的基本功能,你可以再次基础上添加背景音乐,多重关卡,步数显示等功能。