Freehand painting in Java - java

I've been writing this little painting program thing, but whenever I release the mouse and move to another point on the screen, the line is drawn over there. I tried clearing the points when the mouse has been released, but that deletes everything on screen.
import java.awt.Canvas;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import java.awt.Point;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.ArrayList;
public class PaintingCanvas extends Canvas implements MouseMotionListener, MouseListener {
private ArrayList<Point> points = new ArrayList<Point>();
public PaintingCanvas(int width, int height) {
setBounds(0, 0, width, height);
addMouseMotionListener(this);
addMouseListener(this);
}
public void paint(Graphics g) {
for (int i = 0; i < points.size() - 2; i++) {
Point p1 = points.get(i);
Point p2 = points.get(i + 1);
g.drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
#Override
public void mouseDragged(MouseEvent e) {
points.add(e.getPoint());
repaint();
}
}

I suggest:
Call the super.paint(g); method first thing in your paint method.
When the mouse is released, paint the image represented by the points ArrayList to a BufferedImage and then clear the points ArrayList, and then call repaint().
Draw the BufferedImage in the paint method. before drawing your lines (but check that it's not null first). You do this with the Graphics#drawImage(Image image, int x, int y, ...) method.
Better, re-write this to work in Swing by painting in a JPanel's paintComponent method.

Of course this is happening - once you start drawing again, the new points are added after the old ones. Once you paint after that, they are included. You'll need to seperate the different paths from each other.

Have you looked at the Path classes? If you are simply drawing discrete lines to a screen, the GeneralPath class might be a simple solution.
The Drawing Arbitrary Shapes tutorial explains how to use these.
Basically, every time the user pressed the mouse (on a mousePressed event), you would call the path's moveTo(x, y) method. For every segment (replacing what you currently do in the mouseDragged() method), you would call the path's lineTo(x, y) method.
No matter what, you -definitely- need to handle mousePressed or mouseReleased events, or both, as you are looking for some way to indicate the start of a new line/path, rather than using the old one.

private void jPanel1MouseDragged(java.awt.event.MouseEvent evt) {
points.add(evt.getPoint());
for (int i = 0; i < points.size() - 2; i++)
{
Point p1 = points.get(i);
Point p2 = points.get(i + 1);
jPanel1.getGraphics().drawLine(p1.x, p1.y, p2.x, p2.y);
}
}
private void jPanel1MousePressed(java.awt.event.MouseEvent evt) {
points.clear();
points.add(evt.getPoint());
}

I would make a 2d arraylist in which the first array is points added to until you lift up your finger then a new array is added for the second line etc...

Insert a garbage point -1,-1 in points inside mouseReleased method and check for it inside paint method and skip that point. Updated code:
#Override
public void mouseReleased(MouseEvent e) {
points.add(new Point(-1, -1));
}
#Override
public void paint(Graphics g) {
for (int i = 0; i < points.size() - 1; i++) {
Point p = points.get(i+1);
int x2 = p.x;
int y2 = p.y;
if ( x2 == -1 || y2 == -1) {
++i;
//continue // Considered bad practice, can play havoc with your system. (source http://xkcd.com/292/ ).
}
else {
p = points.get(i);
int x1 = p.x;
int y1 = p.y;
g.drawLine(x1, y1, x2, y2);
}
}
}
OP, how it feels to see this question after 3 years? :D

Related

How can I draw a circle that compounds your cursor movement?

I am trying to draw a circle with my cursor while I am moving it. I know the circle wont be perfect but that does not really matter I just need the circle to compound on top of my organic cursor movements. I originally tried to do this with java's awt robot class but that ended up being futile because anytime I moved my mouse massive lines would extend infinitely far from where I made that movement. Here are two sets of code I tried (keep in mind I am calling these from a nativeMousePress event so I am holding down the left click the whole time).
int radius = 100;
for (double i = 0; i < (2 * Math.PI) + Math.PI / 6; i = i + Math.PI / 6) {
PointerInfo pointerA = MouseInfo.getPointerInfo();
Point a = pointerA.getLocation();
int yStart = (int) a.getY();
int xStart = (int) a.getX();
robot.mouseMove((int) ((xStart) + (radius * Math.cos(i))), (int) ((yStart) + (radius * Math.sin(i))));
robot.delay(68);
}
Here is my another attempt I had. I also want to mention that I need the delay in between each stroke.
robot.mouseMove(getX() + 40, getY() + 20);
robot.delay(1000);
robot.mouseMove(getX() + 20, getY() + 40);
robot.delay(1000);
robot.mouseMove(getX() - 20, getY() + 40);
robot.delay(1000);
robot.mouseMove(getX() - 40, getY() + 20);
robot.delay(1000);
robot.mouseMove(getX() - 40, getY() - 20);
robot.delay(1000);
robot.mouseMove(getX() - 20, getY() - 40);
robot.delay(1000);
robot.mouseMove(getX() + 20, getY() - 40);
robot.delay(1000);
robot.mouseMove(getX() + 40, getY() - 20);
public int getX() {
PointerInfo pointerA = MouseInfo.getPointerInfo();
Point a = pointerA.getLocation();
return (int) a.getX();
}
public int getY() {
PointerInfo pointerA = MouseInfo.getPointerInfo();
Point a = pointerA.getLocation();
return (int) a.getY();
}
Ok now here is the image that shows what happens when I barely move my mouse. This happens with both sets of code even though each set of code makes the circle slightly different.
Now to reiterate what I am trying to do I want to be able to move my mouse freely around the canvas while the code tries to make circles which should likely lead to a compounding effect. The issue is when I tried to use the robot class it completely bugs out and makes massive lines every which way if you attempt to move the mouse. I already asked other people about this and no one could figure out a solution to this so I am wondering if there is another class I can access that can do what I am looking for. Anyways here is a picture of what I assume it would look like if I moved my cursor to the right and if my circle code worked. (I already figured out how to do the loop effect)
Lastly I wanted to mention that I dont want a solution that provides code that can make the shape I showed above because thats not the point since I plan to do more complex things with this later. I just really need to know how to automate moving my cursor while having its movements compound with my active mouse movements.
The java.awt.Robot generates inputs events automatically. In your code, for one human input event, robot is generating 12 input events over 68x12 milliseconds. So while robot is still generating 12 events if human gives more input events then mouse pointer location will jump back and forth between robot inputs and human inputs. The problem is both robots and human are giving mouse coordinates at the same time.
one solution could be to ignore human events while robot is generating click events. But distinguishing between robot generated events and human generated events will be very hard task. And things will get complicated.
I think the easiest option will be to draw one circle for one human mouse drag input. Think it as circle/spring pattern brush. Like they have air brush in drawing tools.
I know you don't want the code. But to convey my points better here is minimal code for the approach:
import java.awt.Color;
import java.awt.Frame;
import java.awt.Graphics;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
public class Test extends Frame implements MouseMotionListener {
private int x1, y1, x2, y2;
private double i, radius = 10;
private Object lock = new Object();
public void init() throws Exception {
addWindowListener(new WindowAdapter() {
public void windowClosing(WindowEvent we) {
dispose();
System.exit(0);
}
});
addMouseMotionListener(this);
setBounds(50, 50, 500, 550);
setVisible(true);
}
public static void main(String[] argv) throws Exception {
new Test().init();
}
public void update(Graphics g) {
paint(g);
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.drawLine(x1, y1, x2, y2);
}
public void mouseDragged(MouseEvent me) {
me.consume();
//do this to avoid multiple circles drawing at the same time
synchronized (lock) {
int x = me.getX();
int y = me.getY();
if (x1 == 0) x1 = x;
if (y1 == 0) y1 = y;
i=Math.PI+(Math.PI/6)*2;
for (int j = 0; j < 13; j++, i +=(Math.PI / 6)) {
x2 = (int) (x + (radius * Math.cos(i)));
y2 = (int) (y + (radius * Math.sin(i)));
paint(getGraphics());
x1 = x2;
y1 = y2;
radius += 0.3;
}
try {
Thread.sleep(150); //change delay to suite ease of use
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void mouseMoved(MouseEvent me) { //reset
synchronized(lock) {
radius = 10;
x1 = 0;
y1 = 0;
}
}
}
And the output:

Java snap to grid

Okay so I'm trying to make things snap to a grid I have.
This is how I snap to the screen itself:
int finalCalcX = (mouseX / Handler.gridSpace32) * Handler.gridSpace32;
int finalCalcY = (mouseY / Handler.gridSpace32) * Handler.gridSpace32;
The variable names speak for themselves I think.
Mouse coordinates divided by my tiles being 32x32 pixels, times that again to get the snap-to-grid functionality.
Now this works fine for the screen, but when I want to add it to the "map" itself, I can't just add my map x and y offsets to that, it gets messed up.
I've played around with it for about two days, and I also got it to snap to the map itself, but when I'm say, halfway in the map on both axis, the mouseX and mouseY messes the grid thing up.
It's kind of hard for me to explain, but the offset from the 0, 0 (every origins position, even the screen) PLUS the maps offset when you move away from the origin, gets added to the distance between the cursor itself and the transparent snap-to-grid tile that I'm using to test.
Basically the offset between the maps origin, and the camera, is for some reason the same offset between the cursor and the transparent tile. So the further into the map i move, the further away the tile gets from the cursor, and eventually moves outside the screen width and height...
When I move further into the map, I want the snap-to-grid functionality to stay corret, no matter where on the map I am.
Render method:
for (int y = startY; y < endY; y++) {
for (int x = startX; x < endX; x++) {
gridSpace(graphics, (int) (x * Handler.gridSpace32 - handler.getCamera().getOffsetX()),
(int) (y * Handler.gridSpace32 - handler.getCamera().getOffsetY()));
checkHighlight(graphics);
}
}
The gridSpace is the grid itself.
Here is what's in the highlight at the moment:
int finalCalcX = (mouseX / Handler.gridSpace32) * Handler.gridSpace32;
int finalCalcY = (mouseY / Handler.gridSpace32) * Handler.gridSpace32;
graphics.setColor(new Color(100, 200, 100, 3));
graphics.fillRect(finalCalcX, finalCalcY, Handler.gridSpace32, Handler.gridSpace32);
Sorry for my terrible explanation skills, but that's the best I can do.
What am I doing wrong?
I think I recreated what you are getting, and the problem lies with usage of incorrect sign in calculations of camera position and translation. Executing code below you should get similar behavior to what you described, while uncommenting commented lines (while commenting out the ones that follow them) should give you correct behavior.
package test;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;
import javax.swing.*;
public class SnapToGrid extends JPanel implements MouseMotionListener{
private int[] camera;
private int[] mouse;
private final int gridSize = 16;
SnapToGrid() {
camera = new int[2];
mouse = new int[2];
setFocusable(true);
addMouseMotionListener(this);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
//g2.translate(-camera[0], -camera[1]);
g2.translate(camera[0], camera[1]);
//draw background
for (int i = 0; i < 9; i++)
for (int j = 0; j < 9; j++)
{
Color c = ((j*9) + i) % 2 == 0 ? Color.black : Color.white;
g2.setColor(c);
g2.fillRect(i*gridSize, j*gridSize, gridSize, gridSize);
}
g2.setColor(Color.blue);
int[] snappedPos = getSnappedMousePosition();
g2.fillRect(snappedPos[0], snappedPos[1], gridSize, gridSize);
}
private int[] getSnappedMousePosition() {
return new int[] {
camera[0] + mouse[0] - ((camera[0] + mouse[0]) % gridSize),
camera[1] + mouse[1] - ((camera[1] + mouse[1]) % gridSize)
};
}
public static void main (String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(new SnapToGrid());
frame.pack();
frame.setVisible(true);
}
#Override
public void mouseDragged(MouseEvent e) {
//camera[0] -= e.getX() - mouse[0];
//camera[1] -= e.getY() - mouse[1];
camera[0] += e.getX() - mouse[0];
camera[1] += e.getY() - mouse[1];
mouse[0] = e.getX();
mouse[1] = e.getY();
repaint();
}
#Override
public void mouseMoved(MouseEvent e) {
mouse[0] = e.getX();
mouse[1] = e.getY();
repaint();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(gridSize * 18, gridSize * 18);
}
}
One thing that I really don't like with your code, is that you are not using transformations. Using transformations allows you to separate world from viewport, submits to far easier debugging of issues like this here, and most importantly, if you want to add things like scaling or rotating later on, you only need to add few lines, as opposed to rewriting half of your render method.

Graphics2D.drawPolyline() vs Graphics2D.drawLine()

Is there any benefit to calling drawPolyline() versus iterating through each line and calling drawLine() in the Graphics2D class?
For example:
graphics2d.drawPolyline(xPoints, yPoints, nPoints);
versus:
for (MyBean line : myBeans) {
graphics2d.drawLine(line.getX1Point(), line.getY1Point(), line.getX2Point(), line.getY2Point());
}
Is the first a convenience method for the second?
Fairly new to AWT. (I realize that the first one may be more concise.)
Edit: I call BufferedImage.createGraphics() for the implementation of Graphics2D.
There is an important difference: With drawPolyline you are drawing a single polyline. With drawLine you are drawing individual lines. So far, so obvious. But what does it mean?
The difference mainly shows up when assigning a "non-trivial" Stroke to the graphics object - usually, a certain BasicStroke. This receives several parameters in the constructor. The important one regarding your question is the join parameter. It can be JOIN_BEVEL, JOIN_METER and JOIN_ROUND. It determines how two connected lines are joined. And this, obviously, can only be applied when it is known that the lines are connected, which is only the case in the drawPolyline call. It simply can not be applied for individual drawLine calls.
The following is a screenshot showing this difference. It uses a stroke with a witdh of 15 and a join=BasicStroke.JOIN_ROUND. The left part is drawn with drawPolyline, and the right one is drawn as individual lines:
But you should not usually not use drawPolyline anyhow...
... because it is somehow out-dated and has several shortcomings. First of all, it is a hassle to create the arrays that are required for calling it. And importantly, it only accepts int[] arrays.
The whole Java 2D painting infrastructure was originally focussing on int coordinates, like in Graphics#drawLine(int,int,int,int). This has been generalized, and the Graphics2D methods allow a much greater flexibility here. So the usual way to draw a polyline nowadays would be to create a Shape object containing the polyline. In most cases, this will be a Path2D instance:
Path2D path = new Path2D.Double();
path.moveTo(x0,y0);
path.lineTo(x1,y1);
path.lineTo(x2,y2);
...
graphics2D.draw(path);
However, just for reference, here is the code that was used to create the above image:
import java.awt.BasicStroke;
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.SwingUtilities;
public class DrawLineVsDrawPolyline
{
public static void main(String[] args)
{
SwingUtilities.invokeLater(new Runnable()
{
#Override
public void run()
{
createAndShowGUI();
}
});
}
private static void createAndShowGUI()
{
JFrame f = new JFrame("");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.getContentPane().setLayout(new BorderLayout());
class Line
{
int x1, y1, x2, y2;
Line(int x1, int y1, int x2, int y2)
{
this.x1 = x1;
this.y1 = y1;
this.x2 = x2;
this.y2 = y2;
}
public int getX1Point()
{
return x1;
}
public int getY1Point()
{
return y1;
}
public int getX2Point()
{
return x2;
}
public int getY2Point()
{
return y2;
}
}
int xPoints[] = new int[] { 100, 150, 200 };
int yPoints[] = new int[] { 100, 250, 100 };
int nPoints = xPoints.length;
List<Line> lines = new ArrayList<Line>();
for (int i0=0; i0<nPoints-1; i0++)
{
int i1 = i0+1;
int x1 = xPoints[i0];
int y1 = yPoints[i0];
int x2 = xPoints[i1];
int y2 = yPoints[i1];
lines.add(new Line(x1,y1,x2,y2));
}
JPanel panel = new JPanel()
{
#Override
protected void paintComponent(Graphics gr)
{
super.paintComponent(gr);
Graphics2D g = (Graphics2D)gr;
g.setColor(Color.RED);
g.setStroke(new BasicStroke(20.0f,
BasicStroke.CAP_SQUARE, BasicStroke.JOIN_ROUND));
g.drawPolyline(xPoints, yPoints, nPoints);
g.translate(200, 0);
for (Line line : lines) {
g.drawLine(
line.getX1Point(), line.getY1Point(),
line.getX2Point(), line.getY2Point());
}
}
};
f.getContentPane().add(panel, BorderLayout.CENTER);
f.setSize(500,500);
f.setLocationRelativeTo(null);
f.setVisible(true);
}
}
Bearing in mind that Graphics2D is an abstract class, so you don't know which concrete implementation you are working with, I would use whichever method makes your code cleaner.
I rarely use drawPolyline() because it's usually a pain to get the coordinates into arrays for pass to the method. But you should also consider creating a GeneralPath object which can be passed to the Graphics2D.draw(Shape) method. Drawing a path might be more efficient than drawing individual lines for some implementations of Graphics2D. Occasionally I've noticed also that the output looks slightly better with a path rather than individual lines, particularly if the line color is partially transparent (you can sometimes see darker spots where the end points of the individual lines overlap).

HOw to move my bullet slowly in java?

Hi I am developing a game that a fighter moves right and left and shoots. For the shooting part, I tried to use a for loop to slow the speed down and user can see the bullet. But it wasn't enough. I used sleep too but not a good answer. Now I have no idea what to do.
Here is my paintComponent calss:
package game;
import java.awt.Color;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.geom.Rectangle2D;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import javax.imageio.ImageIO;
import javax.swing.JPanel;
public class PaintComponent extends JPanel implements KeyListener {
int dx = 200-15;
int dy = 450;
int my = 450;
ArrayList<Bullet> bullets = new ArrayList<>();
public Rectangle2D rec =new Rectangle2D.Double(dx , dy, 30, 10);
Rectangle2D recB = new Rectangle2D.Double(dx+13 , my, 6, 6);
// public Polygon pol = new Polygon
private BufferedImage imageBg, imageFi, imageBu;
public PaintComponent() {
this.addKeyListener(this);
this.setFocusable(true);
this.setBackground(Color.white);
try {
imageBg = ImageIO.read(new File("C:\\Java\\NetbeansProjects\\Game\\bg.jpg"));
imageBu = ImageIO.read(new File("C:\\Java\\NetbeansProjects\\Game\\bul.png"));
imageFi = ImageIO.read(new File("C:\\Java\\NetbeansProjects\\Game\\fi.png"));
} catch (IOException ex) {
System.out.println("No background image is available!");
}
}
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
public void moveRec(KeyEvent evt){
switch(evt.getKeyCode()){
case KeyEvent.VK_LEFT:
dx -= 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_RIGHT:
dx += 5;
rec.setRect(dx, dy, 30, 10);
recB.setRect(dx+13, dy, 6, 6);
repaint();
break;
case KeyEvent.VK_SPACE:
Bullet b = new Bullet(dx, dy);
bullets.add(b);
shoot();
break;
}
}
#Override
public void paintComponent(Graphics grphcs)
{super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null){
for(int i=0;i<bullets.size();i++){
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
#Override
public void keyTyped(KeyEvent e) {
}
#Override
public void keyPressed(KeyEvent e)
{moveRec(e);}
#Override
public void keyReleased(KeyEvent e)
{}
}
and this is my bullet calss:
package game;
public class Bullet {
private int x, y, newy;
public Bullet(int x, int y){
this.x = x;
this.y = y;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
I think you are going wrong with slowing down the loop. The last thing you want to do is slow down the game loop or sleep the game loop. This will affect all you objects with in the game.
There are multiple way to go about this:
Smaller increments per tick
One of the most obvious things you could do is make the increment of the bullet smaller. Lets take a look at your shoot(); method:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
for(int j=0; j<200; j++){
bullets.get(i).setdy(my-j);
}
System.out.println(bullets.get(i).getdy());
}
}
}
As far as i understand you are iterating 200x over all the bullets, each tick the bullet's y axis gets changed, using the formula "my - j" or "450 - the tick index"
In order to slow down the bullet you would need to divide the j with a certain number to get the desired speed of the bullet. For instance: "my - (j / 2)" would impact the speed of the bullet. Try to play around with these variables to get the desired speed.
Adding a speed modifier
What a lot of games to is a speed modifier or a base speed for each projectile. This could be of use to you, the only thing i noticed that you are kinda trying to simulate loss of velocity. To achieve this result you would need another variable. Let call that "time to live" for right now.
So if we modify the bullet class it would look like this. Noticed we also have a new function called Move();. This will calculate the next move based upon the variables.
public class Bullet {
private int x, y, newy;
private speed, ttl; //ttl = time to live
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
}
public int Move()
{
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
}
What the code now does is it calculates the speed based upon the time to live variable the longer the bullet live the less velocity it will have. Adjusting the speed variable makes you able to control the bullet better. And to say so myself it looks a lot more neater in the shoot method:
public void shoot(){
if(bullets != null){
for(int i=0; i<bullets.size(); i++){
bullets.get(i).Move();
}
}
}
Of course there is more to it, like checking if the speed and time to live dont go out of bounds and stuff, but i think your smart enough to figure that out ;)
Running it off a timer
As ControlAltDel said you can implement a timer, im not an expert on java so im not going in depth on this. But it surely it is a possibility. Its nothing more then implement the current shoot method inside the tick function of the timer. Of course removing the for i<200 loop. Since its not very effecient.
Anyways
If i did get something wrong or misunderstood (or even grammer mistaked :) ) the problem, im sorry. If there are any question i loved to hear from you ;).
And please not that this is untested code and im only here to explain things you could try to get it working a intended!
Sincerly,
Syntasu.
UPDATE:
Some explaining on how to update the bullet's.
In order to update the bullets we need to make it run off a loop. Since in this case the main loop is were also where the drawing is happening, the "paintComponent" method. There is already a loop withing the paint component to draw the bullet, only thing we have to do is to add our logic regarding the .Move(); method.
The paint component will look as following ( + i also fixed the tabbing ):
#Override
public void paintComponent(Graphics grphcs)
{
super.paintComponent(grphcs);
Graphics2D gr = (Graphics2D) grphcs;
int X = (int) rec.getCenterX();
int Y = (int) rec.getCenterY();
gr.drawImage(imageBg, 0, 0, null);
gr.drawImage(imageFi, X-50, Y-75, null);
gr.setColor(Color.GRAY);
if(bullets != null)
{
for(int i=0;i<bullets.size();i++)
{
//Here is were we loop over the bullet list, lets add the move method
bullets.get(i).Move();
gr.drawImage(imageBu, null, bullets.get(i).getdx(), bullets.get(i).getdy());
repaint();
}
}
gr.draw(recB);
}
The thing is added is "bullets.get(i).Move();". This will now run every frame. This will work in theory (inb4 im not testing these codes). Going by the assumption you use multiple instance's of the bullet class, each class should encapsulate their own speed and time to live variable.
Implementing this will make the shoot method obsolete. What you can do is move the code inside the paintComponent that is related to shooting and move that to the shoot function.
Regarding the time to live variable, i would like to add one more piece to the code. This will take care of garbage collection of bullets. Since now they live indefinitly:
for(int i=0;i<bullets.size();i++)
{
Bullet b = bullets.get(i);
if(b.ttl >= 1)
{
bullets.get(i).Move();
gr.drawImage(imageBu, null, b.getdx(), b.getdy());
}
else
{
//Remove the bullet from the list
//In C# its bullets.Remove(b);
}
repaint();
}
Hopefully this resolves the issue of the bullet not moving. And potential performance issue due the bullets not being destroyed. In before, it there are any questions i love to hear them! ;)
Finally I did it with adding a timer in my bullet class and repaint() in my paintcomponent method!
package game;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import javax.swing.Timer;
public class Bullet {
private int x, y;
private int speed, ttl;
public final Timer timer1;
public Bullet(int x, int y, int speed){
this.x = x;
this.y = y;
this.speed= speed;
this.ttl = 250;
ActionListener actListener = new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Move();
}
};
this.timer1 = new Timer(50, actListener);
timer1.start();
}
public int getdy(){
return y;
}
public void setdy(int newy){
y = newy;
}
public int getdx(){
return x;
}
public int Move(){
//Do some calculation to perform loss of velocity within a reasonable range. Because these number might be overkill
this.speed -= (ttl / 100);
y += this.speed;
ttl--;
return y;
}
}
In your game loop make it update the bullet every so many ticks. You most likely never want to use sleep to slow down something in a game as it will need a new thread or it will sleep the whole game.
If you do not know what a game loop is, a game loop is basically a loop which continually takes input, updates the game (such as a bullet), renders everything, then pauses the entire program (you can use sleep) for the amount of time which the loop has left over from what is expected. You also want to update the game according to how many ticks or milliseconds have passed since the last update. This will prevent the game from running faster or slower on different computers.
I haven't read this all the way but THIS might help.
Also, not sure if this is correct, I think using Canvas instead of a JPanel will be better for making a game. It is what I used for my first java game.
Edit:
If you want to use JPanel and no game loop you can make the shoot method create a new thread and use the sleep to slow it down. Also the way you have the shoot method setup can cause problems if multiple bullets are shot because it will loop through each bullet in two separate loops and if you have a separate thread the bullets array can be modified while it is looping in one thread therefor causing an error.
Try this:
public void shoot(Bullet bullet){
new Thread(new Runnable(){
for(int j=0; j<200; j++){
bullet.setdy(my-j);
try{
Thread.sleep(time);//set the time
catch(Exception e){
e.printStackTrace();
}
}
System.out.println(bullet.getdy());
getBullets().remove(bullet);
}).start();
}
Create a method called getBullets(). Make sure it has the synchronized modifier.
protected synchronized ArrayList<Bullet> getBullets(){
return bullets;
}
This will make sure it can only be modified by one thread at a time.
Finally where the player presses space, change bullets.add(b); to getBullets().add(b);

Drawing shapes in loops

So Im basically just trying to Draw a whole bunch of random triangles to the screen in a loop while changing the colors which seemed not very difficult but i cannot find where my problem lies... it wont loop it just displays one image here's what i have
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Graphics;
import java.util.Random;
import javax.swing.JFrame;
public class ManyTriangles extends Canvas {
public void paint(Graphics g) {
Random r = new Random();
int x1 = r.nextInt(350);
int x2 = r.nextInt(400);
int x3 = r.nextInt(300);
int y1 = r.nextInt(800);
int y2 = r.nextInt(200);
int y3 = r.nextInt(600);
int[] xpts = { x1, x2, x3 };
int[] ypts = { y1, y2, y3 };
int randomColor = r.nextInt(3);
for (int x = 0; x <= 500; x++) {
if (randomColor == 3) {
g.setColor(Color.green);
} else if (randomColor == 2) {
g.setColor(Color.red);
} else if (randomColor == 1) {
g.setColor(Color.blue);
}
g.fillPolygon(xpts, ypts, 3);
}
}
public static void main(String[] args) {
ManyTriangles canvas = new ManyTriangles();
JFrame frame = new JFrame("Lots of Triangle's");
frame.setSize(1024, 768);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.add(canvas);
frame.setVisible(true);
}
}
Your xpts and yppts never change within the loop, so you are painting the same thing over and over again in different colors
You've broken the paint chain by not calling super.paint
You're mixing heavy and light weight components (Canvas on a JFrame), this is not really a good idea...
Instead...
Move the creation of the xpts and yppts into the loop
Call super.paint before doing any custom painting or event better
Use a JPanel instead of a Canvas and override it's paintComponent method instead, making sure you call super.paintComponent before doing any custom painting...
See Painting in AWT and Swing, Performing Custom Painting for more details
Other issues...
Because you're re-generating the output each time paint is called, your output could change at random times (as the repaint manager schedules new repaint requests). If you don't want this, generate the shapes in the constructor or other method, adding them to some kind of List or array and iterate over this within your paintComponent method...

Categories