I am programming a platformer game in Java. Everything works fine and there are no bigger problems, except the flickering on the screen when the Canvas is updated.
I'll roughly explain how my game "engine" works:
In my main method, I have a Loop, that is repeating itself 30 times a second:
while (play) {
Date delay_time = new Date();
delay_time.setTime(delay_time.getTime() + (int) (1000*(1.0/FPS)));
// Here is all the game stuff, like the motion of the player,
// function calling, etc.
Graphics g = can.getGraphics();
can.update(g);
while(new Date().before(delay_time)) {
}
}
My FPS variable is a static final int that is currently set to 30.
So i am updating my Canvas 30 time a second.
Play is a boolean, which controls if the game is still playing, or the player died.
Can is an instance of my class MyCanvas.java.
The Code of the paint() method looks like so:
if (Main.play) {
//NOW
//Draw the now Level Caption
//SIZE: 240
g2.setFont(new Font("Bank Gothic", Font.PLAIN, 240));
g2.setColor(new Color(Math.abs(bg.getRed() - 30), Math.abs(bg.getGreen() - 30), Math.abs(bg.getBlue() - 30)));
g2.drawString("LEVEL " + Main.lvl, 80, 80 + 300 - Main.groundY);
//Draw the ground
g2.setColor(haba);
g2.fillRect(0, Main.screenSize.height - Main.groundY, Main.screenSize.width, Main.groundY);
//Draw the level
g2.setColor(haba);
for (int i = 0; i < Main.LVLWIDTH; i++) {
for (int j = 0; j < Main.LVLHEIGHT; j++) {
if (Main.level[i][j] == '1') {
g2.fillRect(i*(Main.BLOCK_X), (j - Main.LVLHEIGHT)*(Main.BLOCK_Y) + Main.screenSize.height - Main.groundY, Main.BLOCK_X, Main.BLOCK_Y);
}
}
//More drawing stuff...
}
So, as you can see, I am updating quite a lot - 30 times a second. This leads to the problem that my game (which is playing in fullscreen mode BTW) flickers all the time. How do solve that problem? Is a Bufferstrategy the right way? And if yes, how should I do it? Can you please explain the Bufferstartegy or provide links that have great tutorials or explanations, which or not to technical and non-beginner friendly? That would be great.
The BufferStrategy class solved my problem. Here is a good link to it, with a well made example of how to use it:
http://docs.oracle.com/javase/7/docs/api/java/awt/image/BufferStrategy.html
Related
So I was messing with Swing for the first time in a while and I came across a strange issue. So I am adding "shapes" to a list every so often, and then in the paintComponent method of a JPanel I am looping through the list and drawing the shapes. I also draw a shape outside of the for loop for testing purposes.
What happens is the shapes in the for loop will jump around the screen randomly. This only happens when the shapes are drawn in this loop.
What I have tried already:
Updating graphics drivers for both the integrated GPU and discrete GPU
Using java.util.Timer instead of Swing Timer
Using Thread/Runnable
Using things other than ArrayList, such as LinkedList, Vector, and a normal Array.
Trimmed literally everything out of my code except the basics, which is what we're left with here. I was drawing more complex things before when I noticed it.
Changed the timing (PERIOD variable, in millis). It will get worse if I increase or decrease it.
Changed from using System time in milliseconds to the System time in nanoseconds, converted to milliseconds. I know this should be the same but I was running out of ideas.
Here is a gif of the problem (15 seconds long):
image
You'll notice that the small squares will jump around at random intervals. This should not occur. I'm just trying to "spawn" a square at random coords.
Here is the code in a pastebin:
code
I have included all 3 classes in this order: the JPanel class, the Main class (extends JFrame), and the shape class
If any of the links don't work, inform me and I will promptly post other links.
Thanks.
This setup ...
#Override
public final void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
if (this.startTime == -1L) {
this.startTime = Main.timeMillis();
}
final long timeDiff = Main.timeMillis() - this.startTime;
if (this.circlesIndex <= 19 && timeDiff > 2000) {
final int randX = this.rand.nextInt(this.WIDTH);
final int randY = this.rand.nextInt(this.HEIGHT);
this.testShapes.add(new TestShape(randX, randY));
this.startTime = Main.timeMillis();
}
for (TestShape ts : this.testShapes) {
ts.draw(g);
}
g2.setColor(Color.gray);
g2.fill3DRect(350, 400, 100, 60, true);
}
#Override
public final void actionPerformed(final ActionEvent event) {
x++;
if (x > WIDTH) {
x = -50;
}
repaint();
}
is wrong.
Paint is for painting - you should not modify the state of the UI from inside any paint method, do this within your ActionListener. The problem is, your component can be painted for any number of reasons, many of which you don't control or know about
I've been trying to make a simple game in pure java and I've encountered a problem in drawing. I'm trying to keep a relatively high frame-rate but having issues with the fact that JFrame.repaint() cannot be 'forced' and is merely a request to have the frame redrawn at the next available opportunity. As a result, the below code's frame-rate is terrible. However, (and this is the strange part) it seems to only be terrible when my mouse isn't moving. If my mouse is moving and over-top of the window, the frame rate is fast and crisp.
I've tried various online suggestions and even compiled examples on how to do this and they all seem to have the same issue with the frame rate dropping dramatically when my mouse isn't moving over the window.
(I'm using Linux, if that matters)
Any and all help is much appreciated!
import java.awt.*;
import javax.swing.*;
public class Test extends JPanel {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setSize(new Dimension(300, 300));
frame.setVisible(true);
frame.getContentPane().add(new Test());
for (int k = 0; k < 1_000_000; k++) {
frame.repaint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
System.exit(1);
}
}
frame.dispose();
System.exit(0);
}
private int k = 0;
public Test() {
super();
}
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
g.setColor(Color.WHITE);
g.fillRect(0, 0, getWidth(), getHeight());
g.setColor(Color.BLACK);
int height = (int) (((k * 0.01) % 1) * getHeight());
g.drawLine(
0, height,
getWidth(), height
);
k++;
}
}
After far too much research, it turns out that java does not sync / flush the display buffer automatically on many Linux systems. All the repaints and such were correct, however the display buffer was not flushing, thus creating the strange lagging effect.
The Solution:
Toolkit toolkit = Toolkit.getDefaultToolkit(); /* get AWT toolkit */
/* initialize things */
...
while (your_loop) {
/* run your logic */
...
/* paint everything */
...
toolkit.sync(); /* force display buffer to flush */
}
Thank you all for your input
The question is not simple. The code below has not been tested, then just to give you the idea... In the next lines, AWT is the underlying of Swing.
First, you have to keep your paintComponent() very fast (indeed!). This is the first requirement. Basically, for 60 fps, you must draw in less than 15 milliseconds. Forget transaparency and other stuff (works badly on Windows, I don't know for Linux). Try to save calculations when possible.
Second, Execute everything else in a different thread. This is the way I use for my own program. Note every call to AWT (included Swing, of course) must be encapsulated in a call to EventQueue.invokeLater() to ensure you are running stuff in the AWT thread because setting a label MUST NOT be done outside the AWT thread.
Do not forget to create a thread when you receive an input from AWT that takes time!
Third, replace your loop by a timer like
new Timer("Drawer", true).scheduleAtFixedRate( new TimerTak(){
public void run(){
frame.repaint();
}
},
100, // Start in 100 ms
(int)(1000 / 60)); // 60 is the frame rate.
Everything should work smoothly. For the frame count k, use the following:
// You should initialize just before you create the timer...!
static private long startedAt = System.currentTimeMillis();
#Override public void paintComponent(Graphics g) {
super.paintComponent(g);
// Microseconds since the game started.
long k = (System.currentTimeMillis() - startedAt);
// Increment only one by frame (60 fps)
k = (int)((double)k * 60 / 1000.0)
// Draw the game...!
}
That's all. Note some frames can be dropped if the computer is not enough powerful (or CPU intensive is required, or garbage collector...). But, when possible, your game will run at a maximum of 60 fps.
Bonus: if you increment a value each time you go through the paintComponent(), you can find the number of frames dropped or the average number of frames per second really displayed since the start of the game.
I am trying to draw an Oval with a for loop iterating through a list of coordinates that each contain an x value and y value. Currently, it does not seem to be drawing anything after I start the program. It draws the first time, but when I try drawing when the program is running, it doesn't seem to be drawing.
Here is the code for drawing:
private void render(){
bs = display.getCanvas().getBufferStrategy();
if(bs == null){
display.getCanvas().createBufferStrategy(3);
return;
}
g = bs.getDrawGraphics();
//Draw Here!
DrawGrid(g);
g.fillOval(100, 100, 10, 10);//this seems to draw
for(int i = 0; i < points.size();i++){//this doesn't draw....
System.out.println(points.get(i));
g.drawString(points.get(i).toString(), points.get(i).x*100-5+100, points.get(i).y-5-300);
g.fillOval(points.get(i).x*100-5+100, points.get(i).y-5-300, 10, 10);
}
//End Drawing!
bs.show();
g.dispose();
}
If you need more details, I am using graphics from java.awt library. Also, I have done this in the past, but I don't know why it isn't working this time.
This part is your issue:
points.get(i).x*100-5+100
Specifically x*100
You are drawing off screen. We can see this by breaking it down:
Lets assume that you have a point of x=28.
Lets do the math on that:
For X = 28 you will have the following calc: (28 * 100) - (5 + 100) = 2695
That X point of 2695 looks very large to me. You would need a 4k screen or ultrawide screen to see it.
The solution:
Have a think about why you are using x*100, and reduce it so that the point fits on your screen. Also, if you have a small y point, then it will be in the negatives (Example: 15-5-300 = -290), and will probably draw above your screen and out of sight.
First off I would like to say that I understand I am asking a lot so any help would be appreciated and I thank you for your time. I am extremely grateful.
Anyway I am creating a game for my A2 coursework, most commonly known as Checkers. I have completed my code and everything works as I had planned except that the CheckerBoard itself as well as the checkerpieces do not appear to be showing.
The section of were my board should be present is just a black space. Although my board does not appear to be displaying, all of the actions I perform on it such as clicking certain section produces the planned response, and although I've checked through my code I cannot work out what I've done wrong.
Anyway if anyone could possibly spot my mistake or perhaps give me some advice I would be extremely grateful. Thank you
As I of course can't upload my entire code I will do snippets of where I think the problem might lie or just important sections. Thank you
CheckerBoard content = new CheckerBoard(); // Sets the CheckerBoard values into the content to be used in the next line
application.setContentPane(content); // Container holds the values together, Content pane of the CheckerBoard
application.pack(); // Use preferred size of content to set size of application.
Dimension screensize = Toolkit.getDefaultToolkit().getScreenSize();
application.setLocation( (screensize.width - application.getWidth())/2,
(screensize.height - application.getHeight())/2 );
application.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE ); // Sets so that the application can be exited when the application is closed
application.setResizable(false); // This makes it so that the user can't change the application's size.
application.setVisible(true); // Sets it so that the application can actually be seen
The code above is placed within a "public static void main(String[] args)"
Then below that I have:
public CheckerBoard() {
// This is going to be a constructor, this constructor will set the layout manager for the panel to be null, it will then add components to the panel
// and it will set their bounds.
setLayout(null); // So that it will match my requirement specification, I will do the layout myself
setBackground(new Color(0,120,0)); // Dark Green Background colour.
setPreferredSize(new Dimension(350,250)); // The size of the Panel
BoardComplete checkerboardData = new BoardComplete();
add(checkerboardData); // This will create the components and add them to the content pane
add(NewGameButton);
add(ResignButton);
add(MessageDisplay);
// I will now have to produce a method to set the position and size of each component by calling its setBounds() method
checkerboardData.setBounds(20,20,164,164); // Sets the board dimensions
NewGameButton.setBounds(210, 60, 120, 30);
ResignButton.setBounds(210, 120, 120, 30);
MessageDisplay.setBounds(20, 200, 350, 30);
}
And then finally I have a another public class called BoardComplete which contains all the relevant code involving the actionlistners ect. used:
BoardComplete() {
setBackground(Color.BLACK);
addMouseListener(this);
ResignButton = new JButton("Resign");
ResignButton.addActionListener(this);
NewGameButton = new JButton("New Game");
NewGameButton.addActionListener(this);
MessageDisplay = new JLabel("",JLabel.CENTER);
MessageDisplay.setFont(new Font("Serif", Font.BOLD, 14));
MessageDisplay.setForeground(Color.green);
checkerboardData = new DataForCheckers();
MakeaNewGame();
}
I understand that this is a lot to ask but any help would be greatly appreciate and I would be extremely grateful. Thank you.
Edit due to comment: This is the code for my Paint Class:
public void PaintCheckerBoard(Graphics g) {
Graphics2D g2 = (Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
// This will draw a two-pixel black border around the edges of the canvas.
g.setColor(Color.black);
g.drawRect(0,0,getSize().width-1,getSize().height-1);
g.drawRect(1,1,getSize().width-3,getSize().height-3);
// Draw the squares of the checkerboard and the checkers.
for (int row = 0; row < 8; row++) {
for (int col = 0; col < 8; col++) {
if ( row % 2 == col % 2 )
g.setColor(Color.LIGHT_GRAY);
else
g.setColor(Color.GRAY);
g.fillRect(2 + col*20, 2 + row*20, 20, 20);
switch (checkerboardData.PieceLocation(row,col)) {
case DataForCheckers.RED:
g.setColor(Color.RED);
g.fillOval(4 + col*20, 4 + row*20, 15, 15);
break;
case DataForCheckers.BLACK:
g.setColor(Color.BLACK);
g.fillOval(4 + col*20, 4 + row*20, 15, 15);
break;
case DataForCheckers.RED_KING:
g.setColor(Color.RED);
g.fillOval(4 + col*20, 4 + row*20, 15, 15);
g.setColor(Color.WHITE);
g.drawString("K", 7 + col*20, 16 + row*20);
break;
case DataForCheckers.BLACK_KING:
g.setColor(Color.BLACK);
g.fillOval(4 + col*20, 4 + row*20, 15, 15);
g.setColor(Color.WHITE);
g.drawString("K", 7 + col*20, 16 + row*20);
break;
}
}
}
// If there is a game in progress, highlight the legal moves that the player can make.
// It can be seen that in this process, LegalMoves is never null while a game is in progress
if (CheckerMatchInProgress) {
/* First, draw a 2-pixel cyan border around the pieces that can be moved. */
g.setColor(Color.cyan);
for (int i = 0; i < LegalMoves.length; i++) {
g.drawRect(2 + LegalMoves[i].fromCol*20, 2 + LegalMoves[i].fromRow*20, 19, 19);
g.drawRect(3 + LegalMoves[i].fromCol*20, 3 + LegalMoves[i].fromRow*20, 17, 17);
}
// If a piece is selected to be moved, for example if ChosenRow>=0, then
// draw a 2-pixel white border around that piece, and draw a green border
// around each square that the piece can be legally moved to.
if (ChosenRow >= 0) {
g.setColor(Color.white);
g.drawRect(2 + ChosenColumn*20, 2 + ChosenRow*20, 19, 19);
g.drawRect(3 + ChosenColumn*20, 3 + ChosenRow*20, 17, 17);
g.setColor(Color.green);
for (int i = 0; i < LegalMoves.length; i++) {
if (LegalMoves[i].fromCol == ChosenColumn && LegalMoves[i].fromRow == ChosenRow) {
g.drawRect(2 + LegalMoves[i].toCol*20, 2 + LegalMoves[i].toRow*20, 19, 19);
g.drawRect(3 + LegalMoves[i].toCol*20, 3 + LegalMoves[i].toRow*20, 17, 17);
}
}
}
}
} // end PaintCheckerBoard()
So looking through your code on the other site, your custom paint method never seems to be getting called from anywhere. You're supposed to override the paintComponent method from the Component class (JPanel is a subclass of Component). This paintComponent method will be called whenever swing thinks, or is told to redraw something.
So, add this code to your BoardComplete class:
#Override
public void paintComponent(Graphics g){
super.paintComponent(g);
PaintCheckerBoard(g);
}
super.paintComponent will do anything swing would do to paint this component (which at this point is just drawing the background). And then you pass the Graphics object to your own custom paint method, that method will draw your entire game on the Graphics object, and swing will display whatever is drawn on there for you.
So now you have to remember also to call repaint() every time something in your game changes. The repaint() method will just tell swing he should repaint something (note: this tells the awt thread it should repaint the component, so it happens asynchronously from where youre calling repaint()).
If youre still confused, you should probably look up some information or tutorials on how to draw with awt/swing using this method.
NB: I have never used swing before, neither graphics 2D, and I don't program very much...
What I am trying to do is to make a program which takes an array/vector as input. This array, where each index 0,1,2 etc holds either zero or one (int) - which represents "no activity" or "activity" in minute 0,1,2 etc...
I want the program to draw a discontinuous straight horizontal line - representing "activity" vs "no activity" as a function of time - based on the array that was taken as input.
And this should pop up in a panel when I run the code..
The Idea is to show activity/no activity as a function of time, so the line would preferably be shown in a chart ( x-axis & y-axis )... And there will be several of these discontinuous lines above each other - for comparison of different cases.
I have tried for a while to look at examples using swing and graphics 2D, but as I have very limited amount of time - I could really need some help ..
Any code that:
creates a panel, frame etc - where I "easily" can see where I can insert my graph: that is a panel which is ready to display the graph I will make
draw a graph of discontinuous horizontal lines based on an array as described above
...is immensely appreciated :)
added from comment:
Sorry - did not finish my answer :) I could sure try to learn how to use all the different things in Swing frames, panels etc.. But at the moment my main goal is to finish my assignment for school - which is the visualization of data itself - and they do not really care how you get there, the most important thing is that it visualizes something useful... So I thought that I could decrease the time I had to spend on this if I got some code which could get me started - and not have to learn how it all works first.
No need to learn Graphics2D, just go for JFreeChart. Here is a simple tutorial to get you started (A minimum of Java programming knowledge is required though)
This is is an example, I guess it will help
import java.awt.*;
import javax.swing.*;
public class ActivityGraph extends JFrame {
int[] active = {0,1,1,0,0,0,1,0,1,0,1,0,1,1,1,1,0,0,1,0,1,1};
int length = 25, //basic length in pixels for drawing the lines
offset = 50; //so the lines aren't sticked at the border
private ActivityGraph(String name, int x, int y, int width, int height) {
super(name);
setBounds(x, y, width, height);
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel panel = new GraphPanel();
//panel.setBounds(0, 0, 800, 400); not nessesary
add(panel);
}
public static void main(String[] args) {
new ActivityGraph("Activity Graph", 60, 60, 800, 400).setVisible(true);
}
private class GraphPanel extends JPanel {
public void paint(Graphics g) {
g.setColor(Color.white);
g.fillRect(0, 0, 800, 400);
//setting background (method setBackground() doesn't want to work for me)
g.setColor(Color.black);
for(int i = 0; i<active.length; i++) {
if(active[i]==0) {
g.drawLine(offset + i*length, offset + length, offset + i*length + length, offset + length);
}
else {
g.drawLine(offset + i*length, offset, offset + i*length + length, offset);
}
/*
* draw line from XY point to another XY point
* notice that X = Y = 0 point is in left top corner
* so higher Y values will mean "downer" points acctualy
*/
}
}
}
}
If you want, I can send you a graph drawer for math functions (like sinus, power, ...)