Got example 2 working
Player input; and a different way of setting up the renderer
This commit is contained in:
parent
bca92faa88
commit
117589b17b
@ -38,7 +38,7 @@
|
||||
<dependency>
|
||||
<groupId>nz.ac.massey.javaecs</groupId>
|
||||
<artifactId>javaecs</artifactId>
|
||||
<version>0.9.2-PRERELEASE</version>
|
||||
<version>0.9.9-RELEASE_CANDIDATE</version>
|
||||
</dependency>
|
||||
</dependencies>
|
||||
<build>
|
||||
|
BIN
demo2/sprite.png
Normal file
BIN
demo2/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
@ -1,22 +1,121 @@
|
||||
package nz.ac.massey.javaecs.examples;
|
||||
|
||||
import java.io.FileInputStream;
|
||||
import java.util.concurrent.Semaphore;
|
||||
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.image.Image;
|
||||
import javafx.stage.Stage;
|
||||
import nz.ac.massey.javaecs.Engine;
|
||||
import nz.ac.massey.javaecs.Entity;
|
||||
import nz.ac.massey.javaecs.examples.Components.Control;
|
||||
import nz.ac.massey.javaecs.examples.Components.Input;
|
||||
import nz.ac.massey.javaecs.examples.Components.Render;
|
||||
import nz.ac.massey.javaecs.examples.Components.Sprite;
|
||||
import nz.ac.massey.javaecs.examples.Components.Vec2D;
|
||||
import nz.ac.massey.javaecs.examples.Systems.ControlSystem;
|
||||
import nz.ac.massey.javaecs.examples.Systems.PlayerSystem;
|
||||
import nz.ac.massey.javaecs.examples.Systems.RenderSystem;
|
||||
|
||||
/**
|
||||
* Hello world!
|
||||
*/
|
||||
public final class App {
|
||||
private App() {
|
||||
public class App {
|
||||
static App thisApp;
|
||||
public App() {
|
||||
thisApp = this;
|
||||
}
|
||||
|
||||
static Engine gameEngine;
|
||||
public static Stage worldStage;
|
||||
public static Group worldGroup;
|
||||
static Integer syncManager = 0;
|
||||
static Semaphore rendererFinished = new Semaphore(0);
|
||||
/**
|
||||
* Says hello to the world.
|
||||
* @param args The arguments of the program.
|
||||
*/
|
||||
public static void main(String[] args) {
|
||||
Engine gameEngine = new Engine();
|
||||
public static void main(String[] args) throws Exception{
|
||||
/************************************
|
||||
** Initialise engine **
|
||||
*************************************/
|
||||
gameEngine = new Engine(16384);
|
||||
|
||||
/************************************
|
||||
** Register game components **
|
||||
*************************************/
|
||||
gameEngine.registerComponent(Control.class);
|
||||
gameEngine.registerComponent(Render.class);
|
||||
gameEngine.registerComponent(Sprite.class);
|
||||
gameEngine.registerComponent(Vec2D.class);
|
||||
gameEngine.registerComponent(Input.class);
|
||||
|
||||
|
||||
/************************************
|
||||
** Register and initialise systems **
|
||||
*************************************/
|
||||
ControlSystem controlSystem = new ControlSystem(gameEngine);
|
||||
gameEngine.registerSystem(ControlSystem.class, controlSystem);
|
||||
|
||||
RenderSystem renderSystem = new RenderSystem(gameEngine, 10.);
|
||||
gameEngine.registerSystem(RenderSystem.class, renderSystem);
|
||||
|
||||
PlayerSystem playerSystem = new PlayerSystem(gameEngine);
|
||||
gameEngine.registerSystem(PlayerSystem.class, playerSystem);
|
||||
|
||||
|
||||
|
||||
/************************************
|
||||
** Create known entities **
|
||||
*************************************/
|
||||
// Input controller - gets player input
|
||||
Entity inputEntity = gameEngine.createEntity();
|
||||
gameEngine.addComponent(inputEntity, Input.class, new Input());
|
||||
|
||||
Entity playerEntity = gameEngine.createEntity();
|
||||
gameEngine.addComponent(playerEntity, Vec2D.class, new Vec2D(25,25));
|
||||
gameEngine.addComponent(playerEntity, Render.class, new Render(Sprite.class));
|
||||
gameEngine.addComponent(playerEntity, Control.class, new Render(Control.class));
|
||||
// Load the sprite
|
||||
System.err.println(System.getProperty("user.dir"));
|
||||
Image image = new Image(new FileInputStream("sprite.png"));
|
||||
gameEngine.addComponent(playerEntity, Sprite.class, new Sprite(image, 50, 50));
|
||||
// Push rendering into its own thread (to meet the design principle of a JavaFX program)
|
||||
Thread renderThread = new Thread(new RenderStarter());
|
||||
renderThread.start();
|
||||
|
||||
|
||||
System.out.println("Hello World!");
|
||||
// Use a semaphore to block until the renderer is set-up
|
||||
rendererFinished.acquire();
|
||||
|
||||
/************************************
|
||||
** Run init() on the systems **
|
||||
*************************************/
|
||||
|
||||
controlSystem.init(worldStage);
|
||||
playerSystem.init(inputEntity);
|
||||
renderSystem.init(worldGroup);
|
||||
|
||||
double dt = 0.001;
|
||||
double frameRate = 1.0 / 144; // 1 second divided by target number of frames
|
||||
|
||||
boolean exit = false;
|
||||
long startTime = System.nanoTime();
|
||||
int state = 0;
|
||||
|
||||
while (!exit){
|
||||
// Run system updates
|
||||
controlSystem.update(dt);
|
||||
playerSystem.update(dt);
|
||||
renderSystem.update(dt);
|
||||
|
||||
dt = (System.nanoTime() - startTime) / 1e9; // convert nanoseconds to seconds
|
||||
startTime = System.nanoTime();
|
||||
if (dt < frameRate){
|
||||
Thread.sleep((int)((frameRate-dt)*1000));
|
||||
dt = (System.nanoTime() - startTime) / 1e9;
|
||||
startTime = System.nanoTime();
|
||||
}
|
||||
}
|
||||
renderThread.join(); // Wait for any render task to exit
|
||||
}
|
||||
}
|
||||
|
@ -1,5 +1,5 @@
|
||||
package nz.ac.massey.javaecs.examples.Components;
|
||||
|
||||
public class Control {
|
||||
|
||||
public int input = -1;
|
||||
}
|
||||
|
@ -0,0 +1,9 @@
|
||||
package nz.ac.massey.javaecs.examples.Components;
|
||||
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
||||
public class Input {
|
||||
// As this is a user-input- (i.e. timeframe in 10's or 100's of milliseconds) -derived value, the performance impact of
|
||||
// AtomicInteger is irrelevant compared to its synchronisation ability.
|
||||
public AtomicInteger input = new AtomicInteger(-1);
|
||||
}
|
@ -3,5 +3,13 @@ package nz.ac.massey.javaecs.examples.Components;
|
||||
import javafx.scene.image.Image;
|
||||
|
||||
public class Sprite {
|
||||
Image sprite;
|
||||
public Image sprite;
|
||||
public Integer height;
|
||||
public Integer width;
|
||||
|
||||
public Sprite(Image sprite, int height, int width){
|
||||
this.sprite = sprite;
|
||||
this.width = width;
|
||||
this.height = height;
|
||||
}
|
||||
}
|
||||
|
@ -0,0 +1,28 @@
|
||||
package nz.ac.massey.javaecs.examples;
|
||||
|
||||
|
||||
import javafx.application.Application;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
|
||||
public class JFXView extends Application {
|
||||
public static Group root;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
primaryStage.setTitle("JavaECS Demo 2");
|
||||
App.worldGroup = new Group();
|
||||
App.worldStage = primaryStage;
|
||||
Scene scene = new Scene(App.worldGroup, 1024, 1024, Color.BLACK);
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
// Notifiy that the renderer is ready
|
||||
App.rendererFinished.release();
|
||||
}
|
||||
|
||||
public static void run(){
|
||||
launch(new String[0]);
|
||||
}
|
||||
}
|
@ -0,0 +1,16 @@
|
||||
package nz.ac.massey.javaecs.examples;
|
||||
|
||||
import javafx.scene.Group;
|
||||
|
||||
// Add a renderer
|
||||
public class RenderStarter implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
JFXView.run();
|
||||
}
|
||||
void updateScene(Group newGroup){
|
||||
JFXView.root.getChildren().clear();
|
||||
JFXView.root.getChildren().add(newGroup);
|
||||
}
|
||||
}
|
@ -1,17 +1,81 @@
|
||||
package nz.ac.massey.javaecs.examples.Systems;
|
||||
|
||||
import nz.ac.massey.javaecs.ECSSystem;
|
||||
import java.util.BitSet;
|
||||
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.input.KeyCode;
|
||||
import javafx.scene.input.KeyEvent;
|
||||
import javafx.stage.Stage;
|
||||
import nz.ac.massey.javaecs.ECSSystem;
|
||||
import nz.ac.massey.javaecs.Engine;
|
||||
import nz.ac.massey.javaecs.Entity;
|
||||
import nz.ac.massey.javaecs.examples.Components.Control;
|
||||
import nz.ac.massey.javaecs.examples.Components.Input;
|
||||
import nz.ac.massey.javaecs.examples.Components.Render;
|
||||
|
||||
|
||||
/**
|
||||
* The control system creates a handler for input in the world stage.
|
||||
* It then passes input events as they occur to the Input component
|
||||
* The ControlSystem reads this input every frame update and enacts
|
||||
* on the player controller
|
||||
*/
|
||||
public class ControlSystem extends ECSSystem {
|
||||
Engine gameEngine;
|
||||
Entity inputEntity;
|
||||
|
||||
public ControlSystem(Engine gamEngine){
|
||||
this.gameEngine = gamEngine;
|
||||
registrationSet = new BitSet();
|
||||
registrationSet.set(gameEngine.getComponentIndex(Input.class));
|
||||
}
|
||||
|
||||
public void init(Stage world) {
|
||||
// use a single entity to control inputs, it is present in the world stage
|
||||
// and is responsible for collecting & passing data
|
||||
inputEntity = entities.iterator().next();
|
||||
|
||||
// Add an event handler to the stage. This
|
||||
Platform.runLater(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
world.addEventHandler(KeyEvent.KEY_PRESSED, (key) -> {
|
||||
if(key.getCode()==KeyCode.W) {
|
||||
updateKeyPress(0);
|
||||
System.err.println("W pressed");
|
||||
}
|
||||
else if(key.getCode()==KeyCode.S) {
|
||||
updateKeyPress(1);
|
||||
System.err.println("S pressed");
|
||||
}
|
||||
else if(key.getCode()==KeyCode.A) {
|
||||
updateKeyPress(2);
|
||||
System.err.println("A pressed");
|
||||
}
|
||||
else if(key.getCode()==KeyCode.D) {
|
||||
updateKeyPress(3);
|
||||
System.err.println("D pressed");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
public void update(double dt) {
|
||||
//Input inputUpdate = (Input)gameEngine.getComponentData(inputEntity, Input.class);
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
// Updates the input's keypressed state
|
||||
protected void updateKeyPress(int dir){
|
||||
Input inputUpdate = (Input)gameEngine.getComponentData(inputEntity, Input.class);
|
||||
inputUpdate.input.set(dir);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
public void init() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,59 @@
|
||||
package nz.ac.massey.javaecs.examples.Systems;
|
||||
|
||||
import java.util.BitSet;
|
||||
|
||||
import nz.ac.massey.javaecs.ECSSystem;
|
||||
import nz.ac.massey.javaecs.Engine;
|
||||
import nz.ac.massey.javaecs.Entity;
|
||||
import nz.ac.massey.javaecs.examples.Components.Control;
|
||||
import nz.ac.massey.javaecs.examples.Components.Input;
|
||||
import nz.ac.massey.javaecs.examples.Components.Sprite;
|
||||
import nz.ac.massey.javaecs.examples.Components.Vec2D;
|
||||
|
||||
public class PlayerSystem extends ECSSystem{
|
||||
Engine gameEngine;
|
||||
Entity inputEntity; // Entity which recieves event data
|
||||
|
||||
|
||||
public PlayerSystem(Engine gameEngine){
|
||||
this.gameEngine = gameEngine;
|
||||
registrationSet = new BitSet();
|
||||
registrationSet.set(gameEngine.getComponentIndex(Control.class));
|
||||
}
|
||||
|
||||
public void init(Entity inputEntity){
|
||||
this.inputEntity = inputEntity;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(double dt) {
|
||||
int state = ((Input)gameEngine.getComponentData(inputEntity, Input.class)).input.get();
|
||||
// The value of the input may change before control leaves here; sychronisation isn't
|
||||
// hugely important through this section. So we can just ignore the change
|
||||
for (Entity entity : entities) {
|
||||
Vec2D pos = (Vec2D)gameEngine.getComponentData(entity, Vec2D.class);
|
||||
Sprite sprite = (Sprite)gameEngine.getComponentData(entity, Sprite.class);
|
||||
if (state == 0){ // W
|
||||
pos.y -= sprite.height/2;
|
||||
}
|
||||
else if (state == 1){ // S
|
||||
pos.y += sprite.height/2;
|
||||
}
|
||||
else if (state == 2){ // A
|
||||
pos.x -= sprite.width/2;
|
||||
}
|
||||
else if (state == 3){ // D
|
||||
pos.x += sprite.width/2;
|
||||
}
|
||||
}
|
||||
// Finally, unset the state
|
||||
((Input)gameEngine.getComponentData(inputEntity, Input.class)).input.set(-1); // Unset the state, it has been handled
|
||||
}
|
||||
|
||||
}
|
@ -6,19 +6,23 @@ import javafx.application.Application;
|
||||
import javafx.application.Platform;
|
||||
import javafx.scene.Group;
|
||||
import javafx.scene.Scene;
|
||||
import javafx.scene.image.ImageView;
|
||||
import javafx.scene.paint.Color;
|
||||
import javafx.stage.Stage;
|
||||
import nz.ac.massey.javaecs.ECSSystem;
|
||||
import nz.ac.massey.javaecs.Engine;
|
||||
import nz.ac.massey.javaecs.Entity;
|
||||
import nz.ac.massey.javaecs.examples.Components.Control;
|
||||
import nz.ac.massey.javaecs.examples.Components.Input;
|
||||
import nz.ac.massey.javaecs.examples.Components.Render;
|
||||
import nz.ac.massey.javaecs.examples.Components.Sprite;
|
||||
import nz.ac.massey.javaecs.examples.Components.Vec2D;
|
||||
|
||||
/*
|
||||
* This render system is different from demo 1, as its main functionality is provided directly by the program, not as an entity.
|
||||
*/
|
||||
public class RenderSystem extends ECSSystem {
|
||||
JFXView jFXView;
|
||||
Engine gameEngine;
|
||||
Group renderGroup;
|
||||
Thread renderThread;
|
||||
double renderScale;
|
||||
int screenX = 1024;
|
||||
@ -29,69 +33,43 @@ public class RenderSystem extends ECSSystem {
|
||||
// Set registrations
|
||||
registrationSet = new BitSet();
|
||||
registrationSet.set(gameEngine.getComponentIndex(Render.class));
|
||||
registrationSet.set(gameEngine.getComponentIndex(Vec2D.class));
|
||||
this.renderScale = renderScale;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init() {
|
||||
// Spawn a new asynchronous thread (won't rejoin the main program flow)
|
||||
renderThread = new Thread(new RenderStarter());
|
||||
renderThread.start();
|
||||
public void init(Group renderGroup) throws Exception {
|
||||
this.renderGroup = renderGroup;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update() {
|
||||
Group renderScene = new Group();
|
||||
public void init() throws Exception {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void update(double dt) {
|
||||
Group frame = new Group();
|
||||
for (Entity entity : entities) {
|
||||
if (((Render)gameEngine.getComponentData(entity, Render.class)).renderType == Control.class){
|
||||
// Do control updates here
|
||||
}
|
||||
else if (((Render)gameEngine.getComponentData(entity, Render.class)).renderType == Sprite.class){
|
||||
Vec2D vec2d = (Vec2D)gameEngine.getComponentData(entity, Vec2D.class);
|
||||
Vec2D pos = (Vec2D)gameEngine.getComponentData(entity, Vec2D.class);
|
||||
if (((Render)gameEngine.getComponentData(entity, Render.class)).renderType == Sprite.class){
|
||||
// Render the sprite
|
||||
Sprite sprite = (Sprite)gameEngine.getComponentData(entity, Sprite.class);
|
||||
ImageView spriteRender = new ImageView(sprite.sprite);
|
||||
spriteRender.setX(pos.x);
|
||||
spriteRender.setY(pos.y);
|
||||
spriteRender.setFitWidth(sprite.width);
|
||||
spriteRender.setFitHeight(sprite.height);
|
||||
frame.getChildren().add(spriteRender);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Must be run by the UI thread, not the main thread.
|
||||
// This dispatches the operation run() to the UI.
|
||||
Platform.runLater(new Runnable(){
|
||||
@Override
|
||||
public void run() {
|
||||
JFXView.root.getChildren().clear();
|
||||
JFXView.root.getChildren().add(renderScene);
|
||||
renderGroup.getChildren().clear();
|
||||
renderGroup.getChildren().add(frame);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add a renderer
|
||||
public class RenderStarter implements Runnable{
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
JFXView.run();
|
||||
}
|
||||
void updateScene(Group newGroup){
|
||||
JFXView.root.getChildren().clear();
|
||||
JFXView.root.getChildren().add(newGroup);
|
||||
}
|
||||
}
|
||||
|
||||
public static class JFXView extends Application {
|
||||
private static Group root;
|
||||
|
||||
@Override
|
||||
public void start(Stage primaryStage) throws Exception {
|
||||
// based on the colorful circles sample at https://docs.oracle.com/javase/8/javafx/get-started-tutorial/animation.htm
|
||||
root = new Group();
|
||||
Scene scene = new Scene(root, 1024, 1024, Color.BLACK);
|
||||
primaryStage.setScene(scene);
|
||||
primaryStage.show();
|
||||
}
|
||||
|
||||
public static void run(){
|
||||
launch(new String[0]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
BIN
demo2/src/sprite.png
Normal file
BIN
demo2/src/sprite.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.4 KiB |
Loading…
x
Reference in New Issue
Block a user