Why Doesn't My Timer Delay My Code? I am attemping to delay my graph from sorting to create my iteration of a sorting visualizer - swing

I am trying to create a timer that will delay my GUI from sorting the bars representing random integers. This will make the sorting visible and in a style similar to those seen on YouTube. I read online that Swing Timers are recommended for GUI's so I went with one. If you can't already tell, this is my second project so if my code looks bad, please refrain from sending me death threats.
Anyways, when running my code and pressing the button that sorts the graph, the graph is immediately sorted despite having a timer programmed to delay frame.repaint() calls each time. Again, please go easy on me. This is my second time asking questions on StackOverflow, and I've seen vicious lashes at people who forgot a semicolon. Let me know if I programmed my Swing timer wrong, or if it's something else entirely
Thank you
Main Program
import java.util.Random;
import java.util.Scanner;
public class SortVisualizerShell {
public static int[] unsortedArray = arrayGenerator();
public static String requestedSort;
public static int[] arrayGenerator() {
int $N = 500;
int[] array = new int[$N];
Random rand = new Random();
for (int i = 0; i < $N; i++) {
int random_num = rand.nextInt($N);
array[i] = random_num;
}
return array;
}
public static void selectionSort(int[] array, int n) {
for (int i = 0; i < (n - 1); i++) {
int min = i;
for (int j = (i + 1); j < n; j++) {
if (array[min] > array[j]) {
min = j;
}
}
int key = array[min];
while (min > i) {
array[min] = array[min - 1];
min = min - 1;
}
array[i] = key;
}
}
public static void bubbleSort(int[] array, int n) {
boolean swapped;
int i, j, temp;
for (i = 0; i < (n - 1); i++) {
swapped = false;
for (j = 0; j < ((n - 1) - 1); j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
swapped = true;
}
}
if (swapped == false) {
break;
}
}
}
public static void insertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i++) {
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j = j - 1;
}
array[j + 1] = key;
}
}
public static void quickSort(int[] array, int left, int right) {
if (left < right) {
int pivot = partition(array, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot + 1, right);
}
}
public static int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (array[j] < pivot) {
i++;
swap(array, i, j);
}
}
swap(array, i + 1, high);
return (i + 1);
}
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void mergeSort(int[] array, int len) {
if (len < 2) {
return;
}
int middle = len / 2;
int[] left_arr = new int[middle];
int[] right_arr = new int[len - middle];
int k = 0;
for (int i = 0; i < len; i++) {
if (i < middle) {
left_arr[i] = array[i];
}
else {
right_arr[k] = array[i];
k = k + 1;
}
}
mergeSort(left_arr, middle);
mergeSort(right_arr, len - middle);
merge(left_arr, right_arr, array, middle, (len - middle));
}
public static void merge(int[] left_arr, int[] right_arr, int[] array, int left_side,
int right_side) {
int i = 0;
int left = 0;
int right = 0;
while (left < left_side && right < right_side) {
if (left_arr[left] < right_arr[right]) {
array[i++] = left_arr[left++];
}
else {
array[i++] = right_arr[right++];
}
}
while (left < left_side) {
array[i++] = left_arr[left++];
}
while (right < right_side) {
array[i++] = right_arr[right++];
}
}
public static void userInputFrame(String requestedSort, int[] array) {
if (requestedSort.equals("merge sort")) {
mergeSort(array, array.length);
}
else if (requestedSort.equals("quick sort")) {
quickSort(array, 0, array.length - 1);
}
else if (requestedSort.equals("insertion sort")) {
insertionSort(array);
}
else if (requestedSort.equals("bubble sort")) {
bubbleSort(array, array.length);
}
else if (requestedSort.equals("selection sort")) {
selectionSort(array, array.length);
}
}
public static void activateSort(String requestedSort, int[] unsortedArray) {
userInputFrame(requestedSort, unsortedArray);
}
public static int[] unsort(int[] sortedArray) {
int[] unsortedArray = arrayGenerator();
return unsortedArray;
}
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
System.out.println("Welcome to my sort visualizer");
System.out.println("");
System.out.println("Which sort would you like to see in action? ");
requestedSort = myScanner.nextLine();
requestedSort = requestedSort.toLowerCase();
Drawer.createGUI();
myScanner.close();
}
}
GUI Program
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Drawer extends JPanel {
public static SortVisualizerShell sort;
public static int[] y = SortVisualizerShell.unsortedArray;
public static String requestedSort;
public static Timer timer;
public static int delay = 1000;
public static void createGUI() {
JFrame frame = new JFrame();
JButton sortButton = new JButton("Sort");
JButton unsortButton = new JButton("Randomize Array");
JPanel panel = new JPanel();
Drawer draw = new Drawer();
requestedSort = SortVisualizerShell.requestedSort;
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel, BoxLayout.PAGE_AXIS));
panel.add(Box.createRigidArea(new Dimension(0, 5)));
panel.setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
panel.add(draw);
panel.add(sortButton);
panel.add(unsortButton);
frame.add(panel);
frame.getContentPane().setBackground(Color.BLACK);
frame.setVisible(true);
frame.pack();
frame.setSize(550, 650);
sortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
SortVisualizerShell.activateSort(requestedSort, y);
frame.repaint();
}
});
unsortButton.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
y = SortVisualizerShell.unsort(y);
frame.repaint();
}
});
Timer timer = new Timer(1000, sortButton.getAction());
timer.setInitialDelay(1000);
timer.setDelay(1000);
timer.setRepeats(true);
timer.start();
}
public void paintComponent(Graphics g) {
for (int i = 0; i < y.length; i++) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(i, 0, 5, y[i]);
g2d.setColor(Color.WHITE);
g2d.fillRect(i, 0, 5, y[i]);
}
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable() {
public void run() {
createGUI();
}
});
}
}

First, some points to note regarding the code in your question.
You shouldn't mix console application with GUI application.
I would add a JComboBox for choosing the desired sort method.
In the below line of your code, sortButton.getAction() returns null.
Timer timer = new Timer(1000, sortButton.getAction());
Hence your timer essentially does nothing.
Usually the first line of an overridden paintComponent method should be
super.paintComponent(g);
Refer to Performing Custom Painting lesson of Creating a GUI With Swing trail in Oracle's Java tutorials.
Now I believe that you want to perform a repaint after each step of the chosen sort algorithm in order to manifest the visualization of the sort algorithm. In the below code, each sort algorithm is a separate class and each class implements a sortStep method which is declared in interface IntsSort.
I defined an enum
for the different sorting algorithms.
After clicking on the Sort button, a [Swing] timer is launched that invokes the sortStep method of the selected sorting algorithm every second. The timer is stopped when the sortStep method returns true indicating that the sort has completed.
As an added visual aid, I set the cursor to a wait cursor while the sort is
executing and display a JOptionPane informing the user that the sort has terminated.
Note that the below code is missing the classes that implement merge sort and
quick sort. Hopefully you will be able to write those yourself based on the
below code.
The IntsSort interface:
/**
* Sorts array of {#code int}.
*/
public interface IntsSort {
/**
* Performs a single, sorting step.
*
* #return <tt>true</tt> if there are no more sorting steps required indicating that the sort
* has completed.
*/
public boolean sortStep();
}
The bubble sort implementation of IntsSort:
public class BublSort implements IntsSort {
private int[] array;
private int i;
public BublSort(int[] arr) {
array = arr;
i = -1;
}
#Override
public boolean sortStep() {
boolean swapped = false;
i++;
if (i < array.length - 1) {
int temp;
for (int j = 0; j < ((array.length - 1) - 1); j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j + 1];
array[j + 1] = temp;
swapped = true;
}
}
}
return !swapped;
}
}
The insertion sort implementation of IntsSort:
public class InsrtSrt implements IntsSort {
private int[] array;
private int i;
public InsrtSrt(int[] arr) {
array = arr;
i = -1;
}
public boolean sortStep() {
boolean stopSorting;
i++;
int n = array.length;
if (i < n) {
stopSorting = false;
int key = array[i];
int j = i - 1;
while (j >= 0 && array[j] > key) {
array[j + 1] = array[j];
j = j - 1;
}
array[j + 1] = key;
}
else {
stopSorting = true;
}
return stopSorting;
}
}
The selection sort implementation of interface IntsSort:
public class SlctnSrt implements IntsSort {
private int[] array;
private int i;
public SlctnSrt(int[] arr) {
array = arr;
i = -1;
}
#Override
public boolean sortStep() {
boolean stopSorting;
i++;
if (i < array.length - 1) {
stopSorting = false;
int min = i;
for (int j = (i + 1); j < array.length; j++) {
if (array[min] > array[j]) {
min = j;
}
}
int key = array[min];
while (min > i) {
array[min] = array[min - 1];
min = min - 1;
}
array[i] = key;
}
else {
stopSorting = true;
}
return stopSorting;
}
}
The Swing GUI application:
Note that there is no ActionListener for randomizeButton since I am under the impression that you already know how to do that part.
Also, the ActionListener for sortButton uses a method reference.
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Cursor;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JComboBox;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;
import javax.swing.Timer;
public class SortDraw extends JPanel implements Runnable {
private JComboBox<SortExec.SortMethod> sortAlgorithmsCombo;
private JFrame frame;
private Timer timer;
public SortDraw() {
setBorder(BorderFactory.createEmptyBorder(10, 10, 10, 10));
setBackground(Color.darkGray);
setOpaque(false);
}
#Override
public void run() {
createAndDisplayGui();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
int[] y = SortExec.getArray();
if (y != null) {
for (int i = 0; i < y.length; i++) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(i, 0, 5, y[i]);
g2d.setColor(Color.WHITE);
g2d.fillRect(i, 0, 5, y[i]);
}
}
}
private void createAndDisplayGui() {
frame = new JFrame("Algos");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(createTopPanel(), BorderLayout.PAGE_START);
frame.add(this, BorderLayout.CENTER);
frame.add(createButtonsPanel(), BorderLayout.PAGE_END);
frame.setSize(550, 650);
frame.setLocationByPlatform(true);
frame.setVisible(true);
}
private JPanel createButtonsPanel() {
JPanel buttonsPanel = new JPanel();
JButton sortButton = new JButton("Sort");
sortButton.setMnemonic(KeyEvent.VK_S);
sortButton.addActionListener(this::launchSort);
buttonsPanel.add(sortButton);
JButton randomizeButton = new JButton("Randomize");
buttonsPanel.add(randomizeButton);
return buttonsPanel;
}
private JPanel createTopPanel() {
JPanel topPanel = new JPanel();
JLabel label = new JLabel("Sort Algorithm");
topPanel.add(label);
sortAlgorithmsCombo = new JComboBox<>(SortExec.SortMethod.values());
topPanel.add(sortAlgorithmsCombo);
return topPanel;
}
private void launchSort(ActionEvent event) {
SortExec.SortMethod requestedSort = (SortExec.SortMethod) sortAlgorithmsCombo.getSelectedItem();
setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
try {
SortExec.initSort(requestedSort);
}
catch (RuntimeException xRuntime) {
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
throw xRuntime;
}
timer = new Timer(1000, this::performSort);
timer.setInitialDelay(0);
timer.start();
}
private void performSort(ActionEvent event) {
boolean stopSorting = SortExec.performSort();
repaint();
if (stopSorting) {
timer.stop();
setCursor(Cursor.getPredefinedCursor(Cursor.DEFAULT_CURSOR));
JOptionPane.showMessageDialog(frame,
"Sorting completed.",
"Complete",
JOptionPane.INFORMATION_MESSAGE);
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new SortDraw());
}
}
And finally, the SortExec utility class (including the sort algorithms enum):
import java.util.Random;
public class SortExec {
public enum SortMethod {
BUBBLE, INSERTION, MERGE, QUICK, SELECTION;
#Override
public String toString() {
String str;
switch (this) {
case BUBBLE:
str = "bubble sort";
break;
case INSERTION:
str = "insertion sort";
break;
case MERGE:
str = "merge sort";
break;
case QUICK:
str = "quick sort";
break;
case SELECTION:
str = "selection sort";
break;
default:
str = "unknown: " + this.ordinal();
}
return str;
}
}
private static int[] arr;
private static IntsSort sorter;
public static int[] getArray() {
return arr;
}
public static void initSort(SortMethod sortMethod) {
arr = arrayGenerator();
sorter = null;
switch (sortMethod) {
case BUBBLE:
sorter = new BublSort(arr);
break;
case INSERTION:
sorter = new InsrtSrt(arr);
break;
case SELECTION:
sorter = new SlctnSrt(arr);
break;
default:
throw new RuntimeException(sortMethod.toString());
}
}
public static boolean performSort() {
return sorter.sortStep();
}
private static int[] arrayGenerator() {
int $N = 500;
int[] array = new int[$N];
Random rand = new Random();
for (int i = 0; i < $N; i++) {
int random_num = rand.nextInt($N);
array[i] = random_num;
}
return array;
}
}

Related

Why is my GUI graphing a sorted array when running my main program, but graphs an unsorted array when running the program that creates the GUI?

I am currently working on a sorting algorithm visualizer that displays a GUI of a graph that sorts itself. This graph represents an array of random integers. I have not added the sorting functionalities to the GUI. The GUI is created in another class. The main program calls it.
The problem begins when I run the main program. When running the program that creates the GUI, the graph displays the unsorted array that it is based on. When I run my main program, the GUI displays a sorted array. I don't want the sorted array to be displayed. What could be causing this?
Main program:
public class sortVisualizerShell{
public static int[] unsortedArray = arrayGenerator();
public static int[] arrayGenerator() {
int N = 500;
int[] array = new int[N];
Random rand = new Random();
for (int i = 0; i < N; i++) {
int random_num = rand.nextInt(N);
array[i] = random_num;
}
return array;
}
public static void selectionSort(int[] array, int n) {
for (int i = 0; i < (n-1); i++) {
int min = i;
for (int j = (i+1); j < n; j++) {
if (array[min] > array[j]) {
min = j;
}
}
int key = array[min];
while(min > i) {
array[min] = array[min-1];
min = min - 1;
}
array[i] = key;
}
}
public static void bubbleSort(int[] array, int n) {
boolean swapped;
int i,j,temp;
for (i = 0; i < (n-1); i++) {
swapped = false;
for (j = 0; j < ((n-1) - 1); j++) {
if (array[j] > array[j + 1]) {
temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
swapped = true;
}
}
if (swapped == false) {
break;
}
}
}
public static void insertionSort(int[] array) {
int n = array.length;
for (int i = 1; i < n; i++) {
int key = array[i];
int j = i -1;
while (j >= 0 && array[j] > key) {
array[j+1] = array[j];
j = j - 1;
}
array[j+1] = key;
}
}
public static void quickSort(int[] array, int left, int right) {
if (left < right) {
int pivot = partition(array, left, right);
quickSort(array, left, pivot - 1);
quickSort(array, pivot + 1, right);
}
}
public static int partition(int[] array, int low, int high) {
int pivot = array[high];
int i = (low - 1);
for (int j = low; j <= high - 1; j++) {
if (array[j] < pivot) {
i++;
swap(array, i ,j);
}
}
swap(array, i + 1, high);
return (i + 1);
}
public static void swap(int[] array, int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
public static void mergeSort(int[] array, int len) {
if (len < 2) {
return;
}
int middle = len / 2;
int[] left_arr = new int[middle];
int[] right_arr = new int[len-middle];
int k = 0;
for (int i = 0; i<len; i++) {
if (i < middle) {
left_arr[i] = array[i];
}
else {
right_arr[k] = array[i];
k = k + 1;
}
}
mergeSort(left_arr, middle);
mergeSort(right_arr, len-middle);
merge(left_arr, right_arr, array, middle, (len-middle));
}
public static void merge(int[] left_arr, int[] right_arr, int[] array, int left_side, int right_side){
int i = 0;
int left = 0;
int right = 0;
while (left < left_side && right < right_side) {
if (left_arr[left] < right_arr[right]) {
array[i++] = left_arr[left++];
}
else {
array[i++] = right_arr[right++];
}
}
while (left < left_side) {
array[i++] = left_arr[left++];
}
while(right < right_side) {
array[i++] = right_arr[right++];
}
}
public static void userInputFrame(String requestedSort, int[] array) {
if (requestedSort.equals("merge sort")) {
mergeSort(array, array.length);
}
else if (requestedSort.equals("quick sort")) {
quickSort(array, 0, array.length - 1);
}
else if (requestedSort.equals("insertion sort")) {
insertionSort(array);
}
else if (requestedSort.equals("bubble sort")) {
bubbleSort(array, array.length);
}
else if (requestedSort.equals("selection sort")) {
selectionSort(array, array.length);
}
}
public static void main(String[] args) {
Scanner myScanner = new Scanner(System.in);
System.out.println("Welcome to my sort visualizer");
System.out.println("");
System.out.println("Which sort would you like to see in action? ");
String requestedSort = myScanner.nextLine();
requestedSort = requestedSort.toLowerCase();
Drawer draw = new Drawer();
draw.createGUI();
myScanner.close();
userInputFrame(requestedSort,unsortedArray);
}
}
GUI Program:
import java.awt.BorderLayout;
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Container;
import java.awt.Dimension;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import javax.swing.BorderFactory;
import javax.swing.Box;
import javax.swing.BoxLayout;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.JScrollPane;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class Drawer extends JPanel{
public int[] y = sortVisualizerShell.unsortedArray;
public static void createGUI() {
JFrame frame = new JFrame();
JButton button = new JButton();
JPanel panel = new JPanel();
Drawer draw = new Drawer();
panel.setOpaque(false);
panel.setLayout(new BoxLayout(panel,BoxLayout.PAGE_AXIS));
panel.add(Box.createRigidArea(new Dimension(0,5)));
panel.setBorder(BorderFactory.createEmptyBorder(10,10,10,10));
panel.add(button);
panel.add(draw);
frame.add(panel);
frame.getContentPane().setBackground(Color.BLACK);
frame.setVisible(true);
frame.pack();
frame.setSize(550,600);
}
public void paintComponent(Graphics g) {
for (int i = 0; i < y.length;i++) {
Graphics2D g2d = (Graphics2D) g;
g2d.drawRect(i,0, 5, y[i]);
g2d.setColor(Color.WHITE);
};
}
public static void main(String[] args) {
javax.swing.SwingUtilities.invokeLater(new Runnable()
{
public void run()
{
createGUI();
}
});
}
}

"Cannot resolve symbol" in some of the javax.swing.Timer methods in IntelliJ IDEA

I have been searching for a couple hours now and can't seem to find an answer on this.
I import javax.swing.* so that i can use Timer in my program, but while Timer is imported and seem to be functioning, other methods that Timer has cannot be resolved by intellij and I get the following Errors :
Errors picture
enter image description here
Here is my code:
`
import java.util.Random;
import javax.swing.*;
import java.awt.event.ActionEvent;
import java.awt.*;
public class Board
{
Random rn = new Random();
private SquareType[][] squares;
private int height;
private int width;
public Poly falling;
private int fallingX;
private int fallingY;
public Board(final int height, final int width) {
this.width = width;
this.height = height;
squares = new SquareType[height][width];
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
squares[i][j] = SquareType.EMPTY;
}
}
}
public int getHeight() {
return height;
}
public int getWidth() {
return width;
}
public SquareType whichSquareType(int height, int width) {
//Takes in two integers one for height and one for width and returns
// the SquareType of the particular cell
return squares[height][width];
}
public void randomizeBoard() {
SquareType[] myTypes = SquareType.values();
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
squares[i][j] = myTypes[rn.nextInt(myTypes.length)];
}
}
}
public int getFallingX() {
return fallingX;
}
public int getFallingY() {
return fallingY;
}
public Poly getFalling() {
return falling;
}
final Action doOneStep = new AbstractAction()
{
public void actionPerformed(ActionEvent e) {
}
};
final Timer clockTimer = new Timer(500, doOneStep);
clockTimer.setCoalesce(true);
clockTimer.start();
}
`
Note you are calling Timer class methods at Board class declaration level, not from within a Board class method. Those are illegal Java statements and the actual reason of the error messages. You should encapsulate those calls in a new Board class method -let's say initTimer()- and call this method when needed.
On the other hand clockTimer variable declaration and initialization are ok.

libgdx snake logic explained

This is the code from a book I am reading. It is just a snake game. you can paste this code into editor and change 3 texture files to what you have on computer. The code works fine and this is probably really stupid, but I am unable to make the connection between snakeBody parts and their position. How exactly does the body of the snake(not the head) knows where it should go after the head of the snake changes position? Can you elaborate on this? Thank you
// class 1/2:
public class MyGdxGame extends Game {
#Override
public void create () {
setScreen(new GameScreen());
}
}
class 2/2:
package com.mygdx.game;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.Input;
import com.badlogic.gdx.ScreenAdapter;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.graphics.g2d.Batch;
import com.badlogic.gdx.utils.Array;
public class GameScreen extends ScreenAdapter{
private static final float MOVE_TIME = 0.5F;
private static final int SNAKE_MOVEMENT = 32;
private static final int RIGHT = 0;
private static final int LEFT = 1;
private static final int UP = 2;
private static final int DOWN = 3;
private SpriteBatch batch;
private Texture snakeHead;
private Texture snakeBody;
private Texture apple;
private boolean appleAvailable = false;
private int appleX, appleY;
private float timer = MOVE_TIME;
private int snakeX = 0, snakeY = 0;
private int snakeXBeforeUpdate = 0, snakeYBeforeUpdate = 0;
private int snakeDirection = RIGHT;
private Array<BodyPart> bodyParts = new Array<BodyPart>();
#Override
public void show() {
super.show();
batch = new SpriteBatch();
snakeHead = new Texture(Gdx.files.internal("snakehead.png"));
snakeBody = new Texture(Gdx.files.internal("snakeBody.png"));
apple = new Texture(Gdx.files.internal("apple.png"));
}
#Override
public void render(float delta) {
super.render(delta);
queryInput();
timer -= delta;
if (timer <= 0) {
timer = MOVE_TIME;
moveSnake();
checkForOutOfBounds();
updateBodyPartsPosition();
}
checkAppleCollision();
checkAndPlaceApple();
clearScreen();
draw();
}
private void queryInput() {
boolean lPressed = Gdx.input.isKeyPressed(Input.Keys.LEFT);
boolean rPressed = Gdx.input.isKeyPressed(Input.Keys.RIGHT);
boolean uPressed = Gdx.input.isKeyPressed(Input.Keys.UP);
boolean dPressed = Gdx.input.isKeyPressed(Input.Keys.DOWN);
if (lPressed) snakeDirection = LEFT;
if (rPressed) snakeDirection = RIGHT;
if (uPressed) snakeDirection = UP;
if (dPressed) snakeDirection = DOWN;
}
private void moveSnake() {
snakeXBeforeUpdate = snakeX;
snakeYBeforeUpdate = snakeY;
switch (snakeDirection) {
case RIGHT: {
snakeX += SNAKE_MOVEMENT;
return;
}
case LEFT: {
snakeX -= SNAKE_MOVEMENT;
return;
}
case UP: {
snakeY += SNAKE_MOVEMENT;
return;
}
case DOWN: {
snakeY -= SNAKE_MOVEMENT;
return;
}
}
}
private void checkForOutOfBounds() {
if (snakeX >= Gdx.graphics.getWidth()) {
snakeX = 0;
}
if (snakeX < 0) {
snakeX = Gdx.graphics.getWidth() - SNAKE_MOVEMENT;
}
if (snakeY >= Gdx.graphics.getHeight()) {
snakeY = 0;
}
if (snakeY < 0) {
snakeY = Gdx.graphics.getHeight() - SNAKE_MOVEMENT;
}
}
private void updateBodyPartsPosition() {
if (bodyParts.size > 0) {
BodyPart bodyPart = bodyParts.removeIndex(0);
bodyPart.updateBodyPosition(snakeXBeforeUpdate, snakeYBeforeUpdate);
bodyParts.add(bodyPart);
}
}
private void checkAndPlaceApple() {
if (!appleAvailable) {
do {
appleX = MathUtils.random(Gdx.graphics.getWidth() / SNAKE_MOVEMENT - 1) * SNAKE_MOVEMENT;
appleY = MathUtils.random(Gdx.graphics.getHeight() / SNAKE_MOVEMENT - 1) * SNAKE_MOVEMENT;
appleAvailable = true;
} while (appleX == snakeX && appleY == snakeY);
}
}
private void checkAppleCollision() {
if (appleAvailable && appleX == snakeX && appleY == snakeY) {
BodyPart bodyPart = new BodyPart(snakeBody);
bodyPart.updateBodyPosition(snakeX, snakeY);
bodyParts.insert(0,bodyPart);
appleAvailable = false;
}
}
private void clearScreen() {
Gdx.gl.glClearColor(Color.BLACK.r, Color.BLACK.g, Color.BLACK.b, Color.BLACK.a);
Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
}
private void draw() {
batch.begin();
batch.draw(snakeHead, snakeX, snakeY);
for (BodyPart bodyPart : bodyParts) {
bodyPart.draw(batch);
}
if (appleAvailable) {
batch.draw(apple, appleX, appleY);
}
batch.end();
}
private class BodyPart {
private int x, y;
private Texture texture;
public BodyPart(Texture texture) {
this.texture = texture;
}
public void updateBodyPosition(int x, int y) {
this.x = x;
this.y = y;
}
public void draw(Batch batch) {
if (!(x == snakeX && y == snakeY)) batch.draw(texture, x, y);
}
}
}
First it moves the head in moveSnake(), making sure to keep track of where the head was before the move. Then in updateBodyPartsPosition() it puts the last piece of the body where the head was just moved from.

Libgdx collision using box2d working for desktop but failed to collide in Android emulator

My code for collision of a dynamic body to a static body works perfectly for desktop , but when running in Android emulator it can not able to detect collision ,Dynamic body goes down without collision.
I used Libgdx version : 1.5.0 .
Code :
package com.kg.game;
import java.util.Random;
import aurelienribon.tweenengine.BaseTween;
import aurelienribon.tweenengine.Tween;
import aurelienribon.tweenengine.TweenCallback;
import aurelienribon.tweenengine.TweenManager;
import com.badlogic.gdx.ApplicationAdapter;
import com.badlogic.gdx.Gdx;
import com.badlogic.gdx.InputAdapter;
import com.badlogic.gdx.InputProcessor;
import com.badlogic.gdx.graphics.Color;
import com.badlogic.gdx.graphics.GL20;
import com.badlogic.gdx.graphics.OrthographicCamera;
import com.badlogic.gdx.graphics.Texture;
import com.badlogic.gdx.graphics.Texture.TextureFilter;
import com.badlogic.gdx.graphics.g2d.BitmapFont;
import com.badlogic.gdx.graphics.g2d.Sprite;
import com.badlogic.gdx.graphics.g2d.SpriteBatch;
import com.badlogic.gdx.math.MathUtils;
import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;
import com.badlogic.gdx.physics.box2d.Box2DDebugRenderer;
import com.badlogic.gdx.physics.box2d.CircleShape;
import com.badlogic.gdx.physics.box2d.FixtureDef;
import com.badlogic.gdx.physics.box2d.PolygonShape;
import com.badlogic.gdx.physics.box2d.World;
public class MyGdxGame extends ApplicationAdapter implements InputProcessor {
private static final float VIEWPORT_WIDTH = 10;
private static final float BALL_RADIUS = 0.15f;
private static final int MAX_BALLS = 200;
private World world;
private Body[] ballModels;
private Texture bottleTexture;
private Texture ballTexture;
private Sprite[] ballSprites;
private Texture whiteTexture;
private Sprite groundSprite;
private SpriteBatch batch;
private BitmapFont font;
private OrthographicCamera camera;
private final Random rand = new Random();
float w;
float h;
Box2DDebugRenderer debugRenderer;
private final TweenManager tweenManager = new TweenManager();
#Override
public void create() {
debugRenderer = new Box2DDebugRenderer();
world = new World(new Vector2(0, -10), true);
createGround();
createBalls();
batch = new SpriteBatch();
font = new BitmapFont();
font.setColor(Color.BLACK);
w = Gdx.graphics.getWidth();
h = Gdx.graphics.getHeight();
camera = new OrthographicCamera(VIEWPORT_WIDTH, VIEWPORT_WIDTH * h / w);
camera.position.set(0, (VIEWPORT_WIDTH * h / w) / 2, 0);
camera.update();
createSprites();
Gdx.input.setInputProcessor(new InputAdapter() {
#Override
public boolean touchDown(int x, int y, int pointer, int button) {
restart();
return true;
}
});
restart();
}
private void createGround() {
BodyDef bd = new BodyDef();
bd.position.set(0, 0);
bd.type = BodyType.StaticBody;
PolygonShape shape = new PolygonShape();
shape.setAsBox(100, 4);
FixtureDef fd = new FixtureDef();
fd.density = 1;
fd.friction = 0.5f;
fd.restitution = 0.5f;
fd.shape = shape;
world.createBody(bd).createFixture(fd);
shape.dispose();
}
private void createBalls() {
BodyDef ballBodyDef = new BodyDef();
ballBodyDef.type = BodyType.DynamicBody;
CircleShape shape = new CircleShape();
shape.setRadius(BALL_RADIUS);
FixtureDef fd = new FixtureDef();
fd.density = 1;
fd.friction = 0.5f;
fd.restitution = 0.5f;
fd.shape = shape;
ballModels = new Body[MAX_BALLS];
for (int i = 0; i < MAX_BALLS; i++) {
ballModels[i] = world.createBody(ballBodyDef);
ballModels[i].createFixture(fd);
}
shape.dispose();
}
private void createSprites() {
ballTexture = new Texture(Gdx.files.internal("ball.png"));
ballTexture.setFilter(TextureFilter.Linear, TextureFilter.Linear);
ballSprites = new Sprite[MAX_BALLS];
for (int i = 0; i < MAX_BALLS; i++) {
ballSprites[i] = new Sprite(ballTexture);
ballSprites[i].setSize(BALL_RADIUS * 2, BALL_RADIUS * 2);
ballSprites[i].setOrigin(BALL_RADIUS, BALL_RADIUS);
}
whiteTexture = new Texture(Gdx.files.internal("ground.png"));
groundSprite = new Sprite(whiteTexture);
groundSprite.setSize(100, 4);
groundSprite.setPosition(-VIEWPORT_WIDTH / 2, 0);
groundSprite.setColor(Color.BLACK);
}
private float elapsed = 0;
#Override
public void render() {
tweenManager.update(1 / 60f);
world.step(1 / 60f, 10, 10);
debugRenderer.render(world, camera.combined);
float w = Gdx.graphics.getWidth();
float h = Gdx.graphics.getHeight();
GL20 gl = Gdx.gl20;
gl.glClearColor(1, 1, 1, 1);
gl.glClear(GL20.GL_COLOR_BUFFER_BIT);
// Update
for (int i = 0; i < MAX_BALLS; i++) {
Vector2 ballPos = ballModels[i].getPosition();
ballSprites[i].setPosition(ballPos.x - ballSprites[i].getWidth()
/ 2, ballPos.y - ballSprites[i].getHeight() / 2);
ballSprites[i].setRotation(ballModels[i].getAngle()
* MathUtils.radiansToDegrees);
}
// Render
batch.setProjectionMatrix(camera.combined);
batch.begin();
groundSprite.draw(batch);
for (int i = 0; i < MAX_BALLS; i++)
ballSprites[i].draw(batch);
batch.end();
// batch.getProjectionMatrix().setToOrtho2D(0, 0, w, h);
batch.begin();
font.draw(batch, "Touch the screen to restart", 5, h - 5);
batch.end();
}
#Override
public void dispose() {
bottleTexture.dispose();
ballTexture.dispose();
batch.dispose();
font.dispose();
world.dispose();
}
#Override
public boolean keyDown(int keycode) {
return false;
}
#Override
public boolean keyUp(int keycode) {
return true;
}
#Override
public boolean keyTyped(char character) {
return false;
}
#Override
public boolean touchDown(int screenX, int screenY, int pointer, int button) {
return true;
}
#Override
public boolean touchUp(int screenX, int screenY, int pointer, int button) {
return false;
}
#Override
public boolean touchDragged(int screenX, int screenY, int pointer) {
return false;
}
#Override
public boolean mouseMoved(int screenX, int screenY) {
return false;
}
#Override
public boolean scrolled(int amount) {
return false;
}
private void restart() {
Vector2 vec = new Vector2();
for (int i = 0; i < MAX_BALLS; i++) {
float tx = rand.nextFloat() * 1.0f - 0.5f;
float ty = VIEWPORT_WIDTH * h / w;
float angle = rand.nextFloat() * MathUtils.PI * 2;
ballModels[i].setActive(false);
ballModels[i].setLinearVelocity(vec.set(0, 0));
ballModels[i].setAngularVelocity(0);
ballModels[i].setTransform(vec.set(tx, ty), angle);
}
tweenManager.killAll();
Tween.call(new TweenCallback() {
private int idx = 0;
#Override
public void onEvent(int type, BaseTween<?> source) {
if (idx < ballModels.length) {
ballModels[idx].setAwake(true);
ballModels[idx].setActive(true);
idx += 1;
}
}
}).repeat(-1, 0.1f).start(tweenManager);
}
}
Screensots :
this is an idea, looking at the proportions of your images, I think the error may be out there, I would look at these lines:
groundSprite.setSize(100, 4); //<--especially here
groundSprite.setPosition(-VIEWPORT_WIDTH / 2, 0); //<--especially here
.
private void createGround() {
BodyDef bd = new BodyDef();
bd.position.set(0, 0); //<--especially here
bd.type = BodyType.StaticBody;
PolygonShape shape = new PolygonShape();
shape.setAsBox(100, 4); //<--especially here
especially the position and size, if it is standing in the same place, with the change of screen "device".
You tried to render debug? I see out there, if so, you can see your object in the emulated device?
I hope I understand, but not if that be the error

How can I move balls in random directions and how can I keep them bouncing inside the frame?

Here is the code:
public class BallGame extends JPanel implements Runnable {
JPanel panel1 = new JPanel();
private int ballX = 10, ballY = 110, ...;
Thread aThread;
int toRight=5;
int toLeft= -5;
int upWard=5;
int downWard= -5;
int widthBall, heightBall;
public BallGame(){
game=true;
aThread=new Thread(this);
aThread.start();
}
public void paintComponent(Graphics g){
setOpaque(false);
super.paintComponent(g);
g.setColor(Color.RED);
g.fillOval(ballX, ballY, 7,7);
g.setColor(Color.BLUE);
g.fillOval(ballX + 15, ballY + 10, 7,7);
g.setColor(Color.GREEN);
g.fillOval(ballY - 10, ballY - 15, 7,7);
}
public void positionBall(int sx, int sy)
{
ballX = sx;
ballY = sy;
this.widthBall = this.getWidth();
this.heightBall = this.getHeight();
repaint();
}
public void run() {
boolean leftRight = false;
boolean upDown = false;
while(true){
if(game){
if (leftRight)
{
ballX += toRight;
if (ballX >= (widthBall - 5))
leftRight= false;
}
else
{
ballX += toLeft;
if ( ballX <= 0)
leftRight = true;
}
if (upDown)
{
ballY += upWard;
if (ballY >= (heightBall - 5))
upDown = false;
}
else
{
ballY += downWard;
if ( ballY <= 0)
upDown = true;
}
positionBall(ballX, ballY);
try
{
Thread.sleep(70);
}
catch(InterruptedException ex)
{
}
I don't know if the part where I drew the balls was right. The balls move in the same path. How can I move them in different directions and how can I limit them inside the frame? I need this for our case study immediately. Thank you for your time!
In order for the balls to move independently you need to treat them as 3 balls.
The reason why they always go the same direction, is that you use the same delta, just inverting the sign of delta x and delta y, thus you will always keep the same speed, and bounce at 90 degrees.
In the code below which is basically the same as you had, I keep the state of each ball in separate instances, change speed of delta x and delta y once a side is touched, and use the Swing Timer which is a better approach with respect to timing in Swing, as pointed out by Robin above.
I have updated the example, so that 4 balls start in the middle, and they move away from each other. This should give you enough information to adapt it to your requirements. The picture below is produced by only allowing 10 iterations, and setting
ballGame.setOpaque(true);
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import javax.swing.*;
public class BallGame extends JPanel {
private class Ball {
private int x;
private int y;
private int width;
private int height;
private Color color;
private boolean leftRight;
private boolean upDown;
private int deltaX;
private int deltaY;
Ball(Color color, int x, int y, int width, int height) {
this(color, x, y, width, height, false, false);
}
Ball(Color color, int x, int y, int width, int height, boolean leftRight, boolean upDown) {
this.color = color;
this.x = x;
this.y = y;
this.width = width;
this.height = height;
this.leftRight = leftRight;
this.upDown = upDown;
updateDelta();
}
private void updateDelta() {
final int minimumMovement = 5;
final int maxExtra = 10;
deltaY = minimumMovement + (int) (Math.random() * maxExtra);
deltaX = minimumMovement + (int) (Math.random() * maxExtra);
}
public void positionBall() {
if (leftRight) {
x += deltaX;
if (x >= (BallGame.this.getWidth() - width / 2)) {
leftRight = false;
updateDelta();
}
} else {
x += -deltaX;
if (x <= 0) {
leftRight = true;
updateDelta();
}
}
if (upDown) {
y += deltaY;
upDown = !(y >= (BallGame.this.getHeight() - height / 2));
if (y >= (BallGame.this.getHeight() - height / 2)) {
upDown = false;
updateDelta();
}
} else {
y += -deltaY;
if (y <= 0) {
upDown = true;
updateDelta();
}
}
}
public Color getColor() {
return color;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public int getWidth() {
return width;
}
public int getHeight() {
return height;
}
}
private ArrayList<Ball> balls = new ArrayList<>(3);
public BallGame() {
createBalls();
startGame();
}
private void startGame() {
int framesPerSecond = 30;
int timeToWait = 1000 / framesPerSecond;
Timer timer = new Timer(timeToWait, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
for (Ball ball : balls) {
ball.positionBall();
}
repaint();
}
});
timer.start();
}
private void createBalls() {
int startX = 400;
int startY = 200;
balls.add(new Ball(Color.green, startX, startY, 10, 10));
balls.add(new Ball(Color.blue, startX, startY, 15, 15, true, true));
balls.add(new Ball(Color.red, startX, startY, 20, 20, false, true));
balls.add(new Ball(Color.orange, startX, startY, 20, 20, true, false));
}
public void paintComponent(Graphics g) {
Graphics2D g2 = (Graphics2D) g.create();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
for (Ball ball : balls) {
g2.setColor(ball.getColor());
g2.fillOval(ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
g2.setColor(ball.getColor().darker());
g2.drawOval(ball.getX(), ball.getY(), ball.getWidth(), ball.getHeight());
}
g2.dispose();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame("Ball Game");
BallGame ballGame = new BallGame();
ballGame.setOpaque(false);
frame.getContentPane().add(ballGame);
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setMinimumSize(new Dimension(800, 450));
frame.setLocationRelativeTo(null); // Center
frame.pack();
frame.setVisible(true);
}
});
}
}