I'm trying to let a car image follow a path. The car has to "drive" at a constant speed and it has to look smooth. I'm able to let the car follow a list of points but I don't know how I can make the car drive smooth (the car moves to the next point in the list every 2 seconds now) with a constant speed and how to take hardcoded turns. Anyone who can help?
Code
Track class where the track underlay is loaded.
public class Track{
BufferedImage track;
Point trackPosition;
static final Point TRACK_POS = new Point(0, 0);
static final Point SENSOR_POS = new Point(250, 70);
public Track(){
try {
track = ImageIO.read(Roundabout.class.getResource("track.png"));
} catch (Exception ex) {
System.out.println("Problem loading track image: " + ex);
}
trackPosition=new Point(TRACK_POS.x,TRACK_POS.y);
}
public void paint (Graphics g)
{
g.drawImage(track,TRACK_POS.x, TRACK_POS.y, null);
}
}
Car class
public class Car extends JComponent implements Vehicle{
BufferedImage car;
Point carPosition;
static final Point START_POS = new Point(10, 150);
int counter=0;
public Car(){
try {
car = ImageIO.read(Car.class.getResource("beetle_red.gif"));
} catch (Exception e) {
System.out.println("Problem loading car images: " + e);
}
carPosition = new Point(START_POS.x, START_POS.y);
}
public void paint (Graphics g)
{
g.drawImage(car,carPosition.x, carPosition.y, null); //original paint
}
public Point getCarPosition() {
return new Point(carPosition.x,carPosition.y);
}
public void update(){
repaint();
if(counter < Lane.firstLane.size()){
carPosition.x = Lane.firstLane.get(counter).x;
carPosition.y= Lane.firstLane.get(counter).y;
System.out.println("Pos: "+getCarPosition());
counter++;
}
else{
System.out.println("Destination reached");
}
repaint();
}
}
Lane class
public class Lane {
public static List<Point> firstLane = new ArrayList<>(Arrays.asList(new Point(10,135),new Point(124,190),new Point(363,190),new Point(469,210)));
}
Roundabout (main class)
public class Roundabout extends JFrame{
Track track=new Track();
TrafficLight trafficLight=new TrafficLight();
Car car=new Car();
ArrayList<Car> cars = new ArrayList<>();
byte[] array=new byte[]{0,2,1,1}; //test byte array
class Surface extends JPanel {
private void doDrawing(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
Dimension size = getSize();
Insets insets = getInsets();
int w = size.width - insets.left - insets.right;
int h = size.height - insets.top - insets.bottom;
/* Draw the track first */
track.paint(g);
/* Draw a car */
car.paint(g);
cars.add(car); //add to list
trafficLight.paint(g);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
public Roundabout(){
initUI();
}
private void initUI() {
setTitle("Roundabout");
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
add(new Surface());
setSize(580, 550);
setLocationRelativeTo(null);
moveCar();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Roundabout roundabout=new Roundabout();
roundabout.setVisible(true);
}
});
}
public void moveCar() {
Runnable helloRunnable = new Runnable() {
public void run() {
car.update();
repaint();
}
};
ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);
executor.scheduleAtFixedRate(helloRunnable, 0, 2, TimeUnit.SECONDS);
}
}
Essentially, you need to move your car along a line segment, then another line segment, etc...
Here is an explanation of motion along a line
Relevant code, with t being in the range 0 to 1 (essentially t = percent of line crossed at said time):
x = xstart + (xend-xstart) * t
y = ystart + (yend-ystart) * t
It's all math.
In order to determine what speed (as change in t) each segment needs to be cruised at, you'll need to determine its length.
The math is easy:
if t+=(1/10) per 1 second for 10px long line, then its speed is 1 px per second. For a line 339 px long, you would need 339 seconds and t+=(1/339).
Related
I am making Conways Game of Life. In the mouse listener I want the cell to appear/disappear on the screen when I click once. I use a 40x40 boolean array (gameState) of 20x20 pixel cells. I want to paint the squares in my paint method using the co-ordinates of my mouse which i get in its clicked method. However, I am getting a null-pointer exception at line 71 and do not know what to do to solve it.
Main
import javax.swing.*;
import java.awt.*;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.image.BufferStrategy;
public class mainApplication extends JFrame implements Runnable, MouseListener {
private static final Dimension windowsize = new Dimension(80, 600);
private BufferStrategy strategy;
private Graphics offscreenGraphics;
private static boolean isGraphicsInitialised = false;
private static int rows = 40;
private static int columns = 40;
private static int height = windowsize.height;
private static int width = windowsize.width;
private static Cells cells;
private int xArrayElement,yArrayElement, xPosition, yPosition;
private static boolean gameState[][] = new boolean[rows][columns];
public mainApplication() {
System.out.println(System.getProperty("user.dir"));
setDefaultCloseOperation(EXIT_ON_CLOSE);
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width / 2 - windowsize.width / 2;
int y = screensize.height / 2 - windowsize.height / 2;
setBounds(x, y, screensize.width, screensize.height);
setVisible(true);
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenGraphics = strategy.getDrawGraphics();
isGraphicsInitialised = true;
// MouseEvent mouseEvent = new MouseEvent();
addMouseListener(this);
// addMouseMotionListener(MouseEvent);
Thread t = new Thread(this);
t.start();
}
public void mousePressed(MouseEvent e) { }
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) {
if(e.getClickCount() == 1){
xPosition = e.getX();
yPosition = e.getY();
cells.setPosition(xPosition,yPosition);
xArrayElement = (xPosition/20);
yArrayElement = (yPosition/20);
if(gameState[xArrayElement][yArrayElement]){
gameState[xArrayElement][yArrayElement] = false;
}
else if (!gameState[xArrayElement][yArrayElement]) {
gameState[xArrayElement][yArrayElement] = true;
}
}
}
#Override
public void run() {
while (true) {
try { //threads entry point
Thread.sleep(20); //forces us to catch exception
}
catch (InterruptedException e) {
}
}
}
public void paint(Graphics g) {
if (isGraphicsInitialised) {
g = strategy.getDrawGraphics();
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
if (gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.WHITE);
cells.paint(g);
}
else if (!gameState[xArrayElement][yArrayElement]) {
g.setColor(Color.BLACK);
g.fillRect(xPosition, yPosition, 20, 20);
}
strategy.show();
}
}
public static void main(String[]args){
mainApplication test = new mainApplication();
}
}
Cells class
import java.awt.*;
public class Cells {
int x;
int y;
public Cells(){
}
public void setPosition(int xi, int xj){
x = xi;
y = xi;
System.out.println(xi);
System.out.println("sjdkgffdjv" + y);
}
public boolean cellState(boolean visible){
return visible;
}
public void paint(Graphics g){
g.drawRect(x, y, 20,20);
}
}
It's because you haven't initialized your cells variable in Main class..
So try this
private static Cells cells = new Cells();
As #nullPointer has pointed out (sorry, dad joke) you're getting a NPE because you haven't initialized the class member Cells. There are also a few other points to make that might unrelated to the question.
Don't create that thread
Swing already uses a thread to handle UI events and drawing so creating another thread is dangerous.
Make Cells immutable
Cells should be immutable. At no point should you need to set the position of a cell. If you need to change where a Cell is at, just dispose of the object and create a new one at that position.
I am new to graphics and japplets, and I made a rectangle that goes across the screen. But for some reason, it just draws a line across, not removing the old instance of the rectangle once it moves.
Main:
public class Main extends JApplet implements Runnable {
private static final long serialVersionUID = 1L;
private static int width = 900;
private static int height = 600;
public static int fps = 60;
public Thread thread = new Thread(this);
public static Ailoid ailoid = new Ailoid();
public void init() {
setSize(width, height);
setBackground(Color.white);
ailoid.setLocation(new Location(100, 100));
AlienManager.registerAlien(ailoid);
}
public void paint(Graphics g) {
g.setColor(Color.green);
for (Alien alien : AlienManager.getAliens()) {
Location loc = alien.getLocation();
int x = loc.getX();
int y = loc.getY();
g.fillRect(x, y, 10, 20);
}
}
// Thread start
#Override
public void start() {
thread.start();
}
// Thread stop
#SuppressWarnings("deprecation")
#Override
public void destroy() {
thread.stop();
}
#Override
public void run() {
while (true) {
Updater.run();
repaint();
try {
// 1000 divided by fps to get frames per millisecond
Thread.sleep(1000 / fps);
}
catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
Updater:
public class Updater {
public static void run() {
for (Alien alien : AlienManager.getAliens()) {
Location loc = alien.getLocation();
int x = loc.getX();
int y = loc.getY();
alien.setLocation(new Location(x, y));
}
}
}
Why doesn't it remove the old graphics? thank you!
Your main problem is that your paint(...) method does not call the super method, the method that allows the component to redraw its contents:
public void paint(Graphics g) {
super.paint(g);
//....
Having said that, you're far better off not drawing in a top level window but rather in the paintComponent method of a JPanel that is displayed by the applet. If you do this correction, then do the same thing -- call the super method.
class MyPanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
//....
As an aside, this code:
public class Updater {
public static void run() {
for (Alien alien : AlienManager.getAliens()) {
Location loc = alien.getLocation();
int x = loc.getX();
int y = loc.getY();
alien.setLocation(new Location(x, y));
}
}
}
Doesn't look like it's moving things all that much. In fact per this code, your aliens should stay completely still.
Edit
Also, this is never code, code you should never have in your application:
#SuppressWarnings("deprecation")
#Override
public void destroy() {
thread.stop();
}
There's a reason that thread's stop() method is deprecated and should never be called. If you're curious as to why, please check out the API.
Paint class:
public class Paint extends JPanel implements ActionListener {
Image swimmingpool;
Mouse swim = new Mouse();
Timer tm = new Timer(7, this);
public void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println(swim.getdistance()); //prints out 0 ?!?
ImageIcon swimminghold = new ImageIcon(render.class.getResource("resources/Swimmingpoolns.png"));
swimmingpool = swimminghold.getImage();
g.drawImage(swimmingpool, 0,-40,null);
if (swim.getdistance() >= 3) {
System.out.println("test works");
}
}
public void actionPerformed(ActionEvent e) {
repaint();
}
}
Mouse class
public class Mouse implements MouseMotionListener {
private int x1 = 200;
private int y1 = 165;
double distance;
public void mouseMoved(MouseEvent e) {
double distance1 = Math.pow((e.getX() - x1), 2);
double distance2 = Math.pow((e.getY() - y1), 2);
setdistance(Math.sqrt(distance1 + distance2));
// The below prints, and has been
// tested to print the correct distance
System.out.println(getdistance());
}
public void setdistance(double distance) {
this.distance = distance;
}
public double getdistance() {
return distance;
}
}
When I execute System.out.println(getdistance()) in the Mouse class it prints the correct distance whereas if I execute System.out.println(swim.getdistance()); in the paint class prints 0.
Everything I've tried to do still results in distance = 0, in the class public void paintComponent(Graphics g).
What am I not understanding?
As pointed out by #Hunter-mcmillen: you are confused about java operators.
public void mouseMoved(MouseEvent e) {
double distance1 = Math.pow((e.getX() - x1),2);
double distance2 = Math.pow((e.getY() - y1),2); // Math.pow(a,b) == a^b (in a calculator)
setdistance(Math.sqrt(distance1 + distance2));
System.out.println(getdistance());
}
I really recommend that you read more carefully the Java operators before assuming how they work on the language.
EDIT 2:
I recommend also that you create a JPanel or JLabel for this picture, and then load such picture inside this new Jpanel or Label or other component.
public class Paint extends JPanel implements ActionListener {
Mouse swim = new Mouse();
Timer tm = new Timer(7, this);
public void paintComponent(Graphics g) {
// Try this:
ImageIcon swimminghold = new ImageIcon(render.class.getResource("resources/Swimmingpoolns.png"));
swimmingpool = swimminghold.getImage();
JLabel label = new JLabel();
label.setIcon(swimminghold);
label.addMouseMotionListener(swim);
addMouseMotionListener(swim);
label.addMouseMotionListener(swim);
addMouseMotionListener(swim);
//Do something
/* ...*/
}
I have created a class whit two shapes namely two ovals. Here I draw them.
import ...;
public class Drawings extends JPanel{
public double degrees = 0;
public void paintComponent(Graphics g){
super.paintComponent(g);
int xcen = getWidth() / 2;
int ycen = getHeight()/ 2;
int radius = 10;
degrees++;
double radians = Math.toRadians(degrees);
int posx = (int)(100.getDistance() * Math.cos(radians));
int posy = (int)(100.getDistance() * Math.sin(radians));
g.setColor(Color.BLUE);
g.FillOval(xcen + posx, ycen + posy, 20, 20);
g.setColor(Color.GREEN);
g.drawOval(xcen + posx, ycen + posy, 100,100)
}
}
Now I implement it in a main.
import ....;
public class Animate extends JFrame{
public static void main(String [] args)
{
JFrame window = new JFrame();
window.add(new Drawings());
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(500,500);
window.setLocationRelativeTo(null);
window.setVisible(true);
//now I implement the thread to animate the two shapes
Thread paintThread = new Thread(new Runnable(){
#Override
public void run(){
while(true)
{
window.repaint();
try{
Thread.sleep(25);//determines how slow the ovals will move
}catch(InterruptedException e){
e.printStackTrace();
}
}
}
});
paintThread.start();//start the animation
}
}
When the program runs the two Ovals rotate on the screen. But the two ovals rotates at the same speed as I would expect but I would like the two ovals to move at diffident speeds.
I have tried using a method to move them at different speed but with no success.
How would I get the two ovals moving at different speeds?
Make a class to represent an oval. Make two instances. Give the two instances different angular velocities. Currently because you increment degrees by 1.0 every 25 ms you have an angular velocity fixed at 40 degrees per second. If each oval has its own degrees field and you increment the two by different amounts, the ovals will rotate at different rates.
The easiest way is:
import ...;
public class Drawings extends JPanel{
public double degrees = 0;
private int firstOvalSpeed;
private int secondOvalSpeed;
public void paintComponent(Graphics g){
super.paintComponent(g);
int xcen = getWidth() / 2;
int ycen = getHeight()/ 2;
int radius = 10;
degrees++;
double radians = Math.toRadians(degrees);
int posx = (int)(100.getDistance() * Math.cos(radians));
int posy = (int)(100.getDistance() * Math.sin(radians));
g.setColor(Color.BLUE);
g.FillOval(xcen + posx*firstOvalSpeed, ycen + posy*firstOvalSpeed, 20, 20);
g.setColor(Color.GREEN);
g.drawOval(xcen + posx*secondOvalSpeed, ycen + posy*secondOvalSpeed, 100,100)
}
public void setFirstOvalSpeed(int firstOvalSpeed) {
this.firstOvalSpeed = firstOvalSpeed;
}
public void setSecondOvalSpeed(int secondOvalSpeed) {
this.secondOvalSpeed = secondOvalSpeed;
}
}
public class Animate extends JFrame {
public static void main(String[] args) {
final JFrame window = new JFrame();
Drawings drawings = new Drawings();
drawings.setFirstOvalSpeed(1);
drawings.setSecondOvalSpeed(2);
window.add(drawings);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
window.setSize(500, 500);
window.setLocationRelativeTo(null);
window.setVisible(true);
// now I implement the thread to animate the two shapes
Thread paintThread = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
window.repaint();
try {
Thread.sleep(25);// determines how slow the ovals will
// move
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
});
paintThread.start();// start the animation
}
}
so far i have created the class below, however, now i would like to insert a code to make the image slide from left to right and right to left. I would normally use the Sliding platform however it doesn't work when i try to implement it. I am still a beginner in java so i am grateful for your help.
This is the code of my Java Class:
package game;
import city.soi.platform.*;
public class Enemy extends Body implements CollisionListener{
private Game game ;
public Enemy(Game g){
super(g.getWorld(), new PolygonShape(-27.0f,25.0f, -27.0f,-24.0f, 25.0f,-24.0f, 26.0f,25.0f, -27.0f,25.0f));
setImage(new BodyImage("images/enemy.png"));
g.getWorld().addCollisionListener(this);
game = g;
}
public void collide(CollisionEvent e) {
if (e.getOtherBody() == game.getPlayer()){
game.getPlayer().subtractFromScore(75);
System.out.println("You have just lossed 75 Points! Current Score = " + game.getPlayer().getScore());
this.destroy();
}
}
}
Just to be clear i would like everyone i include this class onto a platform it moves from left to right.
Many Thanks,
Moe
This will depend a lot on what you individual requirements, but the basic concepts will remain the same.
Any type of animation in Swing must be executed in such away that it does not block the Event Dispatching Thread. Any blocking action on the EDT will prevent any repaint request (amongst other things) from been processed.
This simple example uses a javax.swing.Timer that ticks every 40 milliseconds or so (about 25 fps) and updates the position of a small "ball"
More complex iterations would require a dedicated Thread. This makes the whole process far more complex as
You should never update/create/modify/change any UI component (or property that the UI may require to perform painting) from any thread other then the EDT
You don't control the paint process. This means a repaint may occur at anytime and if you are modifying any property/object that the paint process requires to render the state of the game, it could cause inconsistencies.
.
public class SimpleBouncyBall {
public static void main(String[] args) {
new SimpleBouncyBall();
}
public SimpleBouncyBall() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (Exception ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new CourtPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class CourtPane extends JPanel {
private Ball ball;
private int speed = 5;
public CourtPane() {
Timer timer = new Timer(40, new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Rectangle bounds = new Rectangle(new Point(0, 0), getSize());
if (ball == null) {
ball = new Ball(bounds);
}
speed = ball.move(speed, bounds);
repaint();
}
});
timer.setRepeats(true);
timer.setCoalesce(true);
timer.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(100, 100);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
if (ball != null) {
Graphics2D g2d = (Graphics2D) g.create();
g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
Point p = ball.getPoint();
g2d.translate(p.x, p.y);
ball.paint(g2d);
g2d.dispose();
}
}
}
public class Ball {
private Point p;
private int radius = 12;
public Ball(Rectangle bounds) {
p = new Point();
p.x = 0;
p.y = bounds.y + (bounds.height - radius) / 2;
}
public Point getPoint() {
return p;
}
public int move(int speed, Rectangle bounds) {
p.x += speed;
if (p.x + radius >= (bounds.x + bounds.width)) {
speed *= -1;
p.x = ((bounds.x + bounds.width) - radius) + speed;
} else if (p.x <= bounds.x) {
speed *= -1;
p.x = bounds.x + speed;
}
p.y = bounds.y + (bounds.height - radius) / 2;
return speed;
}
public void paint(Graphics2D g) {
g.setColor(Color.RED);
g.fillOval(0, 0, radius, radius);
}
}
}