I have a sort of animation going on in which a rectangle is increasing and decreasing in size........what i want to do here is detect the color of a particular location through Robot() class but its not happening..........why?? I also want to know if Robot() can be used outside try or without main() class.
//<applet code=ctry.java width=500 height=500></applet>
import java.awt.*;
import java.applet.Applet;
import java.awt.AWTException;
public class ctry extends Applet implements Runnable{
Thread d=null;
int l=0,t=0,i=250,j=250;
Color color=null;
public void init(){
setBackground(Color.red);
}
public void start(){
d=new Thread(this);
d.start();
}
public void run(){
System.out.println("in run");
try{
Robot robo=new Robot();
System.out.println("after robo");
while(true){
System.out.println("in while");
repaint();
color=robo.getPixelColor(i,j);
System.out.println("Red = " + color.getRed());
l+=10;
t+=10;
d.sleep(100);
if(t>=225)
{t=0;}
if(l>=225)
{l=0;}
}
}
catch(Exception e)
{}
}
public void paint(Graphics g)
{
System.out.println("in paint");
g.setColor(Color.blue);
g.fill3DRect(225,225,l*2,t*2,true);
}
public void destroy()
{}
}
You could try to capture an image instead and check the color there. The Robot.getPixelColor() is very slow, it returns a new Color instance every time. Try using a BufferedImage instead:
Color color = Color.black;
int screenWidth = 768;
int screenHeight = 1024;
Rectangle rectangle = new Rectangle(screenWidth, screenHeight);
BufferedImage image = robot.createScreenCapture(rectangle);
for (int y = 0 ; y < screenHeight ; y++) {
for (int x = 0 ; x < screenWidth ; x++) {
if(image.getRGB(x, y) == color.getRGB()) {
return true;
}
}
}
And yes, you can use the Robot everywhere and you can deal with the exceptions, but most on them are runtime exceptions, so you don't have to.
Related
I am making a game (like Civilization) that has different tile types that I want to render as images. I have 9 different 16x16 png images to load in (called Con1, Con2, etc.), and here is my image loading code: (img[] is my BufferedImage array)
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
I then paint these images with this code: (t[][] is my tile type array)
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if(i[x][y] == 0){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
else if(t[x][y] == 3){
g.drawImage(img[3], x, y, this);
}
else if(t[x][y] == 4){
g.drawImage(img[4], x, y, this);
}
else if(t[x][y] == 5){
g.drawImage(img[5], x, y, this);
}
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
My problem is that it doesn't show up correctly when I run it. I get this image:
that flashes on and off in the top-left corner of the window. What I am supposed to see is an image similar to the top-left portion of the above one (landmasses surrounded by ocean) except large enough to fill the window. Here is some runnable code: (I don't think the code will run without the required images but I would appreciate some help with getting the images to you all.)
//imports
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferedImage;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
public class MCVCon extends JFrame implements KeyListener, MouseListener{
//setting up variables
public BufferedImage[] img = new BufferedImage[9];
private final int WIDTH = 50, HEIGHT = 50;
private boolean loop = false;
private int SCALE = 16;
int t[][] = new int[WIDTH][HEIGHT]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
public MCVCon(){
//creating the window
super("Conqueror");
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
setVisible(true);
requestFocusInWindow();
setDefaultCloseOperation(EXIT_ON_CLOSE);
loadImages();
loop = true;
while(true){
this.repaint();
//delay for repaint
try{
Thread.sleep(50);
}
catch(Exception ex){
ex.printStackTrace();
}
}
}
//load images
public void loadImages(){
for(int i = 0; i < 9; i++){
try {
img[i] = ImageIO.read(this.getClass().getResource("Con"+i+".png"));
}catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
//paint the images
public void paint(Graphics g){
if(loop){
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
for (int x = 0; x < WIDTH; x++){
for (int y = 0; y < HEIGHT; y++){
if (t[x][y] == 0){
g.drawImage(img[0], x, y, this);
}
else if(t[x][y] == 1){
g.drawImage(img[1], x, y, this);
}
r.fillRect(x*SCALE, y*SCALE, SCALE, SCALE);
}
}
g.drawImage(B, 0, 22, this);
}
}
//run the program
public static void main(String[] args) {
new MCVCon();
}
//necessary overrides
#Override
public void keyTyped(KeyEvent e) {
}
#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) {
}
}
I was wondering what the problem might be. Thanks in advance.
So, I had a look at your code, there's no easy way to say, but it's a mess, with compounding issues which would make it very difficult to isolate the origin of any one problem, other than to say, they all feed into each other.
Let's start with the painting...
You're painting directly to the frame. This is generally discouraged for a number of reasons.
Let's start with the fact that JFrame isn't a single component, it's made up of a number compounding components
This makes it inherently dangerous to paint directly to, as any one of the child components could be painted without the frame's paint method been called.
Painting directly to a frame also means you're painting without consideration to the frame's borders/decorations, which are added into the visible area of the window, but wait...
setSize(SCALE*WIDTH, SCALE*HEIGHT+22);
suggests that you've tried to compensate for this, but this is "guess" work, as the decorations could take up more or less space depending on the configuration of the system
And, finally, top level containers aren't actually double buffered.
"But I'm painting to my own buffer" you say - but you're not
You create a BufferdImage and assign it's Graphics context t r
BufferedImage B = new BufferedImage(this.getWidth(), this.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics r = B.getGraphics();
But when you paint anything, you're using g, which is the Graphics context passed to the paint method
g.drawImage(img[0], x, y, this);
This is also woefully inefficient, as you're creating a new BufferedImage each time paint is called, which takes time to create, takes up memory and puts extra strain on the garbage collection process as the local object becomes eligible for disposal almost immediately
Don't even get me started on the "main paint loop"
The next problem you have, is you have no concept of virtual and real world space.
You make a virtual map of your world using t, which maintains information about which tile should be used for a given x/y coordinate, but you never map this to the real world, instead, you paint each tile exactly at the same pixel x/y position, which means they now overlap, tiles have width and height, which means they need to be offset when painted onto the real world.
And finally, which I think is your actually question, is about scaling. There are a number of ways you could apply scaling, you could pre-scale the tiles when you load them, this gives you a lot of control over "how" they are scaled, but locks you into a single scale.
You could instead maintain a list of the scaled tiles, generated from a master list, which can be updated if the scale changes
Or you could simply scale the Graphics context directly.
Now, I've create a simple example, based on your code, correcting for most of the above. It creates a series of randomly colored rectangles at 10x10 pixels, instead of tiles, but the concept is the same.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.geom.AffineTransform;
import java.awt.image.BufferedImage;
import java.util.Random;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;
public class MCVCon extends JFrame {
//setting up variables
public MCVCon() {
//creating the window
super("Conqueror");
add(new GamePane());
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
MCVCon frame = new MCVCon();
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
//necessary overrides
public class GamePane extends JPanel {
public BufferedImage[] img = new BufferedImage[9];
private final int width = 5, height = 5;
private int scale = 16;
int t[][] = new int[width][height]; //terrain type (since I took out the terrain generation it is set to 0 or ocean)
Color[] colors = new Color[]{
Color.RED,
Color.BLUE,
Color.CYAN,
Color.DARK_GRAY,
Color.GRAY,
Color.GREEN,
Color.LIGHT_GRAY,
Color.MAGENTA,
Color.ORANGE,
Color.PINK,
Color.YELLOW
};
int tileHeight = 10;
int tileWidth = 10;
public GamePane() {
loadImages();
Random rnd = new Random();
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int value = rnd.nextInt(9);
System.out.println(value + "- " + colors[value]);
t[x][y] = value;
}
}
Timer timer = new Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
repaint();
}
});
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(width * tileWidth * scale, height * tileHeight * scale);
}
public void loadImages() {
for (int i = 0; i < 9; i++) {
try {
img[i] = new BufferedImage(tileWidth, tileHeight, BufferedImage.TYPE_INT_RGB);
Graphics2D g2d = img[i].createGraphics();
g2d.setColor(colors[i]);
g2d.fill(new Rectangle(0, 0, tileWidth, tileHeight));
g2d.dispose();
} catch (Exception ex) {
System.out.println("Missing Image");
ex.printStackTrace();
}
}
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g.create();
g2d.setTransform(AffineTransform.getScaleInstance(scale, scale));
for (int x = 0; x < width; x++) {
for (int y = 0; y < height; y++) {
g2d.drawImage(img[t[x][y]], x * tileWidth, y * tileHeight, this);
}
}
g2d.dispose();
}
}
}
I'm trying to animate the sprite in my game when a button is pressed, but when I press the button, it skips the animation. Its supposed to go one pixel, change sprites, and then go one more pixel and change back. Here is the code
//for all
import java.nio.file.*;
import javax.imageio.ImageIO;
import java.io.IOException;
import java.awt.image.*;
import java.net.*;
import java.awt.*;
import javax.swing.*;
import static java.lang.invoke.MethodHandles.*;
import java.awt.event.*;
//my Mario class (cut down a lot)
class Mario {
// all numbers multiplied by 2 from OG game
protected Direction dir;
protected int x, y;
protected BufferedImage sprite;
protected String currentSpriteName;
public Mario() {
this.x = 54;
this.y = 808;
dir = Direction.RIGHT;
setSprite(MVCE.SMALLSTANDFACERIGHT);
currentSpriteName = MVCE.SMALLSTANDFACERIGHT;
}
public void moveRight(){
if(this.dir == Direction.LEFT){
this.dir = Direction.RIGHT;
}
else if(this.dir == Direction.RIGHT){
this.x+=1;
}
}
public void animateMoveRight(){
if (currentSpriteName.equals(MVCE.SMALLSTANDFACERIGHT)){
setSprite(MVCE.SMALLWALKFACERIGHT);
}
else if (currentSpriteName.equals(MVCE.SMALLWALKFACERIGHT)){
setSprite(MVCE.SMALLSTANDFACERIGHT);
}
}
public void jump() {
this.y -= 46;
}
public void setSprite(String spriteName) {
URL spriteAtLoc = MVCE.urlGenerator(spriteName);
this.sprite = MVCE.generateAndFilter(sprite, spriteAtLoc);
}
public void getSprite(){
System.out.println(this.currentSpriteName);
}
public void paint(Graphics g) {
Graphics2D g2 = (Graphics2D) g;
g2.drawImage(sprite, 0, 0, null); // DO NOT SET x and y TO ANYTHING,
// this sets 0,0 to top left!!
}
}
// my MarioRender class:
class MarioRender extends JLabel {
protected Mario marioSprite;
public MarioRender() {
marioSprite = new Mario();
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
marioSprite.paint(g2);
setBounds(marioSprite.x, marioSprite.y, marioSprite.sprite.getWidth(), marioSprite.sprite.getHeight());
}
public void moveMarioRight(){
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
//this is my attempt to make it animate
marioSprite.moveRight();
marioSprite.animateMoveRight();
setLocation(this.marioSprite.getX(), this.marioSprite.getY());
repaint();
}
public void jumpMario() {
marioSprite.jump();
setLocation(this.marioSprite.x, this.marioSprite.y);
repaint();
}
}
// direction class, solely for moving
enum Direction {
LEFT, RIGHT
}
// my calling class, which I called MVCE where I make the frame
public class MVCE extends JFrame {
MarioRender m = new MarioRender();
JLabel bg;
public MVCE() {
bg = new JLabel();
this.setSize(868, 915);
this.setVisible(true);
this.add(bg, BorderLayout.CENTER);
bg.setLayout(null);
bg.add(m);
m.setBounds(m.marioSprite.x, m.marioSprite.y, m.marioSprite.sprite.getWidth(),
m.marioSprite.sprite.getHeight());
KeyListener kl = new MoveListener();
this.addKeyListener(kl);
this.setFocusable(true);
this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public static final String SMALLSTANDFACERIGHT = "SmallStandFaceRight.bmp"; // 30
// x
// 32
public static final String SMALLJUMPFACERIGHT = "SmallJumpFaceRight.bmp"; // 32
// x
// 32
// generate URL
public static URL urlGenerator(String name) {
URL u = lookup().lookupClass().getResource(name);
return u;
}
// return image with filtered color
public static BufferedImage generateAndFilter(BufferedImage b, URL u) {
try {
b = ImageIO.read(u);
int width = b.getWidth();
int height = b.getHeight();
int[] pixels = new int[width * height];
b.getRGB(0, 0, width, height, pixels, 0, width);
for (int i = 0; i < pixels.length; i++) {
// System.out.println(pixels[i]);
if (pixels[i] == 0xFFff00fe) {
pixels[i] = 0x00ff00fe;
}
}
BufferedImage newSprite = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB);
newSprite.setRGB(0, 0, width, height, pixels, 0, width);
b = newSprite;
} catch (IOException e) {
System.out.println("sprite not found");
e.printStackTrace();
}
return b;
}
// key listener
class MoveListener implements KeyListener {
public void keyPressed(KeyEvent k) {
if ((k.getKeyCode() == 39)) {
m.moveMarioRight();
///THIS IS SUPPOSED TO MOVE HIM 1, change sprite, and automatically move him back, it moves 2 pixels but no animation
}
if (k.getKeyCode() == 83) { // S key
m.marioSprite.setSprite(SMALLJUMPFACERIGHT);
m.jumpMario();
}
}
public void keyReleased(KeyEvent k) {
}
public void keyTyped(KeyEvent k) {
}
}
public static void main(String[] args) {
MVCE m = new MVCE();
}
}
I tried putting this between the calls to marioMoveRight():
try {
Thread.sleep(200);
} catch(InterruptedException ex) {
Thread.currentThread().interrupt();
}
but it just delays the whole thing. I had also tried using an ActionListener, but I don't know how to make it react only when the key is pushed. as I had it,
I had this class inside of MVCE:
class TickListener implements ActionListener{
public void actionPerformed(ActionEvent a){
m.marioSprite.setSprite(Constants.SMALLWALKFACERIGHT);
repaint();
}
}
and this at the end of the MVCE constructor:
ActionListener ac = new TickListener();
final int DELAY = 1000;
Timer t = new Timer(DELAY, ac);
t.start();
but then, the Mario just moves automatically. I do not want to use a sprite sheet for this project, I am trying to do it as this guy did for SMB1.
Many problems, don't know which one or if any will fix the problem:
Don't use a KeyListener. If a component doesn't have focus the component won't receive the event. Instead use Key Bindings.
Don't use "==" to compare Objects. Instead you should be using the equals(...) method.
Don't override paintComponent. A painting method is for painting only. You should not be changing the bounds of the component in the painting method.
Do basic debugging (problem solving) before asking a question. A simple System.out.println(...) added to various methods will determine if the code is executing as you expect. Then when you ask a question you can ask a specific question telling us which block of code does not execute as you expect.
You never actually call the method animateMoveRight(), and if I understand correcly, that's what's changing the sprite. Also, I doubt that you see the sprite change when calling the same method twice in a row without any delay.
Try putting the animateMoveRight() method into the moveRight() or the moveMarioRight() method and, if neccessary because the animation is too fast, add your delay code back where you had it. Be careful not to let the main thread sleep, as this causes everything to freeze, so start another one or use a timer etc.
EDIT: Good timers
I'm not too familiar with the Timer class, so I end up using the Thread variant. There are many tutorials for that out there, just search for "java threads" or "java multithreading". This is IMO a solid tutorial you can check out.
I was working on a simple "Bouncing Ball"-Animation in Java. The idea is that it initally spawns a single ball moving in a straight line until hitting the panel border, which causes it to bounce off as you would expect. You can then spawn additional balls at position x,y with mouseclicks. So far so good.
My problem is that each ball starts its own thread, and each thread individually draws into the panel at their own intervals, causing the panel to flicker like crazy. I know that such problems can be solved by implementing double buffering, which I've read about, but never quite used myself.
I was wondering about how one would go about using double buffering here and if having many threads painting at the same time can be an issue (or conversely, even the norm)?
Thanks a lot in advance!
Here's the code:
import java.awt.*;
import java.awt.event.*;
import java.util.*;
import javax.swing.*;
class MyCanvas extends JPanel
{
MyCanvas()
{
setBackground(Color.white);
setForeground(Color.black);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
}
public Dimension getMinimumSize()
{
return new Dimension(300,300);
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
}
public class BouncingBalls extends JFrame // main class
{
MyCanvas m_gamefield;
public BouncingBalls()
{
setLayout(new BorderLayout());
m_gamefield = new MyCanvas();
add("Center",m_gamefield);
m_gamefield.addMouseListener(new MeinMausAdapter());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
public void letsgo()
{
Ball first = new Ball(m_gamefield,200,50);
first.start();
}
class MeinMausAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
Ball next = new Ball(m_gamefield,e.getX(),e.getY());
next.start();
}
}
public static void main(String[] args)
{
BouncingBalls test = new BouncingBalls();
test.setVisible(true);
test.pack();
test.letsgo();
}
}
class Ball extends Thread
{
JPanel m_display;
int m_xPos,m_yPos;
int m_dx = 2; // Steps into direction x or y
int m_dy = 2;
Ball(JPanel c,int x,int y)
{
m_display = c;
m_xPos = x;
m_yPos = y;
}
public void run()
{
paintBall(); // Paint at starting position
while(isInterrupted() == false)
{
moveBall();
try
{
sleep(20);
}
catch(InterruptedException e)
{
return;
}
}
}
void paintBall()
{
Graphics g = m_display.getGraphics();
g.fillOval(m_xPos, m_yPos, 20, 20);
g.dispose();
}
void moveBall()
{
int xNew, yNew;
Dimension m;
Graphics g;
g = m_display.getGraphics();
m = m_display.getSize();
xNew = m_xPos + m_dx;
yNew = m_yPos + m_dy;
// Collision detection with borders, "bouncing off":
if(xNew < 0)
{
xNew = 0;
m_dx = -m_dx;
}
if(xNew + 20 >= m.width)
{
xNew = m.width - 20;
m_dx = -m_dx;
}
if(yNew < 0)
{
yNew = 0;
m_dy = -m_dy;
}
if(yNew + 20 >= m.height)
{
yNew = m.height - 20;
m_dy = -m_dy;
}
g.setColor(m_display.getBackground()); // Erases last position by
g.fillRect(m_xPos-2, m_yPos-2, m_xPos+22, m_yPos+22); // painting over it in white
m_xPos = xNew;
m_yPos = yNew;
paintBall(); // paint new position of Ball
g.dispose();
}
}
Don't worry about double buffering when painting with Swing JComponents. They're double buffered by default.
You should, instead of creating each Ball on a different Thread, implement a Swing Timer for the animation. See more at How to Use Swing timers. You can see a good example here where Ball objects are added to a List of Balls and presents at different intervals.
Other Notes
Never use getGraphics of your components. All painting should be done within the Graphics context passed to the paintComponent method. I see you have the method in place. Use it. You can have a draw method in your Ball class that take a Graphics argument, and call that method from within the paintComponent method, passing to it the Graphics context. Example can also be seen in the link above.
You can see more examples here and here and here and here and here and here.
Thanks to peeskillet's excellent references, I've changed the code around a bit by using Swing timers. It's a lot shorter now and forfeits the use of multithreading completely. Also, due to calculating all of the ball positions before actually drawing them (in a single sweeping repaint() as opposed to many smaller ones), the flickering has stopped.
I'm still a bit curious why it is considered bad form to use getGraphics(), though. Does it always lead to flickering (which I had imagined could be removed with an additional layer of of double buffering)? And doesn't paintComponent() become rather bloated in more complex animations if it directs every single act of painting? I'm still fairly new to this, if anybody is wondering.
Here's the new code for those interested:
import java.awt.*;
import java.awt.event.*;
import java.util.ArrayList;
import java.util.List;
import javax.swing.*;
public class BouncingBalls extends JFrame // main class
{
MyCanvas m_gamefield;
public ArrayList<Ball> balls;
public Timer timer = null;
public BouncingBalls()
{
setLayout(new BorderLayout());
m_gamefield = new MyCanvas();
add("Center",m_gamefield);
balls = new ArrayList<Ball>();
timer = new Timer(30, new ActionListener()
{
public void actionPerformed(ActionEvent e)
{
for (Ball b : balls)
{
b.move();
}
repaint();
}
});
m_gamefield.addMouseListener(new MeinMausAdapter());
setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
}
class MeinMausAdapter extends MouseAdapter
{
public void mousePressed(MouseEvent e)
{
balls.add(new Ball(m_gamefield,e.getX(),e.getY()));
}
}
class MyCanvas extends JPanel
{
MyCanvas()
{
setBackground(Color.white);
setForeground(Color.black);
}
public void paintComponent(Graphics g)
{
super.paintComponent(g);
for (Ball b : balls)
{
b.draw(g);
}
}
public Dimension getMinimumSize()
{
return new Dimension(300,300);
}
public Dimension getPreferredSize()
{
return getMinimumSize();
}
}
public void letsgo()
{
balls.add(new Ball(m_gamefield,200,50));
timer.start();
}
public static void main(String[] args)
{
BouncingBalls test = new BouncingBalls();
test.setVisible(true);
test.pack();
test.letsgo();
}
}
class Ball
{
JPanel m_display;
int m_xPos,m_yPos;
int m_dx = 2; // Steps into direction x or y
int m_dy = 2;
Ball(JPanel c,int x,int y)
{
m_display = c;
m_xPos = x;
m_yPos = y;
}
void draw(Graphics g)
{
g.fillOval(m_xPos, m_yPos, 20, 20);
}
void move()
{
int xNeu, yNeu;
Dimension m;
m = m_display.getSize();
xNeu = m_xPos + m_dx;
yNeu = m_yPos + m_dy;
// Collision detection with borders, "bouncing off":
if(xNeu < 0)
{
xNeu = 0;
m_dx = -m_dx;
}
if(xNeu + 20 >= m.width)
{
xNeu = m.width - 20;
m_dx = -m_dx;
}
if(yNeu < 0)
{
yNeu = 0;
m_dy = -m_dy;
}
if(yNeu + 20 >= m.height)
{
yNeu = m.height - 20;
m_dy = -m_dy;
}
m_xPos = xNeu;
m_yPos = yNeu;
}
}
I am trying to move a train across my java window and am having serious problems. I have a Train class in which I made the train, and a Driver class which is supposed to move the train. I need to make the whole train move from right to left until it 'passes' the left edge of the screen. Then add an if statement to change the dx so the train restarts on the right side . The below is what I have tried but it is not working. Can anyone help me please??
public class Driver extends GraphicsProgram
{
//~ Instance/static variables .............................................
private static final int N_STEPS = 1000;
private static final int PAUSE_TIME = 20;
private static final double TRAIN_LENGTH = 320;
//~ Constructor ...........................................................
// ----------------------------------------------------------
/**
* The run() method of the Driver Class.
* Creates an instance of the Train Class.
* Responsible for animating the train across the screen.
*/
public void run()
{
Train train = new Train(getGCanvas());
for (int i = 0; i < N_STEPS; i++) {
train.move(-100, 0);
pause(PAUSE_TIME);
}
Here is a little demo made with swing. Just replace the black rectangle with an image of your train and you're done.
The trick is to use a separate thread (or timer) to do the animation loop (often called game loop). The loop only tells your window to redraw itself, and on each redraw, you first compute the new positions of the animated objects, then you draw them.
import javax.swing.*;
import java.awt.*;
public class TrainDemo {
public static void main(String[] args) {
JFrame frame = new JFrame("Train Demo");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(800, 400);
frame.setLocationRelativeTo(null);
frame.add(new TrainCanvas());
frame.setVisible(true);
}
}
class TrainCanvas extends JComponent {
private int lastX = 0;
public TrainCanvas() {
Thread animationThread = new Thread(new Runnable() {
public void run() {
while (true) {
repaint();
try {Thread.sleep(10);} catch (Exception ex) {}
}
}
});
animationThread.start();
}
public void paintComponent(Graphics g) {
Graphics2D gg = (Graphics2D) g;
int w = getWidth();
int h = getHeight();
int trainW = 100;
int trainH = 10;
int trainSpeed = 3;
int x = lastX + trainSpeed;
if (x > w + trainW) {
x = -trainW;
}
gg.setColor(Color.BLACK);
gg.fillRect(x, h/2 + trainH, trainW, trainH);
lastX = x;
}
}
color c = color(0); float x = 0; float y = 100; float speed = 1;
void setup() { size(200,200); }
void draw() { background(255); move(); display(); }
void move() { x = x + speed; if (x > width) { x = 0; } }
void display() { fill(c); rect(x,y,30,10); }
I have a thread which drops a circle in the y direction. I want to now create several circles on screen dropping at the same time with random x positions.
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Goo
{
protected GooPanel gooPanel;
private boolean loop = true;
protected int width , height;
private int frameTimeInMillis = 50;
private RenderingHints renderingHints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING , RenderingHints.
VALUE_ANTIALIAS_ON);
#SuppressWarnings("serial")
class GooPanel extends JPanel
{
public void paintComponent(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setRenderingHints(renderingHints);
draw(g2d);
}
}
public Goo()
{
this (800, 500);
}
public Goo(int w, int h)
{
width = w;
height = h;
JFrame frame = new JFrame ();
frame.setSize(width , height);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gooPanel = new GooPanel ();
gooPanel.setPreferredSize(new Dimension(w, h));
frame.getContentPane ().add(gooPanel);
frame.pack();
frame.setVisible(true);
}
public void go()
{
while (loop)
{
gooPanel.repaint();
try
{
Thread.sleep(frameTimeInMillis);
} catch (InterruptedException e) {}
}
}
public void draw(Graphics2D g) {}
public void setFrameTime(int millis)
{
frameTimeInMillis = millis;
}
public Component getGooPanel ()
{
return gooPanel;
}
}
My FallingDrop class:
import java.awt.*;
public class FallingDrops extends Goo
{
double x, y, r;
int red, green, blue = 0;
Color a;
FallingDrops()
{
x = width / 2;
r = 10;
y = -r;
}
FallingDrops(double x)
{
this.x = x;
r = 10;
y = -r;
}
public void draw(Graphics2D g)
{
g.setColor(Color.GRAY);
g.fillRect(0, 0, width , height);
g.setColor(Color.WHITE);
g.fillOval ((int) (x - r), (int) (y - r), (int) (2 * r),
(int) (2 * r));
y++;
if (y - r > height)
y = -r;
}
public static void main(String [] args)
{
int num = 10;
Goo gooDrop [] = new FallingDrops[num];
for(int i = 0; i < gooDrop.length; i++)
{
double x = Math.random()*800;
gooDrop[i] = new FallingDrops(x);
System.out.println(x);
gooDrop[i].go();
}
}
}
At current, the loop fails to complete when the go() method is executed; thus only painting ONE object on screen, and not several as indicated in my loop. This is a simple fix I am sure. Any ideas what I am doing wrong?
The method go() never returns. when it is called on the first object in the array, it continues working infinitely. you should either make the repainting in a separate thread that is constantly repainting. or if you want repainting only when drops are added, then remove the while in your go method
public void go()
{
gooPanel.repaint();
try
{
Thread.sleep(frameTimeInMillis);
} catch (InterruptedException e) {}
}
this way it will returns after it had made a repaining and a pause.
while (loop) .. gooPanel.repaint();
Not the way to do custom painting. Establish a Swing Timer and call repaint() in the actionPerformed() method of the listener.
See the Custom Painting lesson in the tutorial for details and working examples.