I have a program that draws a square that I can drive around on a JPanel. I had this problem where whenever I would drive it, it would leave artifacts from where it had been on the screen. Kind of would look like JPanel Drawing Glitch when I would drive it around. Then I found a solution online that at the beginning of my paint(Graphics g) method, I should place a "super.paintComponent(g)".
That worked perfectly. But at the same time I kind of enjoyed have the opportunity to mess around with drawing with this car, so I added a piece of code that allowed me to choose whether or not I would be super.paintComponent(g)'ing. But when I transferred this code to another computer, it no longer had the original bug, not allowing me to paint if I wanted to. My question is: How can I replicate this bug, drawing all my previous positions behind me as I drive?
Here is my code.
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.util.ArrayList;
/**
* Created by chris on 1/5/16.
*/
public class Game extends JPanel{
int[] size = new int[2];
private Car car;
private JFrame frame;
private final double speedScalar = .5;
private final double angleScalar = 5;
boolean isTurningLeft = false;
boolean isTurningRight = false;
boolean isSpeedingUp = false;
boolean isSlowingDown = false;
private static Toolkit tk = Toolkit.getDefaultToolkit();
private ArrayList<Point> rocks = new ArrayList<>();
public static final int width = tk.getScreenSize().width;
public static final int height = tk.getScreenSize().height;
public boolean isDrawing = false;
public Color[] colorChoices = {Color.black,Color.blue,Color.red,Color.orange,Color.pink} ;
public Color chosenColor = Color.black;
public Game(){
frame = new JFrame();
frame.setPreferredSize(new Dimension(width,height));
car = new Car(300,300);
setPreferredSize(new Dimension(width,height));
frame.add(this);
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
System.out.println(e.getKeyChar());
switch(e.getKeyCode()){
case 37:
isTurningLeft = true;
isTurningRight = false;
break;
case 38:
isSpeedingUp = true;
isSlowingDown = false;
break;
case 39:
isTurningLeft = false;
isTurningRight = true;
break;
case 40:
isSpeedingUp = false;
isSlowingDown = true;
break;
}
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case 37:
isTurningLeft = true;
isTurningRight = false;
break;
case 40:
isSpeedingUp = true;
isSlowingDown = false;
break;
case 39:
isTurningLeft = false;
isTurningRight = true;
break;
case 38:
isSpeedingUp = false;
isSlowingDown = true;
break;
case 32:
isDrawing = !isDrawing;
}
if(e.getKeyChar()=='c'){
chosenColor = colorChoices[(int)(Math.random()*5)];
} else if (e.getKeyChar()==' '){
isDrawing=!isDrawing;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()){
case 37:
isTurningLeft = false;
break;
case 40:
isSpeedingUp = false;
break;
case 39:
isTurningRight = false;
break;
case 38:
isSlowingDown = false;
break;
}
}
});
Timer time = new Timer(1000/45, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if(isTurningRight){
car.setAngle(car.getAngle()+Math.toRadians(angleScalar));
} else if (isTurningLeft){
car.setAngle(car.getAngle()-Math.toRadians(angleScalar));
}
if(isSpeedingUp){
car.setSpeed(car.getSpeed()-speedScalar);
} else if (isSlowingDown){
car.setSpeed(car.getSpeed()+speedScalar);
}
if(car.getSpeed()<0){
car.setSpeed(0);
}
car.setX(car.getX() + (Math.cos(car.getAngle())*car.getSpeed()));
car.setY(car.getY() + (Math.sin(car.getAngle())*car.getSpeed()));
if(car.getX()<0){
car.setX(0);
car.setSpeed(0);
}
if(car.getX()>width){
car.setX(width);
car.setSpeed(0);
}
if(car.getY()<0){
car.setY(0);
car.setSpeed(0);
}
if(car.getY()>height){
car.setY(height);
car.setSpeed(0);
}
repaint();
}
});
double end = Math.random()*15;
frame.setVisible(true);
time.start();
}
public void paint(Graphics g){
if(!isDrawing) {
super.paintComponent(g);
}
/**
* Painting the car
*/
g.setColor(chosenColor);
double sin = Math.sin(car.getAngle());
double cos = Math.cos(car.getAngle());
int[] xPoints = {(int)car.getX(),(int)(car.getX()+cos*car.width),(int)(car.getX()-sin*car.length+cos*car.width),
(int)(car.getX()-sin*car.length)};
int[] yPoints = {(int)car.getY(),(int)(car.getY()+car.width*sin),(int)(car.getY()+cos*car.length+sin*car.width),
(int)(car.getY()+car.length*cos)};
Polygon car = new Polygon(xPoints,yPoints,4);
int[] recX = {400,450,450,400};
int[] recY = {400,400,450,450};
Rectangle2D test = new Rectangle(400,400,100,100);
((Graphics2D)g).fill(car);
((Graphics2D)g).fill(test);
if(car.intersects(test)){
chosenColor = Color.red;
}
tk.sync();
}
/**
* Created by chris on 1/5/16.
*/
public class Main {
public static void main(String[] args) {
Game g = new Game();
}
}
/**
* Created by chris on 1/5/16.
* Car class for my driving program.
*/
public class Car {
private double speed;
private double angle;
/**
* Coordinates on plane
*/
private double x;
private double y;
//For some reason this ends up being the size of the front
public int length = 10;
//and this ends up being the size of the sides.
public int width = 25;
public double getSpeed() {
return speed;
}
public void setSpeed(double speed) {
this.speed = speed;
}
public double getAngle() {
return angle;
}
public void setAngle(double angle) {
this.angle = angle;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public Car(double x, double y) {
this.x = x;
this.y = y;
speed = 0;
angle = 0;
}
}
Related
I have created a game similar to snake in which the user is first prompted with a jpanel asking which difficulty they want, and whatever JButton they pick influences the size of the map as well as the delay between the snake movements. I have the map size working just fine, but the delay variable never seems to change. I suspect it has something to do with the way the timer is being casted, but I have no idea how to fix it. I am also wondering how when the program is first ran it seems some of the variables don't update, but the second time it is ran all of them are updated. Here is my class with the original variables and collision detection:
import java.util.Random;
import javax.swing.JLabel;
public class GameEngine extends JPanel implements ActionListener{
//creates the size of the panel as well as creating the resolution for all objects, including the players and food.
static final int sWidth = 600;
static final int sHeight = 600;
public static int size = 24;
static int objectSize = (sHeight*sWidth) / size;
public static int delay = 100;
final int playerX[] = new int[objectSize];
final int playerY[] = new int[objectSize];
int bodySize = 4;
int score = 0;
int appleX;
int appleY;
char direction = 'D';
boolean started = false;
Random random;
Timer timer;
boolean easy;
JLabel score1;
public static String difficulty;
GameEngine(){
random = new Random();
this.setPreferredSize(new Dimension(sWidth,sHeight));
this.setBackground(Color.black);
this.setFocusable(true);
this.addKeyListener(new UserMovement());
gameStart();
}
public void gameStart() {
newApple();
started = true;
timer = new Timer(delay,this);
timer.start();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
draw(g);
}
public void drawHead(Graphics g) {
g.setColor(new Color(100,252,0));
g.fillRect(playerX[0], playerY[0], size, size);
}
public void draw(Graphics g) {
if(started) {
//draws the apples
g.setColor(Color.red);
g.fillOval(appleX, appleY, size, size);
for (int i = 0; i < bodySize; i++) {
if(i == 0) {
drawHead(g);
}
else {
g.setColor(new Color(60,180,0));
g.fillRect(playerX[i], playerY[i], size, size);
}
}
g.setColor(Color.white);
g.setFont(new Font("Bold", Font.BOLD, 20));
FontMetrics metrics = getFontMetrics(g.getFont());
g.drawString("Score: " + score,(sWidth - metrics.stringWidth("Score: " + score))/2,g.getFont().getSize());
}
}
public void newApple(){
appleX = random.nextInt((int)(sWidth/size))*size;
appleY = random.nextInt((int)(sHeight/size))*size;
}
//moves the player by using and modifying their coordinates
public void move() {
for (int i = bodySize; i > 0; i--) {
playerX[i] = playerX[i-1];
playerY[i] = playerY[i-1];
}
switch(direction) {
case 'W':
playerY[0] = playerY[0] - size;
break;
case 'S':
playerY[0] = playerY[0] + size;
break;
case 'A':
playerX[0] = playerX[0] - size;
break;
case 'D':
playerX[0] = playerX[0] + size;
break;
}
}
public void checkFood() {
if(playerX[0] == appleX && playerY[0] == appleY)
{
bodySize++;
score++;
newApple();
}
}
public void checkCol() {
//checks for head collision with the body
for(int i = bodySize; i > 0; i--) {
if((playerX[0] == playerX[i]) && (playerY[0] == playerY[i])) {
started = false;
}
}
//checks if head touches any of the walls of the program
if(playerX[0] < 0) {
started = false;
}
if(playerX[0] > sWidth) {
started = false;
}
if(playerY[0] < 0) {
started = false;
}
if(playerY[0] > sHeight) {
started = false;
}
if(started != true) {
timer.stop();
}
}
public void actionPerformed(ActionEvent e){
if(started == true) {
move();
checkFood();
checkCol();
}
repaint();
}
public class UserMovement extends KeyAdapter {
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()) {
case KeyEvent.VK_LEFT:
if(direction != 'D') {
direction = 'A';
}
break;
case KeyEvent.VK_RIGHT:
if(direction != 'A') {
direction = 'D';
}
break;
case KeyEvent.VK_UP:
if(direction != 'S') {
direction = 'W';
}
break;
case KeyEvent.VK_DOWN:
if(direction != 'W') {
direction = 'S';
}
break;
}
}
public void keyReleased(KeyEvent e) {
}
public void keyTyped(KeyEvent e) {
}
}
}
and here is the code calling and changing the delay and size variables:
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class StartMenu extends JPanel {
StartMenu()
{
JButton easy = new JButton();
JButton hard = new JButton();
this.setPreferredSize(new Dimension(350,240));
this.setLayout(null);
this.setBackground(Color.black);
this.setFocusable(true);
easy.setBounds(75,40,200,40);
hard.setBounds(75,120,200,40);
this.add(easy);
this.add(hard);
easy.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
setVisible(false);
new SnakeStart();
GameEngine.size = 48;
GameEngine.delay = 140;
}
});
hard.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e){
setVisible(false);
new SnakeStart();
GameEngine.size = 24;
GameEngine.delay = 70;
}
});
}
}
I assume your GameEngine instance is created before the StartMenu action listeners are executed. If that assumption is correct, that means that GameEngine.timer with the default value of delay is created in the GameEngine constructor and is not updated after the delay is changed.
You need to make sure that you explicitly update your timer with the new delay value after StartMenu actions are called.
I need to calculate a 2 ovals collision in a java swing mini game.
I have a JPanel which draws my ArrayList of balls Thread's and my player ball. In the run() method of my non player balls i check for a collision between the player ball and the balls Thread ArrayList.
The problem is my collision method is never executed. It's not even getting to my collision if statement. Just never calls the method.
Ball.java:
public class Ball extends Thread
{
int x;
int y;
int velocity = 1;
public int radius = 20;
public boolean directionX = true; // True - right, false - left
public boolean directionY = false; // True - up, false - down
public static final int Y_STARTING_POSITION = 0;
public static final int X_STARTING_POSITION = 0;
public Ball()
{
switch (this.getStartingSide())
{
case 0: // Left
{
this.directionX = true;
this.directionY = true;
this.x = this.getRandomHeight();
this.y = this.getRandomWidth();
break;
}
case 1: // Right
{
this.directionX = false;
this.directionY = false;
this.x = this.getRandomHeight();
this.y = this.getRandomWidth();
break;
}
case 2: // Top
{
this.directionX = true;
this.directionY = false;
this.x = this.getRandomWidth();
this.y = this.getRandomHeight();
break;
}
case 3: // Bottom
{
this.directionX = false;
this.directionY = true;
this.x = this.getRandomWidth();
this.y = this.getRandomHeight();
break;
}
}
}
public int getX()
{
return this.x;
}
public void setX(int x)
{
this.x = x;
}
public int getY()
{
return this.y;
}
public void setY(int y)
{
this.y = y;
}
public void move()
{
if (this.directionX) // Right
{
this.x += this.velocity;
}
else // Left
{
this.x -= this.velocity;
}
if (this.directionY) // Up
{
this.y -= this.velocity;
}
else // Down
{
this.y += this.velocity;
}
}
#Override
public void run()
{
try
{
this.isCollision(); // Never called
Thread.sleep(20);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
/**
* Get a random number varies from 0 to the screen width.
*
* #return The random number.
*
*/
public int getRandomWidth()
{
Random random = new Random();
return random.nextInt((Program.getPanelWidth() - 0) + 1) + 0; // Minimum = 0,maximum = Program.panelWidth
}
/**
* Get a random number varies from 0 to the screen height.
*
* #return The random number.
*
*/
public int getRandomHeight()
{
Random random = new Random();
return random.nextInt((Program.getPanelHeight() - 0) + 1) + 0; // Minimum = 0,maximum = Program.panelHeight
}
/**
* Get the starting side of a ball.
*
* Left - 0.
* Right - 1.
* Top - 2.
* Bottom - 3.
*
* #return
*
*/
public int getStartingSide()
{
Random random = new Random();
return random.nextInt((4 - 0) + 1) + 0; // Minimum = 0,maximum = 3
}
public void isCollision()
{
System.out.println("SSSSSSSSSSSSSSSS");
if (Math.sqrt(Math.pow(MyPanel.playerX + MyPanel.playerRadius - this.getX() + this.radius,2)
+ Math.pow(MyPanel.playerY + MyPanel.playerRadius - this.getY() + this.radius,2))
<= MyPanel.playerRadius + this.radius) // A collision
{
System.exit(0);
}
}
} // End of Ball class
MyPanel.java:
public class MyPanel extends JPanel implements KeyListener
{
private static final long serialVersionUID = 1L;
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color NPC_BALLS_COLOR = Color.RED;
// The player is an oval
public static int playerRadius = 35;
public static int playerX;
public static int playerY;
// True - first player position, false - otherwise
private boolean playerPosition = true;
// Array of all the balls threads
public static ArrayList<Ball> balls = new ArrayList<Ball>();
public MyPanel()
{
this.setBackground(MyPanel.BACKGROUND_COLOR);
this.setFocusable(true);
this.addKeyListener(this);
new Timer(10,new UpdateUI());
}
// Drawing
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final double PANEL_WIDTH = this.getWidth();
final double PANEL_HEIGHT = this.getHeight();
if (this.playerPosition)
{
MyPanel.playerX = (int)(PANEL_WIDTH / 2 - MyPanel.playerRadius);
MyPanel.playerY = (int)(PANEL_HEIGHT / 2 - MyPanel.playerRadius);
this.playerPosition = false;
}
// Drawing the player
g.setColor(Color.BLACK);
g.fillOval(playerX,playerY, MyPanel.playerRadius * 2, MyPanel.playerRadius * 2);
// Drawing npc's balls
g.setColor(MyPanel.NPC_BALLS_COLOR);
for (Ball ball: MyPanel.balls) // ConcurrentModificationException
{
if (ball.isAlive())
{
ball.start();
}
ball.move();
repaint();
g.fillOval(ball.getX(), ball.getY(),
ball.radius * 2, ball.radius * 2);
}
}
// Keyboard listeners
#Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode())
{
case KeyEvent.VK_W: // Up
{
MyPanel.playerY -= 10;
repaint();
break;
}
case KeyEvent.VK_S: // Down
{
MyPanel.playerY += 10;
repaint();
break;
}
case KeyEvent.VK_D: // Right
{
MyPanel.playerX += 10;
repaint();
break;
}
case KeyEvent.VK_A: // Left
{
MyPanel.playerX -= 10;
repaint();
break;
}
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
public class UpdateUI implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
} // End of MyPanel class
Program.java:
public class Program
{
private static int panelWidth;
private static int panelHeight;
public static void main(String[] args)
{
MyFrame frame = new MyFrame();
Program.panelWidth = frame.getWidth();
Program.panelHeight = frame.getHeight();
// Generate a ball each 2 seconds
while (true)
{
try
{
MyPanel.balls.add(new Ball());
Thread.sleep(500);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
} // End of main method
public static int getPanelWidth()
{
return Program.panelWidth;
}
public static int getPanelHeight()
{
return Program.panelHeight;
}
}
The JFrame is nothing special and just adds the JPanel to it and so.
So my isCollision() method is never called even if It's on my run() method of my Thread. How come?
You never started the Thread implemented by your Ball class.
The ConcurrentModificationException is due to the fact that you are adding balls to an ArrayList which is by nature a thread-unsafe datastructure if you don't synchronize it externally. Here you are adding in a thread while iterating the list in the EDT (Event Dispatch Thread). Either you synchronize on the list, or you use a thread-safe data structure (for iteration you still may need to lock the full list).
Have a look at Collections.synchronizedList or CopyOnWriteArrayList (the latter doesn't need synchronizing for iteration).
However, synchronization on each insertion and each iteration seems very inefficient to me, especially because you are aiming for a game which requires fast rendering and background processing. It may be sufficient for a basic game though.
As a sidenote: a better technique for a game would be to use double buffering: render the game graphics in a background thread e.g. on a BufferedImage, and when done, switch the two buffers so that the one you just drew is now displayed on screen, and the other one can be used again for drawing the next frame. You might need a synchronization checkpoint between frames. But this is slightly more advanced, so if you are just starting in Java, I wouldn't spend too much time on it and keep things simple.
You need to call the repaint method in your while loop in the main method so run() can be called. You can do it with this:
panel.repaint():
Also, You must call the run method. You can do it in ball or in the panel.
ball.isAlive() always return false bucause your thread has not been started. If you want to start your threads in paintComponent() method you shouldn't use this method.
public class MyPanel extends JPanel implements KeyListener
{
private static final long serialVersionUID = 1L;
private static final Color BACKGROUND_COLOR = Color.WHITE;
private static final Color NPC_BALLS_COLOR = Color.RED;
// The player is an oval
public static int playerRadius = 35;
public static int playerX;
public static int playerY;
// True - first player position, false - otherwise
private boolean playerPosition = true;
private boolean ballsStarted;
// Array of all the balls threads
public static ArrayList<Ball> balls = new ArrayList<Ball>();
public MyPanel()
{
this.setBackground(MyPanel.BACKGROUND_COLOR);
this.setFocusable(true);
this.addKeyListener(this);
new Timer(10,new UpdateUI());
}
// Drawing
#Override
protected void paintComponent(Graphics g)
{
super.paintComponent(g);
final double PANEL_WIDTH = this.getWidth();
final double PANEL_HEIGHT = this.getHeight();
if (this.playerPosition)
{
MyPanel.playerX = (int)(PANEL_WIDTH / 2 - MyPanel.playerRadius);
MyPanel.playerY = (int)(PANEL_HEIGHT / 2 - MyPanel.playerRadius);
this.playerPosition = false;
}
// Drawing the player
g.setColor(Color.BLACK);
g.fillOval(playerX,playerY, MyPanel.playerRadius * 2, MyPanel.playerRadius * 2);
// Drawing npc's balls
g.setColor(MyPanel.NPC_BALLS_COLOR);
for (Ball ball: MyPanel.balls) // ConcurrentModificationException
{
if (!ballsStarted)
{
ball.start();
}
ball.move();
repaint();
g.fillOval(ball.getX(), ball.getY(),
ball.radius * 2, ball.radius * 2);
}
ballsStarted = true;
}
// Keyboard listeners
#Override
public void keyPressed(KeyEvent e)
{
switch (e.getKeyCode())
{
case KeyEvent.VK_W: // Up
{
MyPanel.playerY -= 10;
repaint();
break;
}
case KeyEvent.VK_S: // Down
{
MyPanel.playerY += 10;
repaint();
break;
}
case KeyEvent.VK_D: // Right
{
MyPanel.playerX += 10;
repaint();
break;
}
case KeyEvent.VK_A: // Left
{
MyPanel.playerX -= 10;
repaint();
break;
}
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
public class UpdateUI implements ActionListener
{
#Override
public void actionPerformed(ActionEvent e)
{
repaint();
}
}
}
I made a program to display interference patterns for light waves. I did this by using the paint method on a JPanel to draw 2 sources and then drawing concentric circles around them. This would be double slit interference, so I allowed one of the sources to move around to experiment with the slit width.
The problem is that when I run this, my computer says it is using 80% of my CPU! There's really not much to it. Circle in the middle, circles around it, and it moves. My code follows.
main class:
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
public class main {
static final int UP = -1;
static final int DOWN = 1;
static final int RIGHT = 1;
static final int LEFT = -1;
static final int NOMOVEMENT = 0;
static int verticalMovement = NOMOVEMENT;
static int horizontalMovement = NOMOVEMENT;
public static void main(String[] args) {
Dimension screenSize = Toolkit.getDefaultToolkit().getScreenSize();
Point s1 = new Point((int)(screenSize.getWidth()/3), 50);
Point s2 = new Point((int)(2*screenSize.getWidth()/3), 50);
JFrame frame = new JFrame("Physics Frame");
frame.setPreferredSize(screenSize);
PhysicsPane pane = new PhysicsPane(screenSize, 15, s1, s2);
pane.setPreferredSize(screenSize);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(pane);
frame.pack();
Timer time = new Timer(1000 / 20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
switch (verticalMovement){
case UP:
s2.changeY(UP);
break;
case DOWN:
s2.changeY(DOWN);
break;
default:
break;
}
switch (horizontalMovement){
case RIGHT:
s2.changeX(RIGHT);
break;
case LEFT:
s2.changeX(LEFT);
break;
default:
break;
}
pane.repaint();
}
});
frame.addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
if(e.getKeyCode()==37){
horizontalMovement = LEFT;
} else if (e.getKeyCode()==38){
verticalMovement = UP;
} else if (e.getKeyCode()==39){
horizontalMovement = RIGHT;
} else if (e.getKeyCode()==40){
verticalMovement = DOWN;
}
if(e.getKeyChar()=='a'){
pane.setWaveLength(2);
}
if(e.getKeyChar()=='s'){
pane.setWaveLength(-2);
}
}
#Override
public void keyReleased(KeyEvent e) {
switch (e.getKeyCode()){
case 37:
horizontalMovement = NOMOVEMENT;
break;
case 38:
verticalMovement = NOMOVEMENT;
break;
case 39:
horizontalMovement = NOMOVEMENT;
break;
case 40:
verticalMovement = NOMOVEMENT;
break;
}
}
});
frame.setVisible(true);
time.start();
}
}
Panel class. If there's an inefficiency with the drawing method, it would be here.
import javax.swing.*;
import java.awt.*;
public class PhysicsPane extends JPanel {
private Dimension size;
private Point[] pointArray = new Point[2];
private double waveLength;
public PhysicsPane(Dimension size, double wavelength, Point source1, Point source2) {
this.size = size;
pointArray[0] = source1;
pointArray[1] = source2;
setPreferredSize(size);
this.waveLength = wavelength;
}
#Override
public void paintComponent(Graphics g){
for (int i = 0; i < 2; i++) {
g.setColor(Color.black);
double x = this.pointArray[i].getX();
double y = this.pointArray[i].getY();
g.fillOval((int)x, (int)y, 2, 2);
for (int j = (int)waveLength; j < 1500; j+=waveLength) {
g.setColor(Color.red);
g.drawOval((int)x-(j/2+1), (int)y-(j/2+1), 2 + j, 2 + j);
}
}
}
public void setWaveLength(double increment){
this.waveLength+=increment;
}
}
Point Class: Really just puts x and y coordinates into one construct. Not important particularly
public class Point {
private double x;
private double y;
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public void changeX(double dX){
this.x+=dX;
}
public void changeY(double dY){
this.y+=dY;
}
}
So what is wrong with my program? Why is such a simple animation consuming so much of my processor?
UPDATED CODE SECTION:
#Override
public void paintComponent(Graphics g){
BufferedImage bi = new BufferedImage(size.width, size.height, BufferedImage.TYPE_INT_RGB);
Graphics graphics = bi.getGraphics();
for (int i = 0; i < 2; i++) {
graphics.setColor(Color.black);
double x = this.pointArray[i].getX();
double y = this.pointArray[i].getY();
graphics.fillOval((int)x, (int)y, 2, 2);
for (int j = (int)waveLength; j < 1500; j+=waveLength) {
graphics.setColor(Color.red);
graphics.drawOval((int)x-(j/2+1), (int)y-(j/2+1), 2 + j, 2 + j);
}
}
g.drawImage(bi, 0, 0, new ImageObserver() {
#Override
public boolean imageUpdate(Image img, int infoflags, int x, int y, int width, int height) {
return false;
}
});
}
The improvement that I recommend is removal of unnecessary tasks being performed, notably how your code updates the pane being drawn on, even when there aren't changes.
The following update reduced CPU usage from 12% to 0% (static frame):
Timer time = new Timer(1000 / 20, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
boolean refresh = false;
switch (verticalMovement) {
case UP:
s2.changeY(UP);
refresh = true;
break;
case DOWN:
s2.changeY(DOWN);
refresh = true;
break;
default:
break;
}
switch (horizontalMovement) {
case RIGHT:
s2.changeX(RIGHT);
refresh = true;
break;
case LEFT:
s2.changeX(LEFT);
refresh = true;
break;
default:
break;
}
if (refresh == true) {
pane.repaint();
}
}
});
I just start learning Java. I write a program like this:
Bacteria.class
import java.awt.*;
public class Bacteria extends Creature{
public static final int COLOR_STEP = 256/MAX_AGE;
Bacteria(Board board, int _x, int _y){
super.board = board;
//initialize age, x, y
super.age = 1;
super.x = _x;
super.y = _y;
}
//Draw a circle representing the creature at its position
//This function is done.
public void draw(Graphics g) {
g.setColor(new Color(0, COLOR_STEP * age, 0));
g.fillOval(x * board.CELL_SIZE, y * board.CELL_SIZE, board.CELL_SIZE, board.CELL_SIZE);
}
public void tick() {
//your code
// this is how the creature lives in one tick
// make the creature get older if it is not dead.
// if it's got older than MAX_AGE then it should die
// ...call board.addDead() to register the dead creature
// if it can have baby then try reproduce()
age++;
if (this.canHaveBaby()) {
this.reproduce();
}
//this.reproduce();
if (age > MAX_AGE) {
board.addDead(this);
}
}
}
Creature.class
import java.awt.*;
public class Creature {
public static final int MAX_AGE = 5;
public Board board;
public int age; // age of the creature measures in ticks
public int x; // (x,y) is the position of the creature
public int y;
Creature(Board board, int _x, int _y) {
this.board = board;
//initialize age, x, y
age = 1;
x = _x;
y = _y;
}
public Creature() {
}
//Draw a circle representing the creature at its position
//This function is done.
public void draw(Graphics g){}
public void tick(){}
public void reproduce() {
//your code
// if it can have a baby then produce a baby
// ...in an empty cell next to its cell.
// board.emptyCell() should be used
// ....to check whether a cell in the board is empty
// and call board.addBaby() to place the baby to the board
//int x_current = x, y_current = y;
if(board.emptyCell(x, y+1)){
Creature baby = new Creature(board,x, y+1);
//System.out.println("1");
board.addBaby( baby);
}
else if(board.emptyCell(x+1,y)){
Creature baby = new Creature(board,x+1, y);
board.addBaby(baby);
}
else if(board.emptyCell(x+1,y+1)){
Creature baby = new Creature(board,x+1, y+1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y)){
Creature baby = new Creature(board,x-1, y);
board.addBaby(baby);
}
else if(board.emptyCell(x,y-1)){
Creature baby = new Creature(board,x, y-1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y-1)){
Creature baby = new Creature(board,x-1, y-1);
board.addBaby(baby);
}
else if(board.emptyCell(x-1,y+1)){
Creature baby = new Creature(board,x-1, y+1);
board.addBaby(baby);
}
else if(board.emptyCell(x+1,y-1)){
Creature baby = new Creature(board,x+1, y-1);
board.addBaby(baby);
}
}
public boolean canHaveBaby() {
//your code
// if the creature is dead or it is too young
// then it cannot have a baby
if(age == 0 || age > MAX_AGE){
return false;
}
return true;
}
public boolean atPosition(int _x, int _y) {
//your code
// return true if the creature is at the position (_x,_y)
// you need this function for board.empty to function correctly
if(_x == x && _y == y){
return true;
}
return false;
}
}
Board.class
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Board extends JPanel {
public static final int CELL_SIZE = 10;
private final int rows = 10;
private final int cols = 20;
ArrayList<Creature> creatureList;
ArrayList<Creature> babies, dead;
// ArrayList<Bacteria> bacteriaList;
// ArrayList<Bacteria> babies_bac, dead_bac;
Board() {
super();
setBackground(Color.white);
creatureList = new ArrayList<Creature>();
//creatureList.size();
babies = new ArrayList<Creature>();
dead = new ArrayList<Creature>();
setPreferredSize(new Dimension(cols * CELL_SIZE, rows * CELL_SIZE));
creatureList.add(new Bacteria(this, 1, 1));
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
for (Creature creature: creatureList) {
creature.draw(g);
}
}
public void tick() {
dead = new ArrayList<Creature>();
babies = new ArrayList<Creature>();
for (Creature creature: creatureList) {
creature.tick();
}
creatureList.addAll(babies);
creatureList.removeAll(dead);
}
public void addBaby(Creature baby) {
if (baby != null) babies.add(baby);
}
public void addDead(Creature deadCreature) {
if (deadCreature != null) dead.add(deadCreature);
}
public boolean emptyCell(int x, int y) {
if (x < 0 || y < 0 || x >= cols || y >= rows) return false;
for (Creature creature : creatureList) {
if (creature.atPosition(x, y)) {
return false;
}
}
for (Creature creature : creatureList) {
if (creature.atPosition(x, y)) {
return false;
}
}
return true;
}
public String getPopulation() {
return String.format("%d", creatureList.size());
}
}
and BacteriaSimulator.java
import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
public class BacteriaSimulator extends JFrame {
private Board board;
private JLabel label;
public BacteriaSimulator() {
initUI();
setTitle("Bacteria Simulator");
setSize(350, 200);
setLocationRelativeTo(null);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
private void initUI() {
JPanel panel = new JPanel();
board = new Board();
panel.add(board);
label = new JLabel(board.getPopulation());
panel.add(label);
JButton button = new JButton("Tick");
button.addActionListener(new ButtonNextListener());
panel.add(button);
add(panel);
pack();
}
public static void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
BacteriaSimulator ex = new BacteriaSimulator();
ex.setVisible(true);
}
});
}
private class ButtonNextListener implements ActionListener {
#Override
public void actionPerformed(ActionEvent e) {
board.tick();
label.setText(board.getPopulation());
repaint();
}
}
}
I want Board.class work with creatureList. However, Board is still initialized with a bacteria (creatureList.add(new Bacteria(this, 1, 1));). I got a problem that is Board.class can not run draw() and tick() method which I want. How to draw objects bacteria and make the right method of tick ()? Can you help me! Thank you!
Your draw() and tick() functions are part of Bacteria.java. They cannot be used from Board.java unless you create an instance of Bacteria and then invoke the methods using that.
I want to make a 2D game for android. But I cannot make my object collide. I want to make the bear and the giraffe to collide when they interact, but it doesn't work.
This is the bear class:
package gamefoundation;
import java.awt.Rectangle;
public class Bear {
//In Java, Class Variables should be private so that only its methods can change them.
private int centerX = 50;
private int centerY = 430;
private int speedX = 0;
private int speedY = 0;
public static Rectangle rect = new Rectangle(0,0,0,0);
private boolean movingLeft = false;
private boolean movingRight = false;
private boolean movingUp = false;
private boolean movingDown = false;
private Giraffe giraffe;
public void update() {
// Moves Character or Scrolls Background accordingly.
if (speedX < 0) {
centerX += speedX;
} else {
if (centerX <= 763) /*batas maks kanan*/{
centerX += speedX;
}
}
if (speedY < 0) {
centerY += speedY;
} else {
if (centerY <= 430) /*batas maks bawah*/ {
centerY += speedY;
}
}
//biar beruangnya gak nembus sebelah kiri
if (centerX + speedX <= 60) {
centerX = 61;
}
//biar beruangnya gak nembus ke atas
if (centerY + speedY <= 60) {
centerY = 61;
}
//collision
rect.setRect(centerX-34, centerY-63, 68, 103);
}
public void moveRight() {
speedX = 6;
}
public void moveLeft() {
speedX = -6;
}
public void moveUp() {
speedY = -6;
}
public void moveDown() {
speedY = 6;
}
public void stop() {
speedX = 0;
speedY = 0;
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public int getSpeedX() {
return speedX;
}
public int getSpeedY() {
return speedY;
}
public void setCenterX(int centerX) {
this.centerX = centerX;
}
public void setCenterY(int centerY) {
this.centerY = centerY;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
public boolean isMovingLeft() {
return movingLeft;
}
public boolean isMovingRight() {
return movingRight;
}
public void setMovingLeft(boolean movingLeft) {
this.movingLeft = movingLeft;
}
public void setMovingRight(boolean movingRight) {
this.movingRight = movingRight;
}
public boolean isMovingUp() {
return movingUp;
}
public boolean isMovingDown() {
return movingDown;
}
public void setMovingUp(boolean movingUp) {
this.movingUp = movingUp;
}
public void setMovingDown(boolean movingDown) {
this.movingDown = movingDown;
}
}
The giraffe class:
package gamefoundation;
import java.awt.Rectangle;
public class Giraffe extends Animal {
private Rectangle r;
public Giraffe(int centerX, int centerY) {
setCenterX(centerX);
setCenterY(centerY);
r = new Rectangle();
}
public void checkVerticalCollision(Rectangle rbody){
if (rbody.intersects(r)){
System.out.println("collision w/ giraffe");
}
}
private void update() {
r.setBounds(getCenterX(), getCenterY(), 20, 20);
}
}
This is the StartingClass:
package gamefoundation;
import java.applet.Applet;
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.net.URL;
public class StartingClass extends Applet implements Runnable, KeyListener {
private Bear bear;
private Image image,beruang,map,burung,buaya,jerapah,monyet,rusa;
private Graphics second;
private URL base;
private static Map mp1,mp2;
private Bird bird;
private Crocodile crocodile;
private Deer deer;
private Giraffe giraffe;
private Monkey monkey;
#Override
public void init() {
setSize(800,480);
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Petualangan Bubu");
try{
base=getDocumentBase();
}catch(Exception e){
//TODO: handle exception
}
//image setups
beruang = getImage(base,"data/beruangsmall.png");
burung = getImage(base,"data/birdsmall.png");
buaya = getImage(base,"data/crocodilesmall.png");
rusa = getImage(base,"data/deersmall.png");
jerapah = getImage(base,"data/giraffesmall.png");
monyet = getImage(base,"data/monkeysmall.png");
map = getImage(base,"data/map.jpg");
}
#Override
public void start() {
mp1 = new Map(0,0);
bear = new Bear();
bird = new Bird(430,20);
crocodile = new Crocodile(410,120);
deer = new Deer(205,30);
giraffe = new Giraffe(580,86);
monkey = new Monkey(600,260);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void stop() {
}
#Override
public void destroy() {
}
#Override
public void run() {
while(true) {
bear.update();
if(bear.getCenterX() == giraffe.getCenterX()) {
giraffe.checkVerticalCollision(bear.rect);
}
repaint();
try{
Thread.sleep(17);
}catch(InterruptedException e) {
e.printStackTrace();
}
}
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
bear.moveUp();
bear.setMovingUp(true);
break;
case KeyEvent.VK_DOWN:
bear.moveDown();
bear.setMovingDown(true);
break;
case KeyEvent.VK_LEFT:
bear.moveLeft();
bear.setMovingLeft(true);
break;
case KeyEvent.VK_RIGHT:
bear.moveRight();
bear.setMovingRight(true);
break;
case KeyEvent.VK_SPACE:
System.out.println("Jump");
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
switch (e.getKeyCode()) {
case KeyEvent.VK_UP:
bear.stop();
break;
case KeyEvent.VK_DOWN:
bear.stop();
break;
case KeyEvent.VK_LEFT:
bear.stop();
break;
case KeyEvent.VK_RIGHT:
bear.stop();
break;
case KeyEvent.VK_SPACE:
System.out.println("Stop jumping");
break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void update(Graphics g) {
if(image==null) {
image = createImage(this.getWidth(), this.getHeight());
second = image.getGraphics();
}
second.setColor(getBackground());
second.fillRect(0, 0, getWidth(), getHeight());
second.setColor(getForeground());
paint(second);
g.drawImage(image, 0, 0, this);
}
#Override
public void paint(Graphics g) {
g.drawImage(map, mp1.getMapX(), mp1.getMapY(), this);
g.drawImage(beruang, bear.getCenterX()-61, bear.getCenterY()-63, this);
g.drawImage(burung, bird.getCenterX(), bird.getCenterY(), this);
g.drawImage(buaya, crocodile.getCenterX(), crocodile.getCenterY(), this);
g.drawImage(rusa, deer.getCenterX(), deer.getCenterY(), this);
g.drawImage(jerapah, giraffe.getCenterX(), giraffe.getCenterY(), this);
g.drawImage(monyet, monkey.getCenterX(), monkey.getCenterY(), this);
g.drawRect((int)bear.rect.getX(), (int)bear.rect.getY(), (int)bear.rect.getWidth(), (int)bear.rect.getHeight());
}
}
As said in the comment your only checking for a collision when the CenterX of both the bear and giraffe are equal. I would suggest simply removing that if check it likely doesn't buy you anything performance wise.