The player score increments by 9 instead of 1 - java

So in this game i'm testing out, whenever the ball hits the paddle, I want the score to increase by 1. But instead it increases by 9 instead. How can I fix this? Here is the code. The 2nd method which is the update() method contains the score increase.
public class PlayState extends State {
private Catcher catcher;
private Ball ball;
boolean visible ;
private int playerScore = 0;
private Font scoreFont;
#Override
public void init() {
catcher = new Catcher(400,425,80,25);
ball= new Ball(200,0,40,40);
}
#Override
public void update() {
visible=true;
catcher.update();
ball.update();
if(ballCollides(catcher)){
playerScore++;
visible = false;
}
}
#Override
public void render(Graphics g) {
//Draw Background
g.setColor(Color.blue);
g.fillRect(0, 0, GameMain.GAME_WIDTH, GameMain.GAME_HEIGHT);
//Draw Ball(s)
if(visible == true){
g.setColor(Color.green);
g.fillOval(ball.getX(),ball.getY(),ball.getWidth(),ball.getHeight());
}
// Draw catcher
g.setColor(Color.white);
g.fillRect(catcher.getX(), catcher.getY(), catcher.getWidth(),
catcher.getHeight());
// Draw Score
g.setFont(scoreFont); // Sets scoreFont as current font
g.setColor(Color.white);
g.drawString("" + playerScore, 350, 40); // Draws String using current font
}
#Override
public void onClick(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void onKeyPress(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_RIGHT){
catcher.moveRight();
}
if (e.getKeyCode()==KeyEvent.VK_LEFT){
catcher.moveLeft();
}
}
private boolean ballCollides(Catcher c) {
return ball.getRect().intersects(c.getRect());
}
#Override
public void onKeyRelease(KeyEvent e) {
// TODO Auto-generated method stub
if (e.getKeyCode() == KeyEvent.VK_LEFT
|| e.getKeyCode() == KeyEvent.VK_RIGHT) {
catcher.stop();
}
}
}
2nd blck of code where the update method is being called
package com.jamescho.game.main;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Image;
import javax.swing.JPanel;
import com.jamescho.framework.util.InputHandler;
import com.jamescho.game.state.LoadState;
import com.jamescho.game.state.State;
#SuppressWarnings("serial")
public class Game extends JPanel implements Runnable {
private int gameWidth;
private int gameHeight;
private Image gameImage;
private Thread gameThread;
private volatile boolean running;
private volatile State currentState;
private InputHandler inputHandler;
public Game(int gameWidth, int gameHeight) {
this.gameWidth = gameWidth;
this.gameHeight = gameHeight;
setPreferredSize(new Dimension(gameWidth, gameHeight));
setBackground(Color.BLACK);
setFocusable(true);
requestFocus();
}
public void setCurrentState(State newState) {
System.gc();
newState.init();
currentState = newState;
inputHandler.setCurrentState(currentState);
}
#Override
public void addNotify() {
super.addNotify();
initInput();
setCurrentState(new LoadState());
initGame();
}
private void initGame() {
running = true;
gameThread = new Thread(this, "Game Thread");
gameThread.start();
}
#Override
public void run() {
// These variables should sum up to 17 on every iteration
long updateDurationMillis = 0; // Measures both update AND render
long sleepDurationMillis = 0; // Measures sleep
while (running) {
long beforeUpdateRender = System.nanoTime();
long deltaMillis = updateDurationMillis + sleepDurationMillis;
updateAndRender(deltaMillis);
updateDurationMillis = (System.nanoTime() - beforeUpdateRender) / 1000000L;
sleepDurationMillis = Math.max(2, 17 - updateDurationMillis);
try {
Thread.sleep(sleepDurationMillis);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// End game immediately when running becomes false.
System.exit(0);
}
private void updateAndRender(long deltaMillis) {
currentState.update(deltaMillis / 1000f);
prepareGameImage();
currentState.render(gameImage.getGraphics());
renderGameImage(getGraphics());
}
private void prepareGameImage() {
if (gameImage == null) {
gameImage = createImage(gameWidth, gameHeight);
}
Graphics g = gameImage.getGraphics();
g.clearRect(0, 0, gameWidth, gameHeight);
}
public void exit() {
running = false;
}
private void renderGameImage(Graphics g) {
if (gameImage != null) {
g.drawImage(gameImage, 0, 0, null);
}
g.dispose();
}
private void initInput() {
inputHandler = new InputHandler();
addKeyListener(inputHandler);
addMouseListener(inputHandler);
}
}

It appears as if ballCollides may return true across multiple updates for a single collision. I am not sure of this without looking at the code for the entire program, but based on what has been posted this would seem to be the most likely case.
One fix would be to keep track of the previous collision state for each catcher and ball combination (if there can be more than a single ball and catcher), and only update the score when the current state is colliding and the previous state was non-colliding.
E.g.
// Example using a single previousCollides state variable...
// Not suitable for a multi catcher / multi-ball game.
// The modifications break the contract this methods name seems to
// imply so the previousCollides logic should probably exist elsewhere
// and this code is only to illustrate the idea.
private boolean ballCollides(Catcher c) {
boolean collides = ball.getRect().intersects(c.getRect());
boolean result = collides && !previousCollides;
previousCollides = collides;
return result;
}
The above is just an example, where to actually keep track of the previous collision data is up to you. Maybe it should be kept within the catcher object itself as that would seem to make the most sense.

Related

Java - Create a button from a shape

I am learning Java currently and have been given the assignmnet of finidhing off a program to create the game Conways's life (we started with some code provided to us and must add features etc to this).
I am currently stuck on a menu option for the game. I want it to start off at the menu screen, wherein buttons appear at the top for "Start", "Random", "Load", Save". I have written code so that the program displays these buttons, through a fillRect option in my paint method.
My question is, how do I use the mousePressed method to recognise the cells selected so that I can get an action to occur when they are selected. I been looking at this for a while but can't seem to get this working.
Any suggestion would be a massive help. I have shared my code below. It's a work in progress but I would really like to get this working before continuing on with the other functionality.
import java.awt.*;
import java.awt.event.*;
import javax.swing.*;
import java.awt.image.*;
public class ConwaysLife extends JFrame implements Runnable, MouseListener {
// member data
private BufferStrategy strategy;
private Graphics offscreenBuffer;
private boolean gameState[][] = new boolean[40][40];
private boolean isGameInProgress = false;
// constructor
public ConwaysLife () {
//Display the window, centred on the screen
Dimension screensize = java.awt.Toolkit.getDefaultToolkit().getScreenSize();
int x = screensize.width/2 - 400;
int y = screensize.height/2 - 400;
setBounds(x, y, 800, 800);
setVisible(true);
this.setTitle("Conway's game of life");
// initialise double-buffering
createBufferStrategy(2);
strategy = getBufferStrategy();
offscreenBuffer = strategy.getDrawGraphics();
// register the Jframe itself to receive mouse events
addMouseListener(this);
// initialise the game state
for (x=0;x<40;x++) {
for (y=0;y<40;y++) {
gameState[x][y]=false;
}
}
// create and start our animation thread
Thread t = new Thread(this);
t.start();
}
// thread's entry point
public void run() {
while ( 1==1 ) {
// 1: sleep for 1/5 sec
try {
Thread.sleep(200);
} catch (InterruptedException e) { }
// 2: animate game objects [nothing yet!]
/*if (isGameInProgress == false) {
this.repaint();
}*/
// 3: force an application repaint
this.repaint();
}
}
// mouse events which must be implemented for MouseListener
public void mousePressed(MouseEvent e) {
while (!isGameInProgress) {
int x = e.getX()/20;
int y = e.getY()/20;
if(x >= 10 && x <= 80 && y >= 40 && y <= 65) {
isGameInProgress = !isGameInProgress;
this.repaint();
}
}
// determine which cell of the gameState array was clicked on
int x = e.getX()/20;
int y = e.getY()/20;
// toggle the state of the cell
gameState[x][y] = !gameState[x][y];
// request an extra repaint, to get immediate visual feedback
this.repaint();
}
public void mouseReleased(MouseEvent e) { }
public void mouseEntered(MouseEvent e) { }
public void mouseExited(MouseEvent e) { }
public void mouseClicked(MouseEvent e) { }
//
// application's paint method
public void paint(Graphics g) {
Font font = new Font("Veranda", Font.BOLD, 20);
g = offscreenBuffer; // draw to off screen buffer
// clear the canvas with a big black rectangle
g.setColor(Color.BLACK);
g.fillRect(0, 0, 800, 800);
/*look to add a while game in progress loop here!!!*/
// draw menu options
if(!isGameInProgress) {
g.setColor(Color.green);
g.fillRect(10, 40, 70, 25);
g.fillRect(100, 40, 100, 25);
g.fillRect(300, 40, 170, 25);
g.setColor(Color.BLACK);
g.setFont(font);
g.drawString("Start", 15, 60);
g.drawString("Random", 105, 60);
g.drawString("Load", 305, 60);
g.drawString("Save", 395, 60);
}
// redraw all game objects
g.setColor(Color.WHITE);
for (int x=0;x<40;x++) {
for (int y=0;y<40;y++) {
if (gameState[x][y]) {
g.fillRect(x*20, y*20, 20, 20);
}
}
}
// flip the buffers
strategy.show();
}
// application entry point
public static void main(String[] args) {
ConwaysLife w = new ConwaysLife();
}
}
You're not going to like the answer, but it's the "correct" way to approach the problem.
What you need to understand is, Swing/AWT is using a "passive" rendering workflow and BufferStrategy is using a "active" rendering workflow, these are incompatible with each other.
As a general rule, you should not be overriding paint of top level containers like JFrame, this is going to end in no end of issues. Instead, you should be starting with something like JPanel and overriding it's paintComponent method instead.
Having said that, there's a "simpler" solution available to you. CardLayout. This will allow you to seperate the workflows of the menu from the game and resolve the issue between Swing/AWT and BufferStrategy
For example...
import java.awt.Canvas;
import java.awt.CardLayout;
import java.awt.Color;
import java.awt.Component;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics;
import java.awt.GridBagConstraints;
import java.awt.GridBagLayout;
import java.awt.Insets;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame frame = new JFrame();
frame.add(new MainPane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class MainPane extends JPanel {
enum View {
MENU, GAME;
}
private CardLayout cardLayout = new CardLayout();
private GamePane gamePane;
public MainPane() {
setLayout(cardLayout);
gamePane = new GamePane();
add(new MenuPane(new MenuPane.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
}), View.MENU);
add(gamePane, View.GAME);
}
protected void add(Component compent, View view) {
add(compent, view.name());
}
protected void showGame() {
show(View.GAME);
gamePane.start();
}
protected void showMenu() {
gamePane.stop();
show(View.MENU);
}
protected void show(View view) {
cardLayout.show(this, view.name());
}
}
public class MenuPane extends JPanel {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
public MenuPane(Observer observer) {
this.observer = observer;
JButton startButton = new JButton("Start");
JButton randomButton = new JButton("Random");
JButton loadButton = new JButton("Load");
JButton saveButton = new JButton("Save");
setLayout(new GridBagLayout());
GridBagConstraints gbc = new GridBagConstraints();
gbc.ipadx = 10;
gbc.ipady = 10;
gbc.insets = new Insets(8, 8, 8, 8);
gbc.weightx = GridBagConstraints.REMAINDER;
add(startButton, gbc);
add(randomButton, gbc);
add(loadButton, gbc);
add(saveButton, gbc);
startButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.startNewGame();
}
});
randomButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.randomGame();
}
});
loadButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.loadGame();
}
});
saveButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent e) {
observer.saveGame();
}
});
}
}
public class GamePane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
public GamePane() {
setBackground(Color.BLACK);
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics graphics = strategy.getDrawGraphics();
FontMetrics fm = graphics.getFontMetrics();
String text = "All your game are belong to us";
int x = (getWidth() - fm.stringWidth(text)) / 2;
int y = (getHeight() - fm.getHeight()) / 2;
graphics.setColor(Color.WHITE);
graphics.drawString(text, x, y + fm.getAscent());
// Render to graphics
// ...
// Dispose the graphics
graphics.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
}
I would strongly recommend that you take the time to read through:
Creating a GUI With Swing
A Visual Guide to Layout Managers
Performing Custom Painting
Painting in AWT and Swing
BufferStrategy and BufferCapabilities
JavaDocs for BufferStrategy which demonstrate how the API should be used.
A "fully" BufferStrategy based approach...
Now, if you can't use Swing, "for reasons", you can still achieve a simular concept using "delegation".
Basically this means "delegating" responsibility for performing some workflow to another. In this case, we want to delegate the rendering and the handling of the mouse events.
This allows you to have a dedicated workflow for the menu and a dedicated workflow for the game, without having to try and mix a lot of state.
Why do I keep on insisting on separating these two workflows? Simply, because it makes it MUCH easier to manage and reason about, but also because it supports the Single Responsibility Principle.
The follow example makes use of Renderable interface to define the core functionality that end "render" delegate will need to implement, in this case, it's pretty simple, we want to tell the renderer to "render" it's content on each paint pass and we want to (optionally) delegate mouse clicked events (this could be done via a second interface or even just the MouseListener interface directly, but I've made it a requirement of the Renderable interface for demonstration purposes.
The "basic" solution to your actual question is found through the use of Rectangle#contains(Point).
This basically inspects each "button" Rectangle to determine if the supplied MouseEvent occurs within it's bounds, if it does, we take action.
It is, however, a little more complicated then that, as we need to have the Rectangles built ahead of time, not difficult, but it's state which is actually reliant on the parent, as we need to know the area in which the renderer is been displayed, run the example, you'll see what I mean 😉
import java.awt.Canvas;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.FontMetrics;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.event.MouseAdapter;
import java.awt.event.MouseEvent;
import java.awt.geom.RoundRectangle2D;
import java.awt.image.BufferStrategy;
import javax.swing.JFrame;
public final class Main {
public static void main(String[] args) {
new Main();
}
public Main() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
MainPane mainPane = new MainPane();
JFrame frame = new JFrame();
frame.add(mainPane);
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
mainPane.start();
}
});
}
public interface Renderable {
public void render(Graphics2D g2d, Dimension size);
// We could just extend from MouseListener
// but I don't need all those event handlers
public void mouseClicked(MouseEvent e);
}
public class MainPane extends Canvas {
private Thread thread;
private volatile boolean isRunning = false;
private Renderable currentRenderer;
private MenuRenderer menuRenderer;
private GameRenderer gameRenderer;
public MainPane() {
setBackground(Color.BLACK);
gameRenderer = new GameRenderer();
menuRenderer = new MenuRenderer(new MenuRenderer.Observer() {
#Override
public void startNewGame() {
showGame();
}
#Override
public void randomGame() {
}
#Override
public void loadGame() {
}
#Override
public void saveGame() {
}
});
showMenu();
addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(MouseEvent e) {
if (currentRenderer == null) {
return;
}
currentRenderer.mouseClicked(e);
}
});
}
protected void showMenu() {
// This may need to tell the game renderer to stop
// or pause
currentRenderer = menuRenderer;
}
protected void showGame() {
currentRenderer = gameRenderer;
}
#Override
public Dimension getPreferredSize() {
return new Dimension(800, 800);
}
protected void start() {
if (isRunning) {
return;
}
createBufferStrategy(3);
isRunning = true;
thread = new Thread(new Runnable() {
#Override
public void run() {
mainLoop();
}
});
thread.start();
}
protected void stop() {
if (!isRunning || thread == null) {
return;
}
isRunning = false;
try {
thread.join();
} catch (InterruptedException ex) {
}
thread = null;
}
protected void mainLoop() {
try {
while (isRunning) {
render();
Thread.sleep(16);
}
} catch (InterruptedException ex) {
}
}
protected void render() {
BufferStrategy strategy = getBufferStrategy();
if (strategy == null && currentRenderer != null) {
return;
}
// Render single frame
do {
// The following loop ensures that the contents of the drawing buffer
// are consistent in case the underlying surface was recreated
do {
// Get a new graphics context every time through the loop
// to make sure the strategy is validated
Graphics2D g2d = (Graphics2D) strategy.getDrawGraphics();
g2d.setBackground(Color.BLACK);
g2d.fillRect(0, 0, getWidth(), getHeight());
RenderingHints hints = new RenderingHints(
RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON
);
g2d.setRenderingHints(hints);
// Render to graphics
currentRenderer.render(g2d, getSize());
// Dispose the graphics
g2d.dispose();
// Repeat the rendering if the drawing buffer contents
// were restored
} while (strategy.contentsRestored());
// Display the buffer
strategy.show();
// Repeat the rendering if the drawing buffer was lost
} while (strategy.contentsLost());
}
}
public class GameRenderer implements Renderable {
#Override
public void render(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
String text = "All your game are belong to us";
int x = (size.width - fm.stringWidth(text)) / 2;
int y = (size.height - fm.getHeight()) / 2;
g2d.setColor(Color.WHITE);
g2d.drawString(text, x, y + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
}
}
public class MenuRenderer implements Renderable {
public interface Observer {
public void startNewGame();
public void randomGame();
public void loadGame();
public void saveGame();
}
private Observer observer;
private String[] menuOptions = new String[]{
"New Game",
"Random",
"Load Game",
"Save Game"
};
private Rectangle[] menuBounds;
private int internalPadding = 20;
private int horizontalGap = 16;
public MenuRenderer(Observer observer) {
this.observer = observer;
}
#Override
public void render(Graphics2D g2d, Dimension size) {
if (menuBounds == null) {
createMenus(g2d, size);
}
renderMenus(g2d);
}
protected void createMenus(Graphics2D g2d, Dimension size) {
FontMetrics fm = g2d.getFontMetrics();
int totalHeight = (((fm.getHeight() + internalPadding) + horizontalGap) * menuOptions.length) - horizontalGap;
int buttonHeight = fm.getHeight() + internalPadding;
menuBounds = new Rectangle[menuOptions.length];
int buttonWidth = 0;
for (int index = 0; index < menuOptions.length; index++) {
int width = fm.stringWidth(menuOptions[index]) + internalPadding;
buttonWidth = Math.max(width, buttonWidth);
}
int yPos = (size.height - totalHeight) / 2;
for (int index = 0; index < menuOptions.length; index++) {
int xPos = (size.width - buttonWidth) / 2;
Rectangle menuRectangle = new Rectangle(xPos, yPos, buttonWidth, buttonHeight);
menuBounds[index] = menuRectangle;
yPos += buttonHeight + (horizontalGap / 2);
}
}
protected void renderMenus(Graphics2D g2d) {
for (int index = 0; index < menuOptions.length; index++) {
String text = menuOptions[index];
Rectangle bounds = menuBounds[index];
renderMenu(g2d, text, bounds);
}
}
protected void renderMenu(Graphics2D g2d, String text, Rectangle bounds) {
FontMetrics fm = g2d.getFontMetrics();
int textWidth = fm.stringWidth(text);
int textXPos = (bounds.x + (internalPadding / 2)) + ((bounds.width - internalPadding - textWidth) / 2);
int textYPos = bounds.y + (internalPadding / 2);
RoundRectangle2D buttonBackground = new RoundRectangle2D.Double(bounds.x, bounds.y, bounds.width, bounds.height, 20, 20);
g2d.setColor(Color.BLUE.darker());
g2d.fill(buttonBackground);
g2d.setColor(Color.WHITE);
g2d.drawString(text, textXPos, textYPos + fm.getAscent());
}
#Override
public void mouseClicked(MouseEvent e) {
if (menuBounds == null) {
return;
}
for (int index = 0; index < menuOptions.length; index++) {
if (menuBounds[index].contains(e.getPoint())) {
switch (index) {
case 0:
observer.startNewGame();
break;
case 2:
observer.randomGame();
break;
case 3:
observer.loadGame();
break;
case 4:
observer.saveGame();
break;
}
}
}
}
}
}

My avatar for Mario won't jump as I intend

I am programming a Mario game for my project for my computer science class, and so far the Mario character can run left and right, crouch, and I have built the background.
Two questions that I have are
when making the background move, should I make a separate class that has all the background images? This way I can switch out the various backgrounds I intend to create.
My current program has the issue of when I try and jump (press the up arrow key), Mario does not jump normally, he lags a bit and then rises a lot. I am trying to make it so Mario can rise lets say, one pixel every 100th of a second, but that is not working, instead, it waits around 1 or 2 seconds then rises 100 pixels and stops.
Frame Class:
package FirstPlatformer;
import javax.swing.JFrame;
public class PlatformerFrame
{
public static void main(String[] args)
{
//change to match your values for width/height
//these can be changed
int w = 1525;
int h = 830;
//sets up a JFrame object with title "Template"
JFrame frame = new JFrame("Template");
//make sure the jframe closes when you hit the 'x'
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//adds the drawing panel to the frame
frame.getContentPane().add(new Platformer(w,h));
//resizes the frame to fit the panel
frame.pack();
//makes it visible
frame.setVisible(true);
}
}
Platformer Class:
package FirstPlatformer;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseListener;
import java.awt.event.MouseMotionListener;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import javax.swing.ImageIcon;
import javax.swing.JPanel;
//change to be your packagename
//all imports are necessary
//must 'extend' JPanel
public class Platformer extends JPanel
{
Random test = new Random();
Color aboveground = new Color (99, 158, 169);
private ImageIcon marioStart, ground, mysteryBlock, smallMarioRight, smallMarioLeft, smallMarioCrouchedLeft, smallMarioCrouchedRight;
//variables for the overall width and height
private int w, h;
boolean start = false;
//crouching mechanism
boolean crouchPressed = false;
boolean crouchReleased = false;
boolean youAreCrouching = false;
//facing direction mechanisms
boolean facingRight = true;
boolean facingLeft = false;
//jumping mechanisms
boolean jumpStart = false;
int jumpCountEqual = 0;
private int number = 0;
private int count = 0;
int baseHeight = 770;
int smallMarioGround = 725;
//variable that establishes marios x coord
int marioX = 0;
//sets up the initial panel for drawing with proper size
public Platformer(int w, int h)
{
setFocusable(true);
this.w = w;
this.h = h;
this.setPreferredSize(new Dimension(w,h));
//creating the images
marioStart = new ImageIcon("src/FirstPlatformer/marioStart.JPG");
//building blocks
ground = new ImageIcon("src/FirstPlatformer/ground.JPG");
mysteryBlock = new ImageIcon("src/FirstPlatformer/mysteryBlock.png");
//small mario actions
smallMarioLeft = new ImageIcon("src/FirstPlatformer/smallMarioRight.png");
smallMarioRight = new ImageIcon("src/FirstPlatformer/smallMarioLeft.png");
smallMarioCrouchedLeft = new ImageIcon("src/FirstPlatformer/marioCrouched.png");
smallMarioCrouchedRight= new ImageIcon("src/FirstPlatformer/marioCrouchedRight.png");
this.addMouseListener(new MouseTracker());
this.addKeyListener(new Keyboard());
}
//all graphical components go here
//this.setBackground(Color c) for example will change background color
public void paintComponent(Graphics g)
{
//this line sets up the graphics - always needed
super.paintComponent(g);
g.setColor(Color.RED);
//all drawings below here:
//creating the starting screen
if(start != true) {
setBackground(Color.BLACK);
marioStart.paintIcon(this,g,240,100);
g.setFont(new Font("Arial", Font.BOLD, 50));
g.drawString("Created by Zane Lanski", 515, 625);
g.drawString("Press Space to Start", 525, 725);
}
else {
//setting the overall background for when mario is aboveground
setBackground(aboveground);
//variable for building the bottom of the level
int groundCount = 0;
//for loop creates the base of the level
for(int groundBase = 0; groundBase <30; groundBase++)
{
ground.paintIcon(this, g, groundCount, 770);
groundCount = groundCount + 52;
}
mysteryBlock.paintIcon(this, g, 500, 500);
if(crouchPressed != true && facingRight == true) {
smallMarioRight.paintIcon(this,g,marioX,smallMarioGround);
}
if(crouchPressed != true && facingLeft == true) {
smallMarioLeft.paintIcon(this,g,marioX,smallMarioGround);
}
if(crouchPressed != false) {
smallMarioCrouchedRight.paintIcon(this,g, marioX, smallMarioGround+18);
}
if(jumpStart == true) {
for(int jumpCount = jumpCountEqual;jumpCount < 100; jumpCount++) {
smallMarioGround = smallMarioGround- 1;
// smallMarioRight.paintIcon(this, g, marioX, smallMarioGround);
try
{
Thread.sleep(10);
}
catch(InterruptedException e)
{
System.out.println(e);
}
repaint();
System.out.println(jumpCount);
jumpCountEqual++;
}
}
}
}
private class MouseTracker implements MouseListener, MouseMotionListener
{
#Override
public void mouseDragged(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseMoved(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseClicked(MouseEvent e) {
}
#Override
public void mousePressed(MouseEvent e) {
// TODO Auto-generated method stub
number++;
count++;
number %= 5;
repaint();
}
#Override
public void mouseReleased(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseEntered(MouseEvent e) {
// TODO Auto-generated method stub
}
#Override
public void mouseExited(MouseEvent e) {
// TODO Auto-generated method stub
}
}
private class Keyboard implements KeyListener
{
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
int key = e.getKeyCode();
repaint();
}
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
int key = e.getKeyCode();
if(key == KeyEvent.VK_RIGHT) {
facingLeft = false;
facingRight = true;
marioX = marioX + 5;
facingRight = true;
facingLeft = false;
}
if(key == KeyEvent.VK_LEFT) {
crouchPressed = false;
marioX = marioX - 5;
facingLeft = true;
facingRight = false;
}
if(key == KeyEvent.VK_DOWN) {
crouchPressed = true;
youAreCrouching = true;
}
if(key == KeyEvent.VK_UP) {
jumpStart = true;
}
if(key == KeyEvent.VK_SPACE) {
start = true;
}
repaint();
}
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
int key = e.getKeyCode();
if(key == KeyEvent.VK_DOWN) {
crouchPressed = false;
}
repaint();
}
}
}

Trouble with drawing a moving paddle for pong flawlessly using Netbeans

I am trying to make Pong. I sent this program to a friend and when he ran it the first time he got my bug but later it ran fine. I always seem to get the bug. My paddle can move up and down. It seems to move correctly and it gets drawn in the right place but it always flickers to the start position and back again. It looks like it gets drawn once at the start position and then at the right positions, flickering between the 2 positions forever.
NetBeans complains about this.addKeyListener(this);. It says "Leaking this in constructor". Here is my code: (sorry in advance, first time posting)
I exported it from netbeans if anyone wants to take a look:
http://s000.tinyupload.com/?file_id=00856291054786890080
public class LeikGluggi extends javax.swing.JPanel implements ActionListener, KeyListener {
Spilari1 spilari1 = new Spilari1();//make paddle
Bolti bolti = new Bolti(); //make ball
public LeikGluggi() {
initComponents();
setSize(Leikbord.GLUGGI_BREIDD,Leikbord.GLUGGI_HAED);
this.addKeyListener(this);
this.setFocusable(true);
int i = 0;
Timer tim = new Timer(50, this);
tim.start();
}
private void uppfaera()//update
{
spilari1.uppfaera();//paddle update
bolti.uppfaera(); // ball update
}
#Override
public void paintComponent(Graphics g)
{
super.paintComponent(g);//
g.setColor(Color.WHITE);
g.fillRect(0,0,Leikbord.GLUGGI_BREIDD,Leikbord.GLUGGI_HAED); //background
g.setColor(Color.BLUE);
spilari1.paint(g); //paddle drawn
bolti.paint(g); //ball drawn
}
#Override
public void actionPerformed(ActionEvent e)
{
uppfaera();//update called
repaint();
}
#Override
public void keyPressed(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_UP)
{
spilari1.setyHradi(-4);
}
else if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
spilari1.setyHradi(4);
}
}
#Override
public void keyReleased(KeyEvent e)
{
if (e.getKeyCode() == KeyEvent.VK_UP)
{
spilari1.setyHradi(0);
}
if (e.getKeyCode() == KeyEvent.VK_DOWN)
{
spilari1.setyHradi(0);
}
}
public void keyTyped(KeyEvent e)
{
}
And the code for my paddle
package is.hi.vidmotsforritun.pong2.teikning;
import java.awt.Color;
import java.awt.Graphics;
public class Spilari1 {
private final int breidd = 10 ;
private final int lengd = 75;
private int y = (Leikbord.GLUGGI_HAED/2)-lengd/2;
private int yHradi = 0;
public Spilari1()
{
}
public void uppfaera()
{
y = y + yHradi;
System.out.println("HRAÐI ER "+yHradi);
}
public void paint(Graphics g)
{
g.setColor(Color.BLUE);
g.fillRect(25, y, breidd, lengd);
System.out.println("Y er "+y);
}
public void setyHradi(int hradi)
{
yHradi = hradi;
}
}
According to this Thread ("http://forums.netbeans.org/post-134877.html") the problem is that you pass a reference of this to an outside class while not fully initialized (in constructor). So you need to do this later in an initialization method

Swing requires createBufferStrategy(2) to properly paint

I have some grids that is painted to screen one by one. I use arrow keys to move grids around as a group. Swing is said to be doubleBuffered by default so I believe frame.createBufferStrategy(2) is a bad practice but the problem is when I don't use manual double buffering, the grids are misaligned and some holes are appearing between them. Using manual double buffering fixes it.
I'm also experiencing some graphical problems(such as a dialog's buttons not displaying properly) in the actual program(not in SSCCE) so I thought it might be caused by the incorrect implementation of the double buffering.
Here is the SSCCE of the program, that causes grids to misalign when not manually double buffered:
package SSCCE;
import java.awt.Color;
import java.awt.FlowLayout;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.LayoutManager;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JPanel;
public class Main {
boolean manuallyDoubleBuffered = false; //change this
static Main main;
public final JFrame frame = new JFrame();
public final Keys keys = new Keys();
private JPanel panel;
private BufferStrategy bufferStrategy;
public static void main(String[] args) {
main = new Main();
main.initiate();
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
public void initiate() {
frameInit();
keys.start();
}
private void frameInit() {
frame.setSize(1200, 750);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setVisible(true);
setUpGUI();
if (manuallyDoubleBuffered)
frame.createBufferStrategy(2); // manual double buffering
bufferStrategy = frame.getBufferStrategy();
}
private void setUpGUI() {
panel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
Graphics2D g2d = (Graphics2D) g;
Main.main.rendering(g2d);
super.paintComponent(g);
}
};
LayoutManager layout = new FlowLayout();
frame.getContentPane().setBackground(Color.black);
panel.setLayout(layout);
panel.setOpaque(false);//
JButton but1 = new JButton("but1");
panel.add(but1);
frame.add(panel);
}
class Looper implements Runnable {
#Override
public void run() {
Main.main.gameLoop();
}
}
private void gameLoop() {
// variables are declared at start
while (true) {
if (manuallyDoubleBuffered)
paint(); // MANUAL double buffering
else
frame.repaint();// no manual double buffering
update();
try {
Thread.sleep(1000 / 60);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}// loop end
private void update() {
move();
}
private void rendering(Graphics2D g2d) {
// // testing
paintGrids(g2d);
}
private void move() {
x += sx;
y += sy;
}
int sx = 0; //speedX
int sy = 0; //speedY
//top left corner of the grid
int x = 0;
int y = 0;
private void paintGrids(Graphics2D g) {
for (int i = 0; i < 100; i++) {
for (int t = 0; t < 100; t++) {
g.setColor(Color.GRAY);
g.fillRect(i * 50 + x, t * 50 + y, 50, 50);
g.setColor(Color.BLACK);
g.drawString(i + "," + t, i * 50 + x, t * 50 + y + 10);
}
}
}
public void paint() {
// uses double buffering system.
do {
do {
Graphics2D g2d = (Graphics2D) bufferStrategy.getDrawGraphics();
g2d.fillRect(0, 0, frame.getWidth(), frame.getHeight());
try {
frame.paint(g2d);
} catch (NullPointerException e) {
e.printStackTrace();
}
g2d.dispose();
} while (bufferStrategy.contentsRestored());
bufferStrategy.show();
} while (bufferStrategy.contentsLost());
}
}
class Keys implements KeyListener {// Trimmed down to shorten SSCCE
private final int leftKey = 37; // left b.
private final int rightKey = 39; // Right b.
private final int upKey = 38;// up k.
private final int downKey = 40;// down k.
public void start() {
Main.main.frame.addKeyListener(this);
Main.main.frame.setFocusable(true);
}
private void left() {
Main.main.sx -= 10;
}
private void right() {
Main.main.sx += 10;
}
private void up() {
Main.main.sy -= 10;
}
private void down() {
Main.main.sy += 10;
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
System.out.println(e.getKeyCode());
switch (e.getKeyCode()) {
case leftKey:
left();
break;
case rightKey:
right();
break;
case downKey:
down();
break;
case upKey:
up();
break;
}
}
#Override
public void keyReleased(KeyEvent arg0) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent arg0) {
// TODO Auto-generated method stub
}
}// END OF THE KEYS CLASS
Oracle tutorials of swing does not explain the usage with a game loop. What is the best way to do it? Am I doing anything wrong?
In case the visual error is not reproduced on other computers, I'm uploading a screenshot:
Black lines are caused by the misalinging of the rectangles. They don't exist when manual double buffering is set to true.
Thanks in advance.
Edit: I've forgot to mention that the black lines occur when grids are moving.
I' have also found out, manual double buffering drastically reduces performance.
Edit 2 : I've fixed the problem and posted it as an answer but feel free to comment on my code. Main class(except the gameLoop) is similar to the actual main class I use in my program.
I couldn't see any change in the background. Here's the code change I made.
public static void main(String[] args) {
main = new Main();
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
main.initiate();
}
});
// --START LOOP--
Thread loop = new Thread(main.new Looper());
loop.start();
}
You must always start a Swing application with a call to SwingUtilities.invokeLater.
I've found the problem and writing here in case something like that ever happens to anyone else.
The problem was caused due to program being multi-threaded. Top left coordinates of the grids(x and y) were updated by the other thread in the middle of the paintGrids() method. Manual double buffering was slowing the program down (by hundreds of times) and that was allowing the paintGrids method to finish painting before x and y was updated by the keys.
To fix it I've added the following to the start of the paintGrids method:
int x = this.x;
int y = this.y;

Problems with Java's Paint method, ridiculous refresh velocity

I'm developing a very simple version of R-Type as work for the university, but despite it works, the craft velocity is a lot of slow, so the movement is ugly and clumsy.
I use the method repaint for refresh the screen, there are others methods or ways best than it?
Video of Movement
Paint method at main Panel
#Override
public void paint(Graphics g) {
super.paint(g);
Graphics2D g2 = (Graphics2D) g;
g2.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
RenderingHints.VALUE_ANTIALIAS_ON);
g2.drawImage(fondo, 0, 0,1200,600,this);
pj.paint(g2);
g2D=g2;
}
PJ's paint method
public void paint(Graphics2D g) {
g.drawImage(imagen,x,y,this);
}
PJ's move method
public void move (KeyEvent e) {
int dx = 0; int dy = 0;
int code = e.getKeyCode();
switch (code) {
case KeyEvent.VK_Q: dy-=1; break;
case KeyEvent.VK_A: dy+=1; break;
case KeyEvent.VK_P: dx+=1; break;
case KeyEvent.VK_O: dx-=1; break;
}
int x = (getX()<maxX&&getX()!=0) ? getX()+dx : getX();
int y = (getY()<maxY&&getY()!=0) ? getY()+dy : getY();
if (getY()>=maxY||getY()==0) {
if (dy==+1) y=y+1;
}
setPosicion(x, y);
}
The image fondo should already be scaled to 1200x600.
I am not sure, but is super.paint(g) needed? You might also use paintComponent.
The event handling (you seem to be moving by 1 pixel on key down), must be done correctly. I would have set the direction and speed (1px), and leave it to a swing timer to do the continuous moving.
Repainting best is done resilient/flexible: repaint(20L) (50 frames per second);
events like key-down maybe with EventQueue.invokeLater(new Runnable() { ... });.
Especially you might use repaint with the changed area.
You can find a great example of a similar program here. The example demonstrates creating a new thread and having that thread sleep every iteration through the main loop.
Here is another question about loading images for games in Java.
It looks like swing itself is pretty crummy for using images in games. You may want to consider using a more suitable library.
Below is simple example using a background as simple game loop. It updates the state of the game objects and calculates the required delay in order to maintain the required fps.
The game object (Ship) has the ability to accelerate/decelerate over a short period of time
import java.awt.BorderLayout;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import java.awt.geom.Path2D;
import javax.swing.AbstractAction;
import javax.swing.ActionMap;
import javax.swing.InputMap;
import javax.swing.JFrame;
import javax.swing.JPanel;
import javax.swing.KeyStroke;
import javax.swing.UIManager;
import javax.swing.UnsupportedLookAndFeelException;
public class AnimationTest {
public static void main(String[] args) {
new AnimationTest();
}
public AnimationTest() {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
try {
UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName());
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException | UnsupportedLookAndFeelException ex) {
}
JFrame frame = new JFrame("Test");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new BorderLayout());
frame.add(new GamePane());
frame.pack();
frame.setLocationRelativeTo(null);
frame.setVisible(true);
}
});
}
public class GamePane extends JPanel {
private Ship ship;
public GamePane() {
ship = new Ship();
Thread thread = new Thread(new MainLoop(this));
thread.setDaemon(true);
thread.start();
InputMap im = getInputMap(WHEN_IN_FOCUSED_WINDOW);
ActionMap am = getActionMap();
// Key controls...
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "upPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "downPressed");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, true), "upReleased");
im.put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, true), "downReleased");
am.put("upPressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Change the direction...
ship.setDirection(-1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("downPressed", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Change direction
ship.setDirection(1);
// Accelerate by 1 per frame
ship.setVelocity(1);
}
});
am.put("upReleased", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
am.put("downReleased", new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
// Deccelerate by 1 per frame
ship.setVelocity(-1);
}
});
}
#Override
public Dimension getPreferredSize() {
return new Dimension(200, 200);
}
public void updateState() {
// Update the state of the game objects.
// This would typically be better done in
// some kind of model
ship.update(getWidth(), getHeight());
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// Paint the game state...
Graphics2D g2d = (Graphics2D) g.create();
ship.paint(g2d);
g2d.dispose();
}
}
public class MainLoop implements Runnable {
private GamePane pane;
private int fps = 25;
public MainLoop(GamePane pane) {
this.pane = pane;
}
#Override
public void run() {
// Wait until the screen is ready
while (pane.getHeight() <= 0) {
try {
Thread.sleep(125);
} catch (InterruptedException ex) {
}
}
// Main loop
while (true) {
// Start time loop
long startTime = System.currentTimeMillis();
// Update the game state
pane.updateState();
// Calculate the amount of time it took to update
long elasped = System.currentTimeMillis() - startTime;
// Calculate the number of milliseconds we need to sleep
long sleep = Math.round((1000f / fps) - elasped);
pane.repaint();
if (sleep > 0) {
try {
Thread.sleep(sleep);
} catch (InterruptedException ex) {
}
}
}
}
}
public static class Ship {
public static int MAX_SPEED = 8;
private int direction = 0;
private int velocity = 0;
private int x;
private int y;
private int speed = 0;
private Path2D shape;
private boolean initState;
public Ship() {
shape = new Path2D.Float();
shape.moveTo(0, 0);
shape.lineTo(5, 5);
shape.lineTo(0, 10);
shape.lineTo(0, 0);
shape.closePath();
initState = true;
}
public void setDirection(int direction) {
this.direction = direction;
}
public void setVelocity(int velocity) {
this.velocity = velocity;
}
public void update(int width, int height) {
if (initState) {
y = (height - 10) / 2;
initState = false;
} else {
// Add the velocity to the speed
speed += velocity;
// Don't over accelerate
if (speed > MAX_SPEED) {
speed = MAX_SPEED;
} else if (speed < 0) {
speed = 0;
}
// Adjust out position if we're moving
if (speed > 0) {
y += (direction * speed);
}
// Bounds check...
if (y - 5 < 0) {
y = 5;
} else if (y + 5 > height) {
y = height - 5;
}
}
}
public void paint(Graphics2D g2d) {
int yPos = y - 5;
g2d.translate(10, yPos);
g2d.fill(shape);
g2d.translate(-10, -yPos);
}
}
}

Categories

Resources