之前有一個第一課,講精靈動畫的。那個時候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<WObject> mObjects = new ArrayList<WObject>();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<KeyEvent>() {@Overridepublic void handle(KeyEvent event) {onKeyPressed(event);}});getParent().getScene().setOnKeyReleased(new EventHandler<KeyEvent>() {@Overridepublic 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<ActionEvent>() {@Overridepublic 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;@Overridepublic 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); } @Overridepublic void draw(GraphicsContext gc) { gc.setFill(Color.CHOCOLATE); gc.fillRect(getX(), getY(), getWidth(), getHeight());}@Overridepublic 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);}@Overrideprotected 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;}}@Overrideprotected 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 {@Overrideprotected void loadBefore() {setWindowSize(800, 600);}@Overrideprotected void loadEnd() {TestScreen testScreen = new TestScreen(WSystem.WIDTH, WSystem.HEIGHT);getRoot().getChildren().add(testScreen);testScreen.start();testScreen.initEvents();getScene().setFill(Color.BLACK);}@Overrideprotected 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