I'm going to create a moving circle for my later project, and the circle will keep moving, and it interior color will change like color emitting , the changing color will from little circle to larger circle in 5 levels, so how to keep each color change to stay a while and I hope these code present with thread, so I create two thread for the purpose, one control circle moving, another control the circle's interior color emit
here is my code:
import java.awt.*;
import static java.awt.Color.black;
import static java.awt.Color.yellow;
import static java.awt.FlowLayout.RIGHT;
import java.awt.event.*;
import java.awt.geom.Arc2D;
import java.awt.geom.Ellipse2D;
import java.awt.geom.GeneralPath;
import java.awt.geom.Rectangle2D;
import static java.lang.Math.abs;
import java.util.Random;
import javax.swing.*;
import java.util.concurrent.ExecutorService;
class thepane extends JPanel implements Runnable{
public float x,y,r;
public float speedx,speedy;
thepane(float lx,float ly,float lr, float sx,float sy){
loadspeed(sx,sy);
load(lx,ly,lr);
for(int i=0;i<5;i++)
fc[i]=new Color(nd.nextInt(255),nd.nextInt(255),nd.nextInt(255));
}
public void load(float lx,float ly,float lr){
x=lx;y=ly;r=lr;
}
public void loadspeed(float sx,float sy){
speedx=sx;speedy=sy;
}
public void xmoving(){
x+=speedx;
}
public void ymoving(){
y-=speedy;
}
public void touchbond(){
if(x>getWidth()-r||x<0)
speedx*=-1;
if(y>getHeight()-r||y<0)
speedy*=-1;
if(x>getWidth()-r)
x=getWidth()-r;
else if(x<0)
x=0;
if(y>getHeight()-r)
y=getHeight()-r;
else if(y<0)
y=0;
}
Random nd=new Random();
int colorcount=0;
int emitcount=0;
boolean emit=false;
Color[] fc=new Color[5];
Graphics2D comp2D ;
Thread athread;
#Override
public void paintComponent(Graphics comp) {
comp2D = (Graphics2D) comp;
//create rectangle background
comp2D.setColor(Color.BLACK);
comp2D.fillRect(0, 0, getWidth(), getHeight());
//set information text
comp2D.setFont( new Font("Arial", Font.BOLD, 12));
comp2D.setColor(Color.WHITE);
comp2D.drawString("Centre("+(x+r/2)+' '+(y+r/2)+"), xspeed: "+speedx+" yspeed: "+speedy, 10f,10f );
comp2D.drawString("panel width "+getWidth()+" panel height "+getHeight()+" circle radius "
+r, 10f, 22f);
}
//thread run()
#Override
public void run() {
x=100;y=100;
System.out.println("thread in pane start!!!! (current colorcount = "+colorcount+')');
while(true){
circleEmit(fc[colorcount%5]);
repaint();
sleeping(1);
// comp2D=(Graphics2D)this.getGraphics();
// colorEmit(comp2D);
}
}
//wait method
public void waiting(){
try{wait();}
catch(Exception e){}}
public void waiting2D(int time){
try{comp2D.wait(time);}
catch(Exception e){}
}
public void waiting(int time){
try{wait(time);}
catch(Exception e){}
}
//sleep method
public void sleeping(int n){
try{
Thread.sleep(n);
}catch(Exception f){
System.out.print(f);
}
}
Ellipse2D.Float[] e=new Ellipse2D.Float[5];
public void loade(){
float centrex=x+r/2,centrey=y+r/2;
e[0]= new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
e[1]= new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
e[2]= new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
e[3]= new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
e[4]= new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r);
}
public Color ff;
public synchronized void circleEmit(Color fc){
comp2D=(Graphics2D)this.getGraphics();
loade();
comp2D.setColor(fc);
comp2D.fill(e[emitcount%5]);
waiting(5);
emitcount++;
}
public synchronized void callnotify(){
this.notify();
}
//iterative way to generate color emit
public void colorEmit(Graphics2D comp2D){
//create circle
//set circle property
float centrex=x+r/2,centrey=y+r/2;//so x=centrex-r/2;y=centrey+r/2
Ellipse2D.Float e1 = new Ellipse2D.Float(centrex-r/10, centrey-r/10, r/5, r/5);
Ellipse2D.Float e2 = new Ellipse2D.Float(centrex-r/5, centrey-r/5, 2*r/5, 2*r/5);
Ellipse2D.Float e3 = new Ellipse2D.Float(centrex-3*r/10, centrey-3*r/10, 3*r/5, 3*r/5);
Ellipse2D.Float e4 = new Ellipse2D.Float(centrex-2*r/5, centrey-2*r/5, 4*r/5, 4*r/5);
Ellipse2D.Float e5 = new Ellipse2D.Float(centrex-r/2, centrey-r/2, r, r);
if(colorcount>=4)
emit(comp2D,fc[(colorcount-4)%5],e5);
waiting(1000);
if(colorcount>=3)
emit(comp2D,fc[(colorcount-3)%5],e4);
waiting(1000);
if(colorcount>=2)
emit(comp2D,fc[(colorcount-2)%5],e3);
waiting(1000);
if(colorcount>=1)
emit(comp2D,fc[(colorcount-1)%5],e2);
waiting(1000);
emit(comp2D,fc[colorcount%5],e1);
waiting(1000);
colorcount++;
}
private void emit(Graphics2D comp,Color thecolor,Ellipse2D.Float f){
comp.setColor(thecolor);
comp.fill(f);
}
}
//------------------------------------------------------------------------------------
//main class
public class drawpanel extends Thread implements ActionListener{
JFrame frame=new JFrame();
thepane panel;
JButton FlyingBalls=new JButton("balls"),exit=new JButton("Exit"),stop=new JButton("Stop");
JButton slow=new JButton("slow down"),resume=new JButton("resume");
Float x,y,r;
public void sleeping(int n){
try{
Thread.sleep(n);
}catch(Exception f){
System.out.print(f);
}
}
Thread newthread,pthread;
Thread[] five=new Thread[5];
drawpanel(){
frame.setTitle("FlyingBalls");
frame.setLocation(100, 100);
frame.setLayout(null);
//x,y,r,speedx,speedy
panel=new thepane(nd.nextInt(800),nd.nextInt(500),40,nd.nextFloat()*20+1,nd.nextFloat()*10+1);
panel.setSize(800,500);
frame.setSize(810,580);
frame.add(panel);
FlyingBalls.setSize(80,30);exit.setSize(70,30);stop.setSize(70,30);slow.setSize(140,30);
resume.setSize(100,30);
FlyingBalls.addActionListener(this);
exit.addActionListener(this);
stop.addActionListener(this);slow.addActionListener(this);resume.addActionListener(this);
frame.add(FlyingBalls);frame.add(exit); frame.add(stop);frame.add(slow);frame.add(resume);
FlyingBalls.setLocation(20,500);exit.setLocation(190, 500);stop.setLocation(110,500);
slow.setLocation(270,500);resume.setLocation(420,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//control moving ball
newthread=new Thread(this);
//control color change
for(int i=0;i<5;i++){
five[i]=new Thread(panel);
}
// newthread.start();
panel.colorcount++;
five[0].start();
panel.colorcount=2;
// five[1].start();
panel.waiting(5);
}
public static void main(String[] arg){
drawpanel apanel=new drawpanel();
}
int bw=800,bh=500;
void setp(){
x=panel.x;y=panel.y;
}
void touchbond(){
System.out.println("width:"+panel.getWidth()+"Height:"+panel.getHeight());
System.out.println("xposition:"+x+"yposition:"+y);
if(x+r>panel.getWidth()){
panel.speedx*=-1;
x=bw-r;
}
else if(x-r<0){
panel.speedx*=-1;
x=r;
}
if(y-r<0){
panel.speedy*=-1;
y=r;
}
else if(y+r>panel.getHeight()){
panel.speedy*=-1;
y=bh-r;
}
panel.x=x;panel.y=y;
}
int T=10;
Random nd=new Random();
#Override
public void run(){
r=panel.r;
panel.loadspeed(-6.33f,-3.4f);
while(true){
if(stopcount==0){//button control variable
panel.xmoving();panel.ymoving();
panel.touchbond();
sleeping(T);}
panel.loade();
// panel.callnotify();
// panel.colorEmit(panel.comp2D);
panel.repaint();
}
}
#Override
public void start(){
}
int count=0,stopcount=0;
#Override
public void actionPerformed(ActionEvent e) {
if(e.getSource()==exit){
System.exit(0);
}
if(e.getSource()==FlyingBalls){
//panel=new thepane();
}
if(e.getSource()==resume){
stopcount=0;T=10;
panel.emit=false;
}
if(e.getSource()==slow){
if(count%2==0)
T=500;
else
T=10;
count++;
}
if(e.getSource()==stop){
stopcount++;
panel.emit=true;
}
}
}
So, lots of theory to cover.
Firstly...
Animation is not easy, good animation is hard.
Swing is single threaded and is not thread safe
This means that you should not perform any long running or blocking operations within the context of the Event Dispatching Thread.
It also means that you shouldn't modify the UI or anything the UI relies on from outside the context of the Event Dispatching Thread
More threads != more work
More threads doesn't always mean you're going to get more done. In fact, in this scenario, it could really cause a huge number of issues, as you need the ability to reason out the state at a single point in time (when painting)
Animation Theory
Okay, animation is simply the illusion of change, how you accomplish that will come down to the problem you trying to solve.
For me, the best animations are time based animations, not linear.
A linear animation keeps updating from its start state till it reaches its end state, in a constant progression. These don't tend to scale well and can suffer issues on low performant systems.
A time based animation is one where the amount of time is defined and then, based on a anchor time (ie start time) and the state of the animation is updated based on the amount of time which is passed. This is a really simple way to achieve "frame dropping". You'd also be very surprised to find that in general terms, time based animations tend to look better across more platforms.
A time based animation is also more capable of generating "easement" effects, but that's getting way deeper then we need to go right now.
Okay, but what's this got to do with your problem? Well, actually, quite a bit.
The first thing we need is some kind of "main-loop" from which all the animation can be driven. Typically, I'd look to a good animation library, but failing that, a simple Swing Timer will do the basic good really well.
It generates its ticks in the Event Dispatching Thread, which makes it very useful for our needs. See How to Use Swing Timers for more details
So, we start with something like...
private Timer timer;
//...
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
// Update the state
repaint();
}
});
//...
timer.start();
This gives us our "main loop", from which we can update the state as needed and then trigger a repaint of the component.
For the purpose of this demonstration, I'm going to devise a self-contained unit of "animation duration", used to track the amount of time which has passed since the animation was started, this is personal choice, but it would allow me to drive a number of animations and it contains the logic to a single unit of work.
public class AnimationDuration {
private Duration duration;
private Instant startedAt;
public AnimationDuration(Duration duration) {
this.duration = duration;
}
public Duration getDuration() {
return duration;
}
public void start() {
startedAt = Instant.now();
}
public void stop() {
startedAt = null;
}
public boolean isRunning() {
return startedAt != null;
}
public float getProgress() {
Duration runningTime = Duration.between(startedAt, Instant.now());
if (runningTime.compareTo(duration) > 0) {
runningTime = duration;
}
long total = duration.toMillis();
float progress = runningTime.toMillis() / (float) total;
return progress;
}
}
This basically allows to trigger the animation to start running (anchor point in time) and then get the progress of the animation at any point in time. This provides a normalised concept from 0-1, so if we want to make it longer or shorter, all we do is adjust the duration and everything else is taken care of.
For your specific problem, I'd consider some kind of "time line" or "key frames", which defines that certain actions should occur at certain points of time along the time line.
Now, the following is a really simple concept, but it gets the job.
public interface KeyFrame {
public float getProgress();
}
public class TimeLine<K extends KeyFrame> {
private List<K> keyFrames;
public TimeLine() {
keyFrames = new ArrayList<>(25);
}
// Returns the key frames between the current progression
public K getKeyFrameAt(float progress) {
for (int index = 0; index < keyFrames.size(); index++) {
K keyFrame = keyFrames.get(index);
if (progress >= keyFrame.getProgress()) {
if (index + 1 < keyFrames.size()) {
K nextFrame = keyFrames.get(index + 1);
// But only if your between each other
if (progress < nextFrame.getProgress()) {
return keyFrame;
}
} else {
// Nothing after me :D
return keyFrame;
}
}
}
return null;
}
public void add(K keyFrame) {
keyFrames.add(keyFrame);
Collections.sort(keyFrames, new Comparator<KeyFrame>() {
#Override
public int compare(KeyFrame lhs, KeyFrame rhs) {
if (lhs.getProgress() > rhs.getProgress()) {
return 1;
} else if (lhs.getProgress() < rhs.getProgress()) {
return -1;
}
return 0;
}
});
}
}
This allows you to define certain KeyFrames along the timeline, based on a normalised concept of time and then provides the ability to get the KeyFrame based on the current progression through animation.
There are much more complex solutions you might consider, which would generate self contained events based on time progressions automatically, but I prefer been able to driver the animation itself independently, makes these types of things more flexible - add a JSlider and you can manipulate the progression manually ;)
The next thing we need is something to carry the properties for the circle KeyFrame ...
public class CirclePropertiesKeyFrame implements KeyFrame {
private float progress;
private double radius;
private Color color;
public CirclePropertiesKeyFrame(float progress, double radius, Color color) {
this.progress = progress;
this.radius = radius;
this.color = color;
}
#Override
public float getProgress() {
return progress;
}
public Color getColor() {
return color;
}
public double getRadius() {
return radius;
}
#Override
public String toString() {
return "KeyFrame progress = " + getProgress() + "; raidus= " + radius + "; color = " + color;
}
}
Now, we need to put it together...
public class TestPane extends JPanel {
private AnimationDuration timelineDuration;
private TimeLine<CirclePropertiesKeyFrame> timeLine;
private Timer timer;
private CirclePropertiesKeyFrame circleProperties;
public TestPane() {
timelineDuration = new AnimationDuration(Duration.ofSeconds(10));
timeLine = new TimeLine<>();
timeLine.add(new CirclePropertiesKeyFrame(0, 5, Color.CYAN));
timeLine.add(new CirclePropertiesKeyFrame(0.2f, 10, Color.BLUE));
timeLine.add(new CirclePropertiesKeyFrame(0.4f, 15, Color.GREEN));
timeLine.add(new CirclePropertiesKeyFrame(0.6f, 20, Color.YELLOW));
timeLine.add(new CirclePropertiesKeyFrame(0.8f, 25, Color.MAGENTA));
timer = new Timer(5, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
if (timelineDuration.isRunning()) {
float progress = timelineDuration.getProgress();
if (progress >= 1.0) {
timelineDuration.stop();
}
CirclePropertiesKeyFrame keyFrame = timeLine.getKeyFrameAt(progress);
circleProperties = keyFrame;
}
repaint();
}
});
}
#Override
public void addNotify() {
super.addNotify();
timelineDuration.start();
timer.start();
}
#Override
public void removeNotify() {
super.removeNotify();
timer.stop();
timelineDuration.stop();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
if (circleProperties != null) {
double radius = circleProperties.radius;
double xPos = (getWidth() / 2) - radius;
double yPos = (getHeight() / 2) - radius;
g2d.setColor(circleProperties.color);
g2d.fill(new Ellipse2D.Double(xPos, yPos, radius * 2, radius * 2));
}
g2d.dispose();
}
}
And then we end up with something like...
Now, this is a 10 second sequence, so every 2 seconds it will update. Try changing the duration of the AnimationDuration and see what happens.
Note This is a non-repeating animation (it doesn't loop). You could make it loop, but the calculation to do so becomes more complicate, as you need to consider by how much you're over the expected Duration and then apply that to the next cycle, so it looks smooth
But what about movement?
Well, actually, pretty much already answered that question. You would also place the movement code inside the Timers ActionListener, right before the repaint request. In fact, I might be tempted to create some kind of class that could take the current KeyFrame information and combine it with the location properties, this would then be used the paintComponent method to draw the circle.
I want to blend the animation states ...
Well, that's a much more difficult question, especially when it comes to colors.
The basic theory is, you need the two key frames which set either side of the current progression. You would then apply a "blending" algorithm to calculate the amount of change to be applied between the two key frames.
Not impossible, just a step more difficult
package checkers;
import java.awt.Color;
import java.awt.Dimension;
import javax.swing.JButton;
import javax.swing.JPanel;
enum Job{SPAWN, KING, NORM};
enum myColor{RED, BLACK};
int tileRow;
int tileCol;
Job job;
myColor side;
JButton button;
Checker piece;
Color color;
public class Tile
{
public Tile(int posRow, int posCol, JPanel panel, ActionListener listener)
{
int tileRow = posRow;
int tileCol = posCol;
if(tileRow > 9 || tileRow < 1)
job = Job.KING;
else if(tileRow < 4 || tileRow > 6)
job = Job.SPAWN;
else
job = Job.NORM;
button = new JButton();
button.setPreferredSize(new Dimension(83, 83));
if(tileRow%2==0)
{
if(tileCol%2==0)
{
color = Color.BLACK;
}
else
color = Color.RED;
}
else
{
if(tileCol%2!=0)
{
color = Color.BLACK;
}
else
color = Color.RED;
}
button.addActionListener((java.awt.event.ActionListener) listener);
}
public void Reset()
{
}
public boolean isClicked(Object source)
{
if(source == button)
return true;
else
return false;
}
}
EDIT I edited in the entirety of my code body. The myColor closing brace is, as far as Eclipse knows, 'supposed' to be after the classBody.
Eclipse wants me to remove myColor's closing brace, and replace it with a semi-colon; regardless of whether or not I place the semi-colon, Eclipse tells me that the closing brace is not supposed to be there, and if I remove it, reads my classBody closing brace as being the EnumBody closing brace.
I dunno what the hell is going on, but it's definitely causing weird things to happen within the class (making a Tile class+object for a game of checkers).
And by weird things, I mean I can't make an array of Tile objects from another class if I want Eclipse to read Tile as having no errors.
Not exactly sure the error message you are getting. Below example EnumIssue.java works fine:
public class EnumIssue {
enum Job
{
SPAWN, KING, NORM
}
enum myColor
{
RED, BLACK
}
public static void main(String[] args) {
Job j = Job.SPAWN;
myColor c = myColor.BLACK;
System.out.println(j);
System.out.println(c);
}
}
Output:
SPAWN
BLACK
Added after entire code was provided in question:
Move the variable declarations in the class Tile. The updated snippet is below:
:
:
import javax.swing.JPanel;
enum Job{SPAWN, KING, NORM};
enum myColor{RED, BLACK};
//This is where current variable declarations are. Move them inside class.
public class Tile
{
//This is where variable declarations are moved to.
int tileRow;
int tileCol;
Job job;
myColor side;
JButton button;
Checker piece;
Color color;
public Tile(int posRow, int posCol, JPanel panel, ActionListener listener)
{
int tileRow = posRow;
:
:
I am creating an app where bubbles bounce around the screen, you get points for popping them, and then they re-spawn.
Each bubble is exactly identical except for the speed and direction integers used to control their motion around the screen.
As of now I have a single ImageView called "bubble". Then I run the method randoms() which I created.
Code for randoms():
public void randoms(){
ySpeedRand = new Random();
xSpeedRand = new Random();
yPolarityRand = new Random();
xPolarityRand = new Random();
ySpeed = ySpeedRand.nextInt(5) + 4;
xSpeed = xSpeedRand.nextInt(5) + 4;
yPolarity = yPolarityRand.nextInt(3) + 1;
xPolarity = xPolarityRand.nextInt(3) + 1;
if (xPolarity == 1){
xSpeed*=-1;
}
if (yPolarity == 2){
ySpeed*=-1;
}
}
Then I have a listener to check when the bubble is tapped and then I make it invisible, rerun the randoms() block, and then make it visible.
This is the handler that controls the position of the bubbles:
final Handler handler = new Handler();
Runnable run = new Runnable() {
#Override
public void run() {
bubble.setX(bubble.getX()+xSpeed);
bubble.setY(bubble.getY()+ySpeed);
handler.postDelayed(this, 5);
}
};handler.post(run);
And I use this code to check if the bubble is still on the screen:
final Handler checkHandler = new Handler();
Runnable check = new Runnable() {
#Override
public void run() {
if (bubble.getX()>SCREEN_WIDTH-bubble.getWidth()/2){xSpeed*=-1;}
if (bubble.getX()<0){xSpeed*=-1;}
if (bubble.getY()>SCREEN_HEIGHT-bubble.getHeight()){ySpeed*=-1;}
if (bubble.getY()<bubble.getHeight()/2+barHeight+actionBorder.getHeight()){ySpeed*=-1;}
handler.postDelayed(this, 50);
}
};checkHandler.post(check);
Is there an easy way to simply expand this system so I can call a method createbubble() and then have the new bubbles assume the exact properties as the old ones, but with newly generated randoms?
I am 100% stuck and I cant find anything. Any help is extremely appreciated!
I was a little bit confused reading your question. Not sure why you're trying to use ImageView. Going with this as your goal:
Create a game which spawns bubbles which can be popped
When popped, bubbles should be moved to a random space on the screen
When bubbles move off the screen, reverse their direction.
I would make the following classes at least:
Launcher, where your main() method is stored and your game loop run
Window, which extends Canvas and wherein you'll setup a JFrame
Control, where physics are calculated every time Launcher completes a loop
Entity, an abstract java object
Bubble
Here's one way to make a game loop: http://www.java-gaming.org/index.php?topic=24220.0
Here's an example of how to make a JFrame:
import java.awt.Canvas;
import javax.swing.JFrame;
public class Window extends Canvas{
public Window(Launcher launcher){
JFrame frame = new JFrame("Title");
frame.setSize(700,500);
frame.setResizable(false);
frame.setLocationRelativeTo(null);;
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(launcher);
frame.setVisible(true);
launcher.setFrame(frame);
}
}
To keep track of the bubbles, I would declare and initialize a LinkedList<> in the Control class. To create bubbles run something like this in Control:
public class Control{
public LinkedList<Entity> entity = new LinkedList<Entity>();
Random rand = new Random();
public Control(){
//You will have to define these constants in the Launcher class
entity.add(
rand.nextInt(Launcher.WINDOW_WIDTH),
rand.nextInt(Launcher.WINDOW_HEIGHT)
);
}
//Called every time Launcher completes a game loop
tick(){
for(int i=0; i<entity.size(); i++){
entity.get(i).tick();
}
}
}
In the Entity class:
public abstract class Entity{
protected double x;
protected double y;
public Entity(int x, int y){
this.x = x;
this.y = y;
}
public abstract void tick();
public abstract void render(Graphics g);
//Getters and setters here ...
}
In the Bubble class:
public class Bubble extends Entity{
public double x = 0;
public double y = 0;
public Bubble(int x, int y){
super(x,y);
}
public void tick(){
//Physics go here
}
public void render(){
//Graphics functions go here
}
}
If any of this doesn't make sense, let me know and I'll explain in more detail and even go as far as to make the entire program for you considering the simplicity of the task.
There is a problem with the repaint() method in Java. I made a new thread that constantly repaints the screen. When I release the spacebar I want my player to fall smoothly by setting its position and then waiting for 50 milliseconds and looping that 20 times. Instead, it waits the whole amount of time in the loop, then repaints. I am wondering why it doesn't constantly repaint the changes in the players co-ordinates. Thank you.
(Edit) Thanks everyone for the help. This is my first time using stack overflow, and I am only 13 and still learning java, so I probably will go back to the tutorials again.
My 'a' class (main):
public class a {
public static void main(String[] args) {
JFrame frame = new JFrame("StickFigure Game");
frame.setSize(740, 580);
frame.setDefaultCloseOperation(frame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
b board = new b();
frame.add(board);
frame.addKeyListener(board);
}
}
My 'b' class (JPanel/drawing):
public class b extends JPanel implements KeyListener {
c player = new c();
public class MyRunnable implements Runnable {
public void run() {
while (true)
repaint();
}
}
MyRunnable run = new MyRunnable();
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(player.getImage(), player.getX(), player.getY(), 80, 140,
null);
}
public b() {
Thread thread = new Thread(new MyRunnable());
thread.start();
}
public static void slow(int n) {
long t0, t1;
t0 = System.currentTimeMillis();
do {
t1 = System.currentTimeMillis();
} while (t1 - t0 < n);
}
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_D) {
player.setPos(player.getX() + 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_A) {
player.setPos(player.getX() - 6, player.getY());
}
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
player.setPos(player.getX(), player.getY() - 60);
}
}
public void keyReleased(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_SPACE) {
for (int i = 0; i < 20; i++) {
slow(50);
player.setPos(player.getX(), player.getY() + 2);
}
}
}
public void keyTyped(KeyEvent e) {
}
}
my 'c' class (player):
public class c {
private ImageIcon i = new ImageIcon("guy.png");
private Image img = i.getImage();
private int x = 0;
private int y = 100;
public void wait(int what) {
try {
Thread.sleep(what);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public c() {
}
public Image getImage() {
return img;
}
public int getX() {
return x;
}
public int getY() {
return y;
}
public void setPos(int mx, int my) {
x = mx;
y = my;
}
}
I haven't gone through all the code here but here are some pointers:
Swing has its own concurrency mechanisms which allow you to handle UI updates. You can use a Swing Timer rather than a raw Thread. Related is the use of Thread.sleep - don't do this, it only blocks the EDT and prevents UI updates.
The Swing paint chain mechanism requires you to override paintComponent rather than paint.
Always use Key Bindings rather than KeyListeners in Swing. KeyListeners require component focus to work to interact with the KeyEvents. Key Bindings do not have this limitation.
"There is a problem with the repaint() method in java." Did you consider that perhaps the problem is with your code instead? You are blocking the event thread and giving the system no time to do the intermediate repaints. In particular, this method:
public static void slow (int n){
long t0,t1;
t0=System.currentTimeMillis();
do{
t1=System.currentTimeMillis();
}
while (t1-t0<n);
}
and this loop:
for(int i = 0;i<20;i++){
slow(50);
player.setPos(player.getX(), player.getY()+2);
}
do not relinquish control to the system so that repaints can actually happen. Rewrite those using Swing timers. Look at this tutorial for an introduction on how to use these.
Also, your thread that constantly calls repaint() in a tight loop:
public void run(){
while(true) repaint();
}
is a terrible idea. You don't need to call repaint() at full CPU speed. Once every 30 milliseconds or so is fine for animation. Again, consider using Swing utilities to do this rather than writing your own looping thread.
The repaint is only a "request" to paint as soon as possible. so when you call it it causes a call to the paint method as soon as possible.
from here
So basically you just flooding the scheduled calls of paint or update with while(true) repaint();.
Oracle's stance on painting in AWT and Swing
One way you could do it, or should I say how I would do it, is to make your c class implement KeyListener, so that when a key is pressed (and only when it is pressed) you update it's location.
So move your KeyListener methods to class c, in your class b constructor you can add the call this.addKeyListener(player) or make a method void addPlayer(c player) that adds it.
java: I'm stuck on the error The type Pong.Move1 must implement the inherited abstract method
KeyListener.keyTyped(KeyEvent) when I used keyListener. I don't get what it means? Help?Here's the Thread I'm having trouble on...
private class Move1 extends Thread implements KeyListener{
public void run(){
addKeyListener(this);
while(true){
//hitRight makes you lose.
//point is how many times it ricochets.
if(ball.intersects(borderRight)){
hitRight = true;
}
if(ball.intersects(borderLeft)){
point++;
}
}
}
public void keyPressed(KeyEvent event){
while(event.getKeyCode()==40||event.getKeyCode()=='s'){
direction = DOWN;
Thread.sleep(500);
}
}
public void KeyReleased(KeyEvent event){
}
public void KeyTyped(KeyEvent event){
}
}
I'm also stuck on the Thread.sleep(500); line I have. It says Unhandled exception type InterruptedException. Any help? Thx.
Oh, I forgot something.
1:When I try to run it, the only error I get is:
Exception in thread "main" java.lang.Error: Unresolved compilation problem: at Pong.main(Pong.java:50). That's the public static void main(String[] args) line. My complete code is at the end so you can look at it (plz)
2:I'm using eclipse.
3:I am basically a beginner (not really)
My complete code:
import java.awt.*;
import javax.swing.*;
import javax.swing.event.*;
import java.awt.event.*;
import javax.swing.JFrame;
import javax.swing.JTextField;
import javax.swing.event.EventListenerList;
public class Pong extends JFrame{
public final int WIDTH = 1000, HEIGHT = 1000;
final int UP = 1, DOWN = 2;
boolean hitRight;
int point = 0;
int direction;
Rectangle bg = new Rectangle(0,0,WIDTH,HEIGHT);
Rectangle borderLeft = new Rectangle(0,0,WIDTH/320,HEIGHT);
Rectangle borderRight = new Rectangle(WIDTH-WIDTH/320,0,WIDTH/320,HEIGHT);
Rectangle borderTop = new Rectangle(borderLeft.x,borderLeft.y,WIDTH,HEIGHT/35);
Rectangle borderBottom = new Rectangle(0,HEIGHT-HEIGHT/320,WIDTH,HEIGHT/320);
Rectangle ball = new Rectangle(WIDTH/2,HEIGHT/2,WIDTH/64,HEIGHT/64);
Rectangle board = new Rectangle(WIDTH-WIDTH/160,0,WIDTH/128,HEIGHT/10);
public void paint(Graphics graphics){
super.paint(graphics);
graphics.setColor(Color.BLACK);
graphics.fillRect(bg.x,bg.y,bg.width,bg.height);
graphics.setColor(Color.RED);
graphics.fillRect(borderLeft.x, borderLeft.y, borderLeft.width, borderLeft.height);
graphics.fillRect(borderRight.x, borderRight.y, borderRight.width, borderRight.height);
graphics.fillRect(borderTop.x, borderTop.y, borderTop.width, borderTop.height);
graphics.fillRect(borderBottom.x, borderBottom.y, borderBottom.width, borderBottom.height);
graphics.setColor(Color.WHITE);
graphics.fillRect(ball.x,ball.y,ball.width,ball.height);
graphics.fillRect(board.x,board.y,board.width,board.height);
}
/**
* This Pong game made by me.
* This has no copied code.
* Any similarities are coincidences.
* #param args
*/
/*
* The constructor.
*/
public Pong(){
super("Pong");
setSize(WIDTH, HEIGHT);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Pong p = new Pong();
}
/*
* The move thread.
*/
private class Move1 extends Thread implements KeyListener{
public void run(){
addKeyListener(this);
while(true){
//hitRight makes you lose.
//point is how many times it ricochets.
if(ball.intersects(borderRight)){
hitRight = true;
}
if(ball.intersects(borderLeft)){
point++;
}
}
}
public void keyPressed(KeyEvent event){
while(event.getKeyCode()==40||event.getKeyCode()=='s'){
direction = DOWN;
Thread.sleep(500);
}
}
public void KeyReleased(KeyEvent event){
}
public void KeyTyped(KeyEvent event){
}
}
}
/*
* End of move thread...
*/
Your first error, related to KeyListener, is simply a case error. In Java, methods and variables are named following camelCase, and the language is case-sensitive, so you must define your method as keyTyped instead of KeyTyped. The same applies for keyReleased.
As for your second error, you must make changes necessary to handle the InterruptedException declared to be thrown by Thread#sleep. You can surround that method call in a try-catch block. (However, note that the KeyEvent's keycode will never change, so you've got a potential infinite while loop there.)
I recommend reading a tutorial or two on implementing a key listener.
In order to implement KeyListener, you need to implement all of the methods that that individual interface contains.
These are:
keyPressed
keyDown
keyTyped
In order to use interfaces you must implement all of their methods.
Also, Thread.sleep(500) might generate an exception. Java is basically requiring you to handle an error just in case something goes wrong. To do this, you need a try...catch, like so:
try
{
Thread.sleep(500);
}
catch(InterruptedException e)
{
System.out.println("Error!");
}