今天来实现一个简单的贪吃蛇应用,效果如下:
GameController的作用
你已经可以根据一个Grid画出来游戏界面了,接下来就要开始处理用户的按键输入了。
还记得总体设计概览图吗们已经实现了大部分的类,也增加了一些新的类,现在应该是这个样子了:
这里要再次提高MVC模式,系统可以分为三个部分,模型(Model)、视图(View)和控制器(Controller):Model:业务功能、核心数据结构与算法,对应蓝色部分
View:负责界面显示,对应黄色部分
Controller:负责转发用户操作事件,对事件进行处理,对应红色部分
模型和视图已经基本完成了,我们在界面中画出了贪吃蛇以及它的食物,现在,让我们学习如何通过键盘操作让贪吃蛇动起来。这就需要GameController粉墨登场了。
接收键盘事件
首先我们需要一个按键监听器,当玩家敲击键盘的时候,就可以通过按键监听器知道玩家敲击了什么按键。
Java已经为我们提供好了键盘监听的接口,其接口定义如下:
public interface KeyListener extends EventListener {
public void keyPressed(KeyEvent e);
public void keyReleased(KeyEvent e);
public void keyTyped(KeyEvent e);
}
Java将键盘输入分成了三个步骤,按下(press),释放(release),键入(type),对应了KeyListener的三个方法:keyPressed: 按下某个键时会调用该方法
keyReleased: 释放某个键时会调用该方法
keyTyped: 键入某个键时会调用该方法
我们只需要让GameController实现该接口,即可完成一个按键监听器的实现:
public class GameController implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
// 这里处理按键
}
@Override
public void keyReleased(KeyEvent e) {
}
@Override
public void keyTyped(KeyEvent e) {
}
}
keyReleased()和keyTyped()方法不需要用到,我们只需要在keyPressed()方法中进行事件处理。
这样GameController就可以我们的游戏控制中心,我们可以通过它监听键盘并实现对界面的控制,
当然,我们需要通过下列语句在SnakeApp进行init()初始化时将GameController注册进window中:
SnakeApp.java
window.addKeyListener(gameController);
处理键盘事件
现在贪吃蛇还不能自动动起来,因此我们先让贪吃蛇接收到一个方向键时,就进行移动。所以keyPressed()方法的核心逻辑是:收到按键事件
根据按键情况,做一次移动
移动后重现显示界面
比如处理向上移动的代码逻辑如下:
public class GameController implements KeyListener {
@Override
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) {
grid.changeDirection(Direction.UP);
}
// repaint the canvas
}
}
处理好所有影响游戏状态的事件,你已经拥有了一只跟着你按键移动的贪吃蛇,不过你不按键它是静止不动的,你离完成一个完整的贪吃蛇游戏只差最后一步了。
如何让贪吃蛇移动起来
让贪吃蛇不断地移动,一个直观的处理方式是,在一个while循环中不断调用Grid.nextRound()方法:
while (running) {
grid.nextRound();
}
不过每次调用nextRound()之间需要有一个时间间隔,需要给游戏玩家反应时间来在下一次移动之前进行操作,比如改变方向。
这时就可以使用Thread.sleep()方法来让当前的执行暂时停止:
while (running) {
try {
Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);
} catch (InterruptedException e) {
break;
}
grid.nextRound();
}
Settings.DEFAULT_MOVE_INTERVAL的值为200,这样玩家每一次移动有0.2秒的时间来进行操作。
上面这段代码显然需要在一个新的线程中跑,否则其他线程就可能被影响,比如在接收用户输入的线程中跑这段代码的话,就无法接收用户输入了,因为都在那Sleep了。
实现游戏线程
要实现游戏线程,其实就是把第一节中的while循环代码放入到一个线程类的run()方法中。
那么哪个类适合作为线程类呢个线程里不断调用Grid.nextRun()方法,并且还要即时地更新界面,显然这也是术语GameController的职责,所以让GameController实现Runnable接口,让它成为一个线程类。
同时为了控制一次游戏是否结束,增加一个boolean类型的标志running。
public class GameController implements Runnable, KeyListener {
private final Grid grid;
private final GameView gameView;
private boolean running;
public GameController(Grid grid, GameView gameView) {
this.grid = grid;
this.gameView = gameView;
this.running = true;
}
@Override
public void run() {
while (running) {
try {
Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);
} catch (InterruptedException e) {
break;
}
// 进入游戏下一步
// 如果结束,则退出游戏
// 如果继续,则绘制新的游戏页面
}
running = false;
}
}
run()函数中的核心逻辑是典型的控制器(Controller)逻辑:修改模型(Model):调用Grid的方法使游戏进入下一步
更新视图(View):调用GameView的方法刷新页面
我们可以给GameView增加一个结束游戏时的处理方法,可以在run()方法中调用:
public void showGameOverMessage() {
JOptionPane.showMessageDialog(null, “游戏结束”, “游戏结束”, JOptionPane.INFORMATION_MESSAGE);
}
这里简单弹出一个显示游戏结束信息的对话框。
启动线程
如何启动线程呢nakeApp的init()方法中增加一条语句即可:
…
gameController = new GameController(grid, gameView);
window.addKeyListener(gameController);
// 启动线程
new Thread(gameController).start();
EDT(Event Dispatching Thread,字面上翻译成“事件分配线程”)是Java GUI应用中的一个线程,这个线程管理着所有SWING GUI事件和整个UI界面。我们对UI相关的修改都应该放到EDT中。
我们将SnakeApp也实现Runnable接口,init()方法更名为run()方法。
在main函数中添加启动代码:
public static void main(String[] args) {
SwingUtilities.invokeLater(new SnakeApp());
}
在GameController中对UI的修改也应该通过SwingUtilities.invokeLater()方法来完成。
至此,一个完整的贪吃蛇游戏就搞定了。
需要注意的是,多线程程序往往涉及到线程同步的问题,多个线程同时访问一个变量会影响业务逻辑时,就需要专门的同步处理。在贪吃蛇应用中,事件处理线程和现在实现的GameController线程都会访问Grid的direction变量,只不过访问和修改的顺序对游戏并没有什么影响,所以我们可以不做同步处理。
更进一步
如果你希望更进一步地优化贪吃蛇应用,现在的应用已经非常好扩展了,比如:积分功能:可以创建得分规则的类(模型类的一部分), 在GameController的run()方法中计算得分
变速功能:比如加速功能,减速功能,可以在GameController的keyPressed()方法中针对特定的按键设置每一次移动之间的时间间隔,将Thread.sleep(Settings.DEFAULT_MOVE_INTERVAL);替换为动态的时间间隔即可
更漂亮的游戏界面:修改GameView中的drawXXX方法,比如可以将食物渲染为一张图片,Graphics有drawImage方法
如果希望更多了解Swing编程,则可以在游戏界面上增加更多的组件,比如积分的Lable和启动结束的按钮等
所以贪吃蛇应用非常适合入门Java编程的同学。可以了解用面向对象的方式来编程解决问题,学习如何设计类,如何选择数据结构、Java Swing编程和多线程编程的基础知识。这中间也涉及很多Java编程经常碰到的问题,比如匿名类和回调方法等,你会发现你对于Java SE编程会有更深入的掌握。
想进一步深入了解贪吃蛇应用的更多细节知识,请阅读完整的教程

小编重点推荐:
更多精彩内容请访问天码营 站
文章知识点与官方知识档案匹配,可进一步学习相关知识Java技能树首页概览93751 人正在系统学习中 相关资源:snake:蛇-恶意软件存储动物园-其它代码类资源-CSDN文库
声明:本站部分文章及图片源自用户投稿,如本站任何资料有侵权请您尽早请联系jinwei@zod.com.cn进行处理,非常感谢!