Created
April 23, 2021 08:13
-
-
Save Da9el00/955e6d9703cc9363d22ba3b51b62cd9e to your computer and use it in GitHub Desktop.
Snake game with JavaFX
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sample; | |
import javafx.animation.Animation; | |
import javafx.animation.KeyFrame; | |
import javafx.animation.Timeline; | |
import javafx.event.ActionEvent; | |
import javafx.fxml.FXML; | |
import javafx.fxml.Initializable; | |
import javafx.scene.control.Button; | |
import javafx.scene.input.KeyCode; | |
import javafx.scene.input.KeyEvent; | |
import javafx.scene.input.MouseEvent; | |
import javafx.scene.layout.AnchorPane; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.Rectangle; | |
import javafx.util.Duration; | |
import java.net.URL; | |
import java.util.ArrayList; | |
import java.util.List; | |
import java.util.ResourceBundle; | |
public class Controller implements Initializable { | |
//A snake body part is 50x50 | |
private final Double snakeSize = 50.; | |
//The head of the snake is created, at position (250,250) | |
private Rectangle snakeHead; | |
//First snake tail created behind the head of the snake | |
private Rectangle snakeTail_1; | |
//x and y position of the snake head different from starting position | |
double xPos; | |
double yPos; | |
//Food | |
Food food; | |
//Direction snake is moving at start | |
private Direction direction; | |
//List of all position of thew snake head | |
private final List<Position> positions = new ArrayList<>(); | |
//List of all snake body parts | |
private final ArrayList<Rectangle> snakeBody = new ArrayList<>(); | |
//Game ticks is how many times the snake have moved | |
private int gameTicks; | |
@FXML | |
private AnchorPane anchorPane; | |
@FXML | |
private Button startButton; | |
Timeline timeline; | |
private boolean canChangeDirection; | |
@FXML | |
void start(MouseEvent event) { | |
for (Rectangle snake : snakeBody) { | |
anchorPane.getChildren().remove(snake); | |
} | |
gameTicks = 0; | |
positions.clear(); | |
snakeBody.clear(); | |
snakeHead = new Rectangle(250, 250, snakeSize, snakeSize); | |
snakeTail_1 = new Rectangle(snakeHead.getX() - snakeSize, snakeHead.getY(), snakeSize, snakeSize); | |
xPos = snakeHead.getLayoutX(); | |
yPos = snakeHead.getLayoutY(); | |
direction = Direction.RIGHT; | |
canChangeDirection = true; | |
food.moveFood(); | |
snakeBody.add(snakeHead); | |
snakeHead.setFill(Color.RED); | |
timeline.setCycleCount(Animation.INDEFINITE); | |
timeline.play(); | |
snakeBody.add(snakeTail_1); | |
anchorPane.getChildren().addAll(snakeHead, snakeTail_1); | |
} | |
@Override | |
public void initialize(URL url, ResourceBundle resourceBundle) { | |
timeline = new Timeline(new KeyFrame(Duration.seconds(0.3), e -> { | |
positions.add(new Position(snakeHead.getX() + xPos, snakeHead.getY() + yPos)); | |
moveSnakeHead(snakeHead); | |
for (int i = 1; i < snakeBody.size(); i++) { | |
moveSnakeTail(snakeBody.get(i), i); | |
} | |
canChangeDirection = true; | |
//System.out.println((xPos + snakeHead.getX()) + "-----" + (yPos + snakeHead.getY())); | |
eatFood(); | |
gameTicks++; | |
if(checkIfGameIsOver(snakeHead)){ | |
timeline.stop(); | |
} | |
})); | |
food = new Food(-50,-50,anchorPane,snakeSize); | |
} | |
//Change position with key pressed | |
@FXML | |
void moveSquareKeyPressed(KeyEvent event) { | |
if(canChangeDirection){ | |
if (event.getCode().equals(KeyCode.W) && direction != Direction.DOWN) { | |
direction = Direction.UP; | |
} else if (event.getCode().equals(KeyCode.S) && direction != Direction.UP) { | |
direction = Direction.DOWN; | |
} else if (event.getCode().equals(KeyCode.A) && direction != Direction.RIGHT) { | |
direction = Direction.LEFT; | |
} else if (event.getCode().equals(KeyCode.D) && direction != Direction.LEFT) { | |
direction = Direction.RIGHT; | |
} | |
canChangeDirection = false; | |
} | |
} | |
//Create another snake body part | |
@FXML | |
void addBodyPart(ActionEvent event) { | |
addSnakeTail(); | |
} | |
//Snake head is moved in the direction specified | |
private void moveSnakeHead(Rectangle snakeHead) { | |
if (direction.equals(Direction.RIGHT)) { | |
xPos = xPos + snakeSize; | |
snakeHead.setTranslateX(xPos); | |
} else if (direction.equals(Direction.LEFT)) { | |
xPos = xPos - snakeSize; | |
snakeHead.setTranslateX(xPos); | |
} else if (direction.equals(Direction.UP)) { | |
yPos = yPos - snakeSize; | |
snakeHead.setTranslateY(yPos); | |
} else if (direction.equals(Direction.DOWN)) { | |
yPos = yPos + snakeSize; | |
snakeHead.setTranslateY(yPos); | |
} | |
} | |
//A specific tail is moved to the position of the head x game ticks after the head was there | |
private void moveSnakeTail(Rectangle snakeTail, int tailNumber) { | |
double yPos = positions.get(gameTicks - tailNumber + 1).getYPos() - snakeTail.getY(); | |
double xPos = positions.get(gameTicks - tailNumber + 1).getXPos() - snakeTail.getX(); | |
snakeTail.setTranslateX(xPos); | |
snakeTail.setTranslateY(yPos); | |
} | |
//New snake tail is created and added to the snake and the anchor pane | |
private void addSnakeTail() { | |
Rectangle rectangle = snakeBody.get(snakeBody.size() - 1); | |
Rectangle snakeTail = new Rectangle( | |
snakeBody.get(1).getX() + xPos + snakeSize, | |
snakeBody.get(1).getY() + yPos, | |
snakeSize, snakeSize); | |
snakeBody.add(snakeTail); | |
anchorPane.getChildren().add(snakeTail); | |
} | |
public boolean checkIfGameIsOver(Rectangle snakeHead) { | |
if (xPos > 300 || xPos < -250 ||yPos < -250 || yPos > 300) { | |
System.out.println("Game_over"); | |
return true; | |
} else if(snakeHitItSelf()){ | |
return true; | |
} | |
return false; | |
} | |
public boolean snakeHitItSelf(){ | |
int size = positions.size() - 1; | |
if(size > 2){ | |
for (int i = size - snakeBody.size(); i < size; i++) { | |
if(positions.get(size).getXPos() == (positions.get(i).getXPos()) | |
&& positions.get(size).getYPos() == (positions.get(i).getYPos())){ | |
System.out.println("Hit"); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
private void eatFood(){ | |
if(xPos + snakeHead.getX() == food.getPosition().getXPos() && yPos + snakeHead.getY() == food.getPosition().getYPos()){ | |
System.out.println("Eat food"); | |
foodCantSpawnInsideSnake(); | |
addSnakeTail(); | |
} | |
} | |
private void foodCantSpawnInsideSnake(){ | |
food.moveFood(); | |
while (isFoodInsideSnake()){ | |
food.moveFood(); | |
} | |
} | |
private boolean isFoodInsideSnake(){ | |
int size = positions.size(); | |
if(size > 2){ | |
for (int i = size - snakeBody.size(); i < size; i++) { | |
if(food.getPosition().getXPos() == (positions.get(i).getXPos()) | |
&& food.getPosition().getYPos() == (positions.get(i).getYPos())){ | |
System.out.println("Inside"); | |
return true; | |
} | |
} | |
} | |
return false; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sample; | |
public enum Direction { | |
UP, DOWN, RIGHT, LEFT | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sample; | |
import javafx.scene.layout.AnchorPane; | |
import javafx.scene.paint.Color; | |
import javafx.scene.shape.Rectangle; | |
import java.util.Random; | |
public class Food { | |
private Position position; | |
private Rectangle rectangle; | |
private Color color = Color.GREEN; | |
private AnchorPane pane; | |
private Random random = new Random(); | |
private int size; | |
public Food(double xPos, double yPos, AnchorPane pane, double size) { | |
this.pane = pane; | |
this.size = (int) size; | |
position = new Position(xPos,yPos); | |
rectangle = new Rectangle(position.getXPos(),position.getYPos(),size,size); | |
rectangle.setFill(color); | |
pane.getChildren().add(rectangle); | |
} | |
public Position getPosition() { | |
return position; | |
} | |
public void moveFood(){ | |
getRandomSpotForFood(); | |
} | |
public void getRandomSpotForFood(){ | |
int positionX = random.nextInt(12); | |
int positionY = random.nextInt(12); | |
rectangle.setX(positionX * size); | |
rectangle.setY(positionY * size); | |
position.setXPos(positionX * size); | |
position.setYPos(positionY * size); | |
System.out.println((positionX * size) + "---FOOD---" +(positionY * size)); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sample; | |
import javafx.application.Application; | |
import javafx.fxml.FXMLLoader; | |
import javafx.scene.Parent; | |
import javafx.scene.Scene; | |
import javafx.stage.Stage; | |
public class Main extends Application { | |
@Override | |
public void start(Stage primaryStage) throws Exception{ | |
Parent root = FXMLLoader.load(getClass().getResource("sample.fxml")); | |
primaryStage.setTitle("Hello World"); | |
primaryStage.setScene(new Scene(root)); | |
primaryStage.show(); | |
} | |
public static void main(String[] args) { | |
launch(args); | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package sample; | |
public class Position { | |
private double xPos; | |
private double yPos; | |
public Position(double xPos, double yPos) { | |
this.xPos = xPos; | |
this.yPos = yPos; | |
} | |
public double getXPos() { | |
return xPos; | |
} | |
public double getYPos() { | |
return yPos; | |
} | |
public void setXPos(double xPos) { | |
this.xPos = xPos; | |
} | |
public void setYPos(double yPos) { | |
this.yPos = yPos; | |
} | |
@Override | |
public String toString() { | |
return "Position{" + | |
"xPos=" + xPos + | |
", yPos=" + yPos + | |
'}'; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<?xml version="1.0" encoding="UTF-8"?> | |
<?import javafx.scene.control.Button?> | |
<?import javafx.scene.layout.AnchorPane?> | |
<AnchorPane fx:id="anchorPane" maxHeight="-Infinity" maxWidth="-Infinity" minHeight="-Infinity" minWidth="-Infinity" onKeyPressed="#moveSquareKeyPressed" prefHeight="600.0" prefWidth="600.0" style="-fx-background-color: #EAF4D3;" xmlns="http://javafx.com/javafx/15.0.1" xmlns:fx="http://javafx.com/fxml/1" fx:controller="sample.Controller"> | |
<children> | |
<Button fx:id="startButton" layoutX="32.0" layoutY="34.0" mnemonicParsing="false" onKeyPressed="#moveSquareKeyPressed" onMouseClicked="#start" text="Restart" /> | |
<Button layoutX="517.0" layoutY="34.0" mnemonicParsing="false" onAction="#addBodyPart" text="Add" /> | |
</children> | |
</AnchorPane> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment