当前位置:主页   - 电脑 - 程序设计 - JAVA
J2ME连连看基础功能源代码(含详细注释)
来源:网络   作者:   更新时间:2012-06-05
收藏此页】    【字号    】    【打印】    【关闭

//界面类代码
import javax.microedition.lcdui.Canvas;
import javax.microedition.lcdui.Graphics;
/**
* 连连看游戏界面
*/
public class LinkCanvas extends Canvas implements Runnable{
  /**游戏逻辑类*/
  GameEngine engine;  
  /**屏幕宽度*/
  int width;
  /**屏幕高度*/
  int height;
  public LinkCanvas(){
    //创建对象
    engine = new GameEngine();
    //获得屏幕的高度和宽度
    width = getWidth();
    height = getHeight();
    //启动线程
    Thread t = new Thread(this);
    t.start();
  }
  /**
  * 绘制方法
  */
  protected void paint(Graphics g) {
    //清屏
    clearScreen(g);
    //绘制地图
    engine.paintMap(g);
    //绘制选择框
    engine.paintSelectArea(g);
    //绘制连线
    engine.paintLinkLine(g);
  }
  /**
  * 清屏方法
  * @param g 画笔
  */
  private void clearScreen(Graphics g){
    g.setColor(0xffffff);
    g.fillRect(0, 0, width, height);
    g.setColor(0);
  }
  public void keyPressed(int keyCode){
    int action = getGameAction(keyCode);
    switch(action){
    case UP:
      engine.moveUP();
      break;
    case DOWN:
      engine.moveDown();
      break;
    case LEFT:
      engine.moveLeft();
      break;
    case RIGHT:
      engine.moveRight();
      break;
    case FIRE:
      engine.fire();//选择块
      break;
    }
  }
  public void run() {
    try{
      while(true){
        //延时
        Thread.sleep(100);
        //每次判断逻辑
        engine.action();
        repaint();
      }
    }catch(Exception e){
      e.printStackTrace();
    }
  }
}
//逻辑类源代码
import java.util.*;
import javax.microedition.lcdui.*;
/**
* 游戏数据和逻辑类
*/
public class GameEngine {
  /**选中块的个数*/
  private int selectTileNum = 0;
  //第一个选择块的行号和列号
  /**行号*/
  private int firstRow;
  /**列号*/
  private int firstCol;
  //第二个选择块的行号和列号
  /**行号*/
  private int secondRow;
  /**列号*/
  private int secondCol;
  //当前选择框,默认在左上角
  /**当前选择框的行号*/
  private int cRow;
  /**当前选择框的列号*/
  private int cCol;
  /**最大行数*/
  private final int MAX_ROW = 10;
  /**最大列数*/
  private final int MAX_COL = 10;
  /**地图数据,0代表空,数据1-10分别代表十种不同的结构*/
  private int[][] map = new int[MAX_ROW][MAX_COL];
  /**随机数对象*/
  private Random ran = new Random();
  //地图区域左上角的坐标
  private final int LEFTX = 20;
  private final int LEFTY = 50;
  /**每个单元格的宽度*/
  private final int TILE_WIDTH = 20;
  /**每个单元格的高度*/
  private final int TILE_HEIGHT = 20;
  /**连线类型*/
  private int linkType;
  /**无法连线*/
  private final int NO_LINK = 0;
  /**水平连线*/
  private final int H_LINK = 1;
  /**垂直联系*/
  private final int V_LINK = 2;
  /**一个拐点,先移动x*/
  private final int ONE_CORNER_FIRSTX = 3;
  /**一个拐点,先移动y*/
  private final int ONE_CORNER_FIRSTY = 4;
  /**两个拐点,待完善*/
  private final int TWO_CORNER = 5;
  /**
  * 两次拐弯的行号和列号
  * 数据格式为:
  *  第一个拐点的行号,第一个拐点的列号,第二个拐点的行号,第二个拐点的列号
  */
  int[] p = new int[4];
  public GameEngine(){
    //初始化地图数据
    initMap();
  }
  /**
  * 初始化地图数据
  */
  private void initMap(){
    for(int row = 0;row < map.length;row++){
      for(int col = 0;col < map[row].length;col++){
        map[row][col] = row + 1;
      }
    }
    //循环打乱10次
    int tempRow;
    int tempCol;
    int temp;
    for(int i = 0;i < 10;i++){
      for(int row = 0;row < map.length;row++){
        for(int col = 0;col < map[row].length;col++){
          //随机行号
          tempRow = Math.abs(ran.nextInt() % 10);
          //随机列号
          tempCol = Math.abs(ran.nextInt() % 10);
          //如果不是同一个单元格,则交换数据
          if(!((tempRow == row) && (tempCol == col))){
            temp = map[row][col];
            map[row][col] = map[tempRow][tempCol];
            map[tempRow][tempCol] = temp;
          }
        }
      }
    }
  }
  /**
  * 绘制地图数据
  * @param g 画笔
  */
  public void paintMap(Graphics g){
    for(int row = 0;row < map.length;row++){
      for(int col = 0;col < map[row].length;col++){
        //如果没有数据,则跳过
        if(map[row][col] == 0){
          continue;
        }else{//绘制方块
          //绘制方框
          g.drawRect(LEFTX + col * TILE_WIDTH, LEFTY + row * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
          //绘制数字
          g.drawString(String.valueOf(map[row][col]), LEFTX + col * TILE_WIDTH + 5,
              LEFTY + row * TILE_HEIGHT + 4,
              Graphics.TOP | Graphics.LEFT);
        }
      }
    }
  }
  /**
  * 绘制选择框
  * @param g 画笔
  */
  public void paintSelectArea(Graphics g){
    //绘制当前选择框
    g.setColor(0xff00);
    g.drawRect(LEFTX + cCol * TILE_WIDTH, LEFTY + cRow * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
    g.setColor(0);
    //绘制选中项
    switch(selectTileNum){
    case 1: //选择一个
      g.setColor(0xff0000);
      g.drawRect(LEFTX + firstCol * TILE_WIDTH, LEFTY + firstRow * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
      g.setColor(0);
      break;
    case 2: //选中两个
      g.setColor(0xff0000);
      g.drawRect(LEFTX + firstCol * TILE_WIDTH, LEFTY + firstRow * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
      g.drawRect(LEFTX + secondCol * TILE_WIDTH, LEFTY + secondRow * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
      g.setColor(0);
      break;
    }
  }
  /**
  * 绘制方块连线
  * @param g 画笔
  */
  public void paintLinkLine(Graphics g){
    //如果无连线,则直接返回
    if(linkType == NO_LINK){
      return;
    }
    //根据连线类型实现绘制
    //绘制到方块的中心点
    switch(linkType){
    case H_LINK://水平
    case V_LINK://垂直
      paintLine(g,firstRow,firstCol,secondRow,secondCol);
      break;
    case ONE_CORNER_FIRSTX://一个拐弯,先移动X
      //水平线
      paintLine(g,firstRow,firstCol,firstRow,secondCol);
      //*直线
      paintLine(g,firstRow,secondCol,secondRow,secondCol);
      break;
    case ONE_CORNER_FIRSTY://一个拐弯,先移动Y
      //水平线
   摹 paintLine(g,firstRow,firstCol,secondRow,firstCol);
      //垂直线
      paintLine(g,secondRow,firstCol,secondRow,secondCol);
      break;
    case TWO_CORNER:
      //块1到第一个拐点的连线
      paintLine(g,firstRow,firstCol,p[0],p[1]);
      //两个拐点之间的连线
      paintLine(g,p[0],p[1],p[2],p[3]);
      //第二个拐点到块2的连线
      paintLine(g,p[2],p[3],secondRow,secondCol);
      break;
    }
    //逻辑代码,清除连接类型
    linkType = NO_LINK;
  }
  /**
  * 绘制两个方块中心点的连线
  * @param g 画笔
  * @param r1 方块1的行号
  * @param c1 方块1的列号
  * @param r2 方块2的行号
  * @param c2 方块2的列号
  */
  private void paintLine(Graphics g,int r1,int c1,int r2,int c2){
    g.drawLine(LEFTX + c1 * TILE_WIDTH + TILE_WIDTH/2,
        LEFTY + r1 * TILE_HEIGHT + TILE_HEIGHT/2,
        LEFTX + c2 * TILE_WIDTH + TILE_WIDTH/2,
        LEFTY + r2 * TILE_HEIGHT + TILE_HEIGHT/2);
  }
  /**
  * 向左移动选择框
  */
  public void moveLeft(){
    if(cCol > 0){
      cCol--;
    }
  }
  /**
  * 向右移动选择框
  */
  public void moveRight(){
    if(cCol < MAX_COL -1){
      cCol++;
    }
  }
  /**
  * 向上移动选择框
  */
  public void moveUP(){
    if(cRow > 0){
      cRow--;
    }
  }
  /**
  * 向下移动选择框
  */
  public void moveDown(){
    if(cRow < MAX_ROW - 1){
      cRow++;
    }
  }
  /**
  * 确定键逻辑处理
  */
  public void fire(){
    //如果选择的块为空,则直接返回
    if(map[cRow][cCol] == 0){
      return;
    }
    //选中的块的数量增加1
    selectTileNum++;
    //判别存储位置
    switch(selectTileNum){
    case 1: //第一次选择
      firstRow = cRow;
      firstCol = cCol;
      break;
    case 2: //第二次选择
      //选择同一个块,2个选择块都失去选中
      if((firstRow == cRow) && (firstCol == cCol)){
        selectTileNum = 0;
        return;
      }
      secondRow = cRow;
      secondCol = cCol;
      break;
    }
  }
  /**
  * 判断(r1,c1)块和(r2,c2)块中间是否为空行
  * 不包含这两个块
  * @param r1 块1的行号
  * @param c1 块1的列号
  * @param r2 块2的行号
  * @param c2 块2的列号
  * @return true代表为空,false代表不为空
  */  
  private boolean isEmptyRow(int r1,int c1,int r2,int c2){
    //判断是否位于同一行
    if(r1 != r2){
      return false;
    }
    //判断两个块的相对位置
    if(c1 > c2){ //第一块位于右侧
      for(int col = c1 - 1;col > c2;col--){
        //如果有非空块
        if(map[r1][col] != 0){
          return false;
        }
      }
    }else{ //第一块位于左侧
      for(int col = c2 - 1;col > c1;col--){
        //如果有非空块
        if(map[r1][col] != 0){
          return false;
        }
      }
    }
    return true;
  }
  /**
  * 判断块(r1,c1)和块(r2,c2)之间是否是空列
  * 不包含这两个块
  * @param r1 块1的行号
  * @param c1 块1的列号
  * @param r2 块2的行号
  * @param c2 块2的列号
  * @return true代表为空,false代表不为空
  */
  private boolean isEmptyCol(int r1,int c1,int r2,int c2){
    //判断是否位于同一列
    if(c1 != c2){
      return false;
    }
    //判断两个块的相对位置
    if(r2 > r1){//第一个块在上方
      for(int row = r1 + 1;row < r2;row++){
        //如果有非空块
        if(map[row][c1] != 0){
          return false;
        }
      }
    }else{//第二个块在上方
      for(int row = r2 + 1;row < r1;row++){
        //如果有非空块
        if(map[row][c1] != 0){
          return false;
        }
      }
    }
    return true;
  }
  /**
  * 判断一个块是否为空
  * @param r 块的行号
  * @param c 块的列号
  * @return true代表为空,false代表不空
  */
  private boolean isEmptyCell(int r,int c){
    return map[r][c] == 0;
  }  
  /**
  * 是否是一次转弯实现连线
  * @return NO_LINK代表没有连线,其他值代表对应的连线类型
  */
  private int isOneCornerLink(int r1,int c1,int r2,int c2){
    //先移动行,再移动列
    if(isEmptyCell(r1,c2)){ //转折点为空
      if(isEmptyRow(r1,c1,r1,c2) & isEmptyCol(r1,c2,r2,c2)){
        return ONE_CORNER_FIRSTX;
      }
    }
    //先移动列,再移动行
    if(isEmptyCell(r2,c1)){//转折点为空
      if(isEmptyCol(r1,c1,r2,c1) & isEmptyRow(r2,c1,r2,c2)) {
        return ONE_CORNER_FIRSTY;
      }
    }
    //无连接
    return NO_LINK;
  }
  /**
  * 是否经过2次转折实现连接
  * @param r1 块1的行号
  * @param c1 块1的列号
  * @param r2 块2的行号
  * @param c2 块2的列号
  * @return true代表可以连接,false代表不能
  */
  private boolean isTwoCornerLink(int r1,int c1,int r2,int c2){
    int result;
    //正常情况,划分成4个方向
    //块1向上
    for(int row = r1 -1;row >= 0;row--){
      //如果有数据不为空,则直接结束该方向的尝试
      if(map[row][c1] != 0){
        break;
      }
      //存储第一个拐点的坐标
      p[0] = row;
      p[1] = c1;
      //每次都尝试转折,则变成一个转点的操作
      result = isOneCornerLink(row,c1,r2,c2);
      //如果可以连接
      if(result != NO_LINK){
        //存储第二个拐点的位置
        switch(result){
        case ONE_CORNER_FIRSTX:
          p[2] = row;
          p[3] = c2;
          break;
        case ONE_CORNER_FIRSTY:
          p[2] = r2;
          p[3] = c1;
          break;
        }
        return true;
      }
    }
    //块1向下
    for(int row = r1 + 1;row < MAX_ROW;row++){
      //如果有数据不为空,则直接结束该方向的尝试
      if(map[row][c1] != 0){
        break;
      }
      //存储第一个拐点的坐标
      p[0] = row;
      p[1] = c1;
      //每次都尝试转折,则变成一个转点的操作
      result = isOneCornerLink(row,c1,r2,c2);
      //如果可以连接
      if(result != NO_LINK){
        //存储第二个拐点的位置
        switch(result){
        case ONE_CORNER_FIRSTX:
          p[2] = row;
          p[3] = c2;
          break;
        case ONE_CORNER_FIRSTY:
          p[2] = r2;
          p[3] = c1;
          break;
        }
        return true;
      }
    }
    //块1向左
    for(int col = c1 -1;col >= 0;col--){
      //如果有数据不为空,则直接结束该方向的尝试
      if(map[r1][col] != 0){
        break;
      }
      //存储第一个拐点的坐标
      p[0] = r1;
      p[1] = col;
      //每次都尝试转折,则变成一个转点的操作
      result = isOneCornerLink(r1,col,r2,c2);
      //如果可以连接
      if(result != NO_LINK){
        //存储第二个拐点的位置
        switch(result){
        case ONE_CORNER_FIRSTX:
          p[2] = r1;
          p[3] = c2;
          break;
        case ONE_CORNER_FIRSTY:
          p[2] = r2;
          p[3] = col;
          break;
        }
        return true;
      }
    }
    //块1向右
    for(int col = c1 + 1;col < MAX_COL;col++){
      //如果有数据不为空,则直接结束该方向的尝试
      if(map[r1][col] != 0){
        break;
      }
      //存储第一个拐点的坐标
      p[0] = r1;
      p[1] = col;
      //每次都尝试转折,则变成一个转点的操作
      result = isOneCornerLink(r1,col,r2,c2);
      //如果可以连接
      if(result != NO_LINK){
        //存储第二个拐点的位置
        switch(result){
        case ONE_CORNER_FIRSTX:
          p[2] = r1;
          p[3] = c2;
          break;
        case ONE_CORNER_FIRSTY:
          p[2] = r2;
          p[3] = col;
          break;
        }
        return true;
      }
    }
    //四个特例,也就是超出地图区域的连接
    //实现地图区域上侧的连接,也就是到上侧是一个空列
    if((isEmptyCol(r1,c1,-1,c1)) & (isEmptyCol(r2,c2,-1,c2))){
      p[0] = -1;
      p[1] = c1;
      p[2] = -1;
      p[3] = c2;
      return true;
    }
    //左侧
    if((isEmptyRow(r1,c1,r1,-1)) & (isEmptyRow(r2,c2,r2,-1))){
      p[0] = r1;
      p[1] = -1;
      p[2] = r2;
      p[3] = -1;
      return true;
    }
    //下侧
    if((isEmptyCol(r1,c1,MAX_ROW,c1)) & (isEmptyCol(r2,c2,MAX_ROW,c2))){
      p[0] = MAX_ROW;
      p[1] = c1;
      p[2] = MAX_ROW;
      p[3] = c2;
      return true;
    }
    //右侧
    if((isEmptyRow(r1,c1,r1,MAX_COL)) & (isEmptyRow(r2,c2,r2,MAX_COL))){
      p[0] = r1;
      p[1] = MAX_COL;
      p[2] = r2;
      p[3] = MAX_COL;
      return true;
    }    
    return false;
  }
  /**
  * 逻辑判断是否有连线
  * @return NO_LINK代表无连线,其它数据代表有连线
  */
  private int logic(){
    //如果数值不同
  > if(map[firstRow][firstCol] != map[secondRow][secondCol]){
      return NO_LINK;
    }
    //判断连接方式
    if(isEmptyRow(firstRow,firstCol,secondRow,secondCol)){ //水平连线
      return H_LINK;
    }
    if(isEmptyCol(firstRow,firstCol,secondRow,secondCol)){//垂直连线
      return V_LINK;
    }
    //一个转点的连接
    int result = isOneCornerLink(firstRow,firstCol,secondRow,secondCol);
    if(result != NO_LINK){
      return result;
    }
    //两个转点的连接
    if(isTwoCornerLink(firstRow,firstCol,secondRow,secondCol)){
      return TWO_CORNER;
    }    
    //返回无连接
    return NO_LINK;
  }
  /**
  * 逻辑判别和逻/处理
  */
  public boolean action(){
    //判断是否选择两个方块
    if(selectTileNum != 2){
      return false;
    }
    boolean b = false;
    //判断是否有连线
    linkType = logic();
    //如果有连线,则消失
    if(linkType != NO_LINK){
      map[firstRow][firstCol] = 0;
      map[secondRow][secondCol] = 0;
      b = true;
    }
    //选择的块数初始化
    selectTileNum = 0;
    return b;
  }
}

其它资源
来源声明

版权与免责声明
1、本站所发布的文章仅供技术交流参考,本站不主张将其做为决策的依据,浏览者可自愿选择采信与否,本站不对因采信这些信息所产生的任何问题负责。
2、本站部分文章来源于网络,其版权为原权利人所有。由于来源之故,有的文章未能获得作者姓名,署“未知”或“佚名”。对于这些文章,有知悉作者姓名的请告知本站,以便及时署名。如果作者要求删除,我们将予以删除。除此之外本站不再承担其它责任。
3、本站部分文章来源于本站原创,本站拥有所有权利。
4、如对本站发布的信息有异议,请联系我们,经本站确认后,将在三个工作日内做出修改或删除处理。
请参阅权责声明