JavaFX游戏开发--第二课 基础游戏框架

之前有一个第一课,讲精灵动画的。那个时候JavaFX 2.2还没出来,所以那一课中根本就没有用到Canvas。


但是既然在JavaFX 2.2出来后,新增加了Canvas,那么就大不一样了。


这一章教程中,我们将在JavaFX中创建一个简单的游戏框架。


首先大家看一看结构,主要的几个类。


这一课,我们只讲基础游戏框架,所以我们只看WApplication,WSystem,WObject 和 WScreen这四个类。


由这四个类组成一个简单的游戏框架。


首先是所有的游戏Object的基类WObject:

package org.wing.jfx.game.core.component;

import javafx.beans.property.BooleanProperty;
import javafx.beans.property.DoubleProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.beans.property.SimpleDoubleProperty;
import javafx.scene.canvas.GraphicsContext;

/**
 * @author wing
 * @date 2012/8/25
 */
public abstract class WObject{
    protected DoubleProperty widthProperty = new SimpleDoubleProperty(0);
    protected DoubleProperty heightProperty = new SimpleDoubleProperty(0);
    protected DoubleProperty xProperty = new SimpleDoubleProperty(0);
    protected DoubleProperty yProperty = new SimpleDoubleProperty(0);    
    protected BooleanProperty visibleProperty = new SimpleBooleanProperty(true);
    protected BooleanProperty updateProperty = new SimpleBooleanProperty(true);
    
    public WObject(double x, double y, double width, double height){
    	this.xProperty = new SimpleDoubleProperty(x);
    	this.yProperty = new SimpleDoubleProperty(y);
    	this.widthProperty = new SimpleDoubleProperty(width);
    	this.heightProperty = new SimpleDoubleProperty(height);
    }
    
    public WObject(){
    	this.xProperty = new SimpleDoubleProperty(0);
    	this.yProperty = new SimpleDoubleProperty(0);
    	this.widthProperty = new SimpleDoubleProperty(0);
    	this.heightProperty = new SimpleDoubleProperty(0);    	
    }
    
    
    public abstract void draw(GraphicsContext gc);
    
    public abstract void update();   
    
    public  DoubleProperty widthProperty() {
        return  widthProperty;
    }
    public double getWidth(){
        return widthProperty.get();
    }
    public void setWidth(double width){
        this.widthProperty.set(width);
    }
    
     public  DoubleProperty heightProperty() {
        return  heightProperty;
    }
    public double getHeight(){
        return heightProperty.get();
    }
    public void setHeight(double height){
        this.heightProperty.set(height);
    }   
    
    
    public  DoubleProperty xProperty() {
        return  xProperty;
    }
    public double getX(){
        return xProperty.get();
    }
    public void setX(double x){
        this.xProperty.set(x);
    }      
          
   public  DoubleProperty yProperty() {
        return  yProperty;
    }
    public double getY(){
        return yProperty.get();
    }
    public void setY(double y){
        this.yProperty.set(y);
    }      
    
    public BooleanProperty visibleProperty(){
    	return visibleProperty;
    }  
    public void setVisible(boolean isVisible){
    	this.visibleProperty.set(isVisible);
    }
    public boolean isVisible(){
    	return visibleProperty.get();
    }
 
    public BooleanProperty updateProperty(){
    	return updateProperty;
    }  
    public void setUpdate(boolean isUpdate){
    	this.updateProperty.set(isUpdate);
    }
    public boolean isUpdate(){
    	return updateProperty.get();
    }
    
    
    public void moveX(double x){
    	this.xProperty.set(getX() + x);
    }
    
    public void moveY(double y){
    	this.yProperty.set(getY() + y);
    }
    
    public boolean isCollisionWith(WObject baseObject){
    	if(getX() + getWidth() > baseObject.getX() && getX() < baseObject.getX() + baseObject.getWidth() && getY() + getHeight() > baseObject.getY() && getY() < baseObject.getY() + baseObject.getHeight()){
    		return true;
    	}
    	return false;
    }   
}

这个类很简单,主要是x,y,width,height,visible,update几个属性,使用了JavaFX中可进行绑定的Property。

另外是几个简单的移动碰撞的方法。


可能以后会增加一些别的东西,但是目前这个类就是这样的。



接下来是WScreen类,这个类是继承于Canvas的,意味着这是一个容器,可以绘制的容器。由于我们的WObject并

未继承任何JavaFX的系统类,那么所有的WObject都需要在WScreen中渲染。而且还需要WScreen提供一个刷新界

面的方法。


package org.wing.jfx.game.core.screen;

import java.util.ArrayList;
import java.util.List;

import org.wing.jfx.game.core.component.WObject;

import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.input.KeyEvent;
import javafx.util.Duration;
/**
 * Screen
 * @author wing
 * 2012/8/25
 */
public abstract class WScreen extends Canvas {
	protected enum GameState {GAME_MENU, GAME_START, GAME_CONTINUE, GAME_HELP, GAME_SET,GAME_EXIT,GAME_PAUSE};
	private List mObjects = new ArrayList();
	private Timeline timeline;
	private KeyFrame keyFrame;
	private int duration = 10;
    protected GameState mGameState = GameState.GAME_MENU;
	public WScreen(double width, double height) {
		super(width, height);
		initTimeLine();	
	}
	
	public void initEvents(){
		getParent().getScene().setOnKeyPressed(new EventHandler() {

			@Override
			public void handle(KeyEvent event) {
				onKeyPressed(event);
			}
		});
		getParent().getScene().setOnKeyReleased(new EventHandler() {

			@Override
			public void handle(KeyEvent event) {
				onKeyReleased(event);
			}
		});
	}
	
	protected abstract void onKeyPressed(KeyEvent event);
	
	protected abstract void onKeyReleased(KeyEvent event);
	

	/**
	 * add the object
	 * @param baseObject the object to be add
	 */
	public void addObject(WObject baseObject){
		this.mObjects.add(baseObject);
	}
	
	/**
	 * remove the object
	 * @param baseObject the object to be remove
	 */
	public void removeObject(WObject baseObject){
		this.mObjects.remove(baseObject);
	}
	
	/**
	 * remove the object with the index
	 * @param index the index of the object
	 */
	public void removeObjectAtIndex(int index){
		this.mObjects.remove(index);
	}
	
	/**
	 * draw the objects
	 * @param gc
	 */
	public void draw(GraphicsContext gc){
		gc.clearRect(0, 0, getWidth(), getHeight());
		for (int i = 0; i < mObjects.size(); i++) {
			WObject wObject = mObjects.get(i);
			if (wObject.isVisible()) {
				wObject.draw(gc);
			}
		}
	}
	
	/**
	 * update all the objects
	 */
	public void update(){
		for (int i = 0; i < mObjects.size(); i++) {
			WObject wObject = mObjects.get(i);
			if (wObject.isUpdate()) {
				mObjects.get(i).update();
			}
		}
	}
	
	/**
	 * init the timeline
	 */
	private void initTimeLine() {
		timeline = new Timeline();
		timeline.setCycleCount(Timeline.INDEFINITE);

		keyFrame = new KeyFrame(Duration.millis(duration), new EventHandler() {
			@Override
			public void handle(ActionEvent arg0) {
				draw(getGraphicsContext2D());
				update();
			}
		});
		timeline.getKeyFrames().add(keyFrame);
	}
	
	/**
	 * start the update timeline
	 */
	public void start(){
		timeline.play();
	}
	
	/**
	 * pause the update timeline
	 */
	public void pause(){
		timeline.pause();
	}
	
	/**
	 * stop the update timeline
	 */
	public void stop(){
		timeline.stop();
	}
	
}

由上面的代码可见,我们包含一个游戏状态的枚举(目前没有用到),一个WObject的列表。然后通过创建一个无

限循环的Timeline来进行所有的WObject的绘制和刷新(可能有更好的方法,但目前这个游戏框架中暂时这样做)。

同时可以进行timeline的控制。

同样的,这里也暂时只提供了KeyPressed和KeyReleased两个事件方法,不过鼠标的事件添加也是很简单的。


下面看看WSystem,这个类暂时很简单,只是记录窗口的大小的类。因为只是简单的框架,其他的东西可能在后续

添加进去。

一般来说使用公共静态变量之类的并不符合Java规范,不过要纠结于此的话,可以将它更改为单例模式,并赋予一

些set get方法。

public class WSystem {
	public static int WIDTH = 800, HEIGHT = 600;
	
	public static void init(int width, int height){
		WIDTH = width;
		HEIGHT = height;
	}
}

再下面看看WApplication,这个是继承与JavaFX中的Application类,作为应用程序的启动类,JavaFX的这个游戏框

架中,任何示例的主类都需要继承WApplication。

package org.wing.jfx.game.core;

import javafx.application.Application;
import javafx.scene.Group;
import javafx.scene.Scene;
import javafx.stage.Stage;

public abstract class WApplication extends Application {
    private Group mGroup;
    private Scene mScene;
	@Override
	public void start(Stage primaryStage) throws Exception {
		loadBefore();
		mGroup= new Group();
		mScene = new Scene(mGroup, WSystem.WIDTH, WSystem.HEIGHT);		
		loadEnd();
		showStage(primaryStage);
	}
	
	protected abstract void loadBefore();
	
	protected abstract void loadEnd();
	
	protected void showStage(Stage stage){
		stage.setScene(mScene);
		stage.show();
	}
	
	protected Scene getScene(){
		return mScene;
	}
	
	protected Group getRoot(){
		return mGroup;
	}
	
	public void setWindowSize(int width, int height){
		WSystem.init(width, height);
	}

}

这里的方法也是很容易理解的。


那么,我们就来看看如何使用这个简单的框架来构造JavaFX程序吧。


首先我们创建一个Rect继承WObject,这样会出现两个需要实现的方法:draw 和 update。


我们这里就简单的画一个矩形,然后在update方法进行移动。


package org.wing.jfx.game.test;

import javafx.scene.canvas.GraphicsContext;
import javafx.scene.paint.Color;

import org.wing.jfx.game.core.component.WObject;

public class Rect extends WObject {
    
    public Rect(double x, double y, double width, double height){
    	super(x, y, width, height);
    }
    
	@Override
	public void draw(GraphicsContext gc) {
         gc.setFill(Color.CHOCOLATE);
         gc.fillRect(getX(), getY(), getWidth(), getHeight());
	}

	@Override
	public void update() {
         moveX(1);
	}

}

这就是这个框架中自己实现的一个简单的Object。


然后创建一个自己的Screen。命名为TestScreen,然后继承WScreen。


当然,只出现了两个Key事件的方法,我们可以在TestScreen中创建上面的Rect,然后添加到TestScreen中,再通

过Key事件来进行控制。


package org.wing.jfx.game.test;

import javafx.scene.input.KeyEvent;

import org.wing.jfx.game.core.WSystem;
import org.wing.jfx.game.core.screen.WScreen;

public class TestScreen extends WScreen {
    private Rect player;
	public TestScreen(double width, double height) {
		super(width, height);
		
		player = new Rect(50, 50, 100, 100);
		addObject(player);
	}

	@Override
	protected void onKeyPressed(KeyEvent event) {
       switch (event.getCode()) {
	case UP:
		player.moveY(-1);
		break;
	case DOWN:
		player.moveY(1);
		break;
	case ENTER:
		addObject(new Rect(Math.random() * WSystem.WIDTH, Math.random() * WSystem.HEIGHT, 100, 100));
		break;
	default:
		break;
	}
	}

	@Override
	protected void onKeyReleased(KeyEvent event) {

	}

}

如上面的代码所示,我们在构造函数中创建了一个坐标50,50 宽 100 高 100 的Rect。然后在事件处理中,上下控

制Rect的移动,通过Enter键,在屏幕上随机增加一个Rect。


由于我们在Rect的Update方法中增加了Move的操作,所以所有的Rect将一直往右移动。


下面创建我们的主类MainClass继承WApplication:


package org.wing.jfx.game.test;

import javafx.scene.paint.Color;
import javafx.stage.Stage;

import org.wing.jfx.game.core.WApplication;
import org.wing.jfx.game.core.WSystem;

public class MainClass extends WApplication {

	@Override
	protected void loadBefore() {
		setWindowSize(800, 600);
	}

	@Override
	protected void loadEnd() {		
		TestScreen testScreen = new TestScreen(WSystem.WIDTH, WSystem.HEIGHT);
		getRoot().getChildren().add(testScreen);
		testScreen.start();
		testScreen.initEvents();
		
		getScene().setFill(Color.BLACK);
	}
	
	@Override
	protected void showStage(Stage stage){
		super.showStage(stage);
		stage.setTitle("JavaFX游戏开发 第二课 基础游戏框架");
	}
	public static void main(String[] args) {
		launch(args);
	}
}

这个就是我们的主类了,我们在loadBefore中设定了窗口的大小,在loadEnd中创建了TestScreen,启动了

TestScreen的循环Timeline,载入了TestScreen的事件,并设定场景背景为黑色,然后重写了父类的showStage方法

修改了程序的标题。


后面的就是用这个简单的游戏框架创建的简单的程序了,只要整个框架搭建好,后面每次进行游戏开发的时候,就

可以节省很多的代码量了。



运行结果倒是不怎样,不过我们在这一章中,主要是构建了游戏框架。


在下一章中,我们会增加动画精灵以及地图的绘制。


转载请注明出处:http://blog.csdn.net/ml3947




你可能感兴趣的:(java)