This is the Driver from a Mario clone I made, which uses FSEM. It has everything you need.
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
import java.awt.image.BufferStrategy;
public class Driver extends JFrame
{
//For full screen mode:
private GraphicsDevice device;
private BufferStrategy bufferStrategy;
//Everything else
private Timer timer = new Timer(UniversalConstants.GAME_SPEED, null);
private JPanel panel;
private TitlePanels titlePanels;
private GridBagLayout gbl;
private GridBagConstraints gbc;
private ImageViewer imageViewer;
private SoundPlayer soundPlayer;
public static void main(String[] args)
{
Driver tpo = new Driver();
tpo.run();
}
private void run()
{
setResizable(false);
setIgnoreRepaint(true);
setUndecorated(true);
Dimension dim = Toolkit.getDefaultToolkit().getScreenSize();
setSize(dim);
soundPlayer = new SoundPlayer();
imageViewer = new ImageViewer();
titlePanels = new TitlePanels(this);
makePanel();
//Was used for FULL SCREEN MODE
device = GraphicsEnvironment.getLocalGraphicsEnvironment().getDefaultScreenDevice();
DisplayMode displayModes[] = device.getDisplayModes();
DisplayMode originalDisplayMode = device.getDisplayMode();
try
{
if (device.isFullScreenSupported())
device.setFullScreenWindow(this);
int biggestMode = 0;
for (int i = 0; i < displayModes.length; i++)
if (displayModes[i].getWidth() + displayModes[i].getHeight() >= displayModes[biggestMode].getWidth() + displayModes[biggestMode].getHeight())
biggestMode = i;
DisplayMode displayMode = displayModes[biggestMode];
if (device.isDisplayChangeSupported())
device.setDisplayMode(displayMode);
} catch (Exception e) { }
//Was used for FULL SCREEN MODE
panel.setDoubleBuffered(true);
setBufferStrategy();
addAnimator();
addKeyListener(new MarioKeyListener());
setVisible(true);
start();
}
private void makePanel()
{
gbl = new GridBagLayout();
gbc = new GridBagConstraints();
getContentPane().setLayout(gbl);
gbc.weightx = 10.0;
gbc.weighty = 10.0;
gbc.fill = GridBagConstraints.BOTH;
panel = (JPanel) titlePanels.currentPanel();
gbc.gridx = 1;
gbc.gridy = 1;
gbc.gridwidth = 1;
gbc.gridheight = 1;
getContentPane().add(panel);
gbl.setConstraints(panel, gbc);
}
public void changePanel()
{
panel.setVisible(false);
getContentPane().remove(panel);
makePanel();
}
//Was used for FULL SCREEN MODE
private void setBufferStrategy()
{
createBufferStrategy(2);
bufferStrategy = getBufferStrategy();
}
//Was used for FULL SCREEN MODE
private void addAnimator()
{
new Thread(new Animator()).start();
//timer.addActionListener(new Animator());
}
private class MyWindowAdapter extends WindowAdapter
{
public void windowClosing (WindowEvent e)
{
System.exit(0);
}
}
public Timer timer()
{
return timer;
}
public void start()
{
timer.start();
}
public void stop()
{
timer.stop ();
}
//Used for FULL SCREEN MODE
private class Animator implements Runnable, ActionListener
{
public void run()
{
while(true)
{
titlePanels.screenUpdate(bufferStrategy);
try {Thread.sleep(15);} catch(Exception e) {}
}
}
public void actionPerformed(ActionEvent a)
{
titlePanels.screenUpdate(bufferStrategy);
}
}
private class MarioKeyListener extends KeyAdapter
{
public void keyPressed(KeyEvent k)
{
titlePanels.currentPanel().keyPressed(k);
}
public void keyReleased(KeyEvent k)
{
titlePanels.currentPanel().keyReleased(k);
}
public void keyTyped(KeyEvent k)
{
titlePanels.currentPanel().keyTyped(k);
}
}
}
The array of titlePanels are basically a group of JPanels that represent different game states, like the title menu, cheat menu, keyboard menu, and network menu. When you actually start the game, it links you up with each panels draw method as needed.
public void screenUpdate(BufferStrategy bufferStrategy)
{
Graphics g = bufferStrategy.getDrawGraphics();
currentPanel.draw(g); // Method to draw to
bufferStrategy.show();
g.dispose();
}
The actual draw method, as you can see, is exactly like in non-FSEM. Basically you just need to worry about getting the graphics from your current buffer strategy.
public void draw (Graphics g)
{
super.paintComponent(g);
if (gameState == START_GAME)
{
g.setColor(Color.BLACK);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.BOLD, 50));
mario.move(getWidth()/2 - mario.width()*2, getHeight()/2 - mario.height());
mario.draw(g);
g.drawString("WORLD " + level.toString(), mario.getX() - 50, mario.getY() - 100);
g.drawString("x " + mario.lives(), mario.getX() + mario.width() + 25, mario.getY() + mario.height());
}
else if (gameState == GAME_OVER)
{
g.setColor(Color.BLACK);
g.fillRect(0,0,getWidth(),getHeight());
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.BOLD, 50));
g.drawString("GAME OVER", getWidth()/2 - 150, getHeight()/2);
}
else
{
if (!inPipeLevel)
level.draw(g);
else
pipeLevel.draw(g);
}
drawScore(g);
}
private void drawScore(Graphics g)
{
g.setColor(Color.WHITE);
g.setFont(new Font("Courier New", Font.BOLD, 22));
g.drawString("MARIO", 10 + getWidth()/4 * 0, 22);
g.drawString("COINS", 10 + getWidth()/4 * 1, 22);
g.drawString("WORLD", 10 + getWidth()/4 * 2, 22);
g.drawString("TIME" , 10 + getWidth()/4 * 3, 22);
g.drawString(" " + mario.score(), 10 + getWidth()/4 * 0, 40);
g.drawString(" " + mario.coins(), 10 + getWidth()/4 * 1, 40);
g.drawString(" " + level.toString(), 10 + getWidth()/4 * 2, 40);
g.drawString(" " + level.timeLeft(), 10 + getWidth()/4 * 3, 40);
}
}
As you can see, I address a lot of the things you did wrong, like not creating a new display mode (looking for one instead), double buffering images, extending JFrame instead of having one inside of a Window. This code may not be commented very well – ask me any questions you may have and I’ll answer them asap.