I have been programming in java for a while now and I've just now started 2D graphics(game development). While coding my game, I have noticed rendering issues with my game. While my player is running around the screen, the pixels will glitch around. It's like lag on a multiplayer game, but its barely noticeable. After researching this issue I have not come up with any solutions, so I'm asking for your help.
MY QUESTION: I am experiencing poor rendering in my game and I'm wondering if my way of rendering is poor. If so, Can you point me to any resources?
MY RENDERING CLASS
public class Render extends JPanel implements Runnable{
int SCREEN_WIDTH,SCREEN_HEIGHT;
Game game;
Thread t;
public Render(int w,int h,Game g){
this.SCREEN_WIDTH = w;
this.SCREEN_HEIGHT = h;
this.game = g;
this.setDoubleBuffered(true);
}
public void run(){
while(true){
repaint();
}
}
public void paint(Graphics g){
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
g2d.translate(-this.game.getCamera().getX(), -game.getCamera().getY());
g2d.setColor(Color.black);
//fill the screen with black
g2d.fillRect(0, 0, game.getLevelHandler().getLevel().getWidth() * 32, game.getLevelHandler().getLevel().getHeight() * 32);
/**for(Entity E: game.getObjects()){
E.draw(g2d);
}*/
//send the graphics object to my player...IS THIS OKAY TO DO?
game.getPlayer().draw(g2d);
}
public void start(){
t = new Thread(this);
t.start();
}
}
public class Player extends Entity{
float dx,dy,moveSpeed;
boolean jumping, canJump;
float terminalVelocity,acceleration = 0;
int GravityCounter,jumps,maxJumps,jumpheight = 0;
public Player(float x, float y, float w, float h, ObjectID id, Game g) {
super(x, y, w, h, id, g);
//gravity
terminalVelocity += 7;
acceleration += 0.2;
moveSpeed += 2.5;
//jumping
jumpheight = 40;
maxJumps = 2;
jumps = maxJumps;
}
public void tick() {
dx = 0;
dy = 0;
collisions();
move();
x += dx;
y += dy;
}
public void collisions(){
}
public void move(){
}
//the drawing
public void draw(Graphics2D g) {
g.setColor(Color.green);
g.drawRect((int)x, (int)y, (int)w, (int)h);
}
}
EDIT: downloadable link here
, If any changes are needed, I'll try and correct it. Also, does this glitching happen when there is a memory leak?
I revised my code because of suggestions, This is what I have now.
RENDERER:
public class Render extends JPanel implements Runnable{
int SCREEN_WIDTH,SCREEN_HEIGHT;
Game game;
Thread t;
public Render(int w,int h,Game g){
this.SCREEN_WIDTH = w;
this.SCREEN_HEIGHT = h;
this.game = g;
this.setDoubleBuffered(true);
}
public void run(){
while(true){
repaint();
try {
Thread.sleep(16);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void paint(Graphics g){
Graphics2D g2d = (Graphics2D) g;
g2d.translate(-this.game.getCamera().getX(), -game.getCamera().getY());
g2d.setColor(Color.black);
g2d.fillRect(0, 0, game.getLevelHandler().getLevel().getWidth() * 32, game.getLevelHandler().getLevel().getHeight() * 32);
for(Entity E: game.getObjects()){
synchronized(E){E.draw(g2d);}
}
synchronized(game.getPlayer()){game.getPlayer().draw(g2d);}
}
public void start(){
t = new Thread(this);
t.start();
}
}
The player class did not change
Source Code: here
I know what's happening here because I was having the exact same problem in the game I'm writing. It sure is a hard thing to find. Your problem is happening because you're rounding your x and y variables when you draw.
g.drawRect((int)x, (int)y, (int)w, (int)h);
This is causing your rectangle to jump around a bit. Because it looks like your rectangle can have decimal coordinates, you need to be able to draw the rectangle at decimal coordinates. There's a pretty easy way to do that, but instead let's use a way that will work regardless of what you're drawing.
You're going to need to use some transformations, personally I like AffineTransform's. There are lots of tutorials about these, but this is the first (non-documentation) result of a google search https://docs.oracle.com/javase/tutorial/2d/advanced/transforming.html. I know you're already doing some transformations, but this is what your draw method will look like after you add them.
public void draw(Graphics2D g) {
AffineTransform original = g.getTransform(); // keep a copy of the original so that we can restore it
AffineTransform transform = (AffineTransform)original.clone();
transform.translate((double)x, (double)y); // translate the transform to the player location, note that x and y can be non-integers here.
g.transform(transform); // apply the transformation.
g.setColor(Color.green);
g.drawRect(0, 0, (int)w, (int)h);
g.setTransform(original); // restore the original transformation
}
For the rest of your walls and stuff, you can use this exact same method. Remember that the "screen" location of an object is the object's game location minus the camera's game location. Good luck with your game.
Related
I have big problem with coding graphic part of my app, where I need to have components one on top of each other:
First I have JFrame (with fixed size)
In it I have two JPanel components. I want them to have colour background.
That's the easy part.
On one of the JPanel components I want to draw fixed shapres - rectangles, lanes, etc. Here I have problem, that I have two classes: one extends JPanel and is background for this part and second extends JComponent and represents element I draw (there is several elements). I don't know how to draw the elements in the JPanel - I tried several methods and nothing showed up. It's important to me that the JComponents should be drawn and conected only with this JPanel, not with whole frame.
On top of that I want to have moving shapes. It's easy when I have only frame and let's say rectangle, because I only change position and call repaint() method, but how to do this to make the moving shapes be connected to and be inside JPanel and to left previous layers in their place?
For my tries I created few classes with rectangles:
import javax.swing.*;
import java.awt.*;
import java.util.ArrayList;
public class Main{
public Main() {
JFrame frame = new JFrame();
frame.setSize(1200, 900);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
JPanel background = new JPanel();
background.setBackground(Color.lightGray);
GreenRect gr = new GreenRect();
gr.setPreferredSize(new Dimension(500,800));
background.add(gr, BorderLayout.WEST);
RedRect rr = new RedRect();
rr.setPreferredSize(new Dimension(500,800));
background.add(rr, BorderLayout.EAST);
frame.add(background);
}
public static void main(String[] args) {
new Main();
}
}
class GreenRect extends JPanel {
ArrayList<BlackRect> r = new ArrayList<>();
ArrayList<MovingRec> m = new ArrayList<>();
public GreenRect() {
setBackground(Color.green);
addRec(10,10);
addRec(50,50);
addRec(100,100);
addRec(1000,1000);
}
public void addRec(int x, int y) {
r.add(new BlackRect(x,y));
}
}
class RedRect extends JPanel {
public RedRect() {
setBackground(Color.red);
}
}
class BlackRect extends JComponent {
int x, y;
int w = 100, h = 100;
public BlackRect (int x, int y){
this.x = x;
this.y = y;
}
#Override
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, w, h);
}
}
class MovingRec extends JComponent {
int x, y;
int w = 20, h = 20;
public MovingRec (int x, int y){
this.x = x;
this.y = y;
}
public void paintComponent(Graphics g) {
super.paintComponents(g);
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.BLUE);
g2d.fillRect(x, y, w, h);
}
public void update() {
x += 5;
y += 5;
}
}
and now I have problems with points 3 and 4, because I can't place black rectangles on background and moving rectangles on the top.
I will be grateful for all help :)
You do not need to (and shouldn’t) extend BlackRect and MovingRect from JComponent.
For example, BlackRect could be a simple object, like:
class BlackRect {
int x, y;
int w = 100, h = 100;
public BlackRect(int x, int y) {
this.x = x;
this.y = y;
}
public void paint(Graphics2D g2d) {
g2d.setColor(Color.BLACK);
g2d.fillRect(x, y, w, h);
}
}
You should override GreenRect’s paint method, to paint rectangles on that panel:
public GreenRect extends JPanel {
// Existing members
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2d = (Graphics2D) g;
for (BlackRect black_rect : r) {
black_rect.paint(g2d);
}
// Also paint list of moving rectangles here
}
}
When GreenRect.repaint() is called, it will paint its background, and all rectangles from the r (and m list when you add that code). If the m rectangles have had their positions updated, they will be drawn at their new positions, so they will appear to be moving. Since moving rectangles are drawn last, they would appear “on top”.
Use a Swing Timer to drive the animation. When the timer expires, it should move all of the moving rectangles slightly (ie, call MovingRec.update()), and call repaint() on GreenRect.
I'm building my first very basic game in JAVA without using any other external libraries.
The problem I got is
You can see that the background is rendered over player how can I fix that
The player rendering code is as follows
#Override
public void render(Graphics g) {
int bulletcount = 0;
//render player
g.setColor(color);
g.fillRect(x,y,32,32);
if(shooting) {
handler.add(new bullet(x + 8, y - 24, ID.bullet, handler));
bulletcount++;
}
}
and the background render code is as follows.
public class background extends GameObject{
private int width;
private Random random = new Random();
public background(int x, int y , ID id,Handler handler){
super(x,y,id,handler);
valy = 2;
width = getWidth(x,800);
}
public void tick() {
y += valy;
if(y>650)
handler.remove(this);
}
public void render(Graphics g) {
g.setColor(Color.white);
g.fillRoundRect(x,y,width,10,1,1);
}
public int getWidth(int x,int width1){
int wid;
while(true){
if((width - x) > 35) {
wid = random.nextInt((width-x-35));
break;
}
else{
x -= 10;
}
}
return wid;
}
}
the main class of game can see the order of flow of execution.
private void render() {
// render the Game
BufferStrategy bs = this.getBufferStrategy();
if(bs == null)
{
this.createBufferStrategy(3);
return;
}
Graphics g = bs.getDrawGraphics();
g.setColor(Color.black);
g.fillRect(0,0,WIDTH,HEIGHT);
handler.render(g);
hod.render(g); //handler is rendering every object in game
g.dispose();
bs.show();
}
private void tick() {
//update Game
handler.tick();
hod.tick();
spawner.tick(); // background is generating in spawner class.
}
I know that telling the problem with this much less code is hard for you all. But I have complete faith in this large and powerful community.
Thanks in advance. Don't hesitate to edit and point out my mistakes Thanks!
If I get the code right, the background is being generated after rendering the player which leads to the background being on top of the player. I would try calling spawner.tick() (where I guess the background is coming from given your code comment) before calling hod.tick(). Hopefully that should resolve this issue.
private void tick() { //update Game
spawner.tick();
handler.tick();
hod.tick();
}
I just had solution!
The problem was in handler.render(g); method:- which is a follows;
private void render(Graphics g){
for(int i =0; i<gameobj.size(); i++){//gameobj is linkedlist of GameObject
GameObject tempobj = gameobj.get(i);
tempobj.render(g);//getting every object and rendering them!
}
The problem was in loop the first object added in the game is player.So , it was getting render first after that the background was getting rendered.
It was a silly mistake.which got right by following change.->
private void render(Graphics g){
for(int i = gameobj.size()-1; i > 0; i--){//gameobj is linkedlist of GameObject
GameObject tempobj = gameobj.get(i);
tempobj.render(g);//getting every object and rendering them! from reverse
}
so, the last object renders first and the first object renders last.
Peace!
I have an image, with a complete transparent background. However when I draw this image, ingame, it has a kind of shade to it, and I have no clue why. I would like to get that out of there. Does anyone have an idea? I don't have the reputation to post images of it apparently... So I'll try to give some more information.
I have the Color.DARK_GRAY as background, and when I draw the image, you see a lighter gray square around it.
Then when I draw a couple of these images ontop of eachother, that square gets lighter and lighter.
If I draw the image ontop of another image however, this effect does not occur.
Here I load the image
public BlackChip() {
this.value = 500;
this.url = "res/images/poker/blackchip.png";
this.file = new File(url);
BufferedImage bi;
try {
bi = ImageIO.read(file);
this.image = bi;
} catch (IOException e) {
e.printStackTrace();
}
}
Here I draw the image
public void renderChip(Chip chip, int x, int y) {
g.drawImage(chip.getImage(), x, y, null);
}
Here I call that method
public void render() {
screen.renderBackground(Color.DARK_GRAY);
pokertable.render(Game.width / 2 - pokertable.getImage().getWidth(null) / 2, 50);
screen.renderChip(cs.getWhiteChip(), 380, 310);
screen.renderChip(cs.getRedChip(), 430, 310);
screen.renderChip(cs.getGreenChip(), 480, 310);
screen.renderChip(cs.getBlueChip(), 530, 310);
screen.renderChip(cs.getBlackChip(), 580, 310); //this one is it
}
link to the images:
https://drive.google.com/file/d/0Bz-4pfUssUeHRWkxaUhodWNILWc/edit?usp=sharing
Well... this doesn't work either because i need 10 reputation to post more then 1 link
you can see the effect on this link, it's the image with full transparent background, drawn multiple times.
I can't tell if this is the exact cause of the problem, because you haven't provided a MCVE but this method
public void renderChip(Chip chip, int x, int y) {
g.drawImage(chip.getImage(), x, y, null);
}
Just looks wrong. All custom painting should be done within the context of the provided Graphics object in the overridden paintComponent method. If you have not overriden paintComponent in a JPanel or a JComponent then you are likely not painting correctly. You may be doing something like
public class SomePanel extends JPanel {
private Graphics g;
public SomePanel() {
g = getGraphics();
}
}
Which is completely wrong. You should instead be doing something like
public class SomePanel extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// do painting here
}
}
You Classes can then have it's own render method that takes a Graphics object as an argument. Then can be called in the paintComponent method. Maybe something like
public class Chip {
private JComponent imageObserver;
private BufferedImage chipImage;
int x, y;
public Chip(BufferedImage chipImage, int x, int y, JComponent imageObserver){
this.chipImage;
this.x = x;
this.y = y;
this.imageObserver = imageObserver;
}
public void renderChip(Graphics g) {
g.getImage(chipImage, x, y, imageObserver);
}
}
And your panel
public class SomePanel extends JPanel {
private List<Chip> chips;
public SomePanel() {
chips = new ArrayList<Chip>();
// add new Chips
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Chip chip: chips) {
chip.renderChip(g);
}
}
}
I am having some trouble while using swing to draw a diamond inside a square.
My code is this,please some one have a look at it and let me know if you can provide a fullfunctional code which is working in creating a diamond inside a square.
The code is:-
import javax.swing.*;
import java.awt.*;
public class MyDrawing extends JPanel
{
static int width=250;
static int height=250;
static int x=0;
static int y=0;
private void doDrawing(Graphics g)
{
Graphics2D g2d = (Graphics2D) g;
g2d.setColor(Color.blue);
//for (int i = 0; i <= 1000; i++)
g2d.drawRect(x, y, width,height);
g2d.rotate(Math.toRadians(-45));
System.out.println(Math.toRadians(-45));
x=0;
y=height/2;
System.out.println(y);
width=(int)Math.pow(Math.pow((width/2),2)*2,0.5);
height=width;
System.out.println("width:"+width+"height:"+height);
g2d.drawRect(y, x, width,height);
}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
doDrawing(g);
}
}
To keep the the diamond inside the sqaure, simply
1. Draw a rectangle
2. Rotate the rectangle about its center.
Rectangle2D rectangle = new Rectangle2D.Double(20, 20, 50, 50);
g2.draw(rectangle);
AffineTransform transform = new AffineTransform();
transform.rotate(Math.PI/4, rectangle.getX() + rectangle.width/2, rectangle.getY() + rectangle.height/2);
g2.draw(transform);
In very basic terms I have a panel that is drawing a line pixel by pixel and is being updated in real time. On top of this I want another panel that draws a box around the current pixel but clears itself later. Both are in real time
My current situation is a wrapper JPanel that has an OverlayLayout. The bottom panel has the line that is drawn with the Graphics2D object fetched from its JPanel. The top panel the box that follows around that is also drawn with the Graphics2D object fetched from its JPanel.
The issues in this system are many. Since I'm just drawing on the Graphics2D object separately and not overriding the JPanel's paint() not only is all the lines lost when the panel needs to repaint, I think I'm going against Swing's threading model by only having a single thread update the screen. I have also not gotten the top panel to work correctly, it just keeps clearing the screen and won't let the bottom panel draw a line.
What would be the best way to approach this situation? I have little experience with image processing and the low-level displaying of images in Swing, I just know the basics. I've heard of BufferedImage, but not knowing where to put that image, if it will be updated when changed later, the efficiency of doing that, and the fact that its buffered scare me off. I'm not sure what to use
Can someone point me in the right direction of what I need to use to accomplish this?
You can accumulate your points in a suitable Shape, such as GeneralPath, as shown in LissajousPanel. Use a javax.swing.Timer to cause periodic updates, and highlight the most recently added point with a surrounding rectangle.
I suggest
that you do not get your JPanel's Graphics or Graphics2D object via getGraphics as this object is not meant to be stable and will not work once the component has been repainted for any reason.
that you instead do all your drawing in a single JPanel's paintComponent method, but first in that method call the super's method.
that you again draw in only one JPanel, not two,
that you draw your line in a BufferedImage, since that is a more permanent part of your image.
That you get the BufferedImage's Graphics2D object via its createGraphics method
That you properly dispose of the BufferedImage's Graphics object when done using it so as to conserve resources.
that you display your BufferedImage in the paintComponent method of your JPanel (after calling the super method first)
that you draw your box, the more fleeting portion of the image, directly in the paintComponent using int class variables to tell paintComponent where to draw and perhaps a class boolean variable to tell if to draw.
and that most important, you review the tutorials on Swing graphics as to do this properly, you'll have to throw all old assumptions out and learn the correct way from the ground up (as we all had to do).
For example:
import java.awt.*;
import java.awt.event.*;
import java.awt.image.BufferedImage;
import javax.swing.*;
#SuppressWarnings("serial")
public class BufferedImageEg extends JPanel {
private static final int BI_WIDTH = 700;
private static final int BI_HEIGHT = 500;
private static final Color BACKGROUND = new Color(255, 255, 240);
private static final int THIS_PT_WIDTH = 12;
private static final int THIS_PT_HEIGHT = THIS_PT_WIDTH;
private static final float THIS_PT_STROKE_WIDTH = 2f;
private static final Color THIS_PT_BORDER_COLOR = Color.red;
private static final Color THIS_PT_FILL_COLOR = new Color(250, 250, 0, 125);
private static final int TIMER_DELAY = 30;
private static final int X_MIN = 0;
private static final int X_MAX = 100;
private static final double X_STEP = 0.1;
private static final double X_SCALE = (double) BI_WIDTH
/ ((double) X_MAX - X_MIN);
private static final double Y_SCALE = 8;
private static final float LINE_WIDTH = 4;
private static final Color LINE_COLOR = Color.blue;
private Point lastPoint = null;
private Point thisPoint = null;
private BufferedImage bImage = new BufferedImage(BI_WIDTH, BI_HEIGHT,
BufferedImage.TYPE_INT_RGB);
private double xValue = X_MIN;
public BufferedImageEg() {
Graphics biG = bImage.getGraphics();
biG.setColor(BACKGROUND);
biG.fillRect(0, 0, BI_WIDTH, BI_HEIGHT);
setBackground(BACKGROUND);
new Timer(TIMER_DELAY, new ActionListener() {
public void actionPerformed(ActionEvent e) {
timerActionPerformed(e);
}
}).start();
}
private void timerActionPerformed(ActionEvent e) {
if (xValue <= X_MAX) {
lastPoint = thisPoint;
double tempX = xValue;
double yValue = function(xValue);
tempX *= X_SCALE;
yValue *= Y_SCALE;
yValue = BI_HEIGHT / 2.0 - yValue;
thisPoint = new Point((int) tempX, (int) yValue);
if (lastPoint != null) {
drawInBufferedImage();
}
xValue += X_STEP;
} else {
((Timer) e.getSource()).stop();
thisPoint = null;
}
repaint();
}
private void drawInBufferedImage() {
Graphics2D g2 = bImage.createGraphics();
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.setStroke(new BasicStroke(LINE_WIDTH, BasicStroke.CAP_ROUND,
BasicStroke.JOIN_ROUND));
g2.setColor(LINE_COLOR);
int x1 = lastPoint.x;
int y1 = lastPoint.y;
int x2 = thisPoint.x;
int y2 = thisPoint.y;
g2.drawLine(x1, y1, x2, y2);
g2.dispose();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(BI_WIDTH, BI_HEIGHT);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.drawImage(bImage, 0, 0, null);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
if (thisPoint != null) {
drawThisPoint(g2);
}
}
private void drawThisPoint(Graphics2D g2) {
int x = thisPoint.x - THIS_PT_WIDTH / 2;
int y = thisPoint.y - THIS_PT_HEIGHT / 2;
Graphics2D g2b = (Graphics2D) g2.create();
g2b.setStroke(new BasicStroke(THIS_PT_STROKE_WIDTH));
g2b.setColor(THIS_PT_FILL_COLOR);
g2b.fillOval(x, y, THIS_PT_WIDTH, THIS_PT_HEIGHT);
g2b.setColor(THIS_PT_BORDER_COLOR);
g2b.drawOval(x, y, THIS_PT_WIDTH, THIS_PT_HEIGHT);
g2b.dispose();
}
private double function(double x) {
return 24 * Math.sin(x / 12.0) * Math.sin(x);
}
private static void createAndShowGui() {
JFrame frame = new JFrame("BufferedImage Example");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new BufferedImageEg());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
public static void main(String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() {
createAndShowGui();
}
});
}
}