今天我闲着没事想用二维数组做一个文字游戏,所以就有了这篇文章。
先看看最终效果图:
首先我们想一下,推箱子这个游戏有什么特点?有一个正方形的游戏区域,有玩家,有箱子,有终点。
所以第一步在先创建几个数据用于代表他们。
先创建一个vector2类用于存储各种会动物品的坐标
//这个类用来存储坐标
public class Vector2
{
private int posX;
private int posY;
{
posX = 0;
posY = 0;
}
public Vector2(int x,int y)
{
this.posX = x;
this.posY = y;
}
public int getPosX()
{
return this.posX;
}
public int getPosY()
{
return this.posY;
}
public void setPosX(int x)
{
this.posX = x;
}
public void setPosY(int y)
{
this.posY = y;
}
}
BoxGame里写我们的main方法,是我们整个游戏的入口点
public class BoxGame
{
//创建一个二维数组表示我们的整个地图
public static String[][] map = new String[10][10];
//玩家坐标
public static Vector2 playerPos = new Vector2();
//箱子坐标
public static Vector2 boxPos = new Vector2();
//代表围墙的字符
public static final String WALL = "H";
//代表玩家的字符
public static final String PLAYER = "&";
//代表箱子的字符
public static final String BOX = "o";
//代表终点的字符
public static final String ENDPOINT = "*";
public static void main(string[] args)
{
}
}
然后再创建一个地图编辑器类,用于管理地图状态,绘制地图等相关操作。
先写几个方法出来,一会再逐个编写方法体
public class mapEditor
{
//用于初始化墙壁(就是固定在游戏边框的)
public static void initWall()
{}
//用于编辑地图
public static void EditMap()
{}
//用于打印出当前地图的信息
public static void printMap()
{}
//用于进行玩家主角的移动操作
public static void movement()
{}
}
先编写初始化墙壁的方法吧,添加一个布尔参数,用于决定是否清空除了墙壁外的中间部分位置
因为后期拓展关卡起来,每通过一关都是要重新绘制一个不一样的关卡的。
public static void initwall(boolean clear)
{
//将地图的边框填充上墙壁字符,代表有墙壁
for (int x =0;x<10;x++)
{
if (x == 0 || x == 9)
{
for (int i=0;i<10;i++)
{
BoxGame.map[x][i] = BoxGame.WALL;
}
}
else
{
BoxGame.map[x][0] = BoxGame.WALL;
BoxGame.map[x][9] = BoxGame.WALL;
}
}
//如果是清除模式,连地图中间的所有内容都清空
if (clear)
{
for (int x=1;x<9;x++)
{
for (int i=1;i<9;i++)
{
BoxGame.map[x][i] = " ";
}
}
}
}
然后是编辑地图方法,添加两个参数,一个是坐标的列表,一个是对应指定坐标的内容(填充进地图内指定坐标)
public static void EditMap(List<Vector2> vector2, String[] info)
{
for (int x=0;x<info.length;x++)
{
BoxGame.map[vector2.get(x).getPosY][vector2.get(x).getPosX] = info[x];
}
}
非常简便~
接下来我们继续编写打印地图的方法,只需要遍历数组中的信息然后加到一个字符串中打印出来就可以啦~实现方法有很多
public static void printMap()
{
//创建一个变量用来存储所有需要打印的信息
String printInfo = "";
for (int x=0;x<10;x++)
{
for (int i=0;i<10;i++)
{
printInfo += BoxGame.map[x][i];
//为了美观让每个字符之间有一个空格
printInfo += " ";
}
//每存储一行添加一个换行符
printInfo += "\n";
}
System.out.println(printInfo);
}
内容也是非常滴简单。
最后来编写我们整个程序最核心的部分,用于判断玩家移动的函数,游戏的主要逻辑判断。
修改返回值为boolean值,当玩家推动箱子的位置为终点时,返回true表示通关,
否则返回false表示未通关。
添加一个string参数,用于接收玩家发出的指令,A左移D右移W上移S下移
public static boolean movement(String dir)
{
//判断玩家发出的指令
switch(dir)
{
//向上移动
case "W":
{
//由于我们打印地图的方法,越往上索引越低,所以是跟正常的反过来的,向上是Y-1。
//判断玩家上方是否为空位
if(BoxGame.map[BoxGame.playerPos.getPosY() - 1][BoxGame.playerPos.getPosX()].equals(" "))
{
//如果为空位的话直接移动,先把当前位置清空
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
//然后更新玩家坐标向上移动
BoxGame.playerPos.setPosY( BoxGame.playerPos.getPosY() - 1);
//在移动后的位置填充玩家字符,表示移动成功
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
//如果上方是箱子
else if(BoxGame.map[BoxGame.playerPos.getPosY() - 1][BoxGame.playerPos.getPosX()].equals(BoxGame.BOX))
{
//再判断箱子的上方是不是终点
if(BoxGame.map[BoxGame.boxPos.getPosY() - 1][BoxGame.boxPos.getPosX()].eqauls(BoxGame.ENDPOINT))
{
//如果是,返回true,关卡胜利
return true;
}
//如果箱子上方是墙壁
else if(BoxGame.map[BoxGame.boxPos.getPosY() - 1][BoxGame.boxPos.getPosX()].eqauls(BoxGame.WALL))
{
//无法移动
return false;
}
//如果箱子上方是空位
else
{
//先移动箱子
//将箱子当前位置清空
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = " ";
//更新箱子坐标
BoxGame.boxPos.setPosY(BoxGame.boxPos.getPosY() - 1);
//将新坐标填入箱子字符,表示箱子移动
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = BoxGame.BOX;
//接下来再更新玩家信息,上面做过注释了,操作相同,这里不再赘述。
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosY( BoxGame.playerPos.getPosY() - 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
}
break;
}
//向下移动
case "S":
{
//操作大庭相径,不过是坐标位置不同,不做赘述。
if(BoxGame.map[BoxGame.playerPos.getPosY() + 1][BoxGame.playerPos.getPosX()].equals(" "))
{
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosY( BoxGame.playerPos.getPosY() + 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
else if(BoxGame.map[BoxGame.playerPos.getPosY() + 1][BoxGame.playerPos.getPosX()].equals(BoxGame.BOX))
{
if(BoxGame.map[BoxGame.boxPos.getPosY() + 1][BoxGame.boxPos.getPosX()].eqauls(BoxGame.ENDPOINT))
{
return true;
}
else if(BoxGame.map[BoxGame.boxPos.getPosY() + 1][BoxGame.boxPos.getPosX()].eqauls(BoxGame.WALL))
{
return false;
}
else
{
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = " ";
BoxGame.boxPos.setPosY(BoxGame.boxPos.getPosY() + 1);
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = BoxGame.BOX;
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosY( BoxGame.playerPos.getPosY() + 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
}
break;
}
//向左移动
case "A":
{
if(BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX() - 1].equals(" "))
{
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosX( BoxGame.playerPos.getPosX() - 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
else if(BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX() - 1].equals(BoxGame.BOX))
{
if(BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX() - 1].eqauls(BoxGame.ENDPOINT))
{
return true;
}
else if(BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX() - 1].eqauls(BoxGame.WALL))
{
return false;
}
else
{
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = " ";
BoxGame.boxPos.setPosX(BoxGame.boxPos.getPosX() - 1);
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = BoxGame.BOX;
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosX( BoxGame.playerPos.getPosX() - 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
}
break;
}
//向右移动
case "D":
{
if(BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX() + 1].equals(" "))
{
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosX( BoxGame.playerPos.getPosX() + 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
else if(BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX() + 1].equals(BoxGame.BOX))
{
if(BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX() + 1].eqauls(BoxGame.ENDPOINT))
{
return true;
}
else if(BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX() + 1].eqauls(BoxGame.WALL))
{
return false;
}
else
{
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = " ";
BoxGame.boxPos.setPosX(BoxGame.boxPos.getPosX() + 1);
BoxGame.map[BoxGame.boxPos.getPosY()][BoxGame.boxPos.getPosX()] = BoxGame.BOX;
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = " ";
BoxGame.playerPos.setPosX( BoxGame.playerPos.getPosX() + 1);
BoxGame.map[BoxGame.playerPos.getPosY()][BoxGame.playerPos.getPosX()] = BoxGame.PLAYER;
}
}
break;
}
}
return false;
}
到此地图编辑器类已经完成,不过我们只完成了游戏的逻辑,还没有制作出一个关卡~
接下来我们使用之前制作的editmap方法来制作一个关卡:
public static void loadLevel_1()
{
//先初始化墙壁
mapEditor.initwall(true);
//创建一个list用来一会传入editmap方法
List<Vector2> list=new ArrayList<>();
//设置关卡中玩家和箱子的坐标
playerPos.setPosX(1); playerPos.setPosY(1); boxPos.setPosY(2); boxPos.setPosX(2);
list.add(new Vector2(1,1)); list.add(new Vector2(2,2)); list.add(new Vector2(8,5));
list.add(new Vector2(1,3)); list.add(new Vector2(2,3)); list.add(new Vector2(3,3));
list.add(new Vector2(2,5)); list.add(new Vector2(3,5)); list.add(new Vector2(3,6));
list.add(new Vector2(3,8)); list.add(new Vector2(4,8)); list.add(new Vector2(5,4));
list.add(new Vector2(5,5)); list.add(new Vector2(5,6)); list.add(new Vector2(6,4));
//创建一个数组用于存储上面按顺序的每个坐标对应的字符
String[] info =new String[]{PLAYER,BOX,ENDPOINT,
WALL,WALL,WALL,
WALL,WALL,WALL,
WALL,WALL,WALL,
WALL,WALL,WALL};
//调用editmap方法
mapEditor.EditMap(list,info);
}
制作完关卡我们还需要一个游戏主循环方法:
先在BoxGame里添加一个静态变量
//用于接收用户输入字符
private static Scanner inputManager=new Scanner(System.in);
然后再编写我们的主循环
public static void mainloop()
{
//打印地图
mapEditor.printMap();
//提示信息
System.out.println("输入W向上移动,S向下移动,A向左移动,D向右移动");
//获取用户输入
String info=inputManager.next();
//传入移动方法,判断是否胜利
if (mapEditor.movement(info))
{
//胜利则提示胜利
System.out.println("恭喜你!赢得了游戏!");
}
//否则游戏继续
else
{
mainloop();
}
}
mainloop方法和loadlevel_1方法我都放在boxgame里,你们随自己喜欢就好~
最后再main方法中调用
public static void main(String[] args)
{
loadLevel_1();
mainloop();
}
这时候打包出来,或者在编译器中运行,就可以看见我们的游戏啦。
至此我们整个项目已经完成,因为我只做了一个关卡所以整个游戏只有一个关卡,小伙伴们可以动动手指,添加自己想要的关卡,修改代码达成有下一关的效果~因为我们已经写好了所有用来拓展的方法,所以做起来应该是很简单的。
收