Menu

Home

fanguangping

JGridWorld


  1. 什么是JGridWorld
  2. 开始我的JGridWorld之旅
    2.1. 下载并添加JGridWorld核心库
    2.2. 创建一个方格游戏
    2.3. 初始化
    2.4. 添加游戏逻辑
    2.5. 配置方格面板
    2.6. 创建并展示游戏界面
    2.7. 实现计算机解题逻辑
  3. FAQ

1. 什么是JGridWorld

JGridWorld是使用Java制作的一款以二维方格类游戏为主的游戏平台


2. 开始我的JGridWorld之旅

2.1. 下载并添加JGridWorld核心库

下载JGridWorld-core-v0.1.0.jar,将它加入到工程的类库中。如果你使用的开发环境是Eclipse,请在构建路径中配置。

2.2. 创建一个方格游戏

新建一个类,假设为MyGridGame,使它继承自GridGame:

public class MyGridGame extends GridGame{    
     @Override                                 
      public boolean action(Step step) {        
         ...                                   
     }                                         
}

其中,action方法是继承至GridGame的方法,表示每个步骤方格状态的改变。Step表示步骤类,它有以下几个属性:

  • Location location //方格的位置
    Location类有row和col属性,分别表示方格所在的行和列(从零开始)
  • Direction direction //方向
    Direction类表示方向,是枚举类型,八个枚举值分别是:
    UP("U"),
    DOWN("D"),
    LEFT("L"),
    RIGHT("R"),
    UPLEFT("UL"),
    UPRIGHT("UR"),
    DOWNLEFT("DL"),
    DOWNRIGHT("DR");
  • ClickType click //鼠标点击的类型
    ClickType是枚举类型,三个枚举值分别是:
    LEFT_CLICK("L"),
    RIGHT_CLICK("R"),
    DOUBLE_CLICK("D");
  • int item //物品
    方格内的物品用不同的int值来表示
  • int keyCode //按键代码
    此按键代码跟KeyEvent的keyCode一致

2.3. 初始化

新建一个类专门用于初始化MyGridGame,假设为MyGridGameCreator,使它实现GridGameCreator接口:

public class MyGridGameCreator implements GridGameCreator{    
      @Override                                                 
      public MyGridGame factory(String filePath) {              
          int[][] map = FileUtil.getMap(filePath);              
          MyGridGame game = new MyGridGame();                   
          int[][] status = new int[map.length][map[0].length];  
          ... //将map转化为status                               
          game.status = status;                                 
          ... //其他操作                                        
      }                                                         
}

其中,factory方法是工厂方法,表示读取filePath路径的文本文件到二维数组中,将此二维数组再转化为GridGame的实际状态,从而初始化一个GridGame,下面是文本文件的例子:

1,1,1,1,1,1,1  
1,1,0,0,0,1,1  
1,0,1,4,2,0,1  
1,0,4,3,0,0,1  
1,0,2,0,2,0,1  
1,1,0,0,0,1,1  
1,1,1,1,1,1,1

2.4. 添加游戏逻辑

现在我们来完善 public boolean action(Step step) 方法,这是方格游戏中最核心的部分。我们现在来实现一种叫“点灯”的游戏,游戏规则是在鼠标点击位置及其上下左右5个地方实现翻转(当灯熄灭时点亮,当灯点亮时熄灭)。游戏逻辑如下:

public static final int SLAKE = 0;  //熄灭的状态
public static final int LIGHT = 1;  //点亮的状态

@Override
public boolean action(Step step) {
    if(step == null || step.location == null) {
        return false;
    }
    Location location = step.location;  //获得鼠标点击位置

    if(isOutOfRange(location))  return false;  //位置不在有效范围内

    flip(location);  //对当前位置实现翻转
    for(Direction d : Direction.FOUR) {  //对上下左右4个方向
        location.move(d);  //当前位置往该方向移动一步
        flip(location);  //对该位置实现翻转
        location.move(d.reverse());  //该位置往反方向移动一步相当于撤销前面所做的移动
    }

    if(fullOfItem(LIGHT)) {  //如果所有方格都是点亮的状态
        win = true;  //游戏获胜
        finished = true;  //游戏结束
    }
    return true;
}

//翻转的实现
private void flip(Location loc) {
    if(isOutOfRange(loc))   return ;

    if(status[loc.row][loc.col] == SLAKE)   status[loc.row][loc.col] = LIGHT;
    else                                    status[loc.row][loc.col] = SLAKE;
}

Location、Direction是最基础的类,GridGame类包含了结合这两个类进行演绎的基本方法。另外,Composite类是多方格合成类,CGridGame扩展了GridGame,并包含演绎Composite的方法;DGridGame继承自GridGame,是双人方格游戏的基础类。

2.5. 配置方格面板

对于方格游戏的展示方面,是否需要手工写大量的代码来完善它呢?答案是否定的,我们采用配置的方式来实现,而且是使用JSON格式的文件进行配置。下面我们来说明一下这些配置项的含义:

{                             
    "offsetX" : 32,             //方格面板在X轴上的偏移量
    "offsetY" : 32,             //方格面板在Y轴上的偏移量
    "gridWidth" : 32,           //方格的宽度
    "gridHeight" : 32,          //方格的高度
    "drawGrid" : true,          //是否画出方格的线条
    "drawGridOnCenter" : false, //是否在中心位置画方格的线条
    "bgColor" : "gray",         //方格面板的背景色,所有涉及到color的配置或者是颜色名,如"white",或者是十六进制值,如"0xFFFFFF"
    "drawComposites" : false,   //是否画出合成方格
    "selectedColor" : "red",    //被选定方格的颜色
    "itemConfigArray" :         //对不同物品的方格配置
    [                           
        {                         
            "item" : "0",           //代表物品的int值,可以用类似"0..9"来表示从0到9的范围
            "width" : 32,           //绘制该物品时的宽度
            "height" : 32,          //绘制该物品时的高度
            "draw" : true,          //是否绘制该物品
            "useImage" : false,     //是否使用图片
            "imagePath" : "",       //图片的位置,如果前面的"item"是一个范围,且"imagePath"包含{item},则将{item}替换为实际值
            "bgColor" : "",         //绘制该物品时的背景色
            "shape" : "RECT",       //该物品的形状,三种基本形状分别是 RECT:矩形、OVAL:椭圆形、NUM:数字
            "color" : "white"       //该物品的前景色
        },                        
        {                         
            "item" : "1",           
            "width" : 32,           
            "height" : 32,          
            "draw" : true,          
            "useImage" : false,     
            "imagePath" : "",       
            "bgColor" : "",         
            "shape" : "RECT",       
            "color" : "yellow"      
        }                         
    ]                            
}

但是我们要注意的是,在渲染方格面板时,不一定是使用status二维数组
,而是使用这样的规则:当renderMap二维数组不为空时,根据renderMap的值来渲染,否则根据status的值进行渲染。这么做有一个好处,就是可以根据需要将status转化为renderMap来渲染,通过添加方法convertRenderMap来实现,这样就不会将方格面板的渲染配置和status绑定了。

2.6. 创建并展示游戏界面

新建一个类,假设为LightingFrame,使它继承自JFrame,添加GridPanel属性:

public class LightingFrame extends JFrame {
    Lighting game = new LightingCreator().factory("data/map/Lighting/1");
    PaintConfig pc = PaintConfig.readConfig("config/Lighting.json");

    GridPanel panel;

    public LightingFrame() {
        ...
        panel = new GridPanel();
        panel.game = game;
        panel.pc = pc;
        getContentPane().add(panel);
        ...

        //如果游戏是以鼠标点击进行,则添加鼠标点击事件
        panel.addMouseListener(new MouseListener() {
            @Override
            public void mouseClicked(MouseEvent e) {
                if(game.finished) {
                    return ;
                }

                Step step = GuiUtil.getStep(panel, e, null);  //根据事件取得Step对象
                game.action(step);
                panel.repaint();
                if(game.win) {
                    GuiUtil.showMessageDialog("Game.win");
                }
            }
            ...
        }

        //如果游戏是以按键进行,则添加按键事件
        this.addKeyListener(new KeyListener() {
            @Override
            public void keyPressed(KeyEvent e) {
                if(game.finished) {
                    return;
                }

                Step step = GuiUtil.getStep(panel, null, e);  //根据事件取得Step对象
                game.action(step);
                panel.repaint();
                if(game.win) {
                    GuiUtil.showMessageDialog("Game.win");
                }
            }
            ...
        });
    }
}

2.7. 实现计算机解题逻辑

系统定义了Resolver接口,专门用于计算机解题:

public interface Resolver {
    Step[] resolve(GridGame game);
}

同时,系统实现了A*算法和IDA*算法,参见org.jgridworld.core.AI.AStar包


3. FAQ

Project Admins: