拧巴人 发表于 2015-6-3 19:33

JavaSwing实现的推箱子项目源码

推(搬)箱子,又名Sokoban,仓库番等,是一款堪称古玩级的电脑游戏。

提起它,笔者相信没什么人会感觉到陌生,更没什么生物会连听都没听说过。它的发展历史之久远,甚至超越了俄罗斯方块(1988年电脑游戏化)。

这款游戏最初起源于日本,是个很难争辩的事实(我知道有人反对,但笔者确实找不到什么有力的反对证据)。他由日本人(哎……)今川宏行在1981年创立游戏规则,并于1982年经日本软件公司Thinking Rabbit正式发布。比较遗憾的是,早期的推箱子并没有PC版,笔者在网络上搜索到的老版游戏也大多为90年以前的Mac OS下程式。

但说起真正令推箱子风靡于PC机的,却该感谢我们的台湾同胞李果兆先生。是他在1994年开发的仓库世家,才真正令推箱子游戏在世界各地大受推崇;仔细说来,推箱子这款小游戏之所以能有今时今日的声望与地位,固然有今川宏行的开创之功,但若说到贡献最大,承前启后的,则非中国台湾的李果兆先生莫属。

推箱子游戏的规则非常简单,就是用尽量少的推动或移动把所有箱子都推到目标点上。箱子只能推动而不能拉动;一次只能推动一个箱子。然而,尽管它的规则是很简单的,但对于不同难度的关卡,所需要的脑力却是截然不同的,有些关卡可能会花费您几个小时、几天甚至几个月的时间,也正是这种简单性和复杂性的结合,最终令推箱子类游戏风靡全球!

本回笔者在Blog中提供的,就是一款Java版推箱子游戏的简单实现。

笔者设定[上、下、左、右]为方向控制 ,键为后退到上一步操作,为重新开始当前关卡,点击键盘上对应关卡的数字键可以直接选关,需要注意的是笔者以HP限制了角色的移动次数,HP归0则挑战失败。

目前版本仅提供有5关,有需要者可参考同类游戏自行扩充,游戏源码在jar内。
游戏截图:




核心代码:
Sokoban.javapackage org.loon.game.simple.sokoban.control;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import org.loon.game.simple.sokoban.GraphicsUtils;
import org.loon.game.simple.sokoban.LSystem;
import org.loon.game.simple.sokoban.SimpleControl;
/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn
* @version 0.1
*/
public class Sokoban extends SimpleControl {
    /**
   *
   */
    private static final long serialVersionUID = 1L;
    private Image backImage = GraphicsUtils.loadImage("image/back1.jpg");
    private Image screenImage;
    // 墙壁图
    private Image floorImage[];
    // 角色精灵
    private RpgSprite sprite;
    // 窗体显示图
    private Graphics screen;
    private String message = "按下 开始进行游戏";
    private String m_stageName[] = { "关卡1", "关卡2", "关卡3", "关卡4", "关卡5", "关卡6",
            "关卡7" };
    private int CS = 32;
    private int maxX, maxY, comps;
    private int stageNo;
    private boolean complete[];
    private boolean boxMove[][];
    private int hp[];
    private int stageStates[][][];
    private int ons[][];
    private int role[];
    private int rolex[];
    private int roley[];
    private int mapx[];
    private int mapy[];
    private int sleep = 0;
    private boolean succeed = false;
    private Stage stage;
    public Sokoban(Stage stage) {
      this.stage = stage;
      this.floorImage = new Image;
      this.sprite = new RpgSprite("image/role1.gif");
      this.maxX = 16;
      this.maxY = 13;
      this.comps = 0;
      this.complete = new boolean;
      this.boxMove = new boolean;
      this.ons = new int;
      this.hp = new int;
      this.stageStates = new int;
      this.role = new int;
      this.rolex = new int;
      this.roley = new int;
      this.mapx = new int;
      this.mapy = new int;
      for (int j = 0; j < 4; j++) {
            floorImage = GraphicsUtils.loadImage("image/back" + (j + 1)
                  + ".gif");
      }
      this.screenImage = GraphicsUtils.createImage(CS * maxX + CS, CS
                * (maxY + 1) + 32, true);
      this.screen = screenImage.getGraphics();
      for (stageNo = 0; stageNo < stage.getMaxStageNo(); stageNo++) {
            this.setupStage();
      }
      this.stageNo = -1;
      // 开场界面
      this.openDraw();
    }
    /**
   * 刷新关卡
   *
   */
    public synchronized void reset(int stageNo) {
      this.stageNo = stageNo;
      this.stage.getMode() = 1;
      this.message = null;
      // 设定关卡
      this.setupStage();
      // 重绘背景
      this.background();
      // 重绘游戏屏幕
      this.drawScreen();
    }
    /**
   * 绘制屏幕背景
   *
   */
    public synchronized void background() {
      screen.drawImage(backImage, 0, 0, null);
      screen.setColor(Color.black);
      screen.fillRect(0, LSystem.HEIGHT - 40, LSystem.WIDTH, 40);
    }
    /**
   * 绘制屏幕图像
   *
   */
    public synchronized void drawScreen() {
      if (stageNo >= stage.getMaxStageNo()) {
            stageNo = 0;
      }
      for (int i = 0; i < mapy; i++) {
            for (int j = 0; j < mapx;) {
                switch (stageStates) {
                case 2:
                case 3:
                case 4:
                case 5:
                  screen.drawImage(floorImage, moveX(j), moveY(i), null);
                default:
                  j++;
                  break;
                }
            }
      }
      for (int n = 0; n < mapy; n++) {
            for (int l = 0; l < mapx; l++)
                switch (stageStates) {
                case 1:
                  screen.drawImage(floorImage, moveX(l), moveY(n), null);
                  screen.setColor(new Color(32, 0, 0));
                  screen.drawLine(moveX(l), moveY(n + 1), moveX(l) + CS - 1,
                            moveY(n + 1));
                  if (l == 0 || l > 0 && stageStates != 1) {
                        screen.setColor(new Color(160, 128, 96));
                        screen.drawLine(moveX(l), moveY(n) + 1, moveX(l),
                              moveY(n) + CS - 2);
                  }
                  if (l == mapx - 1 || l < mapx - 1
                            && stageStates != 1) {
                        screen.setColor(new Color(72, 64, 64));
                        screen.drawLine(moveX(l) + CS - 1, moveY(n), moveX(l)
                              + CS - 1, moveY(n) + CS - 2);
                  }
                  break;
                case 2:
                case 3:
                case 4:
                  switch (stageStates) {
                  default:
                        break;
                  case 2:
                  case 3:
                        screen.drawImage(floorImage, moveX(l), moveY(n),
                              null);
                        break;
                  case 4:
                        break;
                  }
                  if (stageStates != 3)
                        screen.drawImage(floorImage, moveX(l), moveY(n),
                              null);
                  break;
                default:
                  break;
                }
      }
      // System.out.println(role);
      screen.drawImage(sprite.getOnlyMove(role),
                moveX(rolex), moveY(roley), null);
      if (stageStates]] == 4) {
            screen.drawImage(floorImage, moveX(rolex),
                  moveY(roley), null);
      }
      setupDisplay();
    }
    /**
   * 定位到实际的X座标
   *
   * @param i
   * @return
   */
    public int moveX(int i) {
      return (CS * ((maxX - mapx) + 1)) / 2 + CS * i;
    }
    /**
   * 定位到实际的Y座标
   *
   * @param i
   * @return
   */
    public int moveY(int i) {
      return (CS * ((maxY - mapy) + 1)) / 2 + CS * i;
    }
    /**
   * 执行操作
   *
   */
    public synchronized void execute() {
      boolean flag = true;
      for (int i = 0; i < mapy; i++) {
            for (int j = 0; j < mapx; j++) {
                if (stageStates == 4) {
                  flag = false;
                }
            }
      }
      if (flag) {
            stage.getMode() = 3;
            complete = true;
      } else if (hp == 0) {
            stage.getMode() = 2;
      }
      comps = 0;
      for (int n = 0; n < stage.getMaxStageNo(); n++) {
            if (complete) {
                comps++;
            }
      }
    }
    /**
   * 键盘事件处理
   */
    public void keyPressed(KeyEvent e) {
      if (e.getKeyCode() == 10 && stageNo == -1) {
            // 开始关卡1
            reset(0);
      } else if (stageNo < 0) {
            return;
      }
      // 选关(默认为最大支持7关,更多请自行设定)
      if (e.getKeyCode() == 97 || e.getKeyCode() == 49) {
            stageNo = 0;
      } else if (e.getKeyCode() == 98 || e.getKeyCode() == 50) {
            stageNo = 1;
      } else if (e.getKeyCode() == 99 || e.getKeyCode() == 51) {
            stageNo = 2;
      } else if (e.getKeyCode() == 100 || e.getKeyCode() == 52) {
            stageNo = 3;
      } else if (e.getKeyCode() == 101 || e.getKeyCode() == 53) {
            stageNo = 4;
      } else if (e.getKeyCode() == 102 || e.getKeyCode() == 54) {
            stageNo = 5;
      } else if (e.getKeyCode() == 103 || e.getKeyCode() == 55) {
            stageNo = 6;
            // ESC,重新开始
      } else if (e.getKeyCode() == 27) {
            reset(stageNo);
      } else if (stage.getMode() == 1) {
            // 退步
            if (e.getKeyCode() == 83) {
                nextStep(-1);
            }
            // 移动角色
            else if (e.getKeyCode() == 40) {
                nextStep(0);
            } else if (e.getKeyCode() == 37) {
                nextStep(1);
            } else if (e.getKeyCode() == 39) {
                nextStep(2);
            } else if (e.getKeyCode() == 38) {
                nextStep(3);
            } else {
                return;
            }
      } else {
            return;
      }
      // 绘制背景
      background();
      // 绘制游戏画面
      drawScreen();
    }
    /**
   * 切换动作
   *
   * @param i
   */
    public synchronized void nextStep(final int i) {
      boxMove - 1] = false;
      switch (i) {
      case 0:
            if (stageStates + 1]] < 2) {
                return;
            }
            if (stageStates + 1]] < 4) {
                if (stageStates + 2]] < 4) {
                  return;
                }
                stageStates + 1]] += 2;
                stageStates + 2]] -= 2;
                boxMove - 1] = true;
            }
            roley++;
            break;
      case 1:
            if (stageStates] - 1] < 2) {
                return;
            }
            if (stageStates] - 1] < 4) {
                if (stageStates] - 2] < 4) {
                  return;
                }
                stageStates] - 1] += 2;
                stageStates] - 2] -= 2;
                boxMove - 1] = true;
            }
            rolex--;
            break;
      case 2:
            if (stageStates] + 1] < 2) {
                return;
            }
            if (stageStates] + 1] < 4) {
                if (stageStates] + 2] < 4) {
                  return;
                }
                stageStates] + 1] += 2;
                stageStates] + 2] -= 2;
                boxMove - 1] = true;
            }
            rolex++;
            break;
      case 3:
            if (stageStates - 1]] < 2) {
                return;
            }
            if (stageStates - 1]] < 4) {
                if (stageStates - 2]] < 4) {
                  return;
                }
                stageStates - 1]] += 2;
                stageStates - 2]] -= 2;
                boxMove - 1] = true;
            }
            roley--;
            break;
      default:
            if (hp == stage.getMaxHp()) {
                return;
            }
            switch (ons]) {
            case 0:
                if (boxMove]) {
                  stageStates + 1]] += 2;
                  stageStates]] -= 2;
                }
                roley--;
                break;
            case 1:
                if (boxMove]) {
                  stageStates] - 1] += 2;
                  stageStates]] -= 2;
                }
                rolex++;
                break;
            case 2:
                if (boxMove]) {
                  stageStates] + 1] += 2;
                  stageStates]] -= 2;
                }
                rolex--;
                break;
            default:
                if (boxMove]) {
                  stageStates - 1]] += 2;
                  stageStates]] -= 2;
                }
                roley++;
                break;
            }
            break;
      }
      if (i != -1) {
            hp--;
            ons] = i;
      }
      role = ons];
      if (i == -1) {
            hp++;
      }
      execute();
    }
    /**
   * 开场画面
   *
   */
    public synchronized void openDraw() {
      background();
      for (int i = 0; i < 5; i++) {
            for (int j = 0; j < maxX + 1; j++) {
                screen.drawImage(floorImage, CS * j,
                        (CS * i + CS / 2 * maxY) - CS * 2, null);
            }
      }
      GraphicsUtils.setAntialias(screen, true);
      String mes = "怪蜀黍传说 - 勇者推魔王";
      screen.setFont(new Font("华文新魏", 1, 35));
      GraphicsUtils.drawStyleString(screen, mes, (CS * maxX - screen
                .getFontMetrics().stringWidth(mes)) / 2 + 13,
                (CS * maxY) / 2 + 25, Color.black, Color.orange);
      mes = "Java版搬箱子游戏开发入门示例 - 0.1.0";
      screen.setFont(new Font("华文新魏", 0, 30));
      GraphicsUtils.drawStyleString(screen, mes, (CS * maxX - screen
                .getFontMetrics().stringWidth(mes)) / 2 + 13,
                (CS * (maxY + 2)) / 2 - 55, Color.black, Color.white);
      GraphicsUtils.setAntialias(screen, false);
      setupDisplay();
    }
    /**
   * 绘图接口实现
   */
    public synchronized void draw(Graphics g) {
      g.drawImage(screenImage, 0, 0, null);
      if (succeed) {
            if (sleep == 100) {
                // 进入下一关
                reset(++stageNo);
                succeed = false;
                sleep = 0;
            }
            sleep++;
      }
    }
    /**
   * 设置基本数据
   *
   */
    public void setupStage() {
      for (int i = 0; i < maxY; i++) {
            for (int j = 0; j < maxX; j++) {
                stageStates = 0;
            }
      }
      mapx = stage.getConf();
      mapy = stage.getConf();
      rolex = stage.getConf();
      roley = stage.getConf();
      for (int n = 0; n < mapy; n++) {
            for (int l = 0; l < mapx; l++) {
                stageStates = stage.getMapData();
            }
      }
      hp = stage.getMaxHp();
      role = 0;
      for (int i1 = 0; i1 < stage.getMaxHp(); i1++) {
            ons = 0;
      }
      for (int j1 = 0; j1 < stage.getMaxHp(); j1++) {
            boxMove = false;
      }
    }
    /**
   * 设置显示
   *
   */
    public void setupDisplay() {
      if (message == null) {
            screen.setFont(new Font(LSystem.FONT, 0, 12));
            int size = 0;
            for (int j = 0; j < stage.getMaxStageNo(); j++) {
                if (complete) {
                  screen.setColor(Color.gray);
                } else {
                  screen.setColor(Color.white);
                }
                if (j == stageNo) {
                  if (complete) {
                        screen.setColor(Color.magenta);
                  } else {
                        screen.setColor(Color.cyan);
                  }
                }
                screen.drawString(m_stageName, size = (10 + (screen
                        .getFontMetrics().stringWidth("关卡0") + 20)
                        * j), LSystem.HEIGHT - 50);
            }
            screen.setColor(Color.white);
            if (stageNo == -1) {
                // 初始画面,无数据
            } else {
                // 显示状态
                if (hp == 0) {
                  screen.setColor(Color.red);
                } else if (hp <= stage.getMaxHp() / 4) {
                  screen.setColor(Color.orange);
                } else if (hp <= stage.getMaxHp() / 2) {
                  screen.setColor(Color.yellow);
                }
                screen.drawString("主角 HP " + hp + "/"
                        + stage.getMaxHp(), size + 50,
                        LSystem.HEIGHT - 50);
                switch (stage.getMode()) {
                // 生命耗尽
                case 2:
                  screen.setFont(new Font(LSystem.FONT, 1, 30));
                  screen.setColor(Color.red);
                  GraphicsUtils.setAntialias(screen, true);
                  screen.drawString("The End!",
                            (CS * maxX - screen.getFontMetrics().stringWidth(
                                    "The End!")) / 2 + 14,
                            ((CS * (maxY + 1) + screen.getFontMetrics()
                                    .getAscent()) - screen.getFontMetrics()
                                    .getDescent()) / 2);
                  GraphicsUtils.setAntialias(screen, false);
                  break;
                // 胜利
                case 3:
                  screen.setFont(new Font(LSystem.FONT, 2, 40));
                  screen.setColor(Color.darkGray);
                  GraphicsUtils.drawStyleString(screen, "Good job!", (CS
                            * maxX - screen.getFontMetrics().stringWidth(
                            "Good job!")) / 2 + 14, ((CS * (maxY + 1) + screen
                            .getFontMetrics().getAscent()) - screen
                            .getFontMetrics().getDescent()) / 2, Color.black,
                            Color.white);
                  // 胜利标记
                  succeed = true;
                  break;
                }
            }
      }
      // 存在信息时
      else {
            screen.setFont(new Font(LSystem.FONT, 0, 12));
            int n = screen.getFontMetrics().stringWidth(message) + 16;
            int l = screen.getFontMetrics().getAscent()
                  + screen.getFontMetrics().getDescent() + 8;
            int i1 = (CS * (maxX + 1) - n) / 2;
            int j1 = (CS * maxY + 1) - l / 2;
            int k1 = i1 + 8;
            int l1 = CS
                  * maxY
                  + 1
                  + (screen.getFontMetrics().getAscent() - screen
                            .getFontMetrics().getDescent()) / 2;
            screen.setColor(Color.black);
            screen.fillRect(i1, j1, n, l);
            screen.setColor(Color.white);
            screen.drawString(message, k1, l1);
      }
    }
}
启动类:


Main.javapackage org.loon.game.simple.sokoban.main;
import org.loon.game.simple.sokoban.GameCursor;
import org.loon.game.simple.sokoban.GameFrame;
import org.loon.game.simple.sokoban.control.Sokoban;
import org.loon.game.simple.sokoban.control.Stage;
import org.loon.game.simple.sokoban.control.Stage1;
/**
* Copyright 2008 - 2009
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*
* @project loonframework
* @author chenpeng
* @email <a title="" href="http://hi.baidu.com/ceponline" mce_href="http://hi.baidu.com/ceponline" target="_blank">ceponline</a>@yahoo.com.cn
* @version 0.1
*/
public class Main {
    public static void main(String[] args) {
      java.awt.EventQueue.invokeLater(new Runnable() {
            public void run() {
                GameFrame frame = new GameFrame("Java版搬箱子游戏入门示例及源码-0.1.0", 540, 480);
                frame.setCursor(GameCursor.getCursor("image/cursor.png"));
                // 获得搬箱子关卡接口
                Stage stage = new Stage1();
                // 设定游戏控制器
                frame.getGame().setControl(new Sokoban(stage));
                // 游戏全屏
                // frame.updateFullScreen();
                // 是否显示fps
                frame.setFPS(true);
                // 允许的最大刷新率
                frame.setMaxFrames(60);
                frame.mainLoop();
                frame.showFrame();
            }
      });
    }
}


凄凉的风 发表于 2015-6-4 00:11

楼主好屌啊!!!!!!!!!!!!!!!!!!!!!

java宫城大师 发表于 2016-3-17 22:12

学习一下。谢谢:victory:

wudizxt 发表于 2017-8-12 20:49

不错不错不错不错

假意心事 发表于 2019-6-10 16:33

谢谢 最近在研究Swing
页: [1]
查看完整版本: JavaSwing实现的推箱子项目源码