09 智慧桥/ 艾摩君
来源:互联网 发布:js曲线饼图插件 编辑:程序博客网 时间:2024/06/05 17:19
1. 前言
最近, 实在是闲的淡疼, 所以 没事的时候, 想起了以前的这个游戏[fc, 智慧桥 or 艾摩君], 以前小的时候玩的时候感觉非常难啊, 然后 后来大概是高中高考之后自己郁闷的时候, 又回来完了完吧, 当时候是玩到了44关, 但是没有 玩完啊, 深表遗憾
说实话 这个游戏是挺好玩的, 就像一道一道的难题, 在解决难题之前, 总是要进行很多次的想到的尝试方案的执行, 然而 当你解决问题之后, 你会感觉到一股喜悦, 这样的喜悦会让你感觉生活充满了意义, 但是 同时也有一种哀伤, 毕竟陪伴自己的这段时间的这个难题, 对于自己来说不再是难题了, 以后基本上都不会再来思考这个难题了
就好比 同学聚会的时候, 聚会之前是充满喜悦的, 然后聚会之后, 当你一个人沉思的时候, 你总会有一种莫名其妙的伤感..
又或者是一部好看的电视剧 或者电影
好了, 最近 闲的淡疼时候, 就(ˇˍˇ) 想~做做游戏, 这次就拿这个开刀吧, 还是花费了一些时间的的, 不过也有一些收获的
第一 : 是在控制左右方向方面 [这一次的控制不是按一下键, 调用一下方法, 而是在按下键盘的时候设置标志位, 释放键盘灯的时候, 设置回标志位, 在标志位为true这段时间, 使用一个线程, 隔一段时间定时判断一下, 如果为true, 则调用一下方法]
第二 : 双缓冲问题, 先将需要绘制的数据绘制到一个Image缓存中, 然后 在绘制到屏幕上
第三 : 状态模式的时候, 之前曾经看过一次状态模式的场景, 这还是第一次真正的使用呢
这个, 并没有完成, 只是完成了简单的东西, 也就是完成了第一关需要的东西, 因为最近 不想花多的时间来完成剩余的东西了, 因为最近 还需要去看看其他的东西。
难点 : 各个元素绘制, 以及方向键和跳跃键的兼容, 以及判断是否能够移动元素的判断
这个涉及到多线程的是挺多的, 但是并没有太多涉及线程同步的地方
规则 : 捡到所有的boy, 并且进入门就过关 [某些元素可以攻击, 某些元素是否可以穿过]
元素介绍 :
elmo : 我们的主角, 用户控制的角色
boy : 哭闹的孩子, elmo需要收集到所有的boy, 才能够进入门, 进入下一个关卡
brick : 砖块, elmo可以站在上面, elmo可以攻击, elmo不能穿过, 不能独立存在于空中
floor : 地板砖, elmo可以站在上面, elmo不可以攻击, elmo不能穿过, 能独立存在于空中
rack : 岩石, elmo可以站在上面, elmo不可以攻击, elmo不能穿过, 能独立存在于空中
iron : 刚块, elmo可以站在上面, elmo不可以攻击, elmo不能穿过, 能独立存在
door : elmo收集完元素之后的目的地, 到达之后就可以进入下一个关卡
元素继承关系
2. 基本的数据结构介绍
2.1 Element : 所有元素的基类
/** * file name : Element.java * created at : 7:35:42 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 元素public class Element { // 坐标, 图片 protected int x; protected int y; protected Image img; // 初始化 public Element() { } public Element(int x, int y) { setXY(x, y); } // 绘制当前元素 public void paint(Graphics g) { g.drawImage(img, x, y, Constants.GRID_WIDTH, Constants.GRID_HEIGHT, null); } // 驱动元素变化 public void move() { } // setter & getter // ... // for debug ... public String toString() { return this.getClass().getName(); }}
2.2 Elmo : 主角, 控制角色的各种操作
/** * file name : Elmo.java * created at : 7:32:53 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 艾摩君public class Elmo extends Element { // 水平方向上移动一次的偏移, 竖直方向上移动一次的偏移 private int moveHorizonOff = Constants.ELMO_MOVE_HORIZON_OFF; private int moveVerticalOff = Constants.ELMO_MOVE_VERTICAL_OFF; // panel // 移动的方向, 主面板, elmo的当前图像 [向左 或右] // 地图model, 是否向左, 是否向右, 是否向下, 是否在跳 // 是否工具, 是否成功 private int direction; private MainPanel panel; private Image[] imgs = Constants.elmoRight; private Map map; private long lastRunnable; private boolean isLeft; private boolean isRight; private boolean isDown; private boolean isJump; private boolean isAtt; private boolean isSuccess; // 初始化 public Elmo() { } public Elmo(int x, int y, Map map) { super(x, y); this.map = map; direction = Constants.RIGHT; this.img = imgs[Constants.STAND]; isDown = false; isJump = false; isLeft = false; isRight = false; isAtt = false; isSuccess = false; } // 移动相关 // 如果能够向左移动, 则向左移动, 并做移动之后需要做事情 public void left() { if(canLeft(map)) { setDirection(Constants.LEFT); setX(this.x - moveHorizonOff); moveHorizonOff = Constants.ELMO_MOVE_HORIZON_OFF; doStuffAfterMove(map); } } // 如果能够右移动, 则向右移动, 并做移动之后需要做事情 public void right() { if(canRight(map)) { setDirection(Constants.RIGHT); setX(this.x + moveHorizonOff); moveHorizonOff = Constants.ELMO_MOVE_HORIZON_OFF; doStuffAfterMove(map); } } // 下操作 public void down() { if(canDown(map)) { isDown = true; setImage(imgs[Constants.DOWN]); } } // 下的逆操作 public void undown() { isDown = false; setImage(imgs[Constants.STAND]); } // 跳跃 public void jump() { if(canJump(map)) { isJump = true; panel.putTask(new JumpRunnable(this, map)); doStuffAfterMove(map); } } // 落下 private void gravity(int upOff) { setY(this.y + upOff); } // 攻击旁边的元素 public void attack() { if(canAttack(map)) { isAtt = true; doAttack(map); panel.putTask(new AttackRunnable(this, map)); } } // setter & getter public void setImage(Image img) { this.img = img; } public void setPanel(MainPanel panel) { this.panel = panel; } public void setX(int otherX) { this.x = Tools.formatX(otherX); } public void setY(int otherY) { this.y = Tools.formatY(otherY); } public void setAtt(boolean isAtt) { this.isAtt = isAtt; } public void setDirection(int direction) { if(this.direction != direction) { this.direction = direction; if(this.direction == Constants.LEFT) { this.imgs = Constants.elmoLeft; } else { this.imgs = Constants.elmoRight; } } } public void setLeft(boolean isLeft) { this.isLeft = isLeft; } public void setRight(boolean isRight) { this.isRight = isRight; } public boolean isJump() { return isJump; } public void setJump(boolean isJump) { this.isJump = isJump; } public boolean isLeft() { return isLeft; } public boolean isRight() { return isRight; } public boolean isSuccess() { return isSuccess; } // 获取上一次按键到当前按键 的时间差 public boolean isLastRunnableOk() { return true; } // 判断是否能够向左右走 [受限于环境] // 获取左边的一个元素, 然后判断其是否crossable // 如果 当前x为其左边的元素的x相同, 设置当前移动的位移为0, 返回true // 如果 当前再向左移动一步之后x会小于左边元素的x, 更新此次移动的位移为到达左边的元素的位置 private boolean canLeft(Map map) { if(isDown) { return false; } int row = Tools.getRowByY(this.y); int col = Tools.getColByX(this.x); Element leftEle = getLeftELe(map, row, col); if(ifBeTurePreJudge(leftEle)) { return true; } else { if(this.x == Tools.getXByCol(col)) { moveHorizonOff = 0; return true; } else if(this.x - moveHorizonOff < Tools.getXByCol(col)) { moveHorizonOff = this.x - Tools.getXByCol(col); return true; } else { return true; } } } // 类似于canLeft [受限于环境] private boolean canRight(Map map) { if(isDown) { return false; } int row = Tools.getRowByY(this.y); int col = Tools.getColByX(this.x); Element rightEle = getRightELe(map, row, col); if(ifBeTurePreJudge(rightEle) ) { return true; } else { if(this.x + Constants.GRID_WIDTH == Tools.getXByCol(col+1) ) { moveHorizonOff = 0; return true; } else if(this.x + Constants.GRID_WIDTH + moveHorizonOff < Tools.getXByCol(col+1)) { moveHorizonOff = Tools.getXByCol(col+1) - this.x - Constants.GRID_WIDTH; return true; // can't be there ... } else { return true; } } } // 是否可以向下操作 [受限于环境] private boolean canDown(Map map) { if(isJump) { return false; } return true; } // 是否可以跳跃操作 [受限于环境] private boolean canJump(Map map) { if(isJump) { return false; } return true; } // 是否可以攻击操作 [受限于环境] private boolean canAttack(Map map) { if(isAtt) { return false; } return true; } // 是否可以向下掉落 [受限于环境] // 与上面的canRight的区别在于移动到支持元素上面, 就不能在向下面移动了 private boolean canGravity(Map map) { int row = Tools.getRowByY(this.y); int col = Tools.getColByX(this.x); Element downLeftEle = map.getEle(Tools.formatRow(row+1), col ); Element downRightEle = null; if(x != Tools.getXByCol(col) ) { downRightEle = map.getEle(Tools.formatRow(row+1), Tools.formatCol(col+1) ); } if((downLeftEle == null) && (downRightEle == null) ) { moveVerticalOff = Constants.ELMO_MOVE_VERTICAL_OFF; return true; } else { if(!(downLeftEle instanceof Prop) && (! (downRightEle instanceof Prop)) ) { moveVerticalOff = Constants.ELMO_MOVE_VERTICAL_OFF; return true; } else { if( ((downLeftEle == null) || (! ((Prop) downLeftEle).isStandable())) && ((downRightEle == null) || (! ((Prop) downRightEle).isStandable())) ) { moveVerticalOff = Constants.ELMO_MOVE_VERTICAL_OFF; return true; } else { if(this.y + Constants.GRID_HEIGHT == Tools.getYByRow(row+1) ) { return false; } else if(this.y + Constants.GRID_HEIGHT + moveVerticalOff < Tools.getYByRow(row+1)) { moveVerticalOff = Tools.getYByRow(row+1) - this.y - Constants.GRID_HEIGHT; return true; // can't be there ... } else { return true; } } } } } // 获取当前元素"左边"的元素, 判断规则详见下面的注释 private Element getLeftELe(Map map, int row, int col) { Element leftEle = null; // 如果是和物品在同一水平线上,则判断左方向的物品 // 否则 判断脚的左方向上的物品 if(this.y == Tools.getYByRow(row) ) { leftEle = map.getEle(row, Tools.formatCol(col-1) ); } else { leftEle = map.getEle(Tools.formatRow(row+1), Tools.formatCol(col-1) ); } return leftEle; } // 获取当前位置的右边的元素 private Element getRightELe(Map map, int row, int col) { Element rightEle = null; // 如果是和物品在同一水平线上,则判断右方向的物品 // 否则 判断脚的右下方向的物品 if(this.y == Tools.getYByRow(row) ) { rightEle = map.getEle(row, Tools.formatCol(col+1) ); } else { rightEle = map.getEle(Tools.formatRow(row+1), Tools.formatCol(col+1) ); } return rightEle; } // canRight, canLeft 的公共判断部分 private boolean ifBeTurePreJudge(Element ele) { if(ele == null) { return true; } else { if(! (ele instanceof Prop) ) { return true; } else { if(((Prop) ele).isCrossable() ) { return true; } else { return false; } } } } // 一些左右移动之后需要做的事情 // 1. 校验是否可落下, 可落下, 则落下 // 2. 校验是否可获取到boy // 3. 校验是否到达门处, 是否完成任务 private void doStuffAfterMove(Map map) { // ------------------ 1 ------------------ if(!isJump) { if(canGravity(map)) { panel.putTask(new GravityRunnable(this, map)); } } // ------------------ 2 ------------------ int row = Tools.getRowByY(this.y); int col = Tools.getColByX(this.x); Element ele = null; ele = map.getEle(row, col); if(ele instanceof Boy) { map.propBeAttacked(ele); } // ------------------ 3 ------------------ if(ele instanceof Door) { if(map.boysLeft() == 0) { isSuccess = true; } } } // 攻击操作, 根据当前的方向 获取需要攻击的对象, 然后更新map中的model private void doAttack(Map map) { int row = Tools.getRowByY(this.y); int col = Tools.getColByX(this.x); Element tarEle = null; if(this.y == Tools.getYByRow(row) ) { if(imgs == Constants.elmoLeft) { tarEle = map.getEle(row, Tools.formatCol(col-1) ); } else { tarEle = map.getEle(row, Tools.formatCol(col+1) ); } } else { if(imgs == Constants.elmoLeft) { tarEle = map.getEle(Tools.formatRow(row+1), Tools.formatCol(col-1) ); } else { tarEle = map.getEle(Tools.formatRow(row+1), Tools.formatCol(col+1) ); } } if((tarEle != null) && (tarEle instanceof Prop) ) { if(((Prop) tarEle).isAttackable() ) { map.propBeAttacked(tarEle); } } } // 重写move方法 控制移动 [否则 移动不流畅] public void move() { if(isLeft) { left(); } if(isRight) { right(); } } // 重写paint方法 更新控制艾摩君的大小 public void paint(Graphics g) { g.drawImage(img, x, y, Constants.ELMO_WIDTH, Constants.ELMO_HEIGHT, null); } // elmo移动的时候, 创建一个, 添加到线程池中执行, 切换elmo的脚步 [STAND -> GO -> STAND] static class MoveRunnable implements Runnable { private Elmo elmo; public MoveRunnable(Elmo elmo) { this.elmo = elmo; } public void run() { elmo.setImage(elmo.imgs[Constants.GO]); Tools.sleep(Constants.ELMO_MOVE_CHANGE_PIC_INTERVAL); elmo.setImage(elmo.imgs[Constants.STAND]); } } // elmo跳跃的时候, 创建一个, 添加到线程池中执行, 先分成ELMO_JUMP_UP_TIMES 阶段来向上移动 // 然后 向下掉落 static class JumpRunnable implements Runnable { private Elmo elmo; private Map map; public JumpRunnable(Elmo elmo, Map map) { this.elmo = elmo; this.map = map; } public void run() { int upOff = elmo.moveVerticalOff / Constants.ELMO_JUMP_UP_TIMES; elmo.setImage(elmo.imgs[Constants.GO]); for(int i=0; i<Constants.ELMO_JUMP_UP_TIMES; i++) { elmo.setY(elmo.y - upOff); Tools.sleep(Constants.JUMP_CHANGE_PIC_INTERVAL); } new GravityRunnable(elmo, map).run(); } } // elmo向下掉落的时候, 创建一个, 添加到线程池中执行 // 如果 当前元素下面的元素不可standable, 则向下掉落 static class GravityRunnable implements Runnable { private Elmo elmo; private Map map; public GravityRunnable(Elmo elmo, Map map) { this.elmo = elmo; this.map = map; } public void run() { int upOff = elmo.moveVerticalOff / Constants.ELMO_JUMP_UP_TIMES; elmo.setImage(elmo.imgs[Constants.GO]); while(elmo.canGravity(map)) { elmo.gravity(upOff); Tools.sleep(Constants.JUMP_CHANGE_PIC_INTERVAL); } elmo.setImage(elmo.imgs[Constants.STAND]); elmo.isJump = false; elmo.moveVerticalOff = Constants.ELMO_MOVE_VERTICAL_OFF; } } // elmo攻击的时候, 创建一个, 添加到线程池中执行 // 如果 更新显示的图片, 一段时间后更新回来 static class AttackRunnable implements Runnable { private Elmo elmo; private Map map; public AttackRunnable(Elmo elmo, Map map) { this.elmo = elmo; this.map = map; } public void run() { elmo.setImage(elmo.imgs[Constants.ATT]); Tools.sleep(Constants.ELMO_ATTACK_CHANGE_PIC_INTERVAL); elmo.setImage(elmo.imgs[Constants.STAND]); } }}
2.3 Enemy : 敌人[这里没有实现]
/** * file name : Enemy.java * created at : 7:39:06 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 敌人public class Enemy extends Element { // 初始化 public Enemy() { } public Enemy(int x, int y) { super(x, y); }}
2.3 Prop : 道具[所有的非elmo 以及非enemy的其他元素] [控制了影响elmo, enemy的性质]
/** * file name : Prop.java * created at : 2:17:30 PM Oct 27, 2015 * created by 970655147 */package com.hx.elmo;// 道具public class Prop extends Element { // 是否可站立, 是否可攻击, 是否可穿过, 是否可以独立存在于高空 protected boolean standable; protected boolean attackable; protected boolean crossable; protected boolean standaloneable; // 初始化 public Prop() { } public Prop(int x, int y) { super(x, y); standable = true; attackable = false; crossable = false; standaloneable = false; } // setter & getter // 省略若干...}
2.4 Boy : elmo需要收集的boy, 注意更新的move方法 [由MainPanel.threadPool中的一条线程定时调用]
/** * file name : Boy.java * created at : 7:35:33 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 哭闹的小孩..public class Boy extends Prop { // 当前图片的索引 Image[] imgs = Constants.boys; int idx = Tools.nextRandom(imgs.length); // 初始化 public Boy() { } public Boy(int x, int y) { super(x, y); this.img = imgs[idx]; crossable = true; standable = false; } // 累增idx, 并制造一个"循环" private void incIdx() { idx ++; if(idx == imgs.length) { idx = 0; } } // 驱动boy的"移动" public void move() { incIdx(); img = imgs[idx]; }}
2.5 Brick : 砖头 [可被攻击][这里, 我们只介绍一个Brick元素]
/** * file name : Brice.java * created at : 2:22:35 PM Oct 27, 2015 * created by 970655147 */package com.hx.elmo;// 砖public class Brick extends Prop { // 初始化 public Brick() { } public Brick(int x, int y) { super(x, y); this.attackable = true; this.img = Constants.brick; }}
2.6 MainPanel : 控制着状态的改变, 以及响应用户的操作
/** * file name : MainPanel.java * created at : 2:15:01 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 主面板public class MainPanel extends JPanel { // 全局变量 // 每一个元素宽度, 高度 private static int gridWidth = Constants.GRID_WIDTH; private static int gridHeight = Constants.GRID_HEIGHT; // 选择的位置, 是否绘制选中的文字 [用于闪烁选中的文字] // 当前的状态, 是否游戏结束, 是否开始游戏 private Point selected; private boolean isDrawSelected; private State state; private boolean isOver; private boolean isStart; // 业务数据 // 关卡id, 剩余的人数, map model, elmo private int stageId = 1; private int rest = 3; private Map map; private Elmo elmo; // 其他数据 private ThreadPoolExecutor threadPool = (ThreadPoolExecutor) Executors.newFixedThreadPool(Constants.N_THREADS); // 初始化 public MainPanel() { state = State.START_UP; isDrawSelected = true; isOver = false; isStart = false; map = new Map(Tools.generateMap(Constants.STAGE_00) ); initElmo(); } // 初始化elmo private void initElmo() { elmo = map.getElmo(); elmo.setPanel(this); } // 重写paint方法, 绘制panel // 根据不同的状态, 执行不同的业务 public void paint(Graphics g) { switch(state) { case IN_GAME : drawInGame(g); break ; case START_UP : drawInStartUp(g); break ; case STAGE_INFO : drawInStageInfo(g); break ; default : Log.err("error state : " + state.toString() ); break ; } } // 重写update方法 [双缓冲, 先将g中的数据缓冲到一个Image中, 然后在将其绘制到屏幕] public void update(Graphics g){ //覆盖update方法,截取默认的调用过程 Image buffer = createImage(this.getWidth(), this.getHeight()); //创建图形缓冲区 Graphics bufferGraphics = buffer.getGraphics(); //获取图形缓冲区的图形上下文 paint(bufferGraphics); //用paint方法中编写的绘图过程对图形缓冲区绘图 bufferGraphics.dispose(); //释放图形上下文资源 g.drawImage(buffer, 0, 0, this); //将图形缓冲区绘制到屏幕上 } // 绘制在开始状态 // 1. 必要的话初始化selected // 2. 绘制背景 // 3. 绘制文字 // 4. 必要的话清空当前选择的文字 [闪烁控制] // 5. 绘制选择图标 private void drawInStartUp(Graphics g) { if(selected == null) { selected = Constants.START_UP_START_POS; } drawStartUpBg(g); g.setColor(Constants.START_UP_COLOR); g.setFont(Constants.START_UP_FONT); Tools.assert0(Constants.START_UPS_WORDS.length, Constants.START_UPS_WORDS_POS.length); for(int i=0; i<Constants.START_UPS_WORDS.length; i++) { drawString(g, Constants.START_UPS_WORDS, Constants.START_UPS_WORDS_POS, i); } if(!isDrawSelected) { g.setColor(Constants.START_BG_COLOR); int selectedWordIdx = 0; if(selected == Constants.START_UP_START_POS) { selectedWordIdx = 2; } else { selectedWordIdx = 3; } drawString(g, Constants.START_UPS_WORDS, Constants.START_UPS_WORDS_POS, selectedWordIdx); } g.drawImage(Constants.elmoRight[Constants.STAND], selected.x, selected.y, gridWidth, gridHeight, this); } // 绘制正在游戏的 的图像 // 1. 绘制背景图片 // 2. 绘制所有的元素 private void drawInGame(Graphics g) { drawInGameBg(g); Iterator<Element> eles = map.elements(); while(eles.hasNext() ) { Element e = eles.next(); e.paint(g); } } // 绘制关卡信息 // 1. 绘制背景 // 2. 绘制关卡数, 剩余的人数 private void drawInStageInfo(Graphics g) { drawStageInfoBg(g); g.setColor(Constants.STAGE_INFO_COLOR); g.setFont(Constants.STAGE_INFO_FONT); Tools.assert0(Constants.STAGE_INFO_WORDS.length, Constants.STAGE_INFO_WORDS_POS.length); drawString(g, Constants.STAGE_INFO_WORDS[0] + " " + getStageIdString(stageId), Constants.STAGE_INFO_WORDS_POS[0]); drawString(g, Constants.STAGE_INFO_WORDS[1] + " " + getStageIdString(rest), Constants.STAGE_INFO_WORDS_POS[1]); } // 绘制开始状态, 关卡信息, 游戏开始 的背景图片 private void drawStartUpBg(Graphics g) { g.drawImage(Constants.startBg, 0, 0, Constants.FRAME_WIDTH, Constants.FRAME_HEIGHT, this); } private void drawInGameBg(Graphics g) { g.drawImage(Constants.inGameBg, 0, 0, Constants.FRAME_WIDTH, Constants.FRAME_HEIGHT, this); } private void drawStageInfoBg(Graphics g) { g.drawImage(Constants.stageInfoBg, 0, 0, Constants.FRAME_WIDTH, Constants.FRAME_HEIGHT, this); } // 键盘业务处理 // 对于开始界面的时候 // 点击选择键, 更新选择图标的位置 // 点击开始键, 绘制选择文字闪烁的情况, 并切换到关卡信息页面 public void dealKeyPressInStart(KeyEvent e) { if(e.getKeyCode() == Constants.SELECT) { if(selected == Constants.START_UP_START_POS) { selected = Constants.START_UP_CONTINUE_POS; } else { selected = Constants.START_UP_START_POS; } } else if(e.getKeyCode() == Constants.START) { if(! isStart) { isStart = true; selected = null; threadPool.execute(new Runnable() { public void run() { for(int i=0; i<Constants.START_TWINKLE_TIMES; i++) { isDrawSelected = !isDrawSelected; repaint(); Tools.sleep(Constants.START_TWINKLE_INTERVAL); } startRepaint(); showStageInfo(); startGame(); } }); } } repaint(); } // 对于开始游戏界面 // 对于各个操作键的响应 // 如果成功了, 则更新stage public void dealKeyPressInGame(KeyEvent e) { if(e.getKeyCode() == Constants.GO_LEFT) { elmo.setLeft(true); } else if(e.getKeyCode() == Constants.GO_RIGHT) { elmo.setRight(true); elmo.right(); } else if(e.getKeyCode() == Constants.GO_DOWN) { elmo.down(); } else if(e.getKeyCode() == Constants.GO_JUMP) { elmo.jump(); } else if(e.getKeyCode() == Constants.GO_ATTACK) { elmo.attack(); } if(elmo.isSuccess() ) { setStage(getNextStageMap(stageId) ); } } // 更新关卡 // 更新stageId, rest, 更新isOver 停止所有的线程, 等待所有的线程执行完成 // 更新map, 更新isOver // 启动repaint线程, 绘制关卡信息, 启动游戏 [更新可移动元素, 更新移动elmo, 绘制elmo移动时候的步伐] public void setStage(Integer[][] map) { stageId ++; rest ++; isOver = true; Tools.awaitTasksEnd(threadPool, Constants.DEFAULT_CHECK_THREADPOOL_INTERVAL); this.map = new Map(map ); initElmo(); isOver = false; threadPool.execute(new Runnable() { public void run() { startRepaint(); showStageInfo(); startGame(); } }); } // 对于开始界面的时候 // doNothing public void dealKeyReleaseStartUp(KeyEvent e) { } // 对于开始游戏界面 // 响应键盘的操作 public void dealKeyReleaseInGame(KeyEvent e) { if(e.getKeyCode() == Constants.GO_LEFT) { elmo.setLeft(false); } else if(e.getKeyCode() == Constants.GO_RIGHT) { elmo.setRight(false); } else if(e.getKeyCode() == Constants.GO_DOWN) { elmo.undown(); } else if(e.getKeyCode() == Constants.GO_ATTACK) { elmo.setAtt(false); } } // 启动定时重绘线程 public void startRepaint() { threadPool.execute(new Runnable() { public void run() { while(! isOver) { Tools.sleep(Constants.REFRESH_INTERVAL); repaint(); } } }); } // 显示关卡信息 [依赖于定时刷新线程] private void showStageInfo() { state(State.STAGE_INFO); Tools.sleep(Constants.STAGE_INFO_INTERVAL); } // 开始游戏 [更新可移动元素, 更新移动elmo, 绘制elmo移动时候的步伐][依赖于定时刷新线程] private void startGame() { state(State.IN_GAME); putTask(new Runnable() { public void run() { while(! isOver) { Iterator<Element> it = map.movableElements(); while(it.hasNext() ) { it.next().move(); } Tools.sleep(Constants.MOVABLE_ELE_REFRESH_INTERVAL); } } }); putTask(new Runnable() { public void run() { while(! isOver) { Elmo elmo = map.getElmo(); elmo.move(); Tools.sleep(Constants.ELMO_MOVABLE_REFRESH_INTERVAL); } } }); putTask(new Runnable() { public void run() { while(! isOver) { Elmo elmo = map.getElmo(); if(!elmo.isJump() && (elmo.isLeft() || elmo.isRight()) ) { putTask(new MoveRunnable(elmo)); } Tools.sleep(Constants.ELMO_MOVABLE_STEP_REFRESH_INTERVAL); } } }); } // 绘制给定的索引对应的字符串 private void drawString(Graphics g, String[] words, Point[] poses, int idx) { drawString(g, words[idx], poses[idx]); } private void drawString(Graphics g, String word, Point pos) { g.drawString(word, pos.x, pos.y); } // 获取stageId的字符串表示 // 此处的实现为 不足两位, 在前面填充0 private String getStageIdString(int stageId) { if(stageId < 10) { return "0" + String.valueOf(stageId); } return String.valueOf(stageId); } // 获取下一个关卡的地图 public Integer[][] getNextStageMap(int stageId) { return Tools.generateMap(Constants.STAGE_01); } // 想线程池抛去一个任务 public void putTask(Runnable run) { threadPool.execute(run); } // setter & getter // 省略若干...}
2.7 State : 各个状态的枚举
/** * file name : State.java * created at : 2:31:26 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 状态 [当前游戏的状态]public enum State { // 三个状态 [开始状态, 显示关卡信息, 正在游戏状态] START_UP("startUp"), STAGE_INFO("stageInfo"), IN_GAME("inGame"); // 名称 private String name; // 初始化 private State() { this(Constants.DEFAULT_STATE_NAME); } private State(String name) { this.name = name; } // for debug .. public String toString() { return name; }}
2.8 Map : 地图model, 控制着所有元素的存取 取出元素有两种方式, 一种是按照坐标, 另一种是从存储的List中存取
/** * file name : Map.java * created at : 5:02:16 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 地图modelpublic class Map { // 地图model // 艾摩君, 孩子, 敌人, 其他元素, 门 private Integer[][] map; private Element[][] eleMap; private List<Element> elmos; private List<Element> boys; private List<Element> enemys; private List<Element> props; private List<Element> doors; // 初始化 public Map() { } public Map(Integer[][] map) { Tools.assert0(map.length, Constants.ROW_MAX); Tools.assert0(map[0].length, Constants.COL_MAX); this.map = map; elmos = new ArrayList<>(); boys = new ArrayList<>(); enemys = new ArrayList<>(); props = new ArrayList<>(100); doors = new ArrayList<>(1); eleMap = new Element[map.length][map[0].length]; init(map, this); } // 初始化, 构造对象 // 根据map生成eleMap的model, 以及收集各种类型的元素 private void init(Integer[][] map, Map mapObj) { for(int row=0; row<map.length; row++) { for(int col=0; col<map[row].length; col++) { Element curEle = null; if(Tools.isEle(map[row][col], Constants.elmo) ) { curEle = new Elmo(Tools.getXByCol(col), Tools.getYByRow(row), this ); elmos.add(curEle ); curEle = null; map[row][col] = Constants.NULL; } else if(Tools.isEle(map[row][col], Constants.boy) ) { curEle = new Boy(Tools.getXByCol(col), Tools.getYByRow(row) ); boys.add(curEle); } else if(Tools.isEnemy(map[row][col]) ) { curEle = new Enemy(Tools.getXByCol(col), Tools.getYByRow(row) ); enemys.add(curEle ); map[row][col] = Constants.NULL; curEle = null; } else if(Tools.isProps(map[row][col]) ) { if(Tools.isEle(map[row][col], Constants.brick)) { curEle = new Brick(Tools.getXByCol(col), Tools.getYByRow(row) ); } else if (Tools.isEle(map[row][col], Constants.floor)) { curEle = new Floor(Tools.getXByCol(col), Tools.getYByRow(row) ); } else if (Tools.isEle(map[row][col], Constants.rack)) { curEle = new Rack(Tools.getXByCol(col), Tools.getYByRow(row) ); } else if (Tools.isEle(map[row][col], Constants.iron)) { curEle = new Iron(Tools.getXByCol(col), Tools.getYByRow(row) ); } props.add(curEle ); } else { if(map[row][col] != Constants.NULL) { curEle = new Door(Tools.getXByCol(col), Tools.getYByRow(row), Constants.idToImg.get(map[row][col])); doors.add(curEle ); } } eleMap[row][col] = curEle; } } } // 返回所有元素的一个迭代器 // 最后获取elmo, 不然他就变成背景啦 [主要用于主面板重新绘制] public Iterator<Element> elements() { return new Iterator<Element>() { Iterator<Element> boysIt = boys.iterator(); Iterator<Element> enemysIt = enemys.iterator(); Iterator<Element> propsIt = props.iterator(); Iterator<Element> doorIt = doors.iterator(); Iterator<Element> elmosIt = elmos.iterator(); Iterator<Element> curIt = boysIt; public boolean hasNext() { if(curIt.hasNext() ) { return true; } if(curIt == boysIt) { curIt = enemysIt; return hasNext(); } else if(curIt == enemysIt) { curIt = propsIt; return hasNext(); } else if(curIt == propsIt) { curIt = doorIt; return hasNext(); } else if(curIt == doorIt) { curIt = elmosIt; return hasNext(); } else { return false; } } public Element next() { if(hasNext() ) { return curIt.next(); } return null; } public void remove() { throw new RuntimeException("unsupproted exception ..."); } }; } // 返回可移动元素的一个迭代器 [主要用于主面板, 定期调度这些元素的move方法, 比如 : 孩子需要哭, 敌人需要移动, 攻击等等] public Iterator<Element> movableElements() { return new Iterator<Element>() { Iterator<Element> boysIt = boys.iterator(); Iterator<Element> enemysIt = enemys.iterator(); Iterator<Element> curIt = boysIt; public boolean hasNext() { if(curIt.hasNext() ) { return true; } if(curIt == boysIt) { curIt = enemysIt; return hasNext(); } else { return false; } } public Element next() { if(hasNext() ) { return curIt.next(); } return null; } public void remove() { throw new RuntimeException("unsupproted exception ..."); } }; } // 某个元素被攻击了 // 如果是道具, 则操作该位置的元素 [如果其之上的元素不能"腾空"的话, 将其移动下来] // 如果是敌人, 多态 // 啊 这里不是可以在基类中封装一个beAttacked方法嘛... public void propBeAttacked(Element prop) { int row = Tools.getRowByY(prop.y); int col = Tools.getColByX(prop.x); int id = map[row][col]; if(Tools.isProps(id) ) { map[row][col] = Constants.NULL; eleMap[row][col] = null; if(! props.remove(prop)) { boys.remove(prop); } downIfNotStandaloneable(row, col); } else if(Tools.isEnemy(id) ) { } } // 获取剩余的小孩的个数 public int boysLeft() { return boys.size(); } // 如果当前位置上面的元素不能独立存在的话, 则将上面的元素, 移到当前位置, 并递归更新 private void downIfNotStandaloneable(int row, int col) { Element above = eleMap[row-1][col]; if((above != null) && (above instanceof Prop) ) { if(! ((Prop) above).isStandaloneable() ) { map[row][col] = map[row-1][col]; eleMap[row][col] = eleMap[row-1][col]; eleMap[row][col].setXY(Tools.getXByCol(col), Tools.getYByRow(row) ); map[row-1][col] = Constants.NULL; eleMap[row-1][col] = null; downIfNotStandaloneable(row-1, col); } } } // setter & getter // 省略若干...}
2.9 Tools : 常用的工具函数
/** * file name : Tools.java * created at : 2:37:02 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 工具 常量,方法public class Tools { // 工具常量 public static String CRLF = "\r\n"; public static Random ran = new Random(); // 工具方法 // 确保val 和expected相同, 否则 抛出异常 public static void assert0(int val, int expect) { assert0(val, expect, true); } public static void assert0(int val, int expect, boolean isEquals) { if(isEquals ^ (val == expect)) { String symbol = null; if(isEquals) { symbol = "!="; } else { symbol = "=="; } throw new RuntimeException("assert0Exception : " + val + " " + symbol + " " + expect); } } public static <T> void assert0(T val, T expect) { assert0(val, expect, true); } public static <T> void assert0(T val, T expect, boolean isEquals) { if(isEquals ^ (val == expect)) { throw new RuntimeException("assert0Exception : " + val + " != " + expect); } } // 使当前线程休眠sleepMillus public static void sleep(int sleepMillus) { try { Thread.sleep(sleepMillus); } catch (InterruptedException e) { e.printStackTrace(); } } // 根据地图文件生成map public static Integer[][] generateMap(String path) { List<String> lines = null; try { lines = getContentWithList(new File(path)); } catch (IOException e) { // TODO Auto-generated catch block } Tools.assert0(lines, null, false); Iterator<String> it = lines.iterator(); Integer[][] res = new Integer[lines.size()][]; int idx = 0; while(it.hasNext()) { String[] splits = it.next().split("\\s+"); res[idx] = new Integer[splits.length]; for(int i=0; i<splits.length; i++) { res[idx][i] = Integer.valueOf(splits[i]); } idx ++; } return res; } // 获取文件的所有的行, 存储在一个结果的List public static List<String> getContentWithList(File file) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(new FileInputStream(file)) ); List<String> lines = new LinkedList<>(); String line = null; while((line = br.readLine()) != null) { lines.add(line); } br.close(); return lines; } // 通过行列的数据, 获取坐标 public static int getXByCol(int col) { return col * Constants.GRID_WIDTH; } public static int getYByRow(int row) { return row * Constants.GRID_HEIGHT; } public static int getColByX(int x) { return x / Constants.GRID_WIDTH; } public static int getRowByY(int y) { return y / Constants.GRID_HEIGHT; } // 判断是否是给定的元素 public static boolean isEle(Integer id, Image img) { return Constants.idToImg.get(id) == img; } public static boolean isEnemy(Integer id) { return false; } public static boolean isProps(Integer id) { return isEle(id, Constants.brick) || isEle(id, Constants.floor) || isEle(id, Constants.rack) || isEle(id, Constants.iron) || isEle(id, Constants.boy); } // 获取一个随机数 public static int nextRandom(int range) { return ran.nextInt(range); } // 使坐标合法化 [求模] public static int formatX(int x) { if(x < 0 || x >= Constants.FRAME_WIDTH) { x = (x + Constants.FRAME_WIDTH) % Constants.FRAME_WIDTH; } return x; } public static int formatY(int y) { if(y < 0 || y >= Constants.FRAME_HEIGHT) { y = (y + Constants.FRAME_HEIGHT) % Constants.FRAME_HEIGHT; } return y; } public static int formatRow(int row) { if(row < 0 || row >= Constants.ROW_MAX) { row = (row + Constants.ROW_MAX) % Constants.ROW_MAX; } return row; } public static int formatCol(int col) { if(col < 0 || col >= Constants.COL_MAX) { col = (col + Constants.COL_MAX) % Constants.COL_MAX; } return col; } // 等待 线程池中任务结束 [并不关闭线程池] public static void awaitTasksEnd(ThreadPoolExecutor threadPool, int checkInterval) { while (! threadPool.isShutdown() ) { int acitveTaskCount = threadPool.getActiveCount(); if(acitveTaskCount == 0) { break ; } else { Tools.sleep(checkInterval); } } }}
2.10 Constants : 存放常量
/** * file name : Constants.java * created at : 2:27:39 PM Oct 26, 2015 * created by 970655147 */package com.hx.elmo;// 常量配置public class Constants { // 游戏属性配置 [平常元素的宽高, 以及elmo的宽高] public static int GRID_WIDTH = 40; public static int GRID_HEIGHT = 36; public static int ELMO_WIDTH = 36; public static int ELMO_HEIGHT = 32; // 主窗口的宽高, 最多的行数, 列数 public static int FRAME_WIDTH = 640; public static int FRAME_HEIGHT = 500; public static int ROW_MAX = 13; public static int COL_MAX = 16; // 控制配置 // 开始, 选择, 上下左右, 跳跃, 攻击 public static int START = KeyEvent.VK_1; public static int SELECT = KeyEvent.VK_3; public static int GO_UP = KeyEvent.VK_W; public static int GO_RIGHT = KeyEvent.VK_D; public static int GO_DOWN = KeyEvent.VK_S; public static int GO_LEFT = KeyEvent.VK_A; public static int GO_JUMP = KeyEvent.VK_K; public static int GO_ATTACK = KeyEvent.VK_J; // 方向 public static int RIGHT = 0; public static int LEFT = 1; // 各个需要的图片的路径, 以及Image对象, 以及数字到图片的映射[构建地图] // 三张背景图 + 元素图 + 门 + elmo的左右两个方向的各自四张图, boy哭的四张图 public static String START_BG_BG = System.getProperty("user.dir") + "/src/com/hx/elmo/startUpBG.png"; public static String STAGE_INFO_BG = System.getProperty("user.dir") + "/src/com/hx/elmo/stageInfo.png"; public static String IN_GAME_BG = System.getProperty("user.dir") + "/src/com/hx/elmo/inGameBG.png"; public static String BRICK = System.getProperty("user.dir") + "/src/com/hx/elmo/brick.png"; public static String FLOOR = System.getProperty("user.dir") + "/src/com/hx/elmo/floor.png"; public static String RACK = System.getProperty("user.dir") + "/src/com/hx/elmo/rack.png"; public static String IRON = System.getProperty("user.dir") + "/src/com/hx/elmo/iron.png"; public static String LEFT_UP_DOOR = System.getProperty("user.dir") + "/src/com/hx/elmo/leftUpDoor.png"; public static String LEFT_DOWN_DOOR = System.getProperty("user.dir") + "/src/com/hx/elmo/leftDownDoor.png"; public static String RIGHT_UP_DOOR = System.getProperty("user.dir") + "/src/com/hx/elmo/rightUpDoor.png"; public static String RIGHT_DOWN_DOOR = System.getProperty("user.dir") + "/src/com/hx/elmo/rightDownDoor.png"; public static int STAND = 0; public static int GO = 1; public static int ATT = 2; public static int SLEEP = 3; public static int DOWN = 4; public static String[] ELMO_LEFT = new String[] { System.getProperty("user.dir") + "/src/com/hx/elmo/elmoStandLeft.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoGoLeft.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoAttLeft.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoSleepLeft.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoDownLeft.png" }; public static String[] ELMO_RIGHT = new String[] { System.getProperty("user.dir") + "/src/com/hx/elmo/elmoStandRight.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoGoRight.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoAttRight.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoSleepRight.png", System.getProperty("user.dir") + "/src/com/hx/elmo/elmoDownRight.png" }; public static String[] BOYS = new String[] { System.getProperty("user.dir") + "/src/com/hx/elmo/boy_1.png", System.getProperty("user.dir") + "/src/com/hx/elmo/boy_2.png", System.getProperty("user.dir") + "/src/com/hx/elmo/boy_3.png", System.getProperty("user.dir") + "/src/com/hx/elmo/boy_4.png" }; // 默认配置的关卡0, 1, 默认的StageName Stage中使用, 背景颜色 public static String STAGE_00 = System.getProperty("user.dir") + "/src/com/hx/elmo/map00.txt"; public static String STAGE_01 = System.getProperty("user.dir") + "/src/com/hx/elmo/map01.txt"; public static String DEFAULT_STATE_NAME = "unknowName"; public static Color START_BG_COLOR = new Color(144, 208, 255); // 各个id, 图片 public static int NULL = 0; public static int RACK_ID = 1; public static int FLOOR_ID = 2; public static int ELMO_ID = 3; public static int BRICK_ID = 4; public static int BOY_ID = 5; public static int IRON_ID = 6; public static int LEFT_UP_DOOR_ID = 7; public static int LEFT_DOWN_DOOR_ID = 8; public static int RIGHT_UP_DOOR_ID = 9; public static int RIGHT_DOWN_DOOR_ID = 10; public static Image startBg; public static Image stageInfoBg; public static Image inGameBg; public static Image brick; public static Image floor; public static Image rack; public static Image iron; public static Image leftUpDoor; public static Image leftDownDoor; public static Image rightUpDoor; public static Image rightDownDoor; public static Image elmo; public static Image[] elmoLeft; public static Image[] elmoRight; public static Image boy; public static Image[] boys; public static java.util.Map<Integer, Image> idToImg; // 初始化各个图片, 建立id 到图片的映射 static { try { startBg = ImageIO.read(new File(START_BG_BG) ); stageInfoBg = ImageIO.read(new File(STAGE_INFO_BG) ); inGameBg = ImageIO.read(new File(IN_GAME_BG) ); brick = ImageIO.read(new File(BRICK) ); floor = ImageIO.read(new File(FLOOR) ); rack = ImageIO.read(new File(RACK) ); iron = ImageIO.read(new File(IRON) ); leftUpDoor = ImageIO.read(new File(LEFT_UP_DOOR) ); leftDownDoor = ImageIO.read(new File(LEFT_DOWN_DOOR) ); rightUpDoor = ImageIO.read(new File(RIGHT_UP_DOOR) ); rightDownDoor = ImageIO.read(new File(RIGHT_DOWN_DOOR) ); boys = new Image[BOYS.length]; elmoLeft = new Image[ELMO_LEFT.length]; elmoRight = new Image[ELMO_RIGHT.length]; for(int i=0; i<ELMO_LEFT.length; i++) { elmoLeft[i] = ImageIO.read(new File(ELMO_LEFT[i]) ); elmoRight[i] = ImageIO.read(new File(ELMO_RIGHT[i]) ); } for(int i=0; i<BOYS.length; i++) { boys[i] = ImageIO.read(new File(BOYS[i]) ); } elmo = elmoLeft[0]; boy = boys[0]; } catch (IOException e) { e.printStackTrace(); } idToImg = new HashMap<>(); idToImg.put(RACK_ID, rack); idToImg.put(FLOOR_ID, floor); idToImg.put(ELMO_ID, elmo); idToImg.put(BRICK_ID, brick); idToImg.put(BOY_ID, boys[0]); idToImg.put(IRON_ID, iron); idToImg.put(LEFT_UP_DOOR_ID, leftUpDoor); idToImg.put(LEFT_DOWN_DOOR_ID, leftDownDoor); idToImg.put(RIGHT_UP_DOOR_ID, rightUpDoor); idToImg.put(RIGHT_DOWN_DOOR_ID, rightDownDoor); } // 开始按钮的时候 选中文字的闪烁的次数, 每一次闪烁间隔的时间 // MainPanel中线程的个数, 显示stage信息的时候 停滞的时间 public static int START_TWINKLE_TIMES = 5; public static int START_TWINKLE_INTERVAL = 200; public static int N_THREADS = 10; public static int STAGE_INFO_INTERVAL = 1000; // 游戏画面重绘的周期, 可移动元素的重绘的时间周期[boy, enemy], elmo移动的时间周期 [这里和其他的可移动的元素是分开的] // elmo绘制其脚步变化的周期 [在移动才更新图片], elmo移动的时候绘制跑的图片的时间长度 // elmo跳跃的时候 相邻的两个高度绘制的时间间隔, elmo水平移动的长度, elmo竖直方向上移动的长度 // elmo 跳跃一次分为多少个阶段绘制, elmo攻击的时候显示攻击图片的时间 // 过关的时候, 等待所有的线程停止的检查时间 public static int REFRESH_INTERVAL = 30; public static int MOVABLE_ELE_REFRESH_INTERVAL = 100; public static int ELMO_MOVABLE_REFRESH_INTERVAL = 40; public static int ELMO_MOVABLE_STEP_REFRESH_INTERVAL = 400; public static int ELMO_MOVE_CHANGE_PIC_INTERVAL = 200; public static int JUMP_CHANGE_PIC_INTERVAL = 50; public static final int ELMO_MOVE_HORIZON_OFF = 5; public static final int ELMO_MOVE_VERTICAL_OFF = GRID_HEIGHT + 10; public static final int ELMO_JUMP_UP_TIMES = 5; public static int ELMO_ATTACK_CHANGE_PIC_INTERVAL = 1000; public static int DEFAULT_CHECK_THREADPOOL_INTERVAL = 200; // 开始界面需要绘制的数字, 以及其位置 public static Color START_UP_COLOR = Color.WHITE; public static Font START_UP_FONT = new Font("宋体", Font.BOLD, 24); public static String[] START_UPS_WORDS = new String[] { "@ KONAMI 1990", "PLAY SELECT", "START", "CONTINUE" }; public static Point[] START_UPS_WORDS_POS = new Point[] { new Point(200, 230), new Point(220, 280), new Point(160, 310), new Point(350, 310) }; public static Point START_UP_START_POS = new Point(110, 280); public static Point START_UP_CONTINUE_POS = new Point(300, 280); // 显示关卡页面需要绘制的数字, 以及其位置 public static Color STAGE_INFO_COLOR = new Color(224, 80, 0); public static Font STAGE_INFO_FONT = new Font("宋体", Font.BOLD, 24); public static String[] STAGE_INFO_WORDS = new String[] { "STAGE ", "REST " }; public static Point[] STAGE_INFO_WORDS_POS = new Point[] { new Point(260, 230), new Point(260, 280) };}
3 下载链接 [包含图片, 源码] :
http://download.csdn.net/detail/u011039332/9221627
游戏截图 :
fc 原版截图
过关成功
过关失败
- 09 智慧桥/ 艾摩君
- 智慧
- 语言智慧, 智慧语言
- 智慧地球, 智慧城市
- 智慧人生
- 老鹰智慧
- 老子智慧
- 豺狼智慧
- 智慧英语
- 智慧英语
- 智慧英语
- 讲师智慧
- 投机智慧
- 生活智慧
- 《智慧女人》
- 小智慧
- 冷静,智慧
- 智慧人生
- 编程思想之多线程与多进程(1)-以操作系统的角度述说线程与进程
- 栈:接口定义与Person类
- ytu Problem A: A--A Repeating Characters师创杯
- 习题 1-7 是否是闰年?
- 单例模式的创建及使用
- 09 智慧桥/ 艾摩君
- VC中导出类生成动态链接库的方式
- java的线程同步机制synchronized关键字的理解
- Ubuntu14.04下安装vim显示没有可用的软件包vim-gtk
- 杭电1130How Many Trees?
- Matlab实现Hough直线检测
- 面试心得-----转载
- ROS(12):双足机器人开发调研
- PLSQL Developer