We've achieved quite a lot in these few hours of happy coding. I have to admit that i'm still very impressed with JavaFX. That's a lot of functionality for this relatively little amount of code.
Here's what we got so far:
Main.java
package game; import java.util.ArrayList; import java.util.BitSet; import java.util.Iterator; import java.util.List; import java.util.Random; import javafx.animation.AnimationTimer; import javafx.application.Application; import javafx.event.EventHandler; import javafx.scene.Group; import javafx.scene.Scene; import javafx.scene.control.Label; import javafx.scene.image.Image; import javafx.scene.image.ImageView; import javafx.scene.input.KeyCode; import javafx.scene.input.KeyEvent; import javafx.scene.layout.Pane; import javafx.scene.paint.Color; import javafx.stage.Stage; public class Main extends Application { private double SCENE_WIDTH = 400; private double SCENE_HEIGHT = 800; Random rnd = new Random(); private AnimationTimer gameLoop; ImageView backgroundImageView; ImageView playerShip; Image playerBullet; double playerShipSpeed = 4.0; double playerShipDeltaX = 0.0; double playerShipDeltaY = 0.0; double backgroundScrollSpeed = 0.6; Pane backgroundLayer; Pane lowerCloudLayer; Pane bulletLayer; Pane playfieldLayer; Pane upperCloudLayer; Pane debugLayer; Label debugLabel; // note: ordinal may be not appropriate, but we don't have a method in keycode to get the int code, so it'll have to do BitSet keyboardBitSet = new BitSet(); // counter for game loop int frameCount = 0; int fpsCurrent = 0; long prevTime = -1; // list of available cloud images Image clouds[]; Listsprites = new ArrayList (); double cannonChargeTime = 6; // the cannon can fire every n frames double cannonChargeCounter = cannonChargeTime; // initially the cannon is charged double cannonChargeCounterDelta = 1; // counter is increased by this value each frame double cannonBullets = 5; // number of bullets which the cannon can fire in 1 shot (center, left, right) double cannonBulletSpread = 0.6; // dx of left and right bullets double cannonBulletSpeed = 8.0; // speed of each bullet @Override public void start(Stage primaryStage) { try { // create root node Group root = new Group(); // create layers backgroundLayer = new Pane(); lowerCloudLayer = new Pane(); bulletLayer = new Pane(); playfieldLayer = new Pane(); upperCloudLayer = new Pane(); debugLayer = new Pane(); // add layers to scene root root.getChildren().add( backgroundLayer); root.getChildren().add( lowerCloudLayer); root.getChildren().add( bulletLayer); root.getChildren().add( playfieldLayer); root.getChildren().add( upperCloudLayer); root.getChildren().add( debugLayer); // create scene Scene scene = new Scene( root, SCENE_WIDTH,SCENE_HEIGHT); // show stage primaryStage.setScene(scene); primaryStage.show(); // load game assets loadGame(); // add nodes which display debug information addDebugInformation(); // keyboard control addInputControls( scene); // start the game startGameLoop(); } catch(Exception e) { e.printStackTrace(); } } private void addDebugInformation() { debugLabel = new Label(); debugLabel.setTextFill(Color.RED); debugLayer.getChildren().add( debugLabel); } private void addInputControls( Scene scene) { // keyboard handler: key pressed scene.addEventFilter(KeyEvent.KEY_PRESSED, new EventHandler () { @Override public void handle(KeyEvent event) { keyboardBitSet.set(event.getCode().ordinal(), true); } }); // keyboard handler: key up scene.addEventFilter(KeyEvent.KEY_RELEASED, new EventHandler () { @Override public void handle(KeyEvent event) { keyboardBitSet.set(event.getCode().ordinal(), false); } }); } private void loadGame() { // background // -------------------------------- backgroundImageView = new ImageView( getClass().getResource( "assets/maps/canyon.jpg").toExternalForm()); // reposition the map. it is scrolling from bottom of the background to top of the background backgroundImageView.relocate( 0, -backgroundImageView.getImage().getHeight() + SCENE_HEIGHT); // add background to layer backgroundLayer.getChildren().add( backgroundImageView); // player ship // -------------------------------- playerShip = new ImageView( getClass().getResource( "assets/vehicles/anansi.png").toExternalForm()); // center horizontally, position at 70% vertically playerShip.relocate( (SCENE_WIDTH - playerShip.getImage().getWidth()) / 2.0, SCENE_HEIGHT * 0.7); // add to playfield layer playfieldLayer.getChildren().add( playerShip); // clouds // -------------------------------- clouds = new Image[4]; clouds[0] = new Image( getClass().getResource( "assets/environment/cloud-01.png").toExternalForm()); clouds[1] = new Image( getClass().getResource( "assets/environment/cloud-02.png").toExternalForm()); clouds[2] = new Image( getClass().getResource( "assets/environment/cloud-03.png").toExternalForm()); clouds[3] = new Image( getClass().getResource( "assets/environment/cloud-04.png").toExternalForm()); // bullets playerBullet = new Image( getClass().getResource( "assets/bullets/bullet_01.png").toExternalForm()); } private void startGameLoop() { // calculate movement bounds of the player ship // allow half of the ship to be outside of the screen double playerShipMinX = 0 - playerShip.getImage().getWidth() / 2.0; double playerShipMaxX = SCENE_WIDTH - playerShip.getImage().getWidth() / 2.0; double playerShipMinY = 0 - playerShip.getImage().getHeight() / 2.0; double playerShipMaxY = SCENE_HEIGHT - playerShip.getImage().getHeight() / 2.0; // game loop gameLoop = new AnimationTimer() { @Override public void handle(long l) { // get keyboard input // --------------------------- // note: ordinal may be not appropriate, but we don't have an method in keycode to get the int code, so it'll have to do // evaluate keyboard events boolean isUpPressed = keyboardBitSet.get(KeyCode.UP.ordinal()); boolean isDownPressed = keyboardBitSet.get(KeyCode.DOWN.ordinal()); boolean isLeftPressed = keyboardBitSet.get(KeyCode.LEFT.ordinal()); boolean isRightPressed = keyboardBitSet.get(KeyCode.RIGHT.ordinal()); boolean isSpacePressed = keyboardBitSet.get(KeyCode.SPACE.ordinal()); boolean isControlPressed = keyboardBitSet.get(KeyCode.CONTROL.ordinal()); // vertical direction if( isUpPressed && !isDownPressed) { playerShipDeltaY = -playerShipSpeed; } else if( !isUpPressed && isDownPressed) { playerShipDeltaY = playerShipSpeed; } else { playerShipDeltaY = 0d; } // horizontal direction if( isLeftPressed && !isRightPressed) { playerShipDeltaX = -playerShipSpeed; } else if( !isLeftPressed && isRightPressed) { playerShipDeltaX = playerShipSpeed; } else { playerShipDeltaX = 0d; } // scroll background // --------------------------- // calculate new position double y = backgroundImageView.getLayoutY() + backgroundScrollSpeed; // check bounds. we scroll upwards, so the y position is negative. once it's > 0 we have reached the end of the map and stop scrolling if( Double.compare( y, 0) >= 0) { y = 0; } // move background backgroundImageView.setLayoutY( y); // move player ship // --------------------------- double newX = playerShip.getLayoutX() + playerShipDeltaX; double newY = playerShip.getLayoutY() + playerShipDeltaY; // check bounds // vertical if( Double.compare( newY, playerShipMinY) < 0) { newY = playerShipMinY; } else if( Double.compare(newY, playerShipMaxY) > 0) { newY = playerShipMaxY; } // horizontal if( Double.compare( newX, playerShipMinX) < 0) { newX = playerShipMinX; } else if( Double.compare(newX, playerShipMaxX) > 0) { newX = playerShipMaxX; } playerShip.setLayoutX( newX); playerShip.setLayoutY( newY); // limit bullet fire // --------------------------- // charge player cannon: increase a counter by some delta. once it reaches a limit, the cannon is considered charged cannonChargeCounter += cannonChargeCounterDelta; if( cannonChargeCounter > cannonChargeTime) { cannonChargeCounter = cannonChargeTime; } // fire player bullets // --------------------------- boolean isCannonCharged = cannonChargeCounter >= cannonChargeTime; if( isSpacePressed && isCannonCharged) { // x-position: center bullet on center of the ship double bulletX = playerShip.getLayoutX() + playerShip.getImage().getWidth() / 2.0 - playerBullet.getWidth() / 2.0; // y-position: let bullet come out on top of the ship double bulletY = playerShip.getLayoutY(); // create sprite ImageView imgv = new ImageView( playerBullet); Sprite sprite = new Sprite( bulletLayer, imgv, bulletX, bulletY, 0, -cannonBulletSpeed); sprites.add( sprite); // left/right: vary x-axis position for( int i=0; i < cannonBullets / 2.0; i++) { // left imgv = new ImageView( playerBullet); sprite = new Sprite( bulletLayer, imgv, bulletX, bulletY, -cannonBulletSpread * i, -cannonBulletSpeed); sprites.add( sprite); // right imgv = new ImageView( playerBullet); sprite = new Sprite( bulletLayer, imgv, bulletX, bulletY, cannonBulletSpread * i, -cannonBulletSpeed); sprites.add( sprite); } // player bullet uncharged cannonChargeCounter = 0; } // check sprite visibility // remove every sprite that's not visible anymore // --------------------------- Iterator iter = sprites.iterator(); while( iter.hasNext()) { Sprite sprite = iter.next(); // check lower screen bounds if( sprite.getY() > SCENE_HEIGHT) { iter.remove(); continue; } // check upper screen bounds if( (sprite.getY() + sprite.getImageView().getImage().getHeight()) < 0) { iter.remove(); continue; } // check right screen bounds if( sprite.getX() > SCENE_WIDTH) { iter.remove(); continue; } // check left screen bounds if( (sprite.getX() + sprite.getImageView().getImage().getWidth()) < 0) { iter.remove(); continue; } } // move sprites // --------------------------- // move sprites internally for( Sprite sprite: sprites) { sprite.move(); } // move sprites on screen for( Sprite sprite: sprites) { sprite.getImageView().relocate( sprite.getX(), sprite.getY()); } // add random clouds // --------------------------- if( rnd.nextInt(100) == 0) { // determine random layer Pane layer; if( rnd.nextInt(2) == 0) { layer = lowerCloudLayer; } else { layer = upperCloudLayer; } // speed double speed = rnd.nextDouble() * 1.0 + 1.0; // determine random image Image image = clouds[ rnd.nextInt( clouds.length)]; // create sprite ImageView imgv = new ImageView( image); // clouds in the upper layer are less opaque than the ones in the lower layer so that the player ship is always visible // and the upper layer clouds are faster if( layer == upperCloudLayer) { imgv.setOpacity(0.5); speed +=1.0; } // create a sprite and position it horizontally so that half of it may be outside of the view, vertically so that it enters the view with the next movement Sprite sprite = new Sprite( layer, imgv, rnd.nextDouble() * SCENE_WIDTH - imgv.getImage().getWidth() / 2.0, -imgv.getImage().getHeight(), 0, speed); sprites.add( sprite); } // calculate fps // --------------------------- frameCount++; long currTime = System.currentTimeMillis(); if( currTime - prevTime >= 1000) { // get current fps fpsCurrent = frameCount; // reset counter every second prevTime = currTime; frameCount = 0; } // show debug info // --------------------------- debugLabel.setText("FPS: " + fpsCurrent + "\nSprites: " + sprites.size()); } }; gameLoop.start(); } public static void main(String[] args) { launch(args); } }
Sprite.java
package game; import javafx.scene.image.ImageView; import javafx.scene.layout.Pane; public class Sprite { Pane layer; ImageView imageView; double x; double y; double dx; double dy; double rotation; boolean alive = true; public Sprite( Pane layer, ImageView imageView, double x, double y, double dx, double dy) { this.layer = layer; this.imageView = imageView; this.x = x; this.y = y; this.dx = dx; this.dy = dy; layer.getChildren().add( imageView); imageView.relocate(x, y); } public Pane getLayer() { return layer; } public void setLayer(Pane layer) { this.layer = layer; } public ImageView getImageView() { return imageView; } public void setImageView(ImageView imageView) { this.imageView = imageView; } public double getX() { return x; } public void setX(double x) { this.x = x; } public double getY() { return y; } public void setY(double y) { this.y = y; } public double getDx() { return dx; } public void setDx(double dx) { this.dx = dx; } public double getDy() { return dy; } public void setDy(double dy) { this.dy = dy; } public void move() { x += dx; y += dy; } }ps: does anyone know why blogspot changes "Sprite" to "sprite" and adds tags like "</sprite></keyevent></keyevent></sprite></sprite> " to the code parts?
No comments:
Post a Comment