Commits

Anonymous committed 13f5f30 Merge

Megred in Jose's path routing and made some minor changes to it.

Comments (0)

Files changed (11)

defender/src/main/java/com/fxexperience/games/defender/games/simple/Enemy.java

  */
 package com.fxexperience.games.defender.games.simple;
 
+import javafx.animation.Interpolator;
+import javafx.animation.PathTransition;
 import javafx.beans.property.ObjectProperty;
 import javafx.beans.property.ReadOnlyDoubleProperty;
 import javafx.beans.property.ReadOnlyDoubleWrapper;
 import javafx.geometry.Point2D;
 import javafx.scene.Parent;
 import javafx.scene.shape.Path;
+import javafx.util.Duration;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * An enemy. Has life, speed, and some path that it is to follow. It might have different
- * buffs depending on what type of weapon hits it, proximity to other enemies (they might
- * make each other stronger) or whatever else you might come up with.
+ * An enemy. Has life, speed, and some path that it is to follow. It might have
+ * different buffs depending on what type of weapon hits it, proximity to other
+ * enemies (they might make each other stronger) or whatever else you might come
+ * up with.
  */
 public class Enemy extends Parent implements GamePulseListener {
 
     private static final Logger log = LoggerFactory.getLogger(Enemy.class);
-
     /**
      * The maximum life of this enemy.
      */
     private final double maximumLife;
-    public final double getMaximumLife() { return maximumLife; }
 
+    public final double getMaximumLife() {
+        return maximumLife;
+    }
     /**
-     * The Path that the enemy must follow. If the enemy reaches the end of the Path without being destroyed then
-     * the player loses a life and the enemy destroys itself.
+     * The Path that the enemy must follow. If the enemy reaches the end of the
+     * Path without being destroyed then the player loses a life and the enemy
+     * destroys itself.
      */
     private final ObjectProperty<Path> path = new SimpleObjectProperty<>(this, "path");
-    public final Path getPath() { return path.get(); }
-    public final void setPath(Path path) { this.path.set(path); }
-    public final ObjectProperty<Path> pathProperty() { return path; }
+    private PathTransition pathTransition;
+    private long startTime = 0;
 
+    public final Path getPath() {
+        return path.get();
+    }
+
+    public final void setPath(Path path, double distance) {
+        this.path.set(path);
+        pathTransition = new PathTransition(Duration.seconds(distance / speed.getValue()), path, this);
+        pathTransition.setInterpolator(Interpolator.LINEAR);
+        pathTransition.setOrientation(PathTransition.OrientationType.ORTHOGONAL_TO_TANGENT);
+    }
+
+    public final ObjectProperty<Path> pathProperty() {
+        return path;
+    }
     /**
-     * The Path that the enemy must follow. If the enemy reaches the end of the Path without being destroyed then
-     * the player loses a life and the enemy destroys itself.
+     * The Path that the enemy must follow. If the enemy reaches the end of the
+     * Path without being destroyed then the player loses a life and the enemy
+     * destroys itself.
      */
     private final ObjectProperty<Level> level = new SimpleObjectProperty<>(this, "level");
     public final Level getLevel() { return level.get(); }
      * When life reaches 0 (or goes negative), the enemy is destroyed.
      */
     private final ReadOnlyDoubleWrapper life = new ReadOnlyDoubleWrapper(this, "life", 100);
-    public final double getLife() { return life.get(); }
-    public final ReadOnlyDoubleProperty lifeProperty() { return life.getReadOnlyProperty(); }
 
+    public final double getLife() {
+        return life.get();
+    }
+
+    public final ReadOnlyDoubleProperty lifeProperty() {
+        return life.getReadOnlyProperty();
+    }
     /**
      * The natural speed at which the enemy moves. This is the speed at which
      * the enemy will move in the absence of any buffs.
      */
     private final double naturalSpeed;
-    public final double getNaturalSpeed() { return naturalSpeed; }
 
+    public final double getNaturalSpeed() {
+        return naturalSpeed;
+    }
     /**
      * The speed at which the enemy is moving. This can go up or down depending
-     * on different game dynamics (for example, a tower with some "slow down" buff
-     * could cause the speed to decrease for some period of time).
+     * on different game dynamics (for example, a tower with some "slow down"
+     * buff could cause the speed to decrease for some period of time).
      */
     private final ReadOnlyDoubleWrapper speed = new ReadOnlyDoubleWrapper(this, "speed");
-    public final double getSpeed() { return speed.get(); }
-    public final ReadOnlyDoubleProperty speedProperty() { return speed.getReadOnlyProperty(); }
 
+    public final double getSpeed() {
+        return speed.get();
+    }
+
+    public final ReadOnlyDoubleProperty speedProperty() {
+        return speed.getReadOnlyProperty();
+    }
     /**
      * Called when the enemy dies.
      */
     private ObjectProperty<EventHandler<ActionEvent>> onDeath = new SimpleObjectProperty<>(this, "onDeath");
 
-    public final EventHandler<ActionEvent> getOnDeath() { return onDeath.get(); }
-    public final void setOnDeath(EventHandler<ActionEvent> value) { onDeath.set(value); }
-    public final ObjectProperty<EventHandler<ActionEvent>> onDeathProperty() { return onDeath; }
+    public final EventHandler<ActionEvent> getOnDeath() {
+        return onDeath.get();
+    }
+
+    public final void setOnDeath(EventHandler<ActionEvent> value) {
+        onDeath.set(value);
+    }
+
+    public final ObjectProperty<EventHandler<ActionEvent>> onDeathProperty() {
+        return onDeath;
+    }
 
     /**
-     * Creates a new Enemy. You must specify the natural speed of the enemy and its max life.
+     * Creates a new Enemy. You must specify the natural speed of the enemy and
+     * its max life.
+     *
      * @param maximumLife must be positive
      * @param naturalSpeed must be positive. Measured in pixels per second.
      */
     }
 
     // ... etc
-
-
     @Override
     public void pulse() {
+        if (this.startTime == 0) {
+            startTime = System.currentTimeMillis();
+            pathTransition.playFromStart();
+            pathTransition.pause();
+        }
 
-        // todo move forward along the path - currently dodgy
-
-        setTranslateX(getTranslateX() + 1);
-        setTranslateY(getTranslateY() + 1);
-
-        if (getTranslateX() > 800) {
+        double fraction = (System.currentTimeMillis() - startTime) / pathTransition.getDuration().toMillis();
+        pathTransition.interpolate(fraction);
+        if (fraction >= 1.0) {
             destinationReached();
         }
     }
     public Point2D getCenter() {
         Bounds bounds = getBoundsInParent();
         return new Point2D(
-                bounds.getMinX() + (bounds.getWidth()/2.0),
-                bounds.getMinY() + (bounds.getHeight()/2.0));
+                bounds.getMinX() + (bounds.getWidth() / 2.0),
+                bounds.getMinY() + (bounds.getHeight() / 2.0));
     }
 
     protected void destinationReached() {

defender/src/main/java/com/fxexperience/games/defender/games/simple/Level.java

 import java.util.List;
 
 /**
- * This UI represents the game board. It has all the tiles, towers, enemies, background, and so forth.
- * The GameControlPanelUI is separate, and has the money values etc. A single Level has a single path
- * (or set of paths) on which the enemies roam. Multiple waves of enemies will come following one or
- * more of those paths.
+ * This UI represents the game board. It has all the tiles, towers, enemies,
+ * background, and so forth. The GameControlPanelUI is separate, and has the
+ * money values etc. A single Level has a single path (or set of paths) on which
+ * the enemies roam. Multiple waves of enemies will come following one or more
+ * of those paths.
  */
 public class Level extends Region implements GamePulseListener {
+
     private static final Logger log = LoggerFactory.getLogger(Level.class);
 
     private static final double LEVEL_HEIGHT = 600;
     private static final double LEVEL_WIDTH = 800;
-
     private Node background;
     private Rectangle clip;
     private Pane enemyLayer;
     private Pane towerLayer;
     private Pane projectileLayer;
-
     private LinkedList<Wave> waves;
     private Wave wave;
     private Path path;
     private List<Projectile> projectiles;
 
     public Level(final Path path, Node background, Wave... waves) {
-
         getStyleClass().add("level");
-        this.waves = new LinkedList<>(Arrays.asList(waves));
         this.path = path;
         this.background = background;
+        this.waves = new LinkedList<>(Arrays.asList(waves));
 
         enemies = new ArrayList<>();
         enemyLayer = new Pane();
             if (!waves.isEmpty()) {
                 wave = waves.remove();
                 wave.setLevel(this);
-                wave.setPath(path);
             } else {
                 // todo spawning finished - when the remaining enemies are done we have finished the level...
                 log.info("No more enemies to spawn");
         projectileLayer.getChildren().remove(projectile);
     }
 
+    protected final void setPath(Path path) {
+        this.path = path;
+    }
+
+    protected final void setWaves(Wave... waves) {
+        this.waves = new LinkedList<>(Arrays.asList(waves));
+    }
+
     @Override
     protected double computeMinWidth(double v) {
         return computePrefWidth(v);

defender/src/main/java/com/fxexperience/games/defender/games/simple/Wave.java

 import java.util.Arrays;
 import java.util.LinkedList;
 
+public class Wave implements GamePulseListener {
 
-public class Wave implements GamePulseListener {
     private final LinkedList<Enemy> enemies = new LinkedList<>();
     private Path path;
     private final Duration delay;
     private Level level;
     private long lastSpawn;
+    private double pathDistance;
 
-    public Wave(Duration delay, Enemy... enemies) {
+    public Wave(Path path, Duration delay, double pathDistance, Enemy... enemies) {
+        this.path = path;
         this.delay = delay;
+        this.pathDistance = pathDistance;
         this.enemies.addAll(Arrays.asList(enemies));
+        for (Enemy e : enemies) {
+            e.setPath(path, pathDistance);
+        }
     }
 
     public void setLevel(Level level) {
         this.level = level;
     }
 
-    public void setPath(Path path) {
-        this.path = path;
-    }
-
     @Override
     public void pulse() {
         if (level != null) {
     protected void spawn() {
         if (!enemies.isEmpty()) {
             Enemy enemy = enemies.remove();
-            enemy.setPath(path);
             level.addEnemy(enemy);
         }
     }

defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/BlackEnemy.java

 import com.fxexperience.games.defender.games.simple.Enemy;
 import javafx.scene.layout.Region;
 
-/**
- * User: rbair
- * Date: 12/13/12
- * Time: 6:17 PM
- */
 public class BlackEnemy extends Enemy {
     public BlackEnemy() {
-        super(20, 4);
+        super(20, 40);
         Region r = new Region();
         r.getStyleClass().addAll("enemy", "black-enemy");
         getChildren().add(r);

defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/BlueEnemy.java

 import com.fxexperience.games.defender.games.simple.Enemy;
 import javafx.scene.layout.Region;
 
-/**
- * User: rbair
- * Date: 12/13/12
- * Time: 6:17 PM
- */
 public class BlueEnemy extends Enemy {
     public BlueEnemy() {
-        super(100, 10);
+        super(100, 60);
         Region r = new Region();
         r.getStyleClass().addAll("enemy", "blue-enemy");
         getChildren().add(r);

defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/GreenEnemy.java

 
 public class GreenEnemy extends Enemy {
     public GreenEnemy() {
-        super(300, 30);
+        super(300, 70);
         Region r = new Region();
         r.getStyleClass().addAll("enemy", "green-enemy");
         getChildren().add(r);

defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/SpriteEnemy.java

  */
 public class SpriteEnemy extends Enemy {
 
+    private AnimatedImageNode animatedNode;
+
     public SpriteEnemy() {
-        super(300, 10);
-
-        AnimatedImageNode animatedNode = new AnimatedImageNode(new Image("images/flier.png"), 80, 63, 2, 20);
+        super(300, 70);
+        animatedNode = new AnimatedImageNode(new Image("images/flier.png"), 80, 63, 2, 20);
         getChildren().add(animatedNode);
         animatedNode.playFromStart();
     }

defender/src/main/java/com/fxexperience/games/defender/games/simple/enemies/VectorEnemy.java

 
 import com.fxexperience.games.defender.games.simple.Enemy;
 import com.fxexperience.games.defender.games.simple.animation.MorphTransition;
-import java.util.Random;
-import javafx.animation.Interpolator;
-import javafx.animation.KeyFrame;
-import javafx.animation.KeyValue;
 import javafx.animation.ParallelTransition;
-import javafx.animation.Timeline;
-import javafx.animation.Transition;
 import javafx.beans.value.ChangeListener;
 import javafx.beans.value.ObservableValue;
 import javafx.event.ActionEvent;
 import javafx.event.EventHandler;
 import javafx.scene.paint.Color;
-import javafx.scene.shape.Circle;
-import javafx.scene.shape.CircleBuilder;
-import javafx.scene.shape.ClosePath;
-import javafx.scene.shape.Line;
-import javafx.scene.shape.LineTo;
-import javafx.scene.shape.MoveTo;
-import javafx.scene.shape.Path;
-import javafx.scene.shape.Shape;
+import javafx.scene.shape.*;
 import javafx.scene.transform.Rotate;
 import javafx.util.Duration;
 
+import java.util.Random;
+
 /**
  *
  * @author Jose
 public class VectorEnemy extends Enemy {
 
     public VectorEnemy() {
-        super(300, 10);
+        super(300, 40);
         final int radius = 25;
         int numLines = 5;
         int strokeWidth = 1;

defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/BrownLevelFactory.java

                 new LineTo(100, 340),
                 new LineTo(1024, 768)
         );
-
+        double pathDist = PathHelper.getPathDistance(path);
 
         Wave wave1 = new Wave(
-                Duration.seconds(1),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new VectorEnemy(),
                 new VectorEnemy(),
                 new VectorEnemy(),
         );
 
         Wave wave2 = new Wave(
-                Duration.seconds(1),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new GreenEnemy(),
                 new GreenEnemy(),
                 new GreenEnemy(),
         );
 
         Wave wave3 = new Wave(
-                Duration.seconds(3),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new BlackEnemy(),
                 new BlackEnemy(),
                 new BlackEnemy(),
         );
 
         Wave wave4 = new Wave(
-                Duration.seconds(1),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new BlackEnemy(),
                 new BlackEnemy(),
                 new GreenEnemy(),

defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/GrassLevelFactory.java

                 new LineTo(100, 340),
                 new LineTo(1024, 768)
         );
-
+        double pathDist = PathHelper.getPathDistance(path);
 
         Wave wave1 = new Wave(
-                Duration.seconds(1),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new VectorEnemy(),
                 new SpriteEnemy(),
                 new VectorEnemy(),
 
 
         Wave wave2 = new Wave(
-                Duration.seconds(1),
+                path,
+                Duration.millis(1000),
+                pathDist,
                 new VectorEnemy(),
                 new SpriteEnemy(),
                 new VectorEnemy(),

defender/src/main/java/com/fxexperience/games/defender/games/simple/levels/PathHelper.java

+/*
+ * Copyright (c) 2012, FX Experience. All rights  reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification, are
+ * permitted provided that the following conditions are met:
+ *
+ * Redistributions of source code must retain the above copyright notice, this list of
+ * conditions and the following disclaimer.
+ *
+ * Redistributions in binary form must reproduce the above copyright notice, this list of
+ * conditions and the following disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * Neither the name of the author nor the names of its contributors may be used to endorse or
+ * promote products derived from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
+ * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
+ * COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR
+ * TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+ * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package com.fxexperience.games.defender.games.simple.levels;
+
+import javafx.geometry.Point2D;
+import javafx.scene.shape.LineTo;
+import javafx.scene.shape.MoveTo;
+import javafx.scene.shape.Path;
+import javafx.scene.shape.PathElement;
+
+public class PathHelper {
+
+    public static double getPathDistance(Path path) {
+        double distance = 0;
+        Point2D lastPoint = null;
+        Point2D nextPoint = null;
+        for (PathElement pathElement : path.getElements()) {
+            if (pathElement instanceof LineTo) {
+                LineTo lineTo = (LineTo) pathElement;
+                nextPoint = new Point2D(lineTo.getX(), lineTo.getY());
+            } else if (pathElement instanceof MoveTo) {
+                MoveTo moveTo = (MoveTo) pathElement;
+                nextPoint = new Point2D(moveTo.getX(), moveTo.getY());
+            } else {
+                throw new IllegalArgumentException("PathElement of type '" + pathElement.getClass().getName()
+                        + "' is not currently supported for path distance calculation");
+            }
+
+            if (lastPoint != null) {
+                distance += Math.sqrt(Math.pow((nextPoint.getX() - lastPoint.getX()), 2) + Math.pow((nextPoint.getY() - lastPoint.getY()), 2));
+            }
+            lastPoint = nextPoint;
+        }
+        return distance;
+    }
+}