Skip to content

Instantly share code, notes, and snippets.

@Da9el00
Created April 23, 2021 08:13
Show Gist options
  • Save Da9el00/955e6d9703cc9363d22ba3b51b62cd9e to your computer and use it in GitHub Desktop.
Save Da9el00/955e6d9703cc9363d22ba3b51b62cd9e to your computer and use it in GitHub Desktop.
Snake game with JavaFX
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;
}
}
package sample;
public enum Direction {
UP, DOWN, RIGHT, LEFT
}
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));
}
}
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);
}
}
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 +
'}';
}
}
<?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