Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- // ###################### App.java ######################
- package app;
- public class App {
- public static void main(String[] args) throws Exception {
- Window window = new Window();
- System.out.println(window);
- }
- }
- // ###################### Window.java ######################
- package app;
- import java.awt.BorderLayout;
- import java.awt.Container;
- import java.awt.GridLayout;
- import java.awt.event.ActionEvent;
- import java.awt.event.ActionListener;
- import javax.swing.JButton;
- import javax.swing.JFrame;
- import javax.swing.JPanel;
- import app.game.logic.Game;
- import app.game.loop.GameLoop;
- public class Window extends JFrame implements ActionListener {
- private static final long serialVersionUID = -6500357792634135845L;
- private GameLoop gameLoop = new GameLoop(new Game());
- private JButton startButton = new JButton("Start");
- private JButton quitButton = new JButton("Quit");
- public Window() {
- super("Game Title");
- setVisible(true);
- setSize(500, 500);
- setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
- JPanel p = new JPanel();
- p.setLayout(new GridLayout());
- p.add(startButton);
- p.add(quitButton);
- startButton.addActionListener(this);
- quitButton.addActionListener(this);
- Container cp = getContentPane();
- cp.setLayout(new BorderLayout());
- cp.add(gameLoop.gamePanel, BorderLayout.CENTER);
- cp.add(p, BorderLayout.SOUTH);
- }
- public void actionPerformed(ActionEvent e) {
- Object s = e.getSource();
- if (s == startButton) {
- gameLoop.toggleRunning();
- if (gameLoop.isRunning) {
- startButton.setText("Stop");
- gameLoop.runGameLoop();
- } else {
- startButton.setText("Start");
- }
- } else if (s == quitButton) {
- System.exit(0);
- }
- }
- }
- // ###################### GameLoop.java ######################
- package app.game.loop;
- // The game loop controls all the timing of the game. The
- // game refreshes at 30Hz and the frames refresh at 60Hz.
- public class GameLoop {
- // Holds the game's details (which links to its logic).
- final public app.game.loop.GamePanel gamePanel;
- // Running state of the game.
- public boolean isRunning = false;
- public GameLoop(app.game.loop.GamePanel game) {
- this.gamePanel = game;
- }
- // Turn the game loop on or off by toggling.
- public void toggleRunning() {
- isRunning = !isRunning;
- }
- // Starts a game loop on a new thread
- public void runGameLoop() {
- Thread loop = new Thread() {
- public void run() {
- gameLoop();
- }
- };
- loop.start();
- }
- // Main game loop
- private void gameLoop() {
- // Number of nanoseconds in a second (1 billion).
- final int NANOSECONDS_IN_SECOND = 1_000_000_000;
- // Allocated frame time, in nanoseconds (ns).
- final double TIME_BETWEEN_UPDATES = NANOSECONDS_IN_SECOND / 30.0;
- // Maximum number of times to update the game before a new render.
- final int MAX_UPDATES_BEFORE_RENDER = 5;
- // Time since last frame update.
- double lastUpdateTime = System.nanoTime();
- // Last time the frame was rendered.
- double lastRenderTime = System.nanoTime();
- // If the target FPS is reached, don't render again.
- final double TARGET_FPS = 60;
- final double TARGET_TIME_BETWEEN_RENDERS = NANOSECONDS_IN_SECOND / TARGET_FPS;
- // Last second the FPS was updated. Used to calculate the FPS.
- int lastSecondTime = (int) (lastUpdateTime / NANOSECONDS_IN_SECOND);
- while (isRunning) {
- double now = System.nanoTime();
- int updateCount = 0;
- // As long as it's been at least as long as the set frame duration
- // but still able to update again, try update the game again. This
- // can be used to catch up on game updates.
- while (now - lastUpdateTime > TIME_BETWEEN_UPDATES && updateCount < MAX_UPDATES_BEFORE_RENDER) {
- updateGame();
- lastUpdateTime += TIME_BETWEEN_UPDATES;
- updateCount++;
- }
- // If for some reason a game update takes too long, we don't want the
- // frames to catch up. This is used to skip a frame if needed.
- if ( now - lastUpdateTime > TIME_BETWEEN_UPDATES) {
- lastUpdateTime = now - TIME_BETWEEN_UPDATES;
- }
- // To make a smooth render, interpolation is used. This means that
- // the object appears to move at a more constant rate, as the
- // position is determined by how far into a frame the rendering is.
- float proportion = (float) ((now - lastUpdateTime) / TIME_BETWEEN_UPDATES);
- float interpolation = Math.min(1.0f, proportion);
- drawGame(interpolation);
- lastRenderTime = now;
- // Update the FPS we got with the frames counted over the second.
- int thisSecond = (int) (lastUpdateTime / NANOSECONDS_IN_SECOND);
- if (thisSecond > lastSecondTime) {
- gamePanel.fps = gamePanel.frameCount;
- gamePanel.frameCount = 0;
- lastSecondTime = thisSecond;
- }
- // Yield until it has been at least the target time between renders. This saves the CPU from hogging.
- while (now - lastRenderTime < TARGET_TIME_BETWEEN_RENDERS && now - lastUpdateTime < TIME_BETWEEN_UPDATES) {
- Thread.yield();
- // This stops the app from consuming all the CPU. It makes
- // this slightly less accurate, but it is worth it.
- try {Thread.sleep(1);} catch(Exception e) {}
- now = System.nanoTime();
- }
- }
- }
- // Handles game updates.
- private void updateGame() {
- gamePanel.update();
- }
- // Set the interpolation value, and then render the game graphics.
- private void drawGame(float interpolation) {
- gamePanel.interpolation = interpolation;
- gamePanel.repaint();
- }
- }
- // ###################### GamePanel.java ######################
- package app.game.loop;
- import java.awt.Color;
- import java.awt.Graphics;
- import java.awt.geom.Point2D;
- import javax.swing.JPanel;
- // Subclass to add properties and methods. This will hold the main game logic.
- public class GamePanel extends JPanel {
- private static final long serialVersionUID = 6503338914360911369L;
- // Whether the FPS is shown.
- public boolean isShowingFPS = true;
- // Value of interpolation, as a proportion from `0.0` to `1.0`.
- float interpolation;
- // Number of frames so far this second.
- int frameCount;
- // Current FPS the game is running at.
- int fps;
- // Calculates an interpolated point. This takes in the original point
- // and the new point to calculate the point in between.
- public Point2D.Double interpolatedPoint(Point2D.Double fromPoint, Point2D.Double toPoint) {
- double xp = fromPoint.x + (toPoint.x - fromPoint.x) * interpolation;
- double yp = fromPoint.y + (toPoint.y - fromPoint.y) * interpolation;
- return new Point2D.Double(xp, yp);
- }
- // Calculates an interpolated point. This takes in the original point
- // and the delta x and y to calculate the point in between.
- public Point2D.Double interpolatedPoint(Point2D.Double fromPoint, double dx, double dy) {
- return interpolatedPoint(fromPoint, new Point2D.Double(fromPoint.x + dx, fromPoint.y + dy));
- }
- // Creates a `Point2D` object.
- public Point2D.Double createPoint(double x, double y) {
- return new Point2D.Double(x, y);
- }
- // Override to make a custom update. This is called every frame.
- public void update() {}
- // Override to write the rendering code. This is called every frame.
- public void render(Graphics G) {}
- // This method should never be called by the subclass. Refer to using
- // `render()` to write rendering code.
- final protected void paintComponent(Graphics g) {
- g.setColor(getBackground());
- // Show FPS if wanted
- if (isShowingFPS) {
- g.setColor(Color.BLACK);
- g.drawString("FPS: " + fps, 5, 10);
- }
- // Render game graphics according to subclass's implementation.
- render(g);
- // Increment the frame count, as `paintComponent()` is called once per frame.
- frameCount++;
- }
- }
- // ###################### Game.java ######################
- package app.game.logic;
- import java.awt.Color;
- import java.awt.Graphics;
- import java.awt.geom.Point2D;
- import app.game.loop.GamePanel;
- // Subclass to add properties and methods. This will hold the main game logic.
- public class Game extends GamePanel {
- private static final long serialVersionUID = -3768316870032287321L;
- float interpolation;
- float ballX, ballY, lastBallX, lastBallY;
- int ballWidth, ballHeight;
- float ballXVel, ballYVel;
- float ballSpeed;
- int lastDrawX, lastDrawY;
- public Game() {
- ballX = lastBallX = 100;
- ballY = lastBallY = 100;
- ballWidth = 25;
- ballHeight = 25;
- ballSpeed = 25;
- ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
- ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
- }
- @Override
- public void update() {
- super.update();
- lastBallX = ballX;
- lastBallY = ballY;
- ballX += ballXVel;
- ballY += ballYVel;
- if (ballX + ballWidth/2 >= getWidth()) {
- ballXVel *= -1;
- ballX = getWidth() - ballWidth/2;
- ballYVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
- } else if (ballX - ballWidth/2 <= 0) {
- ballXVel *= -1;
- ballX = ballWidth/2;
- }
- if (ballY + ballHeight/2 >= getHeight()) {
- ballYVel *= -1;
- ballY = getHeight() - ballHeight/2;
- ballXVel = (float) Math.random() * ballSpeed*2 - ballSpeed;
- } else if (ballY - ballHeight/2 <= 0) {
- ballYVel *= -1;
- ballY = ballHeight/2;
- }
- }
- @Override
- public void render(Graphics g) {
- super.render(g);
- g.setColor(Color.RED);
- Point2D.Double lastPoint = createPoint(lastBallX, lastBallY);
- Point2D.Double newPoint = createPoint(ballX, ballY);
- Point2D.Double interpolatedPoint = interpolatedPoint(lastPoint, newPoint);
- int drawX = (int) (interpolatedPoint.x - ballWidth/2);
- int drawY = (int) (interpolatedPoint.y - ballHeight/2);
- g.fillOval(drawX, drawY, ballWidth, ballHeight);
- lastDrawX = drawX;
- lastDrawY = drawY;
- }
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement