How to Draw the Same Moving Image Multiple Times

How do I draw the same moving image multiple times?

@KevinWorkman is right. You need some kind of data structure to hold the fireballs. In the example below I used a List of Fireball.

List<Fireball> fireBalls;
...
private class Fireball {

Image fireball;
int x = 150;
int y = 125;

public Fireball(Image image) {
fireball = image;
}

public void drawFireball(Graphics g) {
g.drawImage(fireball, x, y, 50, 50, null);
}
}

To paint them, I just iterate through them. To make them move forward I just increas the x value in the timer and call repaint

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
for (Fireball ball : fireBalls) {
ball.drawFireball(g);
}
}

Sample Image

Here's the complete code

import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.*;
import java.util.List;
import java.util.logging.*;
import javax.imageio.ImageIO;
import javax.swing.*;
import javax.swing.Timer;

public class WannaBeStreetFighter extends JPanel {

private static final int D_W = 700;
private static final int D_H = 250;
private static final int X_INC = 10;

List<Fireball> fireBalls;
BufferedImage ryu;
BufferedImage fireball;
BufferedImage background;

public WannaBeStreetFighter() {

try {
ryu = ImageIO.read(new URL("http://www.sirlin.net/storage/street_fighter/ryu_hadoken_pose.png?__SQUARESPACE_CACHEVERSION=1226531909576"));
background = ImageIO.read(new URL("http://fightingstreet.com/folders/variousinfofolder/ehondasbath/hondasfz3stage.gif"));
fireball = ImageIO.read(new URL("http://farm6.staticflickr.com/5480/12297371495_ec19ded155_o.png"));
} catch (IOException ex) {
Logger.getLogger(WannaBeStreetFighter.class.getName()).log(Level.SEVERE, null, ex);
}

fireBalls = new LinkedList<>();

Timer timer = new Timer(40, new ActionListener() {
@Override
public void actionPerformed(ActionEvent e) {
Iterator<Fireball> it = fireBalls.iterator();
while (it.hasNext()) {
Fireball ball = it.next();
if (ball.x > D_W) {
it.remove();
System.out.println(fireBalls.size());
} else {
ball.x += X_INC;
repaint();
}
}
}
});
timer.start();

InputMap inputMap = getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW);
inputMap.put(KeyStroke.getKeyStroke("SPACE"), "hadouken");
getActionMap().put("hadouken", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
fireBalls.add(new Fireball(fireball));
}
});

}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, D_W, D_H, this);
g.drawImage(ryu, 50, 125, 150, 115, this);
for (Fireball ball : fireBalls) {
ball.drawFireball(g);
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}

private class Fireball {

Image fireball;
int x = 150;
int y = 125;

public Fireball(Image image) {
fireball = image;
}

public void drawFireball(Graphics g) {
g.drawImage(fireball, x, y, 75, 50, null);
}
}

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

Moving Image randomly multiple times

Here is one way to create balls randomly :

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Image;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.net.URL;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import javax.imageio.ImageIO;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class WannaBeStreetFighter extends JPanel {

private static final int D_W = 700, D_H = 250, X_INC = 10;

private final List<Fireball> fireBalls;
private BufferedImage ryu, background;
private final Random rand = new Random();

public WannaBeStreetFighter() {

try {
//fighter image
ryu = ImageIO.read(new URL("https://static1.squarespace.com/static/50f14d35e4b0d70ab5fc4f24/t/53f3260de4b0a13f9746a07c/1408443920310/ryu_hadoken_pose.png"));
background = ImageIO.read(new URL("http://fightingstreet.com/folders/variousinfofolder/ehondasbath/hondasfz3stage.gif"));
} catch (IOException ex) { ex.printStackTrace(); }

fireBalls = new LinkedList<>();

Timer timer = new Timer(40, e -> {

Iterator<Fireball> it = fireBalls.iterator();

while (it.hasNext()) {
Fireball ball = it.next();
if (ball.x > D_W) {
it.remove();
} else {
ball.x += X_INC;
repaint();
}
}

addBalls();
});

timer.start();
}

private void addBalls() {

if(rand.nextInt(100) > 5) return; //reduce the frequency of adding balls
fireBalls.add(new Fireball());
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(background, 0, 0, D_W, D_H, this);
g.drawImage(ryu, 50, 125, 150, 115, this);
for (Fireball ball : fireBalls) {
ball.drawFireball(g);
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}

private class Fireball {

private final Image fireball;
private int x = 180;
private final int y = 140;
private static final int SIZE =25;

public Fireball() {
fireball = getBall();
}

public void drawFireball(Graphics g) {
g.drawImage(fireball, x, y, 25, 25, null);
}

private BufferedImage getBall() {
BufferedImage img = new BufferedImage(SIZE , SIZE,
BufferedImage.TYPE_INT_ARGB);
Graphics2D g2 = img.createGraphics();
g2.setColor(Color.RED);
g2.fillOval(1, 1, SIZE - 2, SIZE - 2);
g2.dispose();
return img;
}
}

public static void main(String[] args) {
SwingUtilities.invokeLater(() -> {
JFrame frame = new JFrame("Best Street Fighter ever");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new WannaBeStreetFighter());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
});
}
}

Sample Image

Flipping and mirroring an image multiple times on a canvas

Flipping an image horizontally (left to right) involves:

  • translate to the horizontal middle of the image. This sets the rotation point as the horizontal middle of the image. It does this by moving the canvas's [0,0] origin horizontally to the middle of the image.

    context.translate(x+img.width/2,0);
  • Use scale to flip the image horizontally. This causes the image to be drawn as a horizontal mirror of itself.

    context.scale(-1,1);
  • drawImage the image offset by half the image's width. This offset is necessary because context.translate has moved the [0,0] to the midpoint of the image so the image must be pulled leftward so that it's drawn at the desired X location.

    context.drawImage(img,-img.width/2,y);

Sample Image

Here's example code and a Demo:

var canvas=document.getElementById("canvas");var ctx=canvas.getContext("2d");var cw=canvas.width;var ch=canvas.height;
var img=new Image();img.onload=start;img.src="https://dl.dropboxusercontent.com/u/139992952/multple/car1.png";function start(){ drawImageExtended(img,50,50); drawImageExtended(img,50,92,true);}
function drawImageExtended(img,x,y,flipHorizontally){ if(flipHorizontally){ ctx.translate(x+img.width/2,0); ctx.scale(-1,1); ctx.drawImage(img,-img.width/2,y); }else{ ctx.drawImage(img,x,y); }}
body{ background-color: ivory; }#canvas{border:1px solid red;}
<canvas id="canvas" width=300 height=300></canvas>

Moving element in canvas copies it multiple times until developer's console is opened

Relying on Paper.js is a good idea as it will do a lot of the work for you.

In fact, you should also use it to draw your background and everything will be a lot simpler.

Here is a sketch demonstrating the solution.

// Draw an image as background.
const raster = new Raster({
source: 'http://assets.paperjs.org/images/marilyn.jpg',
// Lower down the opacity so we can see the rest better.
opacity: 0.4,
// When image is loaded...
onLoad: () => {
// ...make sure that it fills the entire canvas.
raster.fitBounds(view.bounds, true);
}
});

// Draw a circle on top of the image.
const circle = new Path.Circle({
center: view.center,
radius: 50,
fillColor: 'orange',
// On circle drag...
onMouseDrag: event => {
// ...move it.
circle.position += event.delta;
}
});

// Draw intsructions.
new PointText({
content: 'Drag and drop the circle over the image',
point: view.center + [0, -80],
justification: 'center',
fontSize: 24
});

Drawing Multiple JComponents to a Frame

What you want to do is use a data structure of Car objects and loop through them in the paintComonent method. Something like

List<Car> cars = new ArrayList<>();
....
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}

The drawCar method would come from your Car class

public class Car {
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}

public void drawCar(Graphics g) {
g.setColor(Color.BLACK);
// do everything here as you would in a paintComponent method
}
}

See more examples here and here and here and here and here and here.


UPDATE

Here is a simple example use some "Ferraris" I whipped up, also using some animation, but with the same basic points I have above.

Sample Image

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 javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
import javax.swing.Timer;

public class DrawCar extends JPanel{
private static final int D_W = 400;
private static final int D_H = 400;

List<Car> cars;
public DrawCar() {
cars = new ArrayList<>();
cars.add(new Car(100, 300));
cars.add(new Car(200, 100));

Timer timer = new Timer(50, new ActionListener(){
public void actionPerformed(ActionEvent e) {
for (Car car : cars) {
car.move();
repaint();
}
}
});
timer.start();
}

@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Car car : cars) {
car.drawCar(g);
}
}

@Override
public Dimension getPreferredSize() {
return new Dimension(D_W, D_H);
}

public class Car {
private static final int INCREMENT = 5;
int x, y;
public Car(int x, int y) {
this.x = x;
this.y = y;
}
public void drawCar(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(x, y, 100, 30);
g.setColor(Color.BLACK); // body
g.fillOval(x + 15, y + 20, 15, 15); // wheel
g.fillOval(x + 60, y + 20, 15, 15); // wheel
g.fillRect(x + 15, y - 20, 60, 20); // top
}

public void move() {
if (x == D_W) {
x = 0;
} else {
x += INCREMENT;
}
}
}

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

Issue Drawing Multiple Moving Items of Screen Pygame

The draw_asteroid method blits the asteroid image depending on the direction that was passed, and since you pass a random direction each frame, the image is sometimes blitted at the up position (which is called self.speed_up) and other times at the down position (self.speed_down), so it appears to flicker.

I'd recommend to change the code completely and rather use objects or pygame sprites with vectors for the velocity and position which you can set to random values during the instantiation. Then first update the objects altogether in the while loop and finally blit them onto the display.

Edit: Here's a minimal example (you could also use pygame.sprite.Sprites and sprite groups instead of the list).

import sys
import random
import pygame as pg

class Asteroid:

def __init__(self):
self.image = pg.Surface((50, 50))
self.image.fill((150, 60, 10))
self.pos = pg.math.Vector2(random.randrange(1230),
random.randrange(750))
self.vel = pg.math.Vector2(random.uniform(-5, 5),
random.uniform(-5, 5))

def update(self):
self.pos += self.vel

class Game:

def __init__(self):
pg.init()
self.screen = pg.display.set_mode((1280, 800))
self.clock = pg.time.Clock()
self.bg_color = pg.Color(20, 20, 20)
self.asteroids = [Asteroid() for _ in range(10)]
self.done = False

def run(self):
while not self.done:
for event in pg.event.get():
if event.type == pg.QUIT:
self.done = True

for asteroid in self.asteroids:
asteroid.update()

self.screen.fill(self.bg_color)
for asteroid in self.asteroids:
self.screen.blit(asteroid.image, asteroid.pos)

pg.display.flip()
self.clock.tick(30)

Game().run()
pg.quit()
sys.exit()


Related Topics



Leave a reply



Submit