这篇博客承接上一篇,介绍一下MVC的框架建设完毕后如何使用。
在使用了MVC模式后,其他的部件其实只不过是一个普通的类而不是要继承某个frame或panel的看起来十分高大上的类了。同时,这样的类也就不可能去添加监听器。这次我们拿Start界面做例子(毕竟是一个游戏的门面)。那么我们面临三个问题:
1.如何把这些类中的内容画出来。既然没有swing中的一些东西,那么按照常理自然也就不能“画”出这个类想要表达的东西。
2.这个类要修改内存中的各种数值,那么它修改的依据除了线程,另一个重要的依据自然就是我们的操作(键盘或鼠标)。那么,没有添加监听器的再普通不过的类如何知道某个键是否被按下呢?
3.MVC模式下基本都是一些图,那么当我们像点击按钮或进行选择时,系统如何知道我们选了哪个?
上篇博客对这两个问题其实已经有了一定解释,这次我们做一些相对详细一点的解释。
首先是问题一。在上篇博客中,我们有一个统筹这些“看不见”的类的Game类,而Game类中有一个依靠panel线程不断实现的paint方法。
public void paint(Graphics g){
currentObject.draw(g);
}
这里的画笔,自然是从panel中传进来的。按照这种尿性,我们可以知道,一张Image对应着一个Graphics画笔,也就是说,只要我们使用了同样的画笔,无论我们把画笔传到哪个类里边(即便是和带有Image八竿子打不着的某个普通类),只要你拿到了那张Image的画笔,那么,你拿着画笔在千里之外进行各种画图,panel里的Image都会发生各种反应,画上各种图或文字。
例如,我们现在有一个StartObjectextends GameObject,那么根据Game类,Game调用paint从而调用StartObject中的draw方法。
/**
* 具体实现绘画的方法,包括背景和阴阳鱼的旋转
* */
public void draw(Graphics g){
//画背景
g.drawImage(bac, 0, 0, null);
//画并且处理动画效果的阴阳鱼形状的指针,动态效果依靠连续的图实现
g.drawImage(pointers[pointer++], x, y+step*75, null);
if(pointer==25)
pointer = 0;
if(Control.isPressed){
g.drawImage(help, 0, 0, null);
}
}
其中,bac是背景图片,pointers是一组连续的图片,这样就实现了在一个和swing没有特别大关系的类中画图的效果。
其实现在我们可以发现,今后我们的画图方法是由paint方法调用某个指定的方法。也就是说,我们现在的画图方法名字可以由我们自己随便起了,自由万岁!
接下来是问题二。其实这个问题在上篇博客已经介绍过,我们使用了一个Control类记录目前键盘和鼠标的制定键是否被按下。那么,如果我们的StartObject要实现像加入了监听器一样去监听键盘事件时,只要查一下Control类中的某个boolean是否为true就好了。
/**
* 当键盘按下上下时,操作类中的int类型step变化。
* 当键盘按下Z时,执行die方法
* */
@Override
protected void keyResponse() {
//检查Control类的UP是否为true,间接检查键盘的“上”是否被按下
if(Control.UP){
step--;
if(step<0)
step = 3;
super.change();
}
else if(Control.DOWN){
step++;
if(step > 3)
step = 0;
super.change();
}
else if(Control.Z)
die();
}
最后是问题三。根据开始界面的尿性我们知道,一定有那么几个按钮,开始,继续,或者结束。
而开始界面只是一张图,并没有什么按钮。但是,从问题二的代码我们可以看出,解决这个问题其实很简单,只需要定义一个int变量,当按下上下键时step变化,就可以在内存中确确实实的记录现在我们选择了哪个选项。作为int型变量step的应用,我们可以根据上文的keyResponse()方法看一下当Z键(即确定键)按下时,根据目前我们的选择,die方法到底干了些什么。
/**
* 根据当前选择的选项不同
* 进行相应的操作。
* */
@Override
public void die() {
if(super.checkStartGap(200))
switch(step){
case 0:
//新的游戏
new LoadingObject(null);
break;
case 1:
//继续游戏界面
Game.getInstance().setSaving(true);
break;
case 2:
//游戏结束
System.exit(0);
case 3:
//进入反馈界面
new ProblemObject();
}
}
新的GameObject子类会替换掉现在的StartObject,从而让本来在重画StartObject的Game去重画其他的GameObject子类。对于上述代码中LoadingObject和ProblemObject,读者不必深究,只需要知道这是某种GameObject就够了,我们现在要研究的,是如何在一个放眼望去除了图片连鸡毛都没有的普通类里实现对键盘的响应和对内存各种东西的操作。
以下是整个StartObject的代码。
package model.start;
import java.awt.Graphics;
import java.awt.Image;
import java.io.IOException;
import javax.imageio.ImageIO;
import model.Game;
import model.GameObject;
import view.Control;
/**
* 开始界面
* 即包含游戏开始、继续、结束选项的界面
* */
public class StartObject extends GameObject{
private static Image bac,help,pointers[];
private int x=400,y=260,pointer=0;
/**
* 记录当前指针到了哪个选项的前面,绘画并处理确认键按下后的选择
* */
private int step = 0;
static{
pointers = new Image[25];
try {
bac = ImageIO.read(StartObject.class.getClassLoader().getResourceAsStream("source/start/title.png"));
help = ImageIO.read(StartObject.class.getClassLoader().getResourceAsStream("source/start/help.png"));
for(int i=0;i<pointers.length;i++){
pointers[i] = ImageIO.read(StartObject.class.
getClassLoader().getResourceAsStream("source/start/pointer/pointer"+i+".png"));
}
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 启动时自动播放音乐
* */
public StartObject(){
super.musicStart("source/start/title.wav");
super.clearStatic();
}
/**
* 具体实现绘画的方法,包括背景和阴阳鱼的旋转
* */
public void paint(Graphics g){
g.drawImage(bac, 0, 0, null);
g.drawImage(pointers[pointer++], x, y+step*75, null);
if(pointer==25)
pointer = 0;
if(Control.isPressed){
g.drawImage(help, 0, 0, null);
}
}
/**
* 当键盘按下上下时,操作类中的int类型step变化。
* 当键盘按下Z时,执行die方法
* */
@Override
protected void keyResponse() {
if(Control.UP&&super.checkTimeGap(60)){
step--;
if(step<0)
step = 3;
super.change();
}
else if(Control.DOWN&&super.checkTimeGap(60)){
step++;
if(step > 3)
step = 0;
super.change();
}
else if(Control.Z&&super.checkTimeGap(100))
die();
}
/**
* 相应键盘并绘画
* */
@Override
public void draw(Graphics g) {
keyResponse();
paint(g);
}
/**
* 根据当前选择的选项不同
* 进行相应的操作。
* */
@Override
public void die() {
if(super.checkStartGap(200))
switch(step){
case 0:
super.click();
musicStop();
new LoadingObject(null);
break;
case 1:
super.click();
Game.getInstance().setSaving(true);
break;
case 2:
super.click();
System.exit(0);
case 3:
super.click();
new ProblemObject();
}
}
}