I program a simple RTS game now, and it is my first experience in game design. My problem is that when I use createBufferStrategy(2) all swing elements (buttons etc...) not displayed after bufferStrategy.show(); method is invoked. My game is full of buttons, tables and other crap, and I really don't want to code all this by my own. Also I really like Java's layouts, and want to make all GUI on this.
So, here is little code example, not from my game, but it is a good demonstration of my problem. Thanks.
Btw, I understand the source of my problem. I know that swing draw mechanic is event-based, while using bufferstrategy is not event-based. But I don't know how to solve this. Thank you.
And final - I don't want to use default swing event-based approach becouse it is slow for games, and as far as I know the bufferstratgey is only approach for games. Thx.
public static void main(String[] args){
final JFrame frame = new JFrame();
JPanel menuPanel = new JPanel();
final Button button = new Button("Exit");
button.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
System.exit(0);
}
});
menuPanel.add(button);
frame.add(menuPanel);
frame.setPreferredSize(new DimensionUIResource(800, 600));
//frame.setResizable(false);
//frame.setUndecorated(true);
//frame.setIgnoreRepaint(true);
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
final long delay = 1000/60;
frame.pack();
frame.setVisible(true);
final GraphicsDevice device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
if (device.isFullScreenSupported()){
device.setFullScreenWindow(frame);
// this place. If I turn into fullscreen mode button disappear. And if I stay in windowed mode button exist and work;
}
frame.createBufferStrategy(2);
final BufferStrategy bufferStrategy = frame.getBufferStrategy();
Thread renderingThread = new Thread(){
public void run(){
while (true){
long startRenderingTime = System.currentTimeMillis();
Graphics g = bufferStrategy.getDrawGraphics();
g.setColor(Color.white);
g.fillRect(0,0,1680,1050);
//button.paint(g);
//button.paintAll(g);
// I don't know how to draw button!
g.dispose();
if (!bufferStrategy.contentsLost()){
bufferStrategy.show();
}
long endRenderingTime = System.currentTimeMillis();
long actionTime = endRenderingTime-startRenderingTime;
try {
if (actionTime>delay){
sleep(5);
} else {
sleep(delay-actionTime);
}
} catch (InterruptedException e){
return;
}
}
}
};
renderingThread.start();
}
});
}
You're running an infinite loop on Swings EDT, effectively blocking Swing from doing anything.
I don't see for what you even need BufferStrategy when you want to display Swing elements. To combine custom rendering with Swing components you normally just create a component that renders your stuff and add it to the normal layout.
Your component just overwrites paintComponent() and draws whatever it needs to. You can easily trigger an update of your component by calling repaint() on it. This performs usually well enough. Note that Swing components are double buffered by default, so there is no need to work with BufferStrategy there.
If you want to stick to active rendering, you could call Swings rendering chain selectively, for example you could get the Frame's ContentPane and just call paint() in your rendering loop. But hacking it this way may cause unwanted side effects.
Related
I am trying to display images on the screen using Graphics but the screen doesn't load
The output screen appears but only show The black screen and not the images
The code gets compiled properly so why am i not getting the output
package game;
import java.awt.*;
import javax.swing.JFrame;
public class Screen {
private GraphicsDevice vc;
public Screen(){
GraphicsEnvironment env = GraphicsEnvironment.getLocalGraphicsEnvironment();
vc=env.getDefaultScreenDevice();
}
public void setFullScreen(DisplayMode dm, JFrame window){
window.setUndecorated(true);
window.setResizable(false);
vc.setFullScreenWindow(window);
if(dm !=null && vc.isDisplayChangeSupported()){
try{
vc.setDisplayMode(dm);
}catch(Exception ex){}
}
}
public Window getFullSCreenWindow(){
return vc.getFullScreenWindow();
}
public void resotreScreen(){
Window w= vc.getFullScreenWindow();
if(w!=null){
w.dispose();
}
vc.setFullScreenWindow(null );
}
}
package game;
import java.awt.*;
import javax.swing.ImageIcon;
import javax.swing.JFrame;
class Images extends JFrame{
public static void main(String[] args){
DisplayMode dm = new DisplayMode(800,600,16,DisplayMode.REFRESH_RATE_UNKNOWN);
Images i = new Images();
i.run(dm);
}
private Screen s;
private Image bg;
private Image pic;
private boolean loaded;
public void run(DisplayMode dm){
setBackground(Color.BLUE);
setForeground(Color.WHITE);
setFont(new Font("Arial",Font.PLAIN,24));
loaded =false;
s = new Screen();
try{
s.setFullScreen(dm, this);
loadpics();
try{
Thread.sleep(10000);
}catch(Exception ex){}
}finally{
s.resotreScreen();
}
}
public void loadpics(){
bg = new ImageIcon("C:\\Users\\Dhruv\\Downloads\\Ronaldo.jpg").getImage();
pic =new ImageIcon("C:\\Users\\Dhruv\\Downloads\\Messi.jpg").getImage();
loaded= true;
repaint();
}
public void paint(Graphics g){
if(g instanceof Graphics2D){
Graphics2D g2 =(Graphics2D)g;
g2.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
}
if(loaded){
g.drawImage(bg,0,0,null);
g.drawImage(pic,170,180,null);
}
}
}
Let's start with background information...
A JFrame is a container for a JRootPane, which contains the contentPane, JMenuBar and JGlassPane
When you override paint of top level container like JFrame, you are only painting the bottom most component, the JRootPane and it's contents are then painted over the top, making it kind of pointless.
See How to Use Root Panes for more details
Painting is also a complex operation, failing to call super.paint will cause no end of issues, make sure you always call the super paint method before painting unless you really understand how it works and are prepared to do it's job manually.
In Swing you are instead encouraged to extend from a JComponent based class (JPanel been the preferred) and override its paintComponent method and perform your custom paint there
This component can either be added to the window or set as the contentPane or added to some other container depending on your needs.
See Painting in AWT and Swing and Performing Custom Painting for more details.
ImageIcon uses background thread to load it's images, so even though it returns, the image might not be realised (or fully loaded). When you use g.drawImage(bg,0,0,null);, passing null as the ImageObserver, it prevents the container from knowing when the image changes and allowing it to automatically repaint itself.
What's cool is, all Component based classes implement ImageObserver, so pretty much anything which can paint can act as an ImageObserver.
Convention would encourage to pass this as the ImageObserver.
Generally, a better solution is to use ImageIO, which when it loads images, won't return until the image is fully realised.
Have a look at Reading/Loading an Image for more details.
Thread.sleep(10000); is a dangerous thing to use in Swing. It has the potential to stop the UI from been updated or respond to other input and events, making your program appear as if it's hung, because it has. But in your case, it means you're violating the single thread rules of Swing.
Swing is a single threaded environment, you should never perform any action which might block the Event Dispatching Thread and you should never update the UI from outside the context of the EDT.
There are solutions available to help you, Swing Timer for generating periodical events which are dispatched within the EDT and SwingWorker for performing long running operations which have support for updating the UI.
See The Event Dispatch Thread for more details.
My recommendation is to not worry about the full screen support, focus on getting the images painting using a normal window and the add the full screen support. This allows you to solve problems within an isolated set of functionality, making it much easier to solve.
I am working on a code that will generate a random number when you press a button and output that number. I have wrote this code and it compiles but when I press the button nothing works. Can someone please help. Here is some of my code.
public class slotmachine extends JApplet {
JButton b1 = new JButton("START");
JPanel p;
int Int1;
public slotmachine() {
init();
}
public void init() {
this.setLayout(null);
this.setSize(1000, 1000);
JButton b1 = new JButton("START");
b1.setBounds(100, 100, 100, 100);
getContentPane().add(b1);
repaint();
}
public void run() {
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Random random1 = new Random();
int Int1 = random1.nextInt(11);
}
});
}
public void paint(Graphics g) {
g.drawString("Your number is" + Int1, 30, 30);
}
}
Avoid using null layouts, pixel perfect layouts are an illusion within modern ui design. There are too many factors which affect the individual size of components, none of which you can control. Swing was designed to work with layout managers at the core, discarding these will lead to no end of issues and problems that you will spend more and more time trying to rectify
You create a local variable of Int1 within the ActionListener for the button. This has no relationship to the Int1 of the class.
You never tell the UI to update
You break the paint chain by failing to call super.paint (be ready for some seriously weird and wonderful graphics glitches)
You've made the same mistake with b1 as you have with Int1. You create an instance level field, but shadow it with a local variable in init, meaning when start is called, b1 is null, which will result in a NullPointerxception
Instead, add a JLabel to your applet, use it's setText method to display the random value
b1.addActionListener(new ActionListener() {
public void actionPerformed(ActionEvent e) {
Random random1 = new Random();
int Int1 = random1.nextInt(11);
lable.setText(Integer.toString(Int1));
}
});
Also, if possible, I'd avoid using JApplet, they have their own set of issues which can make life more difficult then it needs to be when learning the Swing API. Instead, try using a JPanel for your main container and then add it to an instance of a JFrame.
Also, take a look at:
Understanding Class Members for more information about local and class/instance context for variables
How to Use Labels
Why is it frowned upon to use a null layout in SWING? and Laying Out Components Within a Container
And if your really interested in how painting works, Performing Custom Painting and Painting in AWT and Swing
for more details
So I programmed am applet that makes a ball roll in circles for ever, and I wanted to make the user decide what speed the circle should roll in, but something failed when I added the JFrame:
applet(the stop,destroy and update do not appear because they aren't important, and in start there is nothing):
public class Main extends Applet implements Runnable{
private Image I;
private Graphics GfU;
int ballX, ballY=249;
static int radius=20;
double Memory;
int changeY ,changeX=1;
Speed S = new Speed();
#Override
public void init() {
setSize(750,750);
S.setVisible(true);
}
#Override
public void run() {
while(true){
if(ballY>=250 || ballY<=-250){
changeY=0-changeY;
changeX=0-changeX;
}
ballY+=changeY;
Memory=(double)ballY/250;
Memory=Math.asin(Memory);
Memory=Math.cos(Memory);
ballX=(int)(Memory*250);
if(changeX==-1)
ballX=0-ballX;
repaint();
try {
Thread.sleep(17);
}
catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
#Override
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillOval(ballX-radius+250, ballY-radius+250, radius*2, radius*2);
}
public void setChangeY(int changeY) {
this.changeY = changeY;
}
public void Done(){
S.setVisible(false);
Thread BallRun = new Thread(this);
BallRun.start();
}
}
JFrame:
public class Speed extends JFrame implements ActionListener{
private static final long serialVersionUID = 1L;
public Speed(){
setDefaultCloseOperation(EXIT_ON_CLOSE);
JPanel P = new JPanel();
JLabel L = new JLabel("please enter velosity(pixels per second)");
final JTextField TF = new JTextField("00");
final Main M = new Main();
JButton B = new JButton("OK");
B.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
M.setChangeY(Integer.parseInt(TF.getText()));
M.Done();
}
});
P.add(L,BorderLayout.NORTH);
P.add(TF,BorderLayout.WEST);
}
#Override
public void actionPerformed(ActionEvent arg0) {
}
}
thanks (and sorry if it's bothering you the lack of information)
Here are some things to consider:
Don't use a JFrame. Use a JDialog as a popup window. Also, you should probably not create the dialog in the constructor. Instead you should have a JMenuItem so that the user can click on the menu when they want the popup to display.
Don't use "Applet", that is an AWT component. You should be using "JApplet" in a Swing application.
You should not be overriding the paint() method of the applet. Instead you should be adding a JPanel to the applet and then override the paintComponent(...) with your custom painting.
Don't use a loop to control the animation. Instead you should be using a Swing Timer.
Start by reading the Swing tutorial. There are sections on:
How to Make Applets
How to Use Swing Timers
Performing Custom Painting
setDefaultCloseOperation(EXIT_ON_CLOSE);
This is not allowed even in a fully trusted applet. Closing the frame would close the JVM that runs the applet that launched it. That JVM might also be running other applets.
Look at it like this. The web page that hosts an applet is like a guest, while the web page is a guest house. For an applet to end the JVM is like the guest burning down the guest house while smashing out all the windows.
setDefaultCloseOperation(DISPOSE_ON_CLOSE);
Might 'work' (to not produce an AccessControlException), but really, no applet should be launching frames. Use a JDialog instead.
As a general tip: Ensure the Java Console is configured to show for applets & JWS apps. If there is no output at the default level, raise it and try again. Without the information contained in it, I doubt it would be possible to successfully develop an applet.
Your Speed class extends JFrame, but the only things that you set is setDefaultCloseOperation(EXIT_ON_CLOSE), you should set at least se size of your JFrame with setSize(width, height) and set it visible with: setVisible(true).
Another thing... i can't see where you added your JFrame to the Main class...
You should add it creating a new Speed object: Speed objectname = new Speed()
If i've understood correctly that was your problem.
I think you could read here to learn how to use the JFrame: http://www.dreamincode.net/forums/topic/206344-basic-gui-in-java-using-jframes/
Below is a small example for what i describe
Well, I have a simple JFrame with a JPanel as its contentPane that i repaint with a SwingWorker every 20 ms with panel.repaint().
Also, I have a JDialog opened that shows its own graphics with opengl (I use a AWTGLCanvas from LWJGL library) that swaps buffers and repaint() every time it does paint its content (faster than the 20ms).
The big problem is that in some way repainting my JPanel also affects the JDialog meaning that if i remove the panel.repaint() (which i have done) it works fine! When i have the panel.repaint() the JDialog shows some strange lines like i have cut the graphics in 2 and try to move them together with no success. I dont know if they call it flicker but it may be that problem.
public testFrame(){
panel = new JPanel(){
protected void paintComponent(Graphics g){
//do the painting for the JPanel here
}
}
setContentPane(panel);
updateTime();
}
public void updateTime(){
SwingWorker worker = new SWingWorker(){
#Override
protected Object doInBackground() throws Exception {
while(stopTimer == false){
Thread.sleep(20);
if(isFocused() == true){
timer += 0.2f;
panel.repaint();
}
}
return null;
}
};
worker.execute();
}
And here is my AWTGLCanvas.paint method in my JDialog contentPane
#Override
public void paintGL() {
//do some painting with classic opengl
swapBuffers(); //awtglcanvas built-in method, meaning i cant control it
repaint(); // it actually runs paintGL() again
}
Thanks in advance!
I have a problem with java Xor method:
public class Okno extends JFrame {
public static void main(String[] args) {
Okno okno = new Okno();
}
Window()
{
this.setSize(300,300);
this.setDefaultCloseOperation(EXIT_ON_CLOSE);
JButton button= new JButton("Circle");
button.addActionListener(
new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
Graphics2D g = (Graphics2D)Window.this.getGraphics();
g.setXORMode(Color.red);
g.setStroke(new BasicStroke(10));
g.drawOval(100, 100, 100, 100);
}
});
this.add("South",button);
this.setVisible(true);
}
It paints circle after second click on button. On Graphic from Image it works fine...
If the code works the second time, odds are good you are calling the code incorrectly. For example, you may be requesting a paint callback and then improperly invalidating the screen area, which means that while the view has changed, the is no event to start the repainting routines.
On the second button click, the paint will then detect the first button click's action, which was to change what is drawn.
Swing painting has changed slightly over the years. You might be stuck with an old tutorial or text. Take a look at the latest online offerings to get a good idea of how it should be done.