I'm trying to make a 2D game that should draw a character to the screen. But when I run it, I just get a black screen.
The important bits:
public class StartingClass extends Applet implements Runnable, KeyListener {
private static final long serialVersionUID = 1L;
private Walrus walrus;
private Image image, character;
private Graphics second;
private URL base;
#Override
public void init() {
setSize(800, 480);
setBackground(Color.BLACK);
setFocusable(true);
addKeyListener(this);
Frame frame = (Frame) this.getParent().getParent();
frame.setTitle("Applet");
try {
base = getDocumentBase();
} catch (Exception e) {
// TODO: handle exception
}
// Image Setups
character = getImage(base, "src/data/walrus_right.png");
}
#Override
public void start() {
walrus = new Walrus();
Thread thread = new Thread(this);
thread.start();
}
#Override
public void stop() {
// TODO Auto-generated method stub
}
#Override
public void destroy() {
// TODO Auto-generated method stub
}
#Override
public void run() {
while (true) {
walrus.update();
repaint();
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
#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(character, walrus.getCenterX() - 61, walrus.getCenterY() - 63, this);
}
}
Also, here's my other class:
public class Walrus {
private int centerX = 100;
private int centerY = 382;
private boolean jumped = false;
private int speedX = 0;
private int speedY = 1;
public void update() {
// Moves Character or Scrolls Background accordingly.
if (speedX < 0) {
centerX += speedX;
} else if (speedX == 0) {
System.out.println("Do not scroll the background.");
} else {
if (centerX <= 150) {
centerX += speedX;
} else {
System.out.println("Scroll Background Here");
}
}
// Updates Y Position
if (centerY + speedY >= 382) {
centerY = 382;
}else{
centerY += speedY;
}
// Handles Jumping
if (jumped == true) {
speedY += 1;
if (centerY + speedY >= 382) {
centerY = 382;
speedY = 0;
jumped = false;
}
}
// Prevents going beyond X coordinate of 0
if (centerX + speedX <= 60) {
centerX = 61;
}
}
public void moveRight() {
speedX = 6;
}
public void moveLeft() {
speedX = -6;
}
public void stop() {
speedX = 0;
}
public void jump() {
if (jumped == false) {
speedY = -15;
jumped = true;
}
}
public int getCenterX() {
return centerX;
}
public int getCenterY() {
return centerY;
}
public boolean isJumped() {
return jumped;
}
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 setJumped(boolean jumped) {
this.jumped = jumped;
}
public void setSpeedX(int speedX) {
this.speedX = speedX;
}
public void setSpeedY(int speedY) {
this.speedY = speedY;
}
}
I haven't gotten any errors at this point, but it's not working right.
The problem is that your image is not loaded. The line
getImage(base, "src/data/walrus_right.png");
tries to read the image for a deployed applet and will fail quietly (not throw errors), which is what caused the confusion. Try to replace it with
ImageIO.read(new File(getClass().getResource("relative/path.png").getPath()));
where you should replace the path as specified under getResource API.
Edit:
Break the call into several calls:
Class<? extends StartingClass> clas = getClass();
URL url = clas.getResource("relative/path.png");
String path = url.getPath();
File file = new File(path);
try {
Image image = ImageIO.read(file);
} catch (IOException e) {
e.printStackTrace();
}
and move with the debugger step by step to see which line throws the error. My guess is that the file is not located in the right place, which causes getResource to return null, which causes new File(path) to throw NullPointerException.
Related
Is there any way to efficiently move an object from point A to point B without having to use an inefficient While(true) loop? I tried to repaint Draw class, but it did not help with the performance issues. I am unable to trace the root of the huge performance issue. I tried to research moving entities on Google, GitHub, and others and found nothing useful. Any help is appreciated!
Code:
class Draw extends JPanel {
public static int x;
public static int y;
public static int randx;
public static int randy;
public void paintComponent(Graphics g) {
super.paintComponent(g);
BufferedImage image = null;
BufferedImage image1 = null;
try {
image = ImageIO.read(new File("Small Sheep Icon.png"));
image1 = ImageIO.read(new File("Small Sheep Icon.png"));
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
g.drawImage(image, x, y, null);
g.drawImage(image1, randx, randy, null);
// g.drawString("Hello, World", x, y);
}
}
public static void sheepmove() {
double starttime = System.nanoTime();
double previoustime = 0;
double waittime = 0;
boolean xright = true;
boolean waitswitch = false;
int counter = 0;
while(true) {
double elapsedtime = (System.nanoTime() - starttime)/(1000000000);
if (elapsedtime-waittime >= 1 || waitswitch == false) {
if (waitswitch == true) {
waitswitch = false;
}
waittime = elapsedtime;
if (elapsedtime >= previoustime + 0.1) {
previoustime = elapsedtime;
if (xright == true) {
Draw.randx += 1;
}
if (Draw.randx - 200 >= 50) {
if (counter == 0) {
waitswitch = true;
xright = false;
}
// System.out.println("Moving south...");
Draw.randy += 1;
if (Draw.randy >= 300) {
Draw.randy -= 1;
break;
}
counter++;
}
frame.repaint();
// System.out.print("=");
// System.out.println("Previous Time: " + previoustime);
}
}
// System.out.println(elapsedtime);
// Thread.sleep(1000);
}
}
public static void main(String[] args) {
new Demo();
}
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseMoved(MouseEvent e) {
// System.out.println("Mouse Moved!");
Draw.x = e.getX();
Draw.y = e.getY();
frame.repaint();
}
Another question about inheritance in java: I was wondering about two things:
1) how can I set my program to switch between using an inherited class or not?
2) I'm not sure why my extended class glidingObject.java is not responding to my key presses
Here's my Game.java (which runs the game; I should be passing in some parameter that allows the user to choose which class to use right - either flying object or gliding object? I've also included my two classes for flying object and gliding object)
public class Game extends JPanel {
private static final long serialVersionUID = 1L;
public static int WIDTH = 800, HEIGHT = 750;
public static ArrayList<Rectangle> columns;
public static Random rand;
public static double score;
public static boolean gameOver, started; //two modes, started and gameover
public static String username;
public static String currTime;
public static Timer timer;
public static flyingObject obj;
private PropertyChangeSupport mPcs = new PropertyChangeSupport(this);
public Game(flyingObject object){
obj = object;
timer = new Timer(20, new ActionListener() {
public void actionPerformed(ActionEvent e) {
try {
tick();
} catch (IOException e1) {
e1.printStackTrace();
}
}
});
columns = new ArrayList<Rectangle>();
rand = new Random();
Background bk = new Background();
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
addMouseListener(new MouseAdapter() {
#Override
public void mouseReleased(MouseEvent e) {
started = true;
}
});
PropertyChangeListener listener = new PropertyChangeListener() {
#Override
public void propertyChange(PropertyChangeEvent evt) {
try {
scoreBoard.record();
} catch (IOException e) {
e.printStackTrace();
}
}
};
this.addPropertyChangeListener(listener);
getDate(); //get the date of the game played
mPcs.firePropertyChange("gameOver",false, true); //alert record() method when the game is over
timer.start();
}
//adding the column
public static void addColumn(boolean start) {
int space = 300;
int width = 100;
int height = 50 + rand.nextInt(300);
if (start) {
//add top and bottom
columns.add(new Rectangle(WIDTH + width + columns.size() * 300, HEIGHT - height - 120, width, height + 100));
columns.add(new Rectangle(WIDTH + width + (columns.size() - 1) * 300, 0, width, HEIGHT - height - space));
}
else
{
columns.add(new Rectangle(columns.get(columns.size() - 1).x + 600, HEIGHT - height - 120, width, height + 100));
columns.add(new Rectangle(columns.get(columns.size() - 1).x, 0, width, HEIGHT - height - space));
}
}
//paint the columns
public void paintColumn(Graphics g, Rectangle column) {
g.setColor(Color.white);
g.fillRect(column.x, column.y, column.width, column.height);
}
public static void reset() {
obj = new glidingObject(WIDTH / 2 - 10, HEIGHT / 2 - 10);
columns.clear();
score = 0.0;
addColumn(true);
addColumn(true);
addColumn(true);
addColumn(true);
gameOver = false;
}
public void tick() throws IOException {
if (started) {
int speed = 10;
glidingObject.move();
for (int i = 0; i < columns.size(); i ++) {
Rectangle column = columns.get(i);
column.x -= speed;
if (column.x + column.width < 0) {
columns.remove(column);
if (column.y == 0) {
addColumn(false);
}
}
}
for (Rectangle column: columns) {
if (column.x == glidingObject.X) {
score += 0.5;
}
if (column.intersects(glidingObject.getBounds())) {
gameOver = true;
//when the object crashes, it does not go through the column
if (glidingObject.X <= column.x) {
glidingObject.X = column.x - glidingObject.DIAMETER;
}
else if (glidingObject.Y < column.height) {
glidingObject.Y = column.height;
}
Main.gameOver();
}
}
if (glidingObject.Y > HEIGHT || glidingObject.Y < 0) {
gameOver = true;
//timer.stop();
Main.gameOver();
}
if (glidingObject.Y + glidingObject.YMotion >= HEIGHT) {
gameOver = true;
//timer.stop();
Main.gameOver();
}
}
//update the display
repaint();
}
public void getDate() {
DateFormat dateFormat = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
Date date = new Date();
currTime = dateFormat.format(date);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Background.paint(g);
glidingObject.paint(g);
for (Rectangle column: columns) {
paintColumn(g, column);
}
g.setColor(Color.white);
g.setFont(new Font("Manaspace", 1, 60));
if (!started) {
g.drawString("Click to start!", 75, HEIGHT / 2 - 50);
}
if (gameOver) {
g.drawString("Game Over!", 200, HEIGHT / 2 - 50);
}
if (!gameOver && started) {
g.drawString(String.valueOf(score), WIDTH / 2 - 25, 100);
}
}
}
class flyingobject.java
public class flyingObject implements ActionListener, MouseListener, KeyListener {
static int DIAMETER = 25;
static int Y; // Y position of the unicorn
static int X; // X position of the unicorn
static int YMotion; // Y movement of the unicorn
//parameters are the initial positions
public flyingObject(int xpos, int ypos) {
X = xpos;
Y = ypos; // this changes
}
//getters
public int getX() {
return X;
}
public int getY() {
return Y;
}
//setters
public void setX(int newX) {
X = newX;
}
public void setY(int newY) {
Y = newY;
}
//the bounds of the object (rectangle)
public static Rectangle getBounds() {
return new Rectangle(X, Y, DIAMETER, DIAMETER);
}
//the visible component of the object - this can get overriden by subclasses
public static void paint(Graphics g){
g.setColor(Color.white);
g.fillRect(X, Y, DIAMETER, DIAMETER);
}
//the movement component of the object
public static void jump() {
if (Game.gameOver) {
Game.reset();
Game.gameOver = false;
}
if (!Game.started) {
Game.started = true;
}
else if (!Game.gameOver) {
if (YMotion > 0) {
YMotion = 0;
}
YMotion -= 14;
}
}
public static void move() {
if ((Y > 0) && (Y < Game.HEIGHT)) {
YMotion += 1.5; // gravity
Y += YMotion;
}
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE)
{
jump();
}
}
#Override
public void mouseClicked(MouseEvent e) {
jump();
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
Class glidingobject.java (the game.java should allow the user to choose between just using flying object, or the extended class gliding object)
public class glidingObject extends flyingObject{
//velocity of the object
private static int vx;
private static int vy;
private static int lives;
public glidingObject(int xpos, int ypos) {
super(xpos, ypos);
vx = 0;
vy = 0;
lives = 3;
}
//getter methods
public int getVx() {
return vx;
}
public int getVy() {
return vy;
}
//setter methods
public void setVx(int newVx) {
vx = newVx;
}
public void setVy(int newVy) {
vy = newVy;
}
//moves the object
public static void jump() {
X += vx;
Y += vy;
}
public static void paint(Graphics g) {
g.setColor(Color.CYAN);
g.fillOval(X, Y, DIAMETER, DIAMETER);
}
#Override
public void keyTyped(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
vx = 10;
}
if (e.getKeyCode() == KeyEvent.VK_LEFT){
vx = -10;
}
if (e.getKeyCode() == KeyEvent.VK_UP){
vy = 10;
}
if (e.getKeyCode() == KeyEvent.VK_DOWN){
vy = -10;
}
}
#Override
public void keyPressed(KeyEvent e) {
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
#Override
public void actionPerformed(ActionEvent e) {
}
}
I should be passing in some parameter that allows the user to choose which class to use right - either flying object or gliding object?
This is where interfaces become so powerful. Both the "flying" and "gliding" objects are going to share some common properties/functionality, these should be described by the interface. You Game should then only accept instances of this interface, it shouldn't care what the implementation, only that they adhere to the agreed interface.
This is principle of "code to interface, not implementation"
How complicated this gets is up to you, for example, you could have abstract implementations which describe "air" based entities and "ground" based entities, from which you could have "powered" and "unpowered" implementations of the abstract "air" class, all of which would be tied back to the "game entity" interface
I'm not sure why my extended class glidingObject.java is not responding to my key presses
This is because you're using KeyListener, this well known for it's focus related issues. See How to Use Key Bindings for the recommended solution
Hello everyone I am trying to make a game where the user plays as some kind of character, and trys to collect coins while avoiding monsters that spawn. My program compiles with no error, but nothing is showing up when I run the applet. This could be because of the order of extension I have everything in but I am not sure. Any help would be greatly appreciated (this is for a final school project for my intro to Java class). Here is the code, I know it is long but it all pertains to the question at hand:
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.util.*;
public class Sprite extends JApplet
{
Image image;
int x, y;
boolean isVisible;
public Sprite()
{
isVisible = false;
image = null;
}
public Sprite(Image i)
{
isVisible = true;
image = i;
x = 10;
y = 10;
}
public void setImage(Image img)
{
image = img;
isVisible = true;
}
public void setLocation(int _x, int _y)
{
x = _x;
y = _y;
}
public Rectangle getDimensions()
{
return new Rectangle(x, y, image.getWidth(null), image.getHeight(null));
}
public boolean intersects(Sprite s)
{
return getDimensions().intersects(s.getDimensions());
}
public void setVisible(boolean vis)
{
isVisible = vis;
}
public void paintComponent(Graphics g)
{
if(isVisible)
{
g.drawImage(image, x, y, null);
}
}
}
class Coins extends Sprite
{
int amount;
public Coins(int amt)
{
amount = amt;
}
public int getAmount()
{
return amount;
}
public void setAmount(int amt)
{
amount = amt;
}
}
class AnimateSprite extends Sprite
{
int speed = 5;
int directionX = 1, directionY = 1;
int healthPoints = 100;
final boolean DEAD = false;
final boolean ALIVE = true;
public void moveUp()
{
y -= speed;
}
public void moveDown()
{
y += speed;
}
public void moveLeft()
{
x -= speed;
}
public void moveRight()
{
x += speed;
}
public int getHealthPoints()
{
return healthPoints;
}
public void setHealthPoints(int hp)
{
healthPoints = hp;
}
public boolean hit(int amt)
{
healthPoints -= amt;
if(healthPoints < 0)
return DEAD;
else
return ALIVE;
}
}
class Game extends AnimateSprite implements Runnable, KeyListener
{
AnimateSprite user;
AnimateSprite monster, troll;
Coins ten, twenty;
Thread thread;
Random r;
public void init()
{
r = new Random();
user = new AnimateSprite();
user.setImage(getImage(getCodeBase(), "player.gif"));
monster = new AnimateSprite();
monster.setImage(getImage(getCodeBase(), "monster.gif"));
troll = new AnimateSprite();
troll.setImage(getImage(getCodeBase(), "monster.gif"));
troll.setLocation(350, 350);
setupCoins();
setFocusable(true);
addKeyListener(this);
thread = new Thread(this);
thread.start();
}
public void setupCoins()
{
ten = new Coins(10);
twenty = new Coins(20);
ten.setLocation(400, 350);
twenty.setLocation(450, 50);
ten.setImage(getImage(getCodeBase(), "coins.gif"));
twenty.setImage(getImage(getCodeBase(), "coins.gif"));
}
public void keyPressed(KeyEvent ke) //Event handling
{
int key = ke.getKeyCode();
if(key == KeyEvent.VK_UP)
user.moveUp();
else if(key == KeyEvent.VK_DOWN)
user.moveDown();
else if(key == KeyEvent.VK_LEFT)
user.moveLeft();
else if(key == KeyEvent.VK_RIGHT)
user.moveRight();
}
public void keyReleased(KeyEvent ke) {}
public void keyTyped(KeyEvent ke) {}
public void update(Graphics g) {paint(g);}
public void paint(Graphics g)
{
g.clearRect(0, 0, this.getWidth(), this.getHeight());
ten.paintComponent(g);
twenty.paintComponent(g);
monster.setLocation(r.nextInt(10) - 5 + monster.x, r.nextInt(10 - 5 + monster.y));
monster.paintComponent(g);
user.paintComponent(g);
if(user.intersects(monster))
{
g.setFont(new Font("Serif", Font.BOLD, 26));
g.drawString("YOU HAVE DIED, YOU LOSE!", 20, 100); //Prints this when you lose
thread.interrupt(); //Stopping the thread if you die
}
}
public void run()
{
try //Try catch
{
while(true) //Only does this while when the boolean is true
{
repaint();
Thread.sleep(10); //Thread sleeps
}
} catch(Exception e) {} //Exception handling
}
}
Your order of inheritance seems odd, but its not whats causing the problem. Take a look at this website: http://www.dreamincode.net/forums/topic/28410-application-to-japplet-and-reverse/
Java Applets need to have init(), start(), stop(), and destroy(). You will need to put these methods in your Sprite class for the Applet to function.
I have a program that makes an ellipse when you click somewhere on a JPanel. When I did a test run, it just made slanted lines to the right of where I clicked. Can anyone find the problem? Thanks, here is the code:
This is the code for the click:
final SpriteField mSpritePanel = new SpriteField();
mSpritePanel.addMouseListener(new MouseAdapter()
{
public void mouseClicked(MouseEvent e)
{
float tX = e.getX();
float tY = e.getY();
int tIntWidth;
int tIntHeight;
int tIntRotate = 0;
if(tTextWidth.getText() == null)
{
tTextWidth.setText("50");
}
try
{
tIntWidth = Integer.parseInt(tTextWidth.getText());
}
catch(NumberFormatException ex)
{
tIntWidth = 50;
}
if(tIntWidth == 0)
{
tIntWidth = 50;
}
if(tTextHeight.getText() == null)
{
tTextHeight.setText("50");
}
try
{
tIntHeight = Integer.parseInt(tTextHeight.getText());
}
catch(NumberFormatException ex)
{
tIntHeight = 50;
}
if(tIntHeight == 0)
{
tIntHeight = 50;
}
if(tTextRotation.getText() == null)
{
tTextRotation.setText("0");
}
try
{
tIntRotate = Integer.parseInt(tTextRotation.getText());
}
catch(NumberFormatException ex)
{
tIntRotate = 50;
}
mSpritePanel.CreateSpriteAt(tX, tY, tIntWidth, tIntHeight, tIntRotate);
mSpritePanel.repaint();
}
});
This is the code for my SpriteField class:
public class SpriteField extends JPanel
{
final List<RoundSprite> mSpriteList = new ArrayList<RoundSprite>();
public void CreateSpriteAt(float tX, float tY, int tWidth, int tHeight, int tRotation)
{
RoundSprite mSprite = new RoundSprite();
mSprite.SetPosition(tX, tY);
mSprite.SetSpriteWidth(tWidth);
mSprite.SetSpriteHeight(tHeight);
mSprite.SetSpriteRotation(tRotation);
mSpriteList.add(mSprite);
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
AffineTransform originalTransform = g2.getTransform();
for (RoundSprite mSprite : mSpriteList)
{
mSprite.DrawSprite(g2);
g2.setTransform(originalTransform);
}
}
}
And this is the code for my RoundSprite class:
public class RoundSprite
{
private float mX;
private float mY;
int mWidth;
int mHeight;
int mRotate;
Color mColor;
void DrawSprite(Graphics2D g2)
{
AffineTransform tOldTransform = g2.getTransform();
g2.setColor(mColor);
g2.translate(mX, mY);
g2.rotate(mRotate);
g2.translate(mX - (mWidth / 2), mY - (mHeight / 2));
g2.draw(new Ellipse2D.Double(0, 0, mWidth, mHeight));
g2.setTransform(tOldTransform);
}
public void SetSpriteWidth(int tWidth)
{
mWidth = tWidth;
}
public void SetSpriteHeight(int tHeight)
{
mWidth = tHeight;
}
public void SetSpriteColor(Color tColor)
{
mColor = tColor;
}
public void SetPosition(float x, float y)
{
mX = x;
mY = y;
}
public void SetSpriteRotation(int tRotate)
{
mRotate = tRotate;
}
}
You've got a copy-paste error in your setters for RoundSprite:
public void SetSpriteWidth(int tWidth)
{
mWidth = tWidth; // set the width
}
public void SetSpriteHeight(int tHeight)
{
mWidth = tHeight; // set the width again, leaving mHeight forever 0...
}
This leaves all of your ellipses one-dimensional, so they render as a line.
Just make it mHeight = tHeight instead and your program will work.
Update to respond to the comment on ellipse location
In the RoundSprite#DrawSprite() method you are calling g2.translate() twice. g2.translate() is a relative, not absolute position change, so you don't want to give the mouse coordinates the second time you call it.
replace this:
g2.translate(mX - (mWidth / 2), mY - (mHeight / 2));
with this:
g2.translate(-mWidth/2, -mHeight/2);
to center the ellipse around the mouseclick location.
I am trying to write a Java application which draws multiple balls on screen which bounce off of the edges of the frame. I can successfully draw one ball. However when I add the second ball it overwrites the initial ball that I have drawn. The code is:
import java.awt.*;
import javax.swing.*;
import java.util.ArrayList;
import java.util.List;
public class Ball extends JPanel implements Runnable {
List<Ball> balls = new ArrayList<Ball>();
Color color;
int diameter;
long delay;
private int x;
private int y;
private int vx;
private int vy;
public Ball(String ballcolor, int xvelocity, int yvelocity) {
if(ballcolor == "red") {
color = Color.red;
}
else if(ballcolor == "blue") {
color = Color.blue;
}
else if(ballcolor == "black") {
color = Color.black;
}
else if(ballcolor == "cyan") {
color = Color.cyan;
}
else if(ballcolor == "darkGray") {
color = Color.darkGray;
}
else if(ballcolor == "gray") {
color = Color.gray;
}
else if(ballcolor == "green") {
color = Color.green;
}
else if(ballcolor == "yellow") {
color = Color.yellow;
}
else if(ballcolor == "lightGray") {
color = Color.lightGray;
}
else if(ballcolor == "magenta") {
color = Color.magenta;
}
else if(ballcolor == "orange") {
color = Color.orange;
}
else if(ballcolor == "pink") {
color = Color.pink;
}
else if(ballcolor == "white") {
color = Color.white;
}
diameter = 30;
delay = 40;
x = 1;
y = 1;
vx = xvelocity;
vy = yvelocity;
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(x,y,30,30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(x,y,30,30); //draws circle
}
public void run() {
while(isVisible()) {
try {
Thread.sleep(delay);
} catch(InterruptedException e) {
System.out.println("interrupted");
}
move();
repaint();
}
}
public void move() {
if(x + vx < 0 || x + diameter + vx > getWidth()) {
vx *= -1;
}
if(y + vy < 0 || y + diameter + vy > getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
}
private void start() {
while(!isVisible()) {
try {
Thread.sleep(25);
} catch(InterruptedException e) {
System.exit(1);
}
}
Thread thread = new Thread(this);
thread.setPriority(Thread.NORM_PRIORITY);
thread.start();
}
public static void main(String[] args) {
Ball ball1 = new Ball("red",3,2);
Ball ball2 = new Ball("blue",6,2);
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().add(ball1);
f.getContentPane().add(ball2);
f.setSize(400,400);
f.setLocation(200,200);
f.setVisible(true);
new Thread(ball1).start();
new Thread(ball2).start();
}
}
I wanted to create a List of balls and then cycle through drawing each of the balls but I'm still having trouble adding both balls to the Content Pane.
Thanks for any help.
With your current approach...
The main problem I can see is that you are placing two opaque components on top of each other...actually you may find you're circumventing one of them for the other...
You should be using a null layout manager, otherwise it will take over and layout your balls as it sees fit.
You need to ensure that you are controlling the size and location of the ball pane. This means you've taken over the role as the layout manager...
You need to randomize the speed and location of the balls to give them less chances of starting in the same location and moving in the same location...
Only update the Ball within the context of the EDT.
You don't really need the X/Y values, you can use the panels.
.
public class AnimatedBalls {
public static void main(String[] args) {
new AnimatedBalls();
}
public AnimatedBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new Balls());
frame.setSize(400, 400);
frame.setVisible(true);
}
});
}
public class Balls extends JPanel {
public Balls() {
setLayout(null);
// Randomize the speed and direction...
add(new Ball("red", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
add(new Ball("blue", 10 - (int) Math.round((Math.random() * 20)), 10 - (int) Math.round((Math.random() * 20))));
}
}
public class Ball extends JPanel implements Runnable {
Color color;
int diameter;
long delay;
private int vx;
private int vy;
public Ball(String ballcolor, int xvelocity, int yvelocity) {
if (ballcolor == "red") {
color = Color.red;
} else if (ballcolor == "blue") {
color = Color.blue;
} else if (ballcolor == "black") {
color = Color.black;
} else if (ballcolor == "cyan") {
color = Color.cyan;
} else if (ballcolor == "darkGray") {
color = Color.darkGray;
} else if (ballcolor == "gray") {
color = Color.gray;
} else if (ballcolor == "green") {
color = Color.green;
} else if (ballcolor == "yellow") {
color = Color.yellow;
} else if (ballcolor == "lightGray") {
color = Color.lightGray;
} else if (ballcolor == "magenta") {
color = Color.magenta;
} else if (ballcolor == "orange") {
color = Color.orange;
} else if (ballcolor == "pink") {
color = Color.pink;
} else if (ballcolor == "white") {
color = Color.white;
}
diameter = 30;
delay = 100;
vx = xvelocity;
vy = yvelocity;
new Thread(this).start();
}
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
int x = getX();
int y = getY();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.setColor(color);
g.fillOval(0, 0, 30, 30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(0, 0, 30, 30); //draws circle
}
#Override
public Dimension getPreferredSize() {
return new Dimension(30, 30);
}
public void run() {
try {
// Randamize the location...
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
int x = (int) (Math.round(Math.random() * getParent().getWidth()));
int y = (int) (Math.round(Math.random() * getParent().getHeight()));
setLocation(x, y);
}
});
} catch (InterruptedException exp) {
exp.printStackTrace();
} catch (InvocationTargetException exp) {
exp.printStackTrace();
}
while (isVisible()) {
try {
Thread.sleep(delay);
} catch (InterruptedException e) {
System.out.println("interrupted");
}
try {
SwingUtilities.invokeAndWait(new Runnable() {
#Override
public void run() {
move();
repaint();
}
});
} catch (InterruptedException exp) {
exp.printStackTrace();
} catch (InvocationTargetException exp) {
exp.printStackTrace();
}
}
}
public void move() {
int x = getX();
int y = getY();
if (x + vx < 0 || x + diameter + vx > getParent().getWidth()) {
vx *= -1;
}
if (y + vy < 0 || y + diameter + vy > getParent().getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
// Update the size and location...
setSize(getPreferredSize());
setLocation(x, y);
}
}
}
The "major" problem with this approach, is each Ball has it's own Thread. This is going to eat into your systems resources real quick as you scale the number of balls up...
A Different Approach
As started by Hovercraft, you're better off creating a container for the balls to live in, where the balls are not components but are "virtual" concepts of a ball, containing enough information to make it possible to bounce them off the walls...
public class SimpleBalls {
public static void main(String[] args) {
new SimpleBalls();
}
public SimpleBalls() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException ex) {
} catch (InstantiationException ex) {
} catch (IllegalAccessException ex) {
} catch (UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Spot");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
Balls balls = new Balls();
frame.add(balls);
frame.setSize(400, 400);
frame.setVisible(true);
new Thread(new BounceEngine(balls)).start();
}
});
}
public static int random(int maxRange) {
return (int) Math.round((Math.random() * maxRange));
}
public class Balls extends JPanel {
private List<Ball> ballsUp;
public Balls() {
ballsUp = new ArrayList<Ball>(25);
for (int index = 0; index < 10 + random(90); index++) {
ballsUp.add(new Ball(new Color(random(255), random(255), random(255))));
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball ball : ballsUp) {
ball.paint(g2d);
}
g2d.dispose();
}
public List<Ball> getBalls() {
return ballsUp;
}
}
public class BounceEngine implements Runnable {
private Balls parent;
public BounceEngine(Balls parent) {
this.parent = parent;
}
#Override
public void run() {
int width = getParent().getWidth();
int height = getParent().getHeight();
// Randomize the starting position...
for (Ball ball : getParent().getBalls()) {
int x = random(width);
int y = random(height);
Dimension size = ball.getSize();
if (x + size.width > width) {
x = width - size.width;
}
if (y + size.height > height) {
y = height - size.height;
}
ball.setLocation(new Point(x, y));
}
while (getParent().isVisible()) {
// Repaint the balls pen...
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
getParent().repaint();
}
});
// This is a little dangrous, as it's possible
// for a repaint to occur while we're updating...
for (Ball ball : getParent().getBalls()) {
move(ball);
}
// Some small delay...
try {
Thread.sleep(100);
} catch (InterruptedException ex) {
}
}
}
public Balls getParent() {
return parent;
}
public void move(Ball ball) {
Point p = ball.getLocation();
Point speed = ball.getSpeed();
Dimension size = ball.getSize();
int vx = speed.x;
int vy = speed.y;
int x = p.x;
int y = p.y;
if (x + vx < 0 || x + size.width + vx > getParent().getWidth()) {
vx *= -1;
}
if (y + vy < 0 || y + size.height + vy > getParent().getHeight()) {
vy *= -1;
}
x += vx;
y += vy;
ball.setSpeed(new Point(vx, vy));
ball.setLocation(new Point(x, y));
}
}
public class Ball {
private Color color;
private Point location;
private Dimension size;
private Point speed;
public Ball(Color color) {
setColor(color);
speed = new Point(10 - random(20), 10 - random(20));
size = new Dimension(30, 30);
}
public Dimension getSize() {
return size;
}
public void setColor(Color color) {
this.color = color;
}
public void setLocation(Point location) {
this.location = location;
}
public Color getColor() {
return color;
}
public Point getLocation() {
return location;
}
public Point getSpeed() {
return speed;
}
public void setSpeed(Point speed) {
this.speed = speed;
}
protected void paint(Graphics2D g2d) {
Point p = getLocation();
if (p != null) {
g2d.setColor(getColor());
Dimension size = getSize();
g2d.fillOval(p.x, p.y, size.width, size.height);
}
}
}
}
Because this is driven by a single thread, it is much more scalable.
You can also check out the images are not loading which is a similar question ;)
You need to use two completely distinct classes here -- one for BallContainer which extends JPanel and is the component that draws the Balls, and another for Ball which does not extend anything but rather holds the coordinates and Color of a Ball. BallContainer should hodl a List<Ball> that it iterates through when it moves them and when it paints them.
What you need to do is augment your paintComponent method.
Instead of just drawing one ball, you need to loop through them all, and draw each one.
Example:
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
for (Ball b: balls) {
g.setColor(color);
g.fillOval(x,y,30,30); //adds color to circle
g.setColor(Color.black);
g2.drawOval(x,y,30,30); //draws circle
}
}
package BouncingBallApp.src.copy;
import java.awt.*;
public class Ball {
private Point location;
private int radius;
private Color color;
private int dx, dy;
//private Color[] ballArr;
public Ball(Point l, int r, Color c){
location = l;
radius = r;
color = c;
}
public Ball(Point l, int r){
location = l;
radius = r;
color = Color.RED;
}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public int getRadius() {
return radius;
}
public void setRadius(int radius) {
this.radius = radius;
}
public Color getColor() {
return color;
}
public void setColor(Color color) {
this.color = color;
}
public void setMotion(int dx, int dy){
this.dx = dx;
this.dy = dy;
}
public void move(){
location.translate(dx, dy);
}
public void moveTo(int x, int y){
location.move(x, y);
}
public void paint (Graphics g) {
g.setColor (color);
g.fillOval (location.x-radius, location.y-radius, 2*radius, 2*radius);
}
public void reclectHoriz() {
dy = -dy;
}
public void reclectVert() {
dx = -dx;
}
}
package BouncingBallApp.src.copy;
public class MyApp {
public static void main(String[] args) {
MyFrame frm = new MyFrame(10);
frm.setVisible(true);
for (int i=0; i<1000; i++){
frm.stepTheBall();
}
}
}
package BouncingBallApp.src.copy;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Point;
import java.util.Random;
import javax.swing.JFrame;
public class MyFrame extends JFrame {
public final int FRAMEWIDTH = 600;
public final int FRAMEHEIGHT = 400;
private Ball[] ballArr;
private Random random =new Random ();
private Color[] colors={Color.RED,Color.blue,Color.yellow};
private int ballCnt;
public MyFrame(int ballCnt){
super();
setSize(FRAMEWIDTH, FRAMEHEIGHT);
setTitle("My Bouncing Ball Application");
ballArr = new Ball[ballCnt];
this.ballCnt = ballCnt;
int c;
for (int i=0; i < ballCnt; i++){
int bcn =random.nextInt(colors.length);
Color ballcolor=colors[bcn];
ballArr[i] = new Ball(new Point(50,50),c=(int) (Math.random()*10+3)%8,ballcolor);
int ddx = (int) (Math.random()*10+2)%8;
int ddy = (int) (Math.random()*10+2)%8;
ballArr[i].setMotion(ddx, ddy);
//c++;
}
}
public void paint(Graphics g){
super.paint(g);
for (int i=0; i < ballCnt; i++){
ballArr[i].paint(g);
}
}
public void stepTheBall(){
for (int i=0; i < ballCnt; i++){
ballArr[i].move();
Point loc = ballArr[i].getLocation();
if (loc.x < ballArr[i].getRadius() ||
loc.x > FRAMEWIDTH-ballArr[i].getRadius()){
ballArr[i].reclectVert();
}
if (loc.y < ballArr[i].getRadius() ||
loc.y > FRAMEHEIGHT-ballArr[i].getRadius()){
ballArr[i].reclectHoriz();
}
}
repaint();
try {
Thread.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}