之前有一个第一课,讲精灵动画的。那个时候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