Ok so I have looked around and I didn't find anything that helped me with this so I thought I would go ahead and ask here. I want to have a bunch of circles that collide bounce and generally interact with each other. The problem I am running into is that drawing a lot of circles eats away at the frame rate. So given an array of circles which I have to draw, all of them. Is the only way to draw them simply to draw them all using g.fillOval() in the paintComponent?
For example, in the paint component, I call a function drawBalls(Graphics2D g) that draws all the balls it contains. (that's how I draw stuff by passing it through to the object that draws I don't know if there is a better way)
for(Ball ball:balls){
ball.drawYourself(g) //g is the Graphics2D
}
Thanks
Edit. Sorry if it was a bad question here is the code of my program. I hope it isn't too long.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
import java.io.*;
import javax.imageio.*;
import java.util.*;
import javax.swing.Timer;
//###################################################\\
public class Game extends JFrame implements ActionListener{
private Timer myTimer;
private GamePanel panel;
public Game() {
super("Things.py");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setSize(1000,800);
panel = new GamePanel(this);
add(panel);
myTimer = new Timer(1, this);
myTimer.start();
setResizable(false);
setVisible(true);
}
public void actionPerformed(ActionEvent evt){
panel.repaint();
}
public static void main(String[] arguments) {
Game frame = new Game();
}
}
//###################################################\\
//###################################################\\
class GamePanel extends JPanel implements MouseMotionListener, MouseListener,KeyListener{
// ------------ Variables ----------------------------------------------
private int mx,my;//Mouse position x and y
private boolean[]keys;//If the keys are pressed
private Boolean mousepressed=false;//If the mouse is pressed down
private Game mainframe;//The panel that created this GamePanel
private Color backmenuc;
private Color backcolor;
private bounceBalls bballs;
private bounceBalls bballs2;
// ------------ Constructor --------------------------------------------
public GamePanel(Game m){
//----- Listeners -----
addMouseMotionListener(this);//LISTEN TO ME!!!
addMouseListener(this);//Listen to me!
addKeyListener(this);//listen to me pls
//----- Variables -----
mainframe=m;
keys=new boolean[KeyEvent.KEY_LAST+1];//creates the array for keys
backcolor=new Color(0,0,0);
backmenuc=new Color(255,255,255);
bballs=new bounceBalls(this);
bballs2=new bounceBalls(this);
//----- Load Images -----
//----- End -----
}
// ------------ Drawing ------------------------------------------------
public void paintComponent(Graphics gg){
Graphics2D g=(Graphics2D) gg;
paint.drawRect(g,0,0,getWidth(),getHeight(),backcolor);
if (mousepressed){
bballs.addBall(mx,my,10);
bballs2.addBall(mx+100,my,10);
}
bBallThreading b1=new bBallThreading(g,bballs,"B1");
b1.start();
bBallThreading b2=new bBallThreading(g,bballs2,"B1");
b2.start();
//bballs.update();
//bballs2.update();
bballs.draw(g);
bballs2.draw(g);
System.out.println(bballs.size());
}
// ------------ Misc ---------------------------------------------------
public void addNotify() {
super.addNotify();
}
// ------------ MouseListener ------------------------------------------
public void mouseEntered(MouseEvent e) {}
public void mouseExited(MouseEvent e) {}
public void mouseReleased(MouseEvent e) {
mousepressed=false;
}
public void mouseClicked(MouseEvent e){
}
public void mousePressed(MouseEvent e){
mousepressed=true;
}
// ---------- MouseMotionListener --------------------------------------
public void mouseDragged(MouseEvent e){
mx=e.getX();
my=e.getY();
}
public void mouseMoved(MouseEvent e){
mx=e.getX();
my=e.getY();
}
// ---------- KeyListener -----------------------------------------------
public void keyTyped(KeyEvent e) {}
public void keyPressed(KeyEvent e) {
keys[e.getKeyCode()] = true;
}
public void keyReleased(KeyEvent e) {
keys[e.getKeyCode()] = false;
}
}
//###################################################\\
class bounceBall{
private double bx,by;
private int br;
private double vx,vy;
public bounceBall(int x,int y,int radius){
bx=(double)x;
by=(double)y;
br=radius;
vx=0;
vy=0;
}
public void move(){
if((by+vy)>800){
vy*=-.9;
}
bx+=vx;
by+=vy;
}
public void addV(double x, double y){
vx+=x;
vy+=y;
}
public void draw(Graphics2D g){
g.setColor(new Color(255,0,0));
g.fillOval((int)bx-br,(int)by-br,br*2,br*2);
}
}
class bounceBalls{
GamePanel gp;
ArrayList<bounceBall> balls;
double gravity;
public bounceBalls(GamePanel gamep){
gp=gamep;
gravity=.1;
balls=new ArrayList<bounceBall>();
}
public void draw(Graphics2D g){
//paint.setAA(g,true);
paint.setAlpha(g,.1f);
for(bounceBall ball:balls.toArray(new bounceBall[balls.size()])){
ball.draw(g);
}
paint.setAlpha(g,1f);
//paint.setAA(g,false);
}
public void setGravity(double g){
gravity=g;
}
public void update(){
for(bounceBall ball:balls.toArray(new bounceBall[balls.size()])){
ball.addV(0,gravity);
ball.move();
}
}
public void addBall(int x, int y, int radius){
balls.add(new bounceBall(x,y,radius));
}
public int size(){
return balls.size();
}
}
class bBallThreading implements Runnable{
private Thread t;
private String threadname;
Graphics2D g;
private bounceBalls bballs;
public bBallThreading(Graphics2D g, bounceBalls bballs,String s){
this.g=g;
this.bballs=bballs;
threadname=s;
}
public void run(){
bballs.update();
}
public void start(){
if (t == null)
{
t = new Thread (this, threadname);
t.start ();
}
}
}
//###################################################\\
When I run this I get 4000 circles at 30 fps
bBallThreading b1=new bBallThreading(g,bballs,"B1");
b1.start();
bBallThreading b2=new bBallThreading(g,bballs2,"B1");
b2.start();
The paintComponent() method is for painting. That is all is should do.
You should NOT be starting a Thread in a painting method. Every time Swing paints the component you will be starting two more Threads.
Add a System.out.println(...) statement to that method to see how many times that method is executed.
You should start the Thread when you start the animation.
Related
My program has 3 java files, namely Frame, Dude(which contains the character) and Board(which implements the actionListener). My program is not throwing any error and the images(background and character) are rendering good. But the character is not moving forward.
import javax.swing.*;
public class Frame {
public static void main(String[] args){
JFrame frame= new JFrame("2D Game");
frame.add(new Board());
frame.setVisible(true);
frame.setSize(1200, 600);
}
}
import java.awt.*;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Dude {
int x, dx, y;
Image still;
public Dude(){
ImageIcon i = new ImageIcon("/home/amitabh/Pictures/man1.jpg");
still= i.getImage();
x=10;
y=172;
}
public void move(){
x=x+dx;
}
public int getX(){
return x;
}
public int getY(){
return y;
}
public Image getImage(){
return still;
}
public void keyPressed(KeyEvent e){
int key= e.getKeyCode();
if(key== KeyEvent.VK_LEFT);
dx= -1;
if(key== KeyEvent.VK_RIGHT);
dx= 1;
}
public void keyReleased(KeyEvent e){
int key= e.getKeyCode();
if(key==KeyEvent.VK_LEFT);
dx=0;
if(key==KeyEvent.VK_RIGHT);
dx=0;
}
}
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
public class Board extends JPanel implements ActionListener{
Image img;
Timer time;
Dude p;
public Board(){
p= new Dude();
addKeyListener(new AL());
setFocusable(true);
ImageIcon i= new ImageIcon("/home/amitabh/Pictures/game1.png");
img= i.getImage();
time= new Timer(5,this);
time.start();
}
#Override
public void actionPerformed(ActionEvent e) {
p.move();
repaint();
}
public void paint(Graphics g){
super.paint(g);
Graphics2D g2d= (Graphics2D)g;
g2d.drawImage(img, 0,0, null);
g2d.drawImage(p.getImage(), p.getX(), p.getY(), null);
}
public class AL extends KeyAdapter{
public void keyReleased(KeyEvent e){
p.keyReleased(e);
}
public void KeyPressed(KeyEvent e){
p.keyPressed(e);
}
}
}
Start by talking a very close look at:
if (key == KeyEvent.VK_LEFT);
Does that look funny to you?
if (key == KeyEvent.VK_LEFT); // <--- What's the ; doing here?
Change it to be more like...
if (key == KeyEvent.VK_LEFT) {
dx = 0;
}
And, yes, this is why you're encouraged to use { ... }
Next, take a closer look at...
public void KeyPressed(KeyEvent e) {
See anything wrong there? Why does it start with an uppercase K, that's not the correct method signature
Change to something more like...
#Override
public void keyReleased(KeyEvent e) {
p.keyReleased(e);
}
#Override
public void keyPressed(KeyEvent e) {
p.keyPressed(e);
}
Yes, this is why you're encouraged to use #Override ;)
And finally change your paint method to paintComponent
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
You are encouraged to override paintComponent when performing custom painting, it tends to cause less issues
I'd also encorage you to have a look at the Key Bindings API and favour it over KeyListener as it provides better control of the focus level required to trigger the key events
I'd also encourage your to override getPreferredSize of the Board and return your preferred size from there, rather then setting the frame's size. The frame size includes the frame's decorations, so you content is smaller then the frame size
I can draw static things to the screen, but I want to make them move with user key input. I don't know what to do, I've been searching and searching and haven't come up with an answer yet. Please help!
package com.Game.game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Game extends JFrame
{
final static int width = 500;
final static int height = 500;
public int x = 250;
public int y = 250;
public int changeX = 10;
public int changeY = 10;
public static void main(String[] args)
{
new Game();
}
public Game()
{
KeyListener listener = new KeyListening();
addKeyListener(listener);
setFocusable(true);
DrawingStuff drawingstuff = new DrawingStuff();
add(drawingstuff);
setSize(width, height);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public class DrawingStuff extends JPanel
{
public void paintComponent(Graphics g)
{
super.paintComponent(g);
g.drawString("Hey there!", 300, 300);
g.setColor(Color.RED);
g.fillRect(x, y, 50, 50);
}
}
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
#Override
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode() == KeyEvent.VK_UP)
{
y = y + changeY;
System.out.println("Hey");
drawingstuff.repaint();
}
}
#Override
public void keyReleased(KeyEvent e)
{
}
#Override
public void keyTyped(KeyEvent e)
{
}
}
public void update()
{
}
}
EDIT: Fixed it. I took away the key listener stuff in the constructor method, added a command to focus on "drawingstuff" in the constructor method, and, most importantly, added this bit of code to the end of the constructor method:
while(true)
{
drawingstuff.repaint();
}
The problem is that your KeyListening object has a reference to a different DrawingStuff object than the one you added to your UI inside the Game constructor.
public class KeyListening implements KeyListener
{
DrawingStuff drawingstuff = new DrawingStuff();
...
You should pass a DrawingStuff reference to the KeyListening instance so that it can tell the right object to repaint itself.
Here's my simple code which draws oval near mouse cursor.Each time I click frame is repainted and only one oval can be draw at the time.I would like to know how to make each oval drawn on click to stay on frame.Thank you for each suggestion.
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
public class Buffer extends JPanel implements MouseListener{
public static JFrame frame;
public static boolean check;
public void paintComponent(Graphics g){
super.paintComponent(g);
if(check==true){
g.drawOval((int)MouseInfo.getPointerInfo().getLocation().getX(), (int)MouseInfo.getPointerInfo().getLocation().getY(), 10, 10);
}
}
public static void main(String args[]){
Buffer x=new Buffer();
x.setBackground(Color.cyan);
frame=new JFrame();
frame.setSize(500,500);
frame.addMouseListener(x);
frame.add(x);
frame.setVisible(true);
}
public void mouseClicked(MouseEvent e){
check=true;
repaint();
}
public void mouseEntered(MouseEvent arg0){}
public void mouseExited(MouseEvent arg0){}
public void mousePressed(MouseEvent arg0){}
public void mouseReleased(MouseEvent arg0){}
}
Make an ArrayList of objects representing the ovals. In paintComponent, draw each oval in the list. In the mouse listener, add an oval to the list. Here's an example:
public class Buffer extends JPanel implements MouseListener {
...
private List<Ellipse2D> ovals = new ArrayList<Ellipse2D>();
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (Ellipse2D oval : ovals)
g2d.draw(oval);
}
public void mouseClicked(MouseEvent e) {
ovals.add(new Ellipse2D.Double(e.getX(), e.getY(), 10, 10);
repaint();
}
}
I have a Java paint program that uses a custom JPanel to paint on. While when clicking on the JPanel paints a small oval (or circle, if you will), the oval disappears each time you click on another place. The coordinates also get updated, but the oval does not stay, it moves to wherever the user clicks next...
Here's the code for the custom JPanel:
int xCord, yCord;
public class PaintPanel extends JPanel implements MouseListener {
// default serial whatever...
private static final long serialVersionUID = -6514297510194472060L;
// initial values
int xCord = -10;
int yCord = -10;
public PaintPanel() {
addMouseListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(ProgramUI.currentColor);
g.fillOval(xCord, yCord, 8, 8);
repaint();
}
#Override
public void mouseClicked(MouseEvent m) {
}
#Override
public void mouseEntered(MouseEvent m) {
}
#Override
public void mouseExited(MouseEvent m) {
}
#Override
public void mousePressed(MouseEvent m) {
if (paintPanel.contains(m.getPoint())) {
xCord = m.getX();
yCord = m.getY();
System.out.println("x: " + xCord + " y: " + yCord);
}
}
#Override
public void mouseReleased(MouseEvent m) {
}
}
I need the holding of a mouse to continuously paint an oval until the mouse button is let go. The only problem here is that the mouse oval updates, but does not save it's original position. How do I fix this?
Only one oval is drawn as there is only one fillOval statement drawing a single oval in the paintComponent method so the statement
super.paintComponent(g);
causes any previous painting to be cleared once repaint is called.
To paint multiple ovals, you can paint components from a List<Point> as outlined in Custom Painting Approaches
Don't call repaint from within paintComponent. This creates an infinite loop and degrades performance. If periodic updates are required invoke repaint from the ActionListener of a Swing Timer instead.
that is because the component repaints itself, to make the change permanent you should take the image of the jpanel and set it as background once you finish drawing each time...
You are only painting the last place the user clicked each time. Instead, you need to collect the past clicks and paint them all each time.
This code will do what you want:
package com.sandbox;
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.util.ArrayList;
public class SwingSandbox {
public static void main(String[] args) {
JFrame frame = buildFrame();
frame.add(new PaintPanel());
}
public static class PaintPanel extends JPanel implements MouseListener {
// default serial whatever...
private static final long serialVersionUID = -6514297510194472060L;
ArrayList<Point> points = new ArrayList<Point>();
public PaintPanel() {
addMouseListener(this);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(new Color(250));
for (Point point : points) {
g.fillOval(point.x, point.y, 8, 8);
}
repaint();
}
#Override
public void mouseClicked(MouseEvent m) {
}
#Override
public void mouseEntered(MouseEvent m) {
}
#Override
public void mouseExited(MouseEvent m) {
}
#Override
public void mousePressed(MouseEvent m) {
if (this.contains(m.getPoint())) {
points.add(m.getPoint());
}
}
#Override
public void mouseReleased(MouseEvent m) {
}
}
private static JFrame buildFrame() {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
frame.setSize(200, 200);
frame.setVisible(true);
return frame;
}
}
I made the applet Bouncing Ball and in the class Ball.java I made inner class TimerListener with method repaint(), and when I run the applet, instead of repaint the ball, java paint the ball again and again(not delete, and then paint).
here is my code for the class Ball.java
import javax.swing.Timer;
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Ball extends JPanel {
private int delay = 10;
Timer timer=new Timer(delay, new TimerListener());
private int x=0;
private int y=0;
private int dx=20;
private int dy=20;
private int radius=5;
private class TimerListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
repaint();
}
}
public void paintComponent(Graphics g){
g.setColor(Color.red);
if(x<radius) dx=Math.abs(dx);
if(x>(getWidth()-radius)) dx=-Math.abs(dx);
if(y>(getHeight()-radius)) dy=-Math.abs(dy);
if(y<radius) dy=Math.abs(dy);
x+=dx;
y+=dy;
g.fillOval(x-radius, y-radius, radius*2, radius*2);
}
public void suspend(){
timer.stop();
}
public void resume(){
timer.start();
}
public void setDelay(int delay){
this.delay=delay;
timer.setDelay(delay);
}
}
here is my code for class BallControl.java
import javax.swing.*;
import java.awt.event.*;
import java.awt.*;
public class BallControl extends JPanel{
private Ball ball = new Ball();
private JButton jbtSuspend = new JButton("Suspend");
private JButton jbtResume = new JButton("Resume");
private JScrollBar jsbDelay = new JScrollBar();
public BallControl(){
JPanel panel = new JPanel();
panel.add(jbtSuspend);
panel.add(jbtResume);
//ball.setBorder(new javax.swing.border.LineBorder(Color.red));
jsbDelay.setOrientation(JScrollBar.HORIZONTAL);
ball.setDelay(jsbDelay.getMaximum());
setLayout(new BorderLayout());
add(jsbDelay, BorderLayout.NORTH);
add(ball, BorderLayout.CENTER);
add(panel, BorderLayout.SOUTH);
jbtSuspend.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.suspend();
}
});
jbtResume.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
ball.resume();
}
});
jsbDelay.addAdjustmentListener(new AdjustmentListener() {
public void adjustmentValueChanged(AdjustmentEvent e) {
ball.setDelay(jsbDelay.getMaximum() - e.getValue());
}
});
}
}
Isn't the Timer supposed to also change the Ball object's position? In other words, isn't it supposed to change its x and y values? i.e.,
private class TimerListener implements ActionListener{
public void actionPerformed(ActionEvent e) {
// first change x and y here *****
repaint();
}
}
Else, how is the ball supposed to move much less bounce?
You seem to have this change position code in your paintComponent(...) method, and that is not good since you don't have full control over when or even if this method gets called. For this reason, program logic and code that changes this object's state does not belong inside of that method.
Also, your paintComponent(...) method override needs a call to to the super's paintComponent(...) method on its first line so that the old ball can be erased before a new ball is drawn.