Canvas - good rendering practices? - java
I've been using the Canvas a lot lately for little Java games and i've noticed a lot of strange things can happen. For example, earlier today I created a little game in which the objective is to shoot enemy ships and get points. The game draws tiny 32x32 images on the screen (sometimes slightly bigger) and for a reason I am oblivious to, the game renders oddly.
For example, my ship has a health bar above it's head:
As you can see by the image, the textures are really small. Despite this, the game lags and sometimes things render incorrectly like this for example:
If you look closely at the top of the health bar, you can see that it's been shifted upwards slightly, it puzzles me how his happens as all of my rendering is buffered.
My rendering code:
public void render(){
BufferStrategy bs = getBufferStrategy();
if(bs == null){
createBufferStrategy(3);
return;
}
Graphics2D g = (Graphics2D)bs.getDrawGraphics();
toDrawG.setColor(new Color(0x222222));
toDrawG.fillRect(0, 0, WIDTH, HEIGHT);
draw((Graphics2D)toDrawG);
g.drawImage(toDraw, 0, 0, null);
g.dispose();
bs.show();
}
public void draw(Graphics2D g){
if(Settings.planets){
renderer.renderPlanets();
}
if(level != null){
for(int i = 0 ; i < level.entityList.size(); i++){
if(level.entityList.get(i) != null){
level.entityList.get(i).render(renderer);
}
}
}
renderer.overlayString("Space Game", 20, 20, 24, 0xFFFFFF);
renderer.overlayString(VERSION, 20, 50, 24, 0xFFFFFF);
renderer.overlayString("FPS: " + renderer.fps, 20, 70, 24, 0xFFFFFF);
renderer.overlayString("Ships spawned: " + level.shipsSpawned, 20, 90, 24, 0xFFFFFF);
renderer.overlayString("Time Survived: " + level.time / 100 + "s", 20, 110, 24, 0xFFFFFF);
renderer.overlayString("Physics FPS: " + fps, 20, 130, 24, 0xFFFFFF);
if(currentGui != null){
currentGui.render(renderer);
}else{
map.drawMinimap(SpaceGame.WIDTH-Minimap.WIDTH-20, SpaceGame.HEIGHT- Minimap.HEIGHT-30);
}
}
And my "Render.class" if you need to study it:
package com.maestrum;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.awt.image.ConvolveOp;
import java.awt.image.Kernel;
import java.util.Random;
public class Render implements Runnable{
public Graphics2D g2d;
public double xScroll,yScroll;
public int frames;
public int fps;
public long lastTime;
public int[] pSequence = new int[40];
public SpaceGame game;
public Entity trackedEntity;
public Random rand;
public Render(SpaceGame game, Graphics2D g){
this.game = game;
this.g2d = g;
this.rand = new Random();
for(int i = 0 ; i < 40; i++){
pSequence[i] = rand.nextInt(15) + 1;
}
}
#Override
public void run() {
renderLoop();
}
public void renderLoop(){
while(true){
game.render();
if(System.currentTimeMillis() - lastTime >= 1000){
fps = frames;
frames = 0;
lastTime = System.currentTimeMillis();
}
frames++;
}
}
public void renderPlanets(){
overlayImage(ImageHandler.background, 0, 0, 1.5);
for(int i = 0 ; i < 20; i++){
overlayImage(ImageHandler.planets[pSequence[i]/4][pSequence[i]%4], i * 400 - xScroll/pSequence[i], i * pSequence[i]*40 - yScroll/pSequence[i]*2, pSequence[i]);
}
}
private class PlanetRenderer {
}
public void overlayString(String s, double x, double y, int fontSize, int colour){
drawString(s, x+xScroll, y+yScroll, fontSize, colour);
}
public void overlayRectangle(double x, double y, int xs, int ys, int colour){
drawRectangle(x+xScroll, y+yScroll, xs, ys, colour);
}
public void overlayBlurred(BufferedImage img, double x, double y, double scale){
drawImageBlurred(img, x+xScroll, y+yScroll, scale);
}
public void overlayImage(BufferedImage img, double x, double y, double scale){
drawImage(img, x+xScroll, y+yScroll, scale);
}
public BufferedImage execute(BufferedImage img) {
// TODO Auto-generated method stub
float weight = 1.0f/2.0f;
float [] elements = {weight, weight, weight, weight, weight, weight, weight, weight, weight};
Kernel k = new Kernel (3,3,elements);
ConvolveOp op = new ConvolveOp(k);
BufferedImage dest = new BufferedImage(img.getWidth(), img.getHeight(), img.getType());
op.filter(img, dest);
return dest;
}
public void drawImageBlurred(BufferedImage img, double x, double y, double scale){
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
execute(image);
g2d.drawImage(image, (int)x, (int)y, null);
g.dispose();
}
public void drawString(String s, Vector2D pos, int fontSize, int colour){
drawString(s, pos.x, pos.y, fontSize, colour);
}
public void drawString(String s, double x, double y, int fontSize, int colour){
if(s == null){
return;
}
x -= xScroll;
y -= yScroll;
BufferedImage img = new BufferedImage(s.length()*fontSize+1, fontSize*2, BufferedImage.TYPE_INT_ARGB);
Graphics g = img.getGraphics();
g.setColor(new Color(colour));
g.setFont(new Font("Consolas", Font.BOLD, fontSize));
g.drawString(s, 0, img.getHeight()/2);
g2d.drawImage(img, (int)x, (int)y, null);
g.dispose();
}
public void drawImage(BufferedImage img, Vector2D pos, double scale){
drawImage(img, pos.x, pos.y, scale);
}
public void drawLine(Vector2D v1, Vector2D v2, int colour, int width){
drawLine(v1.x, v1.y, v2.x, v2.y, colour, width);
}
public void drawLine(double x1, double y1, double x2, double y2, int colour, int lWidth){
x1 -= xScroll;
y1 -= yScroll;
x2 -= xScroll;
y2 -= yScroll;
g2d.setColor(new Color(colour));
g2d.setStroke(new BasicStroke(lWidth));
g2d.drawLine((int)x1, (int)y1, (int)x2, (int)y2);
}
public void drawImage(BufferedImage img, double x, double y, double scale){
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
g2d.drawImage(image, (int)x, (int)y, null);
g.dispose();
}
public void drawRectangle(Vector2D pos, int xs, int ys, int colour){
drawRectangle(pos.x, pos.y, xs, ys, colour);
}
public void drawRectangle(double x, double y, int xs, int ys, int colour){
if(xs <= 0){
return;
}
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage(xs, ys, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(new Color(colour));
g.fillRect(0, 0, xs, ys);
g2d.drawImage(image, (int)x, (int)y, null);
g.dispose();
}
public void drawImageRotated(BufferedImage img, Vector2D pos, double scale, double angle) {
drawImageRotated(img, pos.x, pos.y, scale, angle);
}
public void drawImageRotated(BufferedImage img, double x, double y, double scale, double angle) {
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth() * 1.5D), (int)(img.getHeight() * 1.5D), 2);
Graphics2D g = (Graphics2D)image.getGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
g.rotate(Math.toRadians(angle), image.getWidth() / 2, image.getHeight() / 2);
g.drawImage(img, image.getWidth() / 2 - img.getWidth() / 2, image.getHeight() / 2 - image.getHeight() / 2, null);
g2d.drawImage(image, (int)(x-image.getWidth()*scale/2), (int)(y-image.getHeight()*scale/2), (int)(image.getWidth()*scale), (int)(image.getHeight()*scale), null);
g.dispose();
}
}
As you can see in the rendering class, the render process is done as many times a second as possible. This is to give the game the highest possible FPS. If you missed the point of what I'm asking; What good practices should I take into account when rendering stuff using Java? And, what could possibly be causing the space ships health bar to render like so?
NOTE: take a look at this video, I ran that program and got 80FPS with 50000 particles, however with my rendering code (which is obviously of a much lower quality) I can render only a mere 100 or so particles, before things start messing up.
http://www.youtube.com/watch?v=6M3Ze4Eu87Y
This is my tick() function, it gets called every game tick (10ms)
public void tick(){
if(System.currentTimeMillis() - lastTime >= 1000){
fps = frames;
frames = 0;
lastTime = System.currentTimeMillis();
}
frames++;
if(renderer.trackedEntity != null){
renderer.xScroll = renderer.trackedEntity.pos.x-SpaceGame.WIDTH/2;
renderer.yScroll = renderer.trackedEntity.pos.y-SpaceGame.HEIGHT/2;
}
if(level != null && !paused){
level.tick();
}
if(currentGui != null && currentGui.pausesGame()){
paused = true;
}else{
paused = false;
}
}
i think the answer by #mantrid should fix your problem.
as far as performance goes ... there are some obvious "sins" in your code:
Don't draw an image into an image to draw an image
public void drawImage(BufferedImage img, double x, double y, double scale){
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage((int)(img.getWidth()*scale), (int)(img.getHeight()*scale), BufferedImage.TYPE_INT_ARGB);
Graphics g = image.getGraphics();
g.drawImage(img, 0, 0, (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
g2d.drawImage(image, (int)x, (int)y, null);
g.dispose();
}
I don't see the point of this. Why not just do this:
public void drawImage(BufferedImage img, double x, double y, double scale){
g2d.drawImage(img, (int)(x-xScroll), (int)(y-yScroll), (int)(img.getWidth()*scale), (int)(img.getHeight()*scale), null);
}
AAAAAAAAAAAAA
The next one actually burnt my eyes
public void drawRectangle(double x, double y, int xs, int ys, int colour){
if(xs <= 0){
return;
}
x -= xScroll;
y -= yScroll;
BufferedImage image = new BufferedImage(xs, ys, BufferedImage.TYPE_INT_RGB);
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(new Color(colour));
g.fillRect(0, 0, xs, ys);
g2d.drawImage(image, (int)x, (int)y, null);
g.dispose();
}
Why? This is so much simpler and quicker:
public void drawRectangle(double x, double y, int xs, int ys, int colour){
if(xs <= 0)
return;
g2d.setColor(new Color(colour));
g2d.fillRect((int)(x-xScroll), (int)(y-yScroll), xs, ys);
}
The same goes for the drawImageRotated and drawString.
Just draw to the g2d buffer directly, what are you afraid of?
drawImageBlurred
First of all... you're doing it again!
Second: You seem to be applying a convolve operation to an image on each frame, why not just do that operation once (e.g. at the start of the app, or even better yet, in an image editing program).
I don't mean this in a bad way at all, but you are clearly not very experienced with programming in general. I think you could take a look at processing (http://processing.org). I'm sort of biased here because i'm using it myself almost everyday. It is a great learning and prototyping environment written in java that should allow you to stop thinking about implementation details (like flipping buffers) and focus on what you really care for (make a game!).
Alright, hope what i said makes sense. Good luck with your coding! :)
The health bar might be shifted because when game lags, xScroll and yScroll are updated while components/overlays are still being rendered into backbuffer.
To fix that:
1) move game objects proportionally to amout of time elapsed from latest update to keep game speed always constant. add delta parameter to level.tick() and all game objects update method
2) put level.tick() and game.render() within the same loop sequence to ensure game objects are updated first:
currentTime = System.currentTimeMillis();
delta = currentTime - lastTime;
if(level != null && !paused){
level.tick(delta); // introduce delta here
game.render();
}
lastTime = currentTime;
In general, consider these additional steps:
1) set Component.setIgnoreRepaint(true) to speed up rendering
2) optimize images for current display at the start
GraphicsConfiguration gc = GraphicsEnvironment
.getLocalGraphicsEnvironment()
.getDefaultScreenDevice()
.getDefaultConfiguration();
Image optimized = gc.createCompatibleImage(img.getWidth(),img.getHeight(),Transparency.BITMASK);
optimized.getGraphics().drawImage(unoptimized, 0, 0, null);
Related
How to rotate shape from its center as well as from center of the screen
I have created Star shape using drawPolygon() but I want to rotate it from the center point of the star as well as from center of the screen. Here is my code to rotate star from its center: #Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; AffineTransform origX = g2d.getTransform(); AffineTransform newX = (AffineTransform) origX.clone(); newX.rotate(Math.toRadians(angle), x, y + 62); // Rotate about center of the star g2d.setTransform(newX); g2d.drawPolygon(starX, starY, 5); g2d.dispose(); g.dispose(); } If i replace: newX.rotate(Math.toRadians(angle), x, y + 62); to newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2); I can able to rotate it from center of the screen. However, I want to achieve both the effects simultaneously. i.e.: Like the earth which rotate around the sun as well as its own axis. I have tried creating another AffineTransform object but it overwrite the previous one when I set it using g2d.setTransform(newObj); Any suggestions will be extremely helpful. Thank you. Here is my complete code if you want to see import java.applet.Applet; import java.awt.Color; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.geom.AffineTransform; public class Star extends Applet implements Runnable{ private int[] starX; private int[] starY; private Thread mainThread; private int x; private int y; private int angleFromCenterShape; #Override public void init() { this.setSize(800, 480); x = 250; y = 150; angleFromCenterShape = 0; starX = new int[5]; starY = new int[5]; mainThread = new Thread(this); mainThread.start(); this.setBackground(Color.BLACK); } #Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; AffineTransform origX = g2d.getTransform(); AffineTransform newX = (AffineTransform) origX.clone(); newX.rotate(Math.toRadians(angleFromCenterShape), x, y + 65); //Rotate about center of the star g2d.setTransform(newX); g2d.setColor(Color.red); g2d.fillPolygon(starX, starY, 5); g2d.dispose(); g.dispose(); } #Override public void run() { while(true){ angleFromCenterScreen = (angleFromCenterScreen + 1) % 360; // angle loop from 0 to 36o initStar(); try{ Thread.sleep(30); }catch(Exception e){} repaint(); } } private void initStar(){ starX[0] = x; starX[1] = x - 50; starX[2] = x + 75; starX[3] = x - 75; starX[4] = x + 50; starY[0] = y; starY[1] = y + 130; starY[2] = y + 50; starY[3] = y + 50; starY[4] = y + 130; } }
I have found a workaround for my problem but it is not an exact solution. However adding another rotate() on the same AffineTransform object mimic the 2 rotations Here is the code #Override public void paint(Graphics g) { Graphics2D g2d = (Graphics2D) g; AffineTransform origX = g2d.getTransform(); AffineTransform newX = (AffineTransform) origX.clone(); newX.rotate(Math.toRadians(angle), this.getWidth() / 2, this.getHeight() / 2); // Rotate from the center of screen newX.rotate(Math.toRadians(angle), x, y + 65); // Rotate from the center of the shape g2d.setTransform(newX); g2d.setColor(Color.red); g2d.fillPolygon(starX, starY, 5); g2d.dispose(); g.dispose(); }
Drawing graphics in Java
I am trying to draw a circle in side a square and having multiple square circles in java. I am almost done but my output isn't coming out as I wanted to. The picture is what I am trying to do but it's not working out. Here is my code: a.awt.*; public class SquaredCircles { public static final int WIDTH=400; public static final int HEIGHT=400; public static void main (String[] args) { DrawingPanel panel = new DrawingPanel(WIDTH,HEIGHT); Graphics g = panel.getGraphics (); panel.setBackground(new Color(0, 255, 255 ) ); int x = 0; int y = 0; int size = 100; int rows = 5; int numSquares = 1; drawManySquares ( g, numSquares, x, y, size, rows ); x = 10; y = 120; size = 24; rows = 4; numSquares = 4; drawManySquares( g, numSquares, x, y, size, rows ); x = 150; y = 20; size = 40; rows = 6; numSquares = 5; drawManySquares( g, numSquares, x, y, size, rows ); x = 130; y = 275; size = 36; rows = 3; numSquares = 3; drawManySquares( g, numSquares, x, y, size, rows ); } public static void drawManySquares( Graphics g, int numSquares, int x, int y, int size, int rows ) { for ( int i = 0; i < numSquares; i++ ) { for ( int j = 0; j < numSquares; j++ ) { drawOneSquare( g, x + i size, y + j size, size, rows ); } } } public static void drawOneSquare( Graphics g, int x, int y, int size, int rows ) { g.setColor ( Color.GREEN); g.fillRect(x , y, size, size); g.setColor ( Color.YELLOW); g.fillOval ( x, y, size, size); g.setColor ( Color.BLACK); g.drawLine(size / 2, x, size / 2, size); g.setColor ( Color.BLACK); g.drawLine(x, size / 2, size, size / 2); for (int i = 0; i <= rows; i = i + 1) { g.setColor ( Color.BLACK); g.drawOval(x + (i* (size/rows)), y+ (i*(size/rows)), size - (i*(size/rows +10 )) , size - (i*(size/rows +10))); } } }
Start by having a look at Painting in AWT and Swing and Performing Custom Painting to see how painting should be done in Swing Break down your problem into manageable chunks. The first thing you need to be able to do is paint a circle of a given size at a specific location, something like public void paintCircleAt(Graphics2D g2d, int radius, int centerX, int centerY, Color stroke, Color fill) { Ellipse2D.Double circle = new Ellipse2D.Double(centerX - radius, centerY - radius, radius * 2, radius * 2); g2d.setColor(fill); g2d.fill(circle); g2d.setColor(stroke); g2d.draw(circle); } So, this allows you to paint a circle of a given radius around the center points of x/y filled and outlined with the specified color, pretty simple. Now, you need someway to paint a series of circles around the same center point, something like... public void paintCirclesIn(Graphics2D g2d, int count, int radius, int centerX, int centerY, Color stroke, Color fill) { System.out.println(radius + "; " + centerX + "; " + centerY); int delta = radius / count; int innerRadius = radius; for (int index = 0; index < count; index++, innerRadius -= delta) { paintCircleAt(g2d, innerRadius, centerX, centerY, stroke, fill); } } Okay, this basically calculates the difference (delta) between each circle and the paints that many circles with that much difference in their radius from the previous one. Because of the way the painting is done, we start with the outer circle and paint in. And finally, we need someway to paint a square and circles, something like... public void paintCirclesInSquare(Graphics2D g2d, int count, int x, int y, int width, int height, Color squareStroke, Color squareFill, Color circleStroke, Color circleFill) { int centerX = x + (width / 2); int centerY = y + (height / 2); int radius = Math.min(centerX, centerY); Rectangle2D box = new Rectangle2D.Double(x, y, width, height); g2d.setColor(squareFill); g2d.fill(box); g2d.setColor(squareStroke); g2d.draw(box); paintCirclesIn(g2d, count, radius, centerX, centerY, circleStroke, circleFill); g2d.drawLine(centerX, y, centerX, y + height); g2d.drawLine(x, centerY, x + width, centerY); } This, again, simply reuses the existing code we already have and adds to it, painting the square, the circles in the square and finally the lines. Now, from here, you could write a method which took the number of columns/rows you wanted, the x/y position to start from, the size of each of square, the number of circles you need and the colors and reuse this functionality, but I'll leave that up to you ;) Runnable example for you to play with... import java.awt.Color; import java.awt.Dimension; import java.awt.EventQueue; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.RenderingHints; import java.awt.geom.Ellipse2D; import java.awt.geom.Rectangle2D; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.UIManager; import javax.swing.UnsupportedLookAndFeelException; public class CirclesAndSquares { public static void main(String[] args) { new CirclesAndSquares(); } public CirclesAndSquares() { EventQueue.invokeLater(new Runnable() { #Override public void run() { try { UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); } catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) { ex.printStackTrace(); } JFrame frame = new JFrame("Testing"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); frame.add(new TestPane()); frame.pack(); frame.setLocationRelativeTo(null); frame.setVisible(true); } }); } public class TestPane extends JPanel { public TestPane() { } #Override public Dimension getPreferredSize() { return new Dimension(200, 200); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); g2d.setRenderingHint(RenderingHints.KEY_ALPHA_INTERPOLATION, RenderingHints.VALUE_ALPHA_INTERPOLATION_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); g2d.setRenderingHint(RenderingHints.KEY_COLOR_RENDERING, RenderingHints.VALUE_COLOR_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); g2d.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_BILINEAR); g2d.setRenderingHint(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_QUALITY); g2d.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); int x = getWidth() / 2; int y = getHeight() / 2; // paintCircleAt(g2d, Math.min(x, y), y, y, Color.BLACK, Color.YELLOW); // paintCirclesIn(g2d, 5, Math.min(x, y), x, y, Color.BLACK, Color.YELLOW); paintCirclesInSquare(g2d, 5, 0, 0, getWidth() - 1, getHeight() - 1, Color.BLACK, Color.GREEN, Color.BLACK, Color.YELLOW); g2d.dispose(); } public void paintCirclesInSquare(Graphics2D g2d, int count, int x, int y, int width, int height, Color squareStroke, Color squareFill, Color circleStroke, Color circleFill) { int centerX = x + (width / 2); int centerY = y + (height / 2); int radius = Math.min(centerX, centerY); Rectangle2D box = new Rectangle2D.Double(x, y, width, height); g2d.setColor(squareFill); g2d.fill(box); g2d.setColor(squareStroke); g2d.draw(box); paintCirclesIn(g2d, count, radius, centerX, centerY, circleStroke, circleFill); g2d.drawLine(centerX, y, centerX, y + height); g2d.drawLine(x, centerY, x + width, centerY); } public void paintCirclesIn(Graphics2D g2d, int count, int radius, int centerX, int centerY, Color stroke, Color fill) { System.out.println(radius + "; " + centerX + "; " + centerY); int delta = radius / count; int innerRadius = radius; for (int index = 0; index < count; index++, innerRadius -= delta) { paintCircleAt(g2d, innerRadius, centerX, centerY, stroke, fill); } } public void paintCircleAt(Graphics2D g2d, int radius, int centerX, int centerY, Color stroke, Color fill) { Ellipse2D.Double circle = new Ellipse2D.Double(centerX - radius, centerY - radius, radius * 2, radius * 2); g2d.setColor(fill); g2d.fill(circle); g2d.setColor(stroke); g2d.draw(circle); } } }
How to make sure that points drawn on JFrame at random location does not overlap already drawn shapes?
I am creating 100 particles at random location all over my JPanel using random function to calculate x and y. But I have two rectangles also drawn on the panel and I do not want my points to overlap on that area. Is there any way, by which I can create the particles all over the JPanel except those areas covered by rectangle? int x,y=0; super.paintComponent(g); for(int i=0;i<list.size();i++) { x=randomInteger(11,670); // bounds of x between which the particles should be generated (reduced by 1 each) y=randomInteger(11,440); // bounds of y between which the particles should be generated (reduced by 1 each) int radius = 4; g.fillOval(x, y, radius, radius); } x=randomInteger(11,670); y=randomInteger(11,440); drawRobot(g,x,y,50); createObstacles(g,150,225,100,40); createObstacles(g,500,300,40,100); int xpoints[] = {50, 40, 60, 120}; int ypoints[] = {50, 75, 100, 130}; int npoints = 4; createPolygonObstacle(g,xpoints,ypoints,npoints); } private void createPolygonObstacle(Graphics g, int xpoints[], int ypoints[], int npoints) { g.fillPolygon(xpoints, ypoints, npoints); } private void createObstacles(Graphics g, int x, int y, int width, int height) { g.setColor(Color.BLACK); g.fillRect(x, y, width, height); } private void drawRobot(Graphics g, int x, int y, int radius) { g.setColor(Color.GREEN); g.fillOval(x, y, radius, radius); } private static int randomInteger(int min, int max) { Random rand = new Random(); int randomNum = rand.nextInt((max - min) + 1) + min; return randomNum; }
You could take advantage of the Shape API... Rectangle rect = new Rectangle (x, y, width, height); Then you could use it's contains method to determine if it contains a given point... if (rect.contains(x, y)) { // You bad little particle... } You should also know that the Graphics2D can also draw and paint Shape, so you could also do... ((Graphics2D)g).fill(rect); Which should make your life some what easier. As of Java 1.4 (I think), the paint engine is guaranteed to use Graphics2D, so your paintComponent method will always receive an instance of a Graphics2D object. Take a look at 2D Graphics for more details
Random r = new Random(); public void generateParticle(){ int x = r.nextInt(); int y = r.nextInt(); if(x > LeftEdgeOfRectangle || x < RightEdgeOfRectangle){ generateParticle(); return(); } if(y > TopEdgeOfRectangle || y < BottomEdgeOfRectangle){ generateParticle(); return(); } [drawParticleHere] }
How do I rotate a shape in the direction of the mouse in a java applet? [duplicate]
So far I have a java app where I draw a circle(player) and then draw a green rectangle on top(gun barrel). I have it so when the player moves, the barrel follows with it. I want it to find where the mouse is pointing and then rotate the barrel accordingly. For an example of what I mean look at this video I found http://www.youtube.com/watch?v=8W7WSkQq5SU See how the player image reacts when he moves the mouse around? Here's an image of what the game looks like so far: So how do I rotate it like this? Btw I don't like using affinetransform or Graphics2D rotation. I was hoping for a better way. Thanks
Using the Graphics2D rotation method is indeed the easiest way. Here's a simple implementation: int centerX = width / 2; int centerY = height / 2; double angle = Math.atan2(centerY - mouseY, centerX - mouseX) - Math.PI / 2; ((Graphics2D)g).rotate(angle, centerX, centerY); g.fillRect(...); // draw your rectangle If you want to remove the rotation when you're done so you can continue drawing normally, use: Graphics2D g2d = (Graphics2D)g; AffineTransform transform = g2d.getTransform(); g2d.rotate(angle, centerX, centerY); g2d.fillRect(...); // draw your rectangle g2d.setTransform(transform); It's a good idea to just use Graphics2D anyway for anti-aliasing, etc.
Using AffineTransform, sorry, only way I know how :P public class RotatePane extends javax.swing.JPanel { private BufferedImage img; private Point mousePoint; /** * Creates new form RotatePane */ public RotatePane() { try { img = ImageIO.read(getClass().getResource("/MT02.png")); } catch (IOException ex) { ex.printStackTrace(); } addMouseMotionListener(new MouseAdapter() { #Override public void mouseMoved(MouseEvent e) { mousePoint = e.getPoint(); repaint(); } }); } #Override public Dimension getPreferredSize() { return new Dimension(img.getWidth(), img.getHeight()); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); double rotation = 0f; int width = getWidth() - 1; int height = getHeight() - 1; if (mousePoint != null) { int x = width / 2; int y = height / 2; int deltaX = mousePoint.x - x; int deltaY = mousePoint.y - y; rotation = -Math.atan2(deltaX, deltaY); rotation = Math.toDegrees(rotation) + 180; } int x = (width - img.getWidth()) / 2; int y = (height - img.getHeight()) / 2; g2d.rotate(Math.toRadians(rotation), width / 2, height / 2); g2d.drawImage(img, x, y, this); x = width / 2; y = height / 2; g2d.setStroke(new BasicStroke(3)); g2d.setColor(Color.RED); g2d.drawLine(x, y, x, y - height / 4); g2d.dispose(); } } Will produce this effect The red line (point out from the center) will want to follow the cursor.
Why is my image rotating off center?
I'm using Java/Slick 2D to play with graphics and using the mouse to rotate an image. Something strange happens though: the image doesn't necessarily face the mouse. At 45 degrees from the normal line it does, but the further you get, the further your are off. See the images below (the white circle being the mouse, the text being the angle): Here is the rotation code I used: int mX = Mouse.getX(); int mY = HEIGHT - Mouse.getY(); int pX = sprite.x; int pY = sprite.y; int tempY, tempX; double mAng, pAng = sprite.angle; double angRotate=0; if(mX!=pX){ mAng = Math.toDegrees(Math.atan2(mY - pY, mX - pX)); if(mAng==0 && mX<=pX) mAng=180; } else{ if(mY>pY) mAng=90; else mAng=270; } sprite.angle = mAng; sprite.image.setRotation((float) mAng); Any ideas what's going on? I'm assuming it has something to do with the fact that the image coordinates come from the top left, but I don't know how to counter it. FYI: screen 640x460, image 128x128 and centered in window. EDIT: Unfortunately, nothing there really worked. Here is a picture with some more information: EDIT2: Found the answer! had to change: int px/py = sprite.x/y to int pX = sprite.x+sprite.image.getWidth()/2; int pY = sprite.y+sprite.image.getHeight()/2;
It looks like your getting the value of your mouse from the left and setting that distance to your rotation... Here's something that might help: http://www.instructables.com/id/Using-Java-to-Rotate-an-Object-to-Face-the-Mouse/?ALLSTEPS
This is some example code I wrote of a similar question which might help. Now it doesn't use slick, it uses Swing and Graphics2D but it might help you gain some ideas. public class TestRotatePane extends JPanel { private BufferedImage img; private Point mousePoint; public TestRotatePane() { try { img = ImageIO.read(getClass().getResource("/MT02.png")); } catch (IOException ex) { ex.printStackTrace(); } addMouseMotionListener(new MouseAdapter() { #Override public void mouseMoved(MouseEvent e) { mousePoint = e.getPoint(); repaint(); } }); } #Override public Dimension getPreferredSize() { return new Dimension(img.getWidth(), img.getHeight()); } #Override protected void paintComponent(Graphics g) { super.paintComponent(g); Graphics2D g2d = (Graphics2D) g.create(); double rotation = 0f; int width = getWidth() - 1; int height = getHeight() - 1; if (mousePoint != null) { int x = width / 2; int y = height / 2; int deltaX = mousePoint.x - x; int deltaY = mousePoint.y - y; rotation = -Math.atan2(deltaX, deltaY); rotation = Math.toDegrees(rotation) + 180; } int x = (width - img.getWidth()) / 2; int y = (height - img.getHeight()) / 2; g2d.rotate(Math.toRadians(rotation), width / 2, height / 2); g2d.drawImage(img, x, y, this); x = width / 2; y = height / 2; g2d.setStroke(new BasicStroke(3)); g2d.setColor(Color.RED); g2d.drawLine(x, y, x, y - height / 4); g2d.dispose(); } } You will, obviously, need to supply your own image ;)