Moving Objects and Timers

Moving objects and timers

"I want the object to randomly pop up from the bottom of the screen reach a certain ascent and then fall back down"

See the runnable example below. What I do is pass a radomDelayedStart to the Shape. Every tick of the timer, the randomDelayedStart decreases til it reaches 0, that's when the flag to be drawn in raised. Most of the logic is in the Shape class methods, which are called in the Timers Actionlistener. Everything is done in one Timer. For the ascent, I just used a hard coded 50, but you can also pass a random ascent to the Shape. Let me know if you have any questions. I tried to made the code as clear as possible.

Sample Image

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class RandomShape extends JPanel {

private static final int D_HEIGHT = 500;
private static final int D_WIDTH = 400;
private static final int INCREMENT = 8;
private List<Shape> shapes;
private List<Color> colors;
private Timer timer = null;

public RandomShape() {
colors = createColorList();
shapes = createShapeList();

timer = new Timer(30, new ActionListener() {
public void actionPerformed(ActionEvent e) {
for (Shape shape : shapes) {
shape.move();
shape.decreaseDelay();
repaint();
}
}
});
JButton start = new JButton("Start");
start.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
timer.start();
}
});
JButton reset = new JButton("Reset");
reset.addActionListener(new ActionListener(){
public void actionPerformed(ActionEvent e) {
shapes = createShapeList();
timer.restart();
}
});

JPanel panel = new JPanel();
panel.add(start);
panel.add(reset);
setLayout(new BorderLayout());
add(panel, BorderLayout.PAGE_START);
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape shape : shapes) {
shape.drawShape(g);
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(D_WIDTH, D_HEIGHT);
}

private List<Color> createColorList() {
List<Color> list = new ArrayList<>();
list.add(Color.BLUE);
list.add(Color.GREEN);
list.add(Color.ORANGE);
list.add(Color.MAGENTA);
list.add(Color.CYAN);
list.add(Color.PINK);
return list;
}

private List<Shape> createShapeList() {
List<Shape> list = new ArrayList<>();
Random random = new Random();
for (int i = 0; i < 20; i++) {
int randXLoc = random.nextInt(D_WIDTH);
int randomDelayedStart = random.nextInt(100);
int colorIndex = random.nextInt(colors.size());
Color color = colors.get(colorIndex);
list.add(new Shape(randXLoc, randomDelayedStart, color));
}

return list;
}

class Shape {

int randXLoc;
int y = D_HEIGHT;
int randomDelayedStart;
boolean draw = false;
boolean down = false;
Color color;

public Shape(int randXLoc, int randomDelayedStart, Color color) {
this.randXLoc = randXLoc;
this.randomDelayedStart = randomDelayedStart;
this.color = color;
}

public void drawShape(Graphics g) {
if (draw) {
g.setColor(color);
g.fillOval(randXLoc, y, 30, 30);
}
}

public void move() {
if (draw) {
if (y <= 50) {
down = true;
}

if (down) {
y += INCREMENT;
} else {
y -= INCREMENT;
}
}
}

public void decreaseDelay() {
if (randomDelayedStart <= 0) {
draw = true;
} else {
randomDelayedStart -= 1;
}
}

}

public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
JFrame frame = new JFrame();
frame.add(new RandomShape());
frame.pack();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
}

When is a timer needed for moving objects in GUI?

As long as you're only

  • directly reacting to one event
  • and only update the display once

... it's completely OK to do that without timers.

The cases where you can/should use timers is

  • if you have an ongoing animation
  • if you're not directly reacting to a UI event, but call that "changes" from another thread.

Why use timers then?

  • Swing (that you're using) is not thread safe, so changing some UI elements from a Thread other than the EDT (Event Dispatch Thread) might bring disorder and chaos (really strange behaviour) into your UI.
  • If you have ongoing animations - which usually require a loop of some kind - an do all that inside the EDT, the UI will NOT be able to react to any input or even update itself properly.

So you use a timer which automatically:

  • runs in its own thread, so UI does not block
  • and calls the EDT for updates to the UI to keep things thread safe and clean

How do I move an object using a timer and the key press event (Like snake)

1: You are creating variable 'x' every time means locally in player_keyDown event so create it globally.

2: you are using while loop it, not needed as you are already using timer_tick.

3: Instead of Top, Left use Location property of button it gives you X, Y co-ordinate

public partial class Form2 : Form
{
public Form2()
{
InitializeComponent();
timer1.Interval = 1000;
timer1.Start();
}

string x = "right";
private void Player_KeyDown(object sender, KeyEventArgs e)
{
if (e.KeyCode == Keys.W)
{
x = "up";
}
else if (e.KeyCode == Keys.S)
{
x = "down";
}
else if (e.KeyCode == Keys.D)
{
x = "right";
}
else if (e.KeyCode == Keys.A)
{
x = "left";
}
}

private void timer1_Tick(object sender, EventArgs e)
{

if (x == "up")
{
Player.Location = new System.Drawing.Point(Player.Location.X, Player.Location.Y - 10);
}
if (x == "down")
{
Player.Location = new System.Drawing.Point(Player.Location.X, Player.Location.Y + 10);
}
if (x == "right")
{
Player.Location = new System.Drawing.Point(Player.Location.X + 10, Player.Location.Y);
}
if (x == "left")
{
Player.Location = new System.Drawing.Point(Player.Location.X - 10, Player.Location.Y);
}

}

}

C# timers for moving objects

Remove While loop from timer1_Tick method.
This method runs every 400 ms, but in your case at first launch it waits until one dog wins.

Also you should stop the timer after one of dogs win.

private void timer1_Tick(object sender, EventArgs e)
{
for (i = 0; i < Dogs.Length; i++) // there are four dogs
{
if (Dogs[i].Run()) // Run() returns true if full racetrack is covered by this dog
{
Winner = i + 1;
isWon = true;
timer1.Stop();
MessageBox.Show("We have a winner! Dog #" + Winner);
break;
}
}
}

Moving Object along a path: How to call actionPerformed only after previous actionPerformed has finished?

for (int i = controller.getPath().size()-1; i >= 0; i--){
Square target = controller.getPath().get(i);
canvas.startTimer(moving, target);
}

In your code above you iterate through the loop setting the Square position. The square position is set to the end before the Timer even has a chance to fire, so you only ever see the square painted in the last position.

When you use a Timer you don't need to use a loop. The Timer replaces the loop. You simply start the Timer. Then when the Timer fires you do something.

So, your logic would be something like:

  1. Add the Square objects to the ArrayList
  2. Start the Timer
  3. When the ActionListener is invoked you a) get the first Square from the ArrayList b) move the object to the square c) remove the Square from the ArrayList
  4. When the ArrayList is empty you stop the Timer.

Moving an array of shape object using timer and a start button - Java

You have a number of problems...

First, you use your ButtonListener with your javax.swing.Timer, but in the listener, you do nothing to actually update the position of the buttons when the timer ticks. So instead of...

timer = new Timer(DELAY, buttonListener);

You should be doing something more like...

timer = new Timer(DELAY, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
for (Shape shape : shapes) {

shape.move();

}
}
});

Second, you don't actually register the ButtonListener with the start or stop buttons, for example...

start.addActionListener(buttonListener);
stop.addActionListener(buttonListener);

Thirdly, you are calling repaint within the paintComponent method, this is a very bad idea, painting should paint the current state and should do nothing that might otherwise cause a repaint of any kind to trigger, otherwise you will end up in an infinite loop, which consume your CPU until nothing will run...

Take a look at How to use Swing Timers for more details



Related Topics



Leave a reply



Submit