I'm looking to make it so I have a separate window that would show anything, but for my purpose I would like it to show things like the location of objects, their status(whether they have collided with something etc) and so on, the problem is, I have no idea how to go about this.
I would use System.out.println("Stuff: " + thing); but the information would move up too fast.
I made and awesome image to somewhat illustrate what I'm after.
(Edit)
So what I'm looking for is how to make two separate windows like in the picture. They are separate, yet still the one program. The blank behind it is the desktop(Which I should have made a bit more clear) The 2nd debug window can do anything the main window can(By that I mean it can render shapes, images and such). All I'm asking is, how do I achieve this?
What on this would I add/change and where, to make it work?
public class Game extends JFrame implements Runnable, KeyListener
{
JFrame frame;
Graphics2D g2d;
public static void main(String[] args)
{
new Game();
}
public Game()
{
frame = new JFrame();
setTitle("Battle for the Roids");
setSize(SCREENWIDTH, SCREENHEIGHT);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
// Set up assets and initialize loop
}
// Lots of lines later
public void paint(Graphics g)
{
// Set up double/triple buffering
BufferStrategy bs = getBufferStrategy();
if(bs == null)
{
createBufferStrategy(3);
return;
}
g2d = (Graphics2D) g;
g2d = (Graphics2D) bs.getDrawGraphics();
// Add things to be rendered
g2d.dispose();
bs.show();
}
}
I have lots of extra code in there but nothing that would be required. Mostly moving, rotating, key handling etc code.
Idk if this would help you or not, but what you could possibly do is create a new JFrame and have it appear using another class. Create a JLabel object, and have it set all the information for how ever many objects you need to use.
For example, if you want to have it show the user data you might wanna try:
public class WindowSample extends JFrame {
JLabel userData = new JLabel();
add(userData);
userData.setText(//information for user data);
}
hope this helps!
You could use Log4J and write your own appender whose sole responsibility was to output to something like JTextArea or JList, this would decouple the two halves of your application. Then all you would need to do would be to use the Logger to write information about the state of your application.
If that didn't meet your needs, you could write some kind of listener/observer/call-back API that linked into your game model.
You would then be able to respond to changes to the model directly. This has the draw back of potentially slowing you game engine, as it would need to fire updates any time any part of the application changed.
You could over come this by using a separate thread to dispatch these events, but that's still adding overhead.
Personally, I like the second option, but the first is easier to implement ;)
Related
I am trying to create a simple application that shows a red circle that when clicked displays different messages under it. I believe that this part of the code:
g.drawString("DO NOT PRESS", 100, 100);
is coded correctly but no text is displayed on the window that opens. Here is the full code so far:
import java.awt.Graphics;
import javax.swing.JFrame;
public class BigRedButton extends JFrame {
public BigRedButton() {
setTitle("Big Red Button");
setSize(500, 500);
setResizable(false);
setVisible(true);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
}
public void graphics(Graphics g) {
g.drawString("DO NOT PRESS", 100, 100);
}
public static void main(String[] args){
new BigRedButton();
}
}
There is no such method graphics in JFrame, so nothing is calling it.
You should avoid painting directly to top level containers, apart from everthing else, they're not double buffered and will flicker when painted. You should, instead, create a custom component (extending from something like JPanel) and override it's paintComponent method.
You should take the time to read through Performing Custom Painting, Painting in AWT and Swing and 2D Graphics
Also, while your reading up, you should have a read through Initial Threads
Amendment
As pointed out by Andrew, you should use the #Override annotation to ensure that the method you think you are overriding is actually the method being overridden in the first place. This would stop the program from being compiled and save lots of lost time trying to figure out why things aren't working the way you expect them.
I try to optimize the drawing of my application. Currently i have implemented an animation as well as several GUI components. Some are separated exactly, some overlap each other. Currently i face a problem within the overlapping of swing components. A part of the GUI, which overlaps my animation, needs to draw a lot of Strings and - into an Jlist placed - common swing components.
In result the overlapping GUI becomes redrawn as often as the animation gets an update. I tried using quite a lot of different methods to make sure, whats drawn in front of each other. Things like GlassPane, Jlayeredpane. Unfortunately in any of these tries, the overlapping Menus paintcomponent method, which only need to become called as the user interacts with them, gets called frequently due to animation and causes a quite high cpu usage.
Ive tried to position the Menus in a lower order within the Layeredpane, i.e.:
getLayeredPane().add(map, JLayeredPane.DEFAULT_LAYER);
getLayeredPane().add(mapController, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(settings, JLayeredPane.PALETTE_LAYER);
getLayeredPane().add(painter, JLayeredPane.POPUP_LAYER);
During the paint process of the painter i tried to modify the area - i.e.:
#Override
protected void paintComponent(Graphics g) {
g2 = (Graphics2D)g;
g2.setRenderingHints(DefaultResources.getRenderQuality());
g2.clip(JMain.getInstance().getMapBounds());
...}
Well - as soon as the painter component !isOpague(); All components underneath become redrawn. Unfortunately if i do place the menus in a higher order, they as well need to become redrawn with any animation update.
Does somebody has any idea, how to avoid a permanent redraw of overlapping components with an animated component?
The only solution i have seen was using heavyweight containers. Unfortunately the relative positioning has also shown a behavior during moving purposes, which aren't appropriate.
Thanks for any kind of advice!!
Well, its pretty obvious that if you have overlapping non-opaque components all of them will be repainted on any changes in one of them unless you optimize your animation repaint calls to some specific rectangles, so there won't be any useless operations.
Let me describe how Swing works a bit more - all of the paintings you do in paint, paintComponent and other methods (which get called on each component repaint) are done onto subimages of a single image that holds a "cached" version of the whole frame interface.
Now imagine that you change something in your UI (add/remove/repaint component) - that final image (or atleast a small part of it that contains your component) must be properly updated. To do that in case your component is NOT opaque - all of sub-components will be repainted with your component bounds as a repaint rect to create a proper background for your component. If your component is opaque - it will be the only one repainted, but it also has to fill the whole bounds rect on its own, otherwise you will see awful painting artifacts behind your component on each repaint.
To summ up - to avoid pointless repainting of overlapping components there are a few approaches:
Optimize animation repaint calls to areas you actually need to repaint
Use opaque components, unless you want to paint something transparent in your component
Optimize all overlapping components painting operations so repaints won't take much time
There still might be more optimization approaches depending on your specific case, but you will have to find them on your own, since that is impossible without seeing the whole picture.
You can also find a lot of useful information about optimizations in Swing in this book: Filthy Rich Clients
Well in terms of optimization i do have a component, which is causing quite some trouble, but that one i have planned to rewrite. Therefore i just want to make sure, that painting areas are properly. Following that point, i have computed all necessary areas and filled them into a list, which i pass as data has changed.
Its working properly as long as i only have one rectangle applied. As soon as i pass a second one, it seems its y - extension becomes ignored. For instance:
[x=0,y=0,width=322,height=20]
[x=0,y=620,width=322,height=20]
There everything between y=20 and y=620 also gets redrawn.
for (Rectangle rec : clippingAreas) {
painter.repaint(rec);
}
Ok i have tried paintImmediately within the EDT, which does work for now, but i wonder if this is a proper way to go:
SwingUtilities.invokeLater(new Runnable() {
public void run() {
for (Rectangle rec : clippingAreas) {
painter.paintImmediately(rec);
}
}
});
Ok there we go:
package animationissue;
import java.awt.Color;
import java.awt.Graphics;
import javax.swing.JFrame;
import javax.swing.JLayeredPane;
import javax.swing.JPanel;
public class AnimationIssue extends JFrame {
JPanel a = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("map has become repainted");
}
};
JPanel b = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
System.out.println("menu as well");
}
};
public AnimationIssue() {
this.setSize(500, 500);
this.setLayout(null);
a.setSize(400, 400);
b.setSize(400, 200);
this.getLayeredPane().add(a, JLayeredPane.DEFAULT_LAYER); // Map
this.getLayeredPane().add(b, JLayeredPane.PALETTE_LAYER); // Menu
a.setLocation(0, 0);
b.setLocation(0, 100);
a.setBackground(Color.red);
b.setBackground(Color.blue);
Thread t = new Thread(new Runnable() {
#Override
public void run() {
// doin some computations for animation
// cast a repaint after having finished new
//animation information i.e. interpolation
while (true) {
try {
Thread.sleep(2000);
} catch (Exception e) {
}
// case 1 - just a repaint of the whole component - triggering map redraw results in menu redraw
// a.repaint();
// case 2 - repaint of specified rectangle
// Either passing one - the menu does not get repainted, or passing both - menu also gets repainted
//a.repaint(0, 0, 400, 100);
//a.repaint(0, 300, 400, 100);
// paintimmediately works for now
//a.paintImmediately(0, 0, 400, 100);
//a.paintImmediately(0, 300, 400, 100);
// Just repainting Menu does not trigger map to become repainted, except if its opague, but then it should become repainted
b.repaint();
}
}
});
t.start();
}
/**
* #param args the command line arguments
*/
public static void main(String[] args) {
// TODO code application logic here
AnimationIssue f = new AnimationIssue();
f.setVisible(true);
}
}
I was really looking forward to optimize the behavior, that no menu gets redrawn, if its not necessary. You have to imagine the menu of being a component holding several JLists with a lot of String drawing tasks, which really have a massive impact on cpu usage. I havent been wondering, since it gets redrawn approximately 25 times per sec.
I am just uncertain for the current solution, if using paintImmediately is proper. Apart from this - if you or somebody has an alternative - better - way to prevent useless redrawing (i really thought Glasspane or JLayeredPane or isOptimizedDrawing or isOpaque might help out), i am really thankful.
Best regards.
I'm trying to create a java game in which balls randomly drop from the top of the screen and we need to catch the balls using a catcher which is located at the bottom of the screen.
I'm having a difficult time figuring out how to actually draw this onto my JFrame.
I've got a class for my 'catcher', 'ball', 'game space' and I would like to put it all together.
How do I draw my 'catcher' onto my screen?
Currently, I have a 'Game' class which looks like this.
public class Game extends JFrame implements KeyListener {
GameScreen gameScreen;
Catcher playerOneCatcher;
public static void main (String[] args) {
new Game();
}
public Game() {
super("CATCH");
setSize(640,480);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
setLocationRelativeTo(null);
setResizable(false);
addKeyListener(this);
this.gameScreen = new GameScreen();
this.playerOneCatcher = new Catcher(40, 10);
}
I've tried something like this in my Catcher class...
public void paintComponent(Graphics g) {
g.setColor(Color.BLUE);
g.fillRect(positionX, positionY, this.width, this.height);
}
However, its not showing on my screen.
Any help would be greatly appreciated.
You need to rethink your strategy here. Swing is a component framework, with most components intended for building user interfaces. These components are not optimised for what's typically required in games. You want to look into double-buffering, sprites etc. The way to go will be to read up on Graphics2D class (or abandon Swing altogether!)
However answering to your question - if Catcher is a Swing component - you need to add it to the "parent" component, e.g. like this:
this.add(playerOneCatcher);
Same goes to gameScreen but from your snippet it is not obvious what this component is. I hope this helps.
Also, check this out for some ideas: 2D Java Game. Moving sprite above tiled images
Did you call super.paintComponent (g) ? That can cause a few bugs.
Did you call invalidate () or repaint () to repaint the thing you are painting on? I hope you have a special JComponent, and you are not drawing on a JFrame. That is NOT good.
I'm having trouble finding out why the following problem happens: In a program that uses "extends Frame" to create a window, I can use BufferedImage to draw to the graphics context of the Frame (not JFrame), and it looks just fine. However, the moment I declare a JPanel, all of the text drawn by BufferedImage becomes obscured (not completely, but semi-transparent and hard to read), even if I don't add the JPanel to the JFrame.
Here's a simplified version of the code:
import java.awt.*;
import java.awt.event.*;
import javax.swing.Timer;
import java.awt.image.*;
import javax.swing.*;
public class MyProgram extends Frame {
static Frame f;
static Timer timer;
public static void main(String[] args) {
f = new Frame();
f.setSize(400, 200);
f.setResizable(false);
f.setVisible(true);
f.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) {
drawScreen();
}
});
drawScreen();
}
public static void drawScreen() {
BufferedImage off_i = new BufferedImage(f.getWidth(), f.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics g = off_i.createGraphics();
g.setColor(new Color(50, 50, 50));
g.drawString("Hit any key; why does this text change?", 15, f.getHeight() - 10);
Graphics off_g = f.getGraphics();
off_g.drawImage(off_i, 0, 0, null);
JPanel panel = new JPanel();
}
}
I could maybe understand seeing the problem arise if I had added the JPanel to the JFrame and didn't set any bounds to its visibility, but even creating the JPanel gives that issue, which seems weird to me.
Basically, what I'm trying to do here is take an existing program that I have that runs just fine without JPanel, and I want to add to it a JTextArea so that I can accept copy/paste text for modifying the displaying of the program.
My understanding of Java is kind of spotty, as I learned it mainly by hobby and not formally, but I'm always looking to learn more when I can. Thanks for the help!
Update: I discovered that this problem only happens when the draw function is called again, after the JPanel has been declared, though I still don't understand why it does that or how to get around it.
better would be put Image to the JLabel and how ot use Icon
please read Using Swing Components and how to LayoutManagers works
tons examples on java2s.com
Don't mix AWT components with Swing component. That is you should use a JFrame NOT a Frame.
Don't use getGraphics(). Custom Painting is done by overriding the paintComponent() method of a JPanel (or JComponent). You just use the Graphics object that is passed to the method. Then you add the panel to the frame.
As already mentioned using a JLabel is simpler because you don't have to do any custom painting. The tutorial also has a section on "How to Use Icons".
I tried to run your code. And although the effect that you are describing does not happen on my system I can recommend you something.
First try to create your panel before it is visualized. In this case java does not have to re-arrange the components that are already on screen.
Second, if you have to draw things on visible frame call validate() of the container when you are done. This makes java to re-arrange stuff.
Third, when you are using drawXXX() methods create your own class that extends Component, JComponent, Canvas and override its `paint(Graphics) method. In this case the system will call this method every time it needs (e.g. when window is resized) and your UI will be painted again.
BTW I have 2 questions:
why are you using drawText() instead
of Label or JLabel? Use them and
avoid such kind of problems.
Why do you extend your class from Frame and do not use this fact but create yet another instance of Frame?
As an answer to my original question:
It seems that initializing JPanel alongside awt draw() commands causes the text to be antialiased, which makes the text look harder to read, partially obscured, thinner, etc. Although I tried setRenderingHint() with VALUE_TEXT_ANTIALIAS_OFF, it did not solve the problem. But as other posters pointed out it's not best practice to mix the two components.
While this doesn't exactly solve my problem, it does answer the question of what is going on, that being text antialiasing as some result of JPanel (does that sound right?). Ideally I wouldn't want to rewrite all of the code just to add a single JTextArea into an already existing codebase. But perhaps it's good every now and then to revisit old code and revamp it where it may be faulty.
Thanks everyone for the comments, information, and resource links!
I'm working on recreating "Legend of Zelda a Link to the Past" as part of an assignment.
And I've got the following problem, the game world is a BufferedImage of which I subImage() the part I need for the current location of the player. The game works but uses 80 / 110 percent of the CPU. A profile revealed that the culprit is the drawing of the image.
So I figured I put the background in a separate JPanel from the Player, enemies etc JPanel.
Render them on top off each other (JLayeredPane) and repaint the background panel far less often.
But how do I do this how do I tell swing to draw one panel x times a sec and the other y times? If you have a better way of optimizing let me know.
Here's what I've got at the moment:
public class Main extends JFrame
{
private ZeldaGame game = new ZeldaGame();
private View view = new View(game);
private BackgroundView bckView = new BackgroundView(game);
private Controller ctl = new Controller(game, view, bckView, this);
public Main()
{
setLayout(null);
view.setBounds(0, 0, game.getWidth(), game.getHeight());
bckView.setBounds(0, 0, game.getWidth(), game.getHeight());
JLayeredPane pane = new JLayeredPane();
pane.add(bckView, 1);
pane.add(view, 0);
setLayeredPane(pane);
setSize(game.getWidth(), game.getHeight());
setResizable(false);
setLocationRelativeTo(null);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
setVisible(true);
}
public static void main(String[] args)
{
new Main();
}
}
Thank You.
It's not really possible to have different render times because of the way the framebuffer works -- what the result would be is loads of nasty flickering. What you need to do is using Canvas instead of JPanel and JLayeredPane and you can either override the paint() method of the canvas or use bi.getGraphics() and blit the background and the characters on a loop using either of these methods in the correct order. I'd advise using a thin engine like GTGE which will abstract from all the messy details of optimisation. These high level components you're using seriously aren't designed for games, and you shouldn't be using them at all.
Ok I've found the error of my ways. I was using passive rendering while I should have used active rendering. Active rendering basically shuts down the automatic repaints by calling setIgnoreRepaint(true); on the frame and do the loop yourself.
And as an added bonus I don't need to use JPanels.