2

I am currently trying to create a game with falling "asteroids" that a ship has to dodge. I have currently created a pane that is a large circle stacked with multiple smaller circles. Unfortunately, the pane's "hitbox" is a square/rectangle, rather than a circle, which is frustrating. The other alternate way of fixing this would be to create a circle and fill it with an image of an asteroid, but I couldn't find this anywhere. I know of:

circle.setFill(Color.WHATEVER);

but i would like for it to look like an asteroid, not just a simple grey circle falling. To sum it up, how can I set an image to a shape? Or, if there is a way to change the hitbox of a stackpane, a way to do that would also help fix my issue!

Any help is greatly appreciated! Thanks!

EDIT: Below are the 4 files that will make a simplified version of my game work with the same problem:

DodgerRemake:

package javafxapplication6;

import javafx.animation.AnimationTimer;
import javafx.application.Application;
import javafx.beans.binding.BooleanBinding;
import javafx.beans.property.BooleanProperty;
import javafx.beans.property.SimpleBooleanProperty;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.input.KeyCode;
import javafx.scene.layout.BorderPane;
import javafx.scene.layout.StackPane;
import javafx.stage.Stage;

public class DodgerRemake extends Application
{
    private Spaceship thisSpaceShip = new Spaceship();
    private BoxPane gamePane = new BoxPane(thisSpaceShip);
    BorderPane pane = new BorderPane();
    private BooleanProperty upPressed = new SimpleBooleanProperty();
    private BooleanProperty rightPressed = new SimpleBooleanProperty();
    private BooleanProperty downPressed = new SimpleBooleanProperty();
    private BooleanProperty leftPressed = new SimpleBooleanProperty();
    private BooleanBinding anyPressed = upPressed.or(rightPressed).or(downPressed).or(leftPressed);


    protected BorderPane getPane()
    {
        StackPane centerPane = new StackPane();
        centerPane.getChildren().add(gamePane);
        pane.setCenter(centerPane);

        Button thisButton = new Button("Start");
        thisButton.setAlignment(Pos.CENTER);
        thisButton.setFocusTraversable(false);
        thisButton.setOnAction(new startGame());  
        pane.setLeft(thisButton);

        return pane;
    }

    @Override
    public void start(Stage primaryStage) {    
         // Create a scene and place it in the stage
        Scene scene = new Scene(getPane(), 960, 800);
        primaryStage.setTitle("Dodger"); // Set the stage title
        primaryStage.setScene(scene); // Place the scene in the stage
        primaryStage.setResizable(false);
        primaryStage.show(); // Display the stage

        scene.setOnKeyPressed(e -> {
            if (e.getCode() == KeyCode.UP) {
                upPressed.set(true);
            }
            if (e.getCode() == KeyCode.DOWN) {
                downPressed.set(true);
            }
            if (e.getCode() == KeyCode.RIGHT) {
                rightPressed.set(true);
            }
            if (e.getCode() == KeyCode.LEFT) {
                leftPressed.set(true);
            }
        });

        scene.setOnKeyReleased(e -> {
            if (e.getCode() == KeyCode.UP) {
                upPressed.set(false);
            }
            if (e.getCode() == KeyCode.DOWN) {
                downPressed.set(false);
            }
            if (e.getCode() == KeyCode.RIGHT) {
                rightPressed.set(false);
            }
            if (e.getCode() == KeyCode.LEFT) {
                leftPressed.set(false);
            }
        });

        AnimationTimer timer = new AnimationTimer()
        {
            @Override
            public void handle(long timestamp) {

                if (upPressed.get()){
                    gamePane.moveShipUp();
                }
                if (downPressed.get()){
                    gamePane.moveShipDown();
                }
                if (rightPressed.get()) {
                    gamePane.moveShipRight();
                }
                if (leftPressed.get()) {
                    gamePane.moveShipLeft();
                }
            }
        };

        anyPressed.addListener((obs, wasPressed, isNowPressed) ->
        {
            if (isNowPressed) {
                timer.start();
            } else {
                timer.stop();
            }
        });
    }

    class startGame implements EventHandler<ActionEvent>
    {
        @Override
        public void handle(ActionEvent e)
        {
            gamePane.addSpaceship();
            gamePane.startGame();
        }
    }
    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {
        launch(args);
    }

}

BoxPane:

package javafxapplication6;

import java.util.Random;
import javafx.animation.Animation;
import javafx.animation.KeyFrame;
import javafx.animation.Timeline;
import javafx.scene.layout.Pane;
import javafx.util.Duration;

class BoxPane extends Pane
{
    private Spaceship spaceShip;
    private Timeline asteroids;
    private double x;
    private double y;

    BoxPane(Spaceship thisSpaceShip)
    {        
        spaceShip = thisSpaceShip;
        spaceShip.setRotate(180);
    }

    public void addSpaceship()
    {
        getChildren().add(spaceShip);
        x = 400 - spaceShip.getWidth()/2;
        y = 740;
        spaceShip.setTranslateX(x);
        spaceShip.setTranslateY(y);
    }

    public void moveShipLeft()
    {
        if(x > 0)
        {
            spaceShip.setTranslateX(x-5);
            x = x-5;
        }
        else
        {
            spaceShip.setTranslateX(0);
            x = 0;
        }
    }

    public void moveShipRight()
    {
        if(x+spaceShip.getWidth() < getWidth())
        {
            spaceShip.setTranslateX(x+5);
            x = x+5;
        }
        else
        {
            spaceShip.setTranslateX(getWidth() - spaceShip.getWidth());
            x = getWidth() - spaceShip.getWidth();
        }

    }

    public void moveShipUp()
    {
        if(y > 0)
        {
            spaceShip.setTranslateY(y-5);
            y = y-5;
        }
        else
        {
            spaceShip.setTranslateY(0);
            y = 0;
        }
    }

    public void moveShipDown()
    {
        if(y+spaceShip.getHeight() < getHeight())
        {
            spaceShip.setTranslateY(y+5);
            y = y+5;
        }
        else
        {
            spaceShip.setTranslateY(getHeight() - spaceShip.getHeight());
            y = getHeight() - spaceShip.getHeight();
        }
    } 

    public void startGame()
    {
        //addSpaceship();
        asteroids = new Timeline(new KeyFrame(Duration.seconds(.5), e -> displayAsteroid()));
        asteroids.setCycleCount(Timeline.INDEFINITE);
        asteroids.play();
    }

    public void displayAsteroid()
    {
        Asteroid asteroid = new Asteroid();
        getChildren().add(asteroid); 

        Random rand = new Random();
        int randomNum = rand.nextInt((int) (getWidth() - asteroid.getWidth()));
        asteroid.setY(-200);
        asteroid.setX(randomNum);
        asteroid.setTranslateY(asteroid.getY());
        asteroid.setTranslateX(asteroid.getX());

        Timeline timeline = new Timeline();
        KeyFrame keyFrame = new KeyFrame(Duration.millis(50), event -> {
            if(asteroid.getY() > getHeight()|| asteroid.getBoundsInParent().intersects(spaceShip.getBoundsInParent()))
            {
                timeline.stop();
                getChildren().remove(asteroid);
            }
            else
            {
                asteroid.setY(asteroid.getY()+5);
                asteroid.setTranslateY(asteroid.getY());
            }
        });
        timeline.setCycleCount(Animation.INDEFINITE);
        timeline.getKeyFrames().add(keyFrame);
        timeline.play();
    }
}

asteroid class:

package javafxapplication6;

import javafx.geometry.Pos;
import java.util.Random;
import java.util.Vector;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;

public class Asteroid extends StackPane
{
    private Ellipse asteroid = new Ellipse(); 
    private Ellipse hole1 = new Ellipse();
    private Ellipse hole2 = new Ellipse();
    private Ellipse hole3 = new Ellipse();
    private int speed;
    private double y = 0;
    private double x = 0;

    Asteroid()
    {       
        Random rand = new Random();
        int asteroidNum = rand.nextInt(10);
        if(asteroidNum < 6)
        {
            asteroidNum = 0;
        }
        else if(asteroidNum < 8)
        {
            asteroidNum = 1;
        }
        else
        {
            asteroidNum = 2;
        }
        StackPane thisAsteroid = new StackPane();
        VBox vbox = new VBox();
        HBox hbox = new HBox();
        vbox.setAlignment(Pos.CENTER);
        hbox.setAlignment(Pos.CENTER);

        asteroid.setFill(Color.GREY);
        asteroid.setStroke(Color.DIMGREY);
        hole1.setFill(Color.DIMGREY);
        hole1.setStroke(Color.BLACK);
        hole2.setFill(Color.DIMGREY);
        hole2.setStroke(Color.BLACK);
        hole3.setFill(Color.DIMGREY);
        hole3.setStroke(Color.BLACK);

        switch(asteroidNum)
        {
            case 0: asteroid1();
                    vbox.setSpacing(10);
                    hbox.setSpacing(10);
            break;
            case 1: asteroid2();
                    vbox.setSpacing(15);
                    hbox.setSpacing(15);
            break;
            case 2: asteroid3();
                    vbox.setSpacing(25);
                    hbox.setSpacing(25);
            break;
        }

        int holeLayout = rand.nextInt(3);
        switch(holeLayout)
        {
        case 0: hbox.getChildren().addAll(hole1, hole2);
                vbox.getChildren().addAll(hole3, hbox);
                thisAsteroid.getChildren().addAll(asteroid, vbox);
                break;
        case 1: vbox.getChildren().addAll(hole2, hole3);
                hbox.getChildren().addAll(vbox, hole1);
                thisAsteroid.getChildren().addAll(asteroid, hbox);

                break;
        case 2: vbox.getChildren().addAll(hole1, hole3);
                hbox.getChildren().addAll(hole2, vbox);
                thisAsteroid.getChildren().addAll(asteroid, hbox);
                break;
        case 3: hbox.getChildren().addAll(hole1, hole2);
                vbox.getChildren().addAll(hbox, hole3);
                thisAsteroid.getChildren().addAll(asteroid, vbox);
                break;

        }

        this.getChildren().add(thisAsteroid);
    }

    public void asteroid1()
    {
        speed = 10;
        asteroid.setRadiusX(40);
        asteroid.setRadiusY(35);
        asteroid.setStrokeWidth(3);
        hole1.setRadiusX(7);
        hole1.setRadiusY(8);
        hole2.setRadiusX(9);
        hole2.setRadiusY(6);
        hole3.setRadiusX(6);
        hole3.setRadiusY(5);
    }

    public void asteroid2()
    {
        speed = 6;
        asteroid.setRadiusX(60);
        asteroid.setRadiusY(50);
        asteroid.setStrokeWidth(5);
        hole1.setRadiusX(10);
        hole1.setRadiusY(12);
        hole2.setRadiusX(12);
        hole2.setRadiusY(10);
        hole3.setRadiusX(14);
        hole3.setRadiusY(13);
    }

    public void asteroid3()
    {
        speed = 4;
        asteroid.setRadiusX(100);
        asteroid.setRadiusY(90);
        asteroid.setStrokeWidth(8);
        hole1.setRadiusX(20);
        hole1.setRadiusY(18);
        hole2.setRadiusX(18);
        hole2.setRadiusY(22);
        hole3.setRadiusX(23);
        hole3.setRadiusY(19);
    }

    public void setY(double nY)
    {
        y = nY;
    }

    public void setX(double nX)
    {
        x = nX;
    }

    public double getX()
    {
        return x;
    }

    public double getY()
    {
        return y;
    }

    public int getSpeed()
    {
        return speed;
    }
}

spaceship class:

package javafxapplication6;

import javafx.geometry.Pos;
import javafx.scene.layout.HBox;
import javafx.scene.layout.StackPane;
import javafx.scene.layout.VBox;
import javafx.scene.paint.Color;
import javafx.scene.shape.Ellipse;
import javafx.scene.shape.Rectangle;

public class Spaceship extends StackPane
{
    private Rectangle leftWingBottom = new Rectangle();
    private Rectangle leftWingGun = new Rectangle();
    private Rectangle leftWingDesign = new Rectangle();
    private Rectangle leftWingAttachment = new Rectangle();
    private Rectangle leftTransparent = new Rectangle();
    private Rectangle rightWingBottom = new Rectangle();
    private Rectangle rightWingGun = new Rectangle();
    private Rectangle rightWingDesign = new Rectangle();
    private Rectangle rightWingAttachment = new Rectangle();
    private Rectangle rightTransparent = new Rectangle();
    private Rectangle centerCompartmentBottom = new Rectangle();
    private Rectangle centerCompartmentDesign = new Rectangle();
    private Ellipse centerCompartmentTop = new Ellipse();

    Spaceship()
    {
        HBox ship = new HBox();
        VBox leftWing = new VBox();
        VBox rightWing = new VBox();
        VBox leftAttachment = new VBox();
        VBox rightAttachment = new VBox();

        StackPane leftWingStack = new StackPane();
        StackPane rightWingStack = new StackPane();
        StackPane centerCompartmentShip = new StackPane();

        leftTransparent.setFill(Color.TRANSPARENT);
        rightTransparent.setFill(Color.TRANSPARENT);

        // creates the right wing
        rightWingBottom.setHeight(30);
        rightWingBottom.setWidth(8);
        rightWingBottom.setStrokeWidth(1);
        rightWingDesign.setHeight(25);
        rightWingDesign.setWidth(3);
        rightWingStack.setAlignment(Pos.CENTER);
        rightWingStack.getChildren().addAll(rightWingBottom, rightWingDesign);
        rightWingGun.setHeight(5);
        rightWingGun.setWidth(2);
        rightWing.setAlignment(Pos.TOP_CENTER);
        rightWing.getChildren().addAll(rightWingStack, rightWingGun);

        // creates the left wing and gun
        leftWingBottom.setHeight(30);
        leftWingBottom.setWidth(8);
        leftWingBottom.setStrokeWidth(1);
        leftWingDesign.setHeight(25);
        leftWingDesign.setWidth(3);
        leftWingStack.setAlignment(Pos.CENTER);
        leftWingStack.getChildren().addAll(leftWingBottom, leftWingDesign);
        leftWingGun.setHeight(5);
        leftWingGun.setWidth(2);
        leftWing.setAlignment(Pos.TOP_CENTER);
        leftWing.getChildren().addAll(leftWingStack, leftWingGun);

        // attaches the cockpit and right wing together
        rightTransparent.setHeight(5);
        rightTransparent.setWidth(5);
        rightWingAttachment.setHeight(3);
        rightWingAttachment.setWidth(5);
        rightAttachment.setAlignment(Pos.TOP_CENTER);
        rightAttachment.getChildren().addAll(rightTransparent, rightWingAttachment);

        // attaches the cockpit and left wing together
        leftTransparent.setHeight(5);
        leftTransparent.setWidth(5);
        leftWingAttachment.setHeight(3);
        leftWingAttachment.setWidth(5);
        leftAttachment.setAlignment(Pos.TOP_CENTER);
        leftAttachment.getChildren().addAll(leftTransparent, leftWingAttachment);

        // creates the cockpit
        centerCompartmentBottom.setHeight(25);
        centerCompartmentBottom.setWidth(20);
        centerCompartmentBottom.setStrokeWidth(2);
        centerCompartmentTop.setRadiusX(10);
        centerCompartmentTop.setRadiusY(25);
        centerCompartmentTop.setStrokeWidth(2);
        centerCompartmentDesign.setHeight(25);
        centerCompartmentDesign.setWidth(5);
        centerCompartmentShip.setAlignment(Pos.TOP_CENTER);
        centerCompartmentShip.getChildren().addAll(centerCompartmentTop, centerCompartmentBottom, centerCompartmentDesign);

        rightWingBottom.setFill(Color.WHITE);
        rightWingBottom.setStroke(Color.GREY);
        leftWingBottom.setFill(Color.WHITE);
        leftWingBottom.setStroke(Color.GREY);
        rightWingGun.setFill(Color.WHITE);
        leftWingGun.setFill(Color.WHITE);
        rightWingDesign.setFill(Color.TRANSPARENT);
        leftWingDesign.setFill(Color.TRANSPARENT);
        rightWingAttachment.setFill(Color.WHITE);
        rightWingAttachment.setStroke(Color.GREY);
        leftWingAttachment.setFill(Color.WHITE);
        leftWingAttachment.setStroke(Color.GREY);
        centerCompartmentBottom.setFill(Color.WHITE);
        centerCompartmentBottom.setStroke(Color.GREY);
        centerCompartmentTop.setFill(Color.WHITE);
        centerCompartmentTop.setStroke(Color.GREY);
        centerCompartmentDesign.setFill(Color.TRANSPARENT);

        // adds everything to final hbox
        ship.getChildren().addAll(rightWing,
                                  rightAttachment,
                                  centerCompartmentShip,
                                  leftAttachment,
                                  leftWing);

        // adds it to the SpaceShip object
        this.getChildren().add(ship);
    }
}

Hope this helps, thanks!

  • Can you please share a bit more of your code? – Samuel Philipp Mar 12 '19 at 23:06
  • @SamuelP. Of course, I added the displayAsteroid function. If you want me to add anything else or specify anything, please do let me know! – Oscar Lopez Mar 12 '19 at 23:13
  • Can you please share [a Minimal, Complete, and Verifiable example](https://stackoverflow.com/help/mcve) so we can reproduce your issue easily? – Samuel Philipp Mar 12 '19 at 23:18
  • @SamuelP. I added the 4 files needed to fully make a simple program. When launched, simply press start and move using up down left and right! I kept the asteroid and spaceship classes exactly as is for my full game, because in the end thats what we are testing. But all the extra options and stuff like that have been taken out. Hope this helps, and thank you for helping me! – Oscar Lopez Mar 13 '19 at 00:29
  • 1
    Possible duplicate of [How do I add an image inside a rectangle or a circle in JavaFX?](https://stackoverflow.com/questions/22848829/how-do-i-add-an-image-inside-a-rectangle-or-a-circle-in-javafx) – Slaw Mar 13 '19 at 00:38
  • @Slaw I saw that not too long ago, but unfortunately it says you can do it by adding it to a stackpane, which in the end defeats the purpose of adding the image in the first place (the hitbox is a square once again rather than the shape) – Oscar Lopez Mar 13 '19 at 00:42
  • 2
    Look at the [accepted answer](https://stackoverflow.com/a/30879054/6395627) and its use of [`ImagePattern`](https://openjfx.io/javadoc/11/javafx.graphics/javafx/scene/paint/ImagePattern.html). Also take a look at [Checking Collision of Shapes with JavaFX](https://stackoverflow.com/questions/15013913/checking-collision-of-shapes-with-javafx). – Slaw Mar 13 '19 at 00:53

0 Answers0