第一次写东西,好紧张,求大神轻喷。其实自己是想要完成Java课设,想着记录一下自己遇到了什么问题,又是怎么解决的,于是试着写一下博客。
然后是我所理解的课设及MVC模式。
很多人(也包括之前的我)在学了GUI后开始做一些小东西,会想着“怎么在Panel的某个位置加个Label呀”“怎么从开始界面跳到游戏界面呀,是不是要换Panel呀”之类的。但是在看了某教师的雷电教学视频后,发现简单的游戏项目其实只需要一个JFrame,一个JPanel,以及一个监听器和一个repaint线程就够了。
于是在我的理解中,MVC模式就是这样一个东西——游戏分为图形层和内存空间层。图形方面,JPanel不断repaint,而repaint的内容则是内存层中负责统筹整个游戏的Game类。比如这样一个代码:
package view;
//Lib里面是游戏比较常用的数据,放在一块方便修改维护
public final class Lib {
public final static int
gameWIDTH = 800,gameHEIGHT = 600,
intmouseL = 1,mouseR = 3,
fontSize = 28,
TextPoX = 30,TextPoY = 440,
TextWidth = gameWIDTH-2*TextPoX,TextHeight = 120,
TextArc = 20,TextNewLine = 22,
boundsPerImg = 44,
clipX = 9,clipY = 7;
}
package view;
//监听器监听到键盘或鼠标事件时,改变这个类中变量的赋值
public class Control {
public static boolean
UP =false,
DOWN=false,
LEFT=false,
RIGHT=false,
Z=false,
X=false,
C=false,
isPressed = false;
}
package view;
//JFrame类
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
public class GameMain extends JFrame {
private static final long serialVersionUID = 1L;
public static void main(String[] args) {
new ImageSets();
new GameMain();
}
public GameMain(){
//不这样做的话即使弹出对话框选择否也会关掉页面
this.setDefaultCloseOperation(JFrame.DO_NOTHING_ON_CLOSE);
this.addWindowListener(new closeWindowsListener());
this.setSize(Lib.gameWIDTH+6,Lib.gameHEIGHT+25);
//居中显示
this.setLocationRelativeTo(null);
this.setTitle("东方梦黎明");
//不可更改大小
this.setResizable(false);
this.getContentPane().add(new GamePanel());
this.setVisible(true);
}
private class closeWindowsListener extends WindowAdapter{
@Override
public void windowClosing(WindowEvent e) {
if(JOptionPane.showConfirmDialog(null,"确定退出游戏吗?")==JOptionPane.OK_OPTION)
System.exit(0);
}
}
}
package view;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import java.io.IOException;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
//内存层面统筹全局的类
import model.Game;
public class GamePanel extends JPanel implements KeyListener,MouseListener{
BufferedImage doubleImg = new BufferedImage(Lib.gameWIDTH,Lib.gameHEIGHT,1);
Graphics offScreen = doubleImg.getGraphics();
Image cursor;
public GamePanel(){
this.addKeyListener(this);
this.addMouseListener(this);
//改变鼠标指针的外观,不是重点
try {
//使用Loader加载资源而不是new File或某某reader
cursor = ImageIO.read(this.getClass().getClassLoader().getResourceAsStream("source/cursor.png"));
setCursor(Toolkit.getDefaultToolkit().createCustomCursor(cursor, new Point(10, 10), "norm"));
} catch (IOException e1) {
e1.printStackTrace();
}
this.setBackground(Color.BLUE);
Game.getInstance().init();
new Thread(){
public void run(){
while(true){
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
repaint();
}
}
}.start();
//设置焦点
this.setFocusable(true);
}
//JPanel的repaint,本质上是Game实例状态的不断更新和repaint
public void paint(Graphics g){
super.paint(g);
/**双缓冲操作,可以理解为把在doubleImage上画图的画笔传进Game,于是Game各种东西都画在了doubleImage上,然后将这张已经完成的图花在屏幕上*/
Game.getInstance().paint(offScreen);
g.drawImage(doubleImg, 0, 0, null);
g.dispose();
}
@Override
public void keyTyped(KeyEvent e) {
}
@Override
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_UP){
Control.UP=true;
}
if(key == KeyEvent.VK_DOWN){
Control.DOWN=true;
}
if(key == KeyEvent.VK_LEFT){
Control.LEFT=true;
}
if(key == KeyEvent.VK_RIGHT){
Control.RIGHT=true;
}
if(key == KeyEvent.VK_Z){
Control.Z=true;
}
if(key == KeyEvent.VK_X){
Control.X=true;
}
if(key == KeyEvent.VK_C){
Control.C=true;
}
}
@Override
public void keyReleased(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_UP){
Control.UP=false;
}
if(key == KeyEvent.VK_DOWN){
Control.DOWN=false;
}
if(key == KeyEvent.VK_LEFT){
Control.LEFT=false;
}
if(key == KeyEvent.VK_RIGHT){
Control.RIGHT=false;
}
if(key == KeyEvent.VK_Z){
Control.Z=false;
}
if(key == KeyEvent.VK_X){
Control.X=false;
}
if(key == KeyEvent.VK_C){
Control.C=false;
}
}
@Override
public void mousePressed(MouseEvent e) {
Control.isPressed = true;
}
@Override
public void mouseReleased(MouseEvent e) {
Control.isPressed = false;
}
@Override
public void mouseClicked(MouseEvent e) {}
@Override
public void mouseEntered(MouseEvent e) {}
@Override
public void mouseExited(MouseEvent e) {}
}
package view;
//记录游戏的状态,方便Game类对游戏进行管理
public enum Status {
START,LOAD,READY,PAUSE,AVG,RPG,STG,OVER,END,SAVE;
}
package model;
import java.awt.Graphics;
import model.over.GameOverObject;
import model.start.LoadObject;
import model.start.LoadingObject;
import model.start.StartObject;
import view.Status;
//喜闻乐见的Game类,负责内存层面整体的运转
public class Game {
private static Game game = new Game();
private Status status = Status.START;
private GameObject currentObject;
//获得静态Game对象,保证其他类如果要改变Game状态的话操作的是同一个对象
public static Game getInstance(){
return game;
}
public Game(){}
public Game(Status s){
status=s;
}
public void init(){
new StartObject();
}
public GameObject getCurrent(){
return currentObject;
}
public void setCurrent(GameObject object){
currentObject = object;
}
//其他类可以通过这个方法改变游戏状态
public void changeModel(Status stu){
status = stu;
if(status==Status.START){
new StartObject();
}else if(status==Status.RPG){
new LoadingObject(Status.RPG);
}else if(status==Status.OVER)
new GameOverObject();
}
public void paint(Graphics g){
currentObject.draw(g);
}
}
至此,图形界面的工作已经基本完成,剩下的只要改变Game重画的方法,即内存状态的改变和值的修改即可,不用再考虑Label或者Button的各种效果了。
这样子,一个我所理解的大体MVC模式就构建完成了。
当然,如果整个游戏存在某一块基本不变的部分,那么那里加一个button或者其他什么或许也是极好的,这里谈论的是游戏界面一直在变动的Rpg游戏,所以整体来讲并不适用于上述情况。