Java pong jeux graphiques ou geom 2D
Mon premier projet est de faire le jeu Pong en Java.
Parce que j'ai besoin de calculer le delta pour ma position x
et ma position y
, je pense que la meilleure façon est d'utiliser des variables doubles(excusez-moi si je me trompe). Mais vous ne pouvez pas utiliser un double pour remplir une forme dans la lib java.awt.*
.
Ici, je calcule les angles après que la balle a frappé une pagaie
int c = (int) Math.atan2(ball.getPosY(), ball.getPosX());
int delta_x = (int) (1 * Math.cos(c));
int delta_y = (int) (1 * Math.sin(c));
this.dx += delta_x;
this.dy += delta_y;
Avec dx
et dy
j'ai changer la position de la x
et y
de l'pong balle.
Ici je dessine mon pong balle.
g.setColor(Color.WHITE);
g.fillOval(this.posX, this.posY, 25, 25);
Si je veux que dx
et dy
soient plus précis, je dois changer le type de delta_y
et delta_x
en double.
Mais fillOval()
n'est pas applicable avec des variables doubles. Dois-je donc faire mes graphiques dans geom.Point2D.Double
à la place?
2 answers
Utilisez des valeurs doubles pour delta, mais vos coordonnées finales sont converties en int.
Comme ceci (dans un style pseudocode):
int delta_x=1.3, delta_y=-0.4
public void update(){
double x=(pong_ball.getX()+delta_x);
double y=(pong_ball.getY()+delta_y);
pong_ball.setX(x);
pong_ball.setY(y);
}
// in the pong_ball code
public void paint(Graphics g){
paintBall((int) x, (int) y);
}
EDIT: désolé, j'ai réalisé une erreur. Si le delta est assez petit, la balle ne bougera pas! Par conséquent, vous devez également stocker les coordonnées de la balle en doubles et ne lancer que lorsque vous peignez finalement la balle... Désolé pour ça
EDIT 2: notez que le code ci-dessus n'est PAS compilable. vous avez besoin de plus de code (comme les champs xy réels, le code jframe, etc)
Voici un code d'Erin W. Il utilise Graphics 2D:
/*
* Copyright (c) 2007 Eric Woroshow
*
* Permission is hereby granted, free of charge, to any person
* obtaining a copy of this software and associated documentation
* files (the "Software"), to deal in the Software without
* restriction, including without limitation the rights to use,
* copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the
* Software is furnished to do so, subject to the following
* conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
* OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
* WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
* FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
* OTHER DEALINGS IN THE SOFTWARE.
*/
package ca.ericw.pong;
import java.awt.BasicStroke;
import java.awt.Color;
import java.awt.Font;
import java.awt.Frame;
import java.awt.Graphics2D;
import java.awt.RenderingHints;
import java.awt.Stroke;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
import java.awt.event.WindowAdapter;
import java.awt.event.WindowEvent;
import java.awt.image.BufferStrategy;
public class Pong {
public static final String GAME_NAME = "PONG!";
private static final int GAME_WIDTH = 320;
private static final int GAME_HEIGHT = 240;
private static final int GAME_FPS = 60;
private static final int PADDLE_HEIGHT = 60;
private static final int PADDLE_HALFWIDTH = 5;
private static final float PADDLE_SPEED = 2f;
private static final float BALL_RADIUS = 5;
private static final float BALL_SPEED_INCREASE = 1.05f;
private static final int GFX_SPACER = 10;
private static final int SCORE_TO_WIN = 5;
private static final int INTERPOINT_DELAY = GAME_FPS;
private static final int INTERGAME_DELAY = 3 * GAME_FPS;
private Frame window;
private BufferStrategy bufStrat;
private boolean[] keys;
private Font titleFont;
private Font menuFont;
private Font scoreFont;
private Font winnerFont;
private Stroke centreStroke;
private boolean finished;
private int timer;
private long timeThen, timeNow, timeLate;
private enum GameState { MENU, INGAME, POINTSCORED, WINNER };
private GameState state;
private float playerLY, playerRY;
private float ballX, ballY, ballVX, ballVY;
private int playerLScore, playerRScore;
private boolean singlePlayer;
/**
* Creates and runs a new game of Pong.
*/
public Pong() {
init();
run();
quit();
}
/**
* Initializes the game state and display.
*/
private void init() {
// setup game state
titleFont = new Font("Verdana", Font.BOLD, 60);
menuFont = new Font("Verdana", Font.BOLD, 10);
scoreFont = new Font("Fixed Width", Font.BOLD, 80);
winnerFont = new Font("Verdana", Font.BOLD, 18);
centreStroke = new BasicStroke(BALL_RADIUS, BasicStroke.CAP_BUTT,
BasicStroke.JOIN_MITER, 10f, new float[]{8f}, 0f);
keys = new boolean[256];
singlePlayer = false;
state = GameState.MENU;
resetPoint();
// setup the game window
window = new Frame(GAME_NAME);
window.setIgnoreRepaint(true);
window.setUndecorated(true);
window.setSize(GAME_WIDTH, GAME_HEIGHT);
window.setResizable(false);
window.setLocationRelativeTo(null);
window.addWindowListener(new WindowAdapter(){
public void windowClosing(WindowEvent evt){ finished = true; }
});
window.addKeyListener(new KeyAdapter() {
public void keyPressed(KeyEvent e) { keys[e.getKeyCode()] = true; }
public void keyReleased(KeyEvent e) { keys[e.getKeyCode()] = false; }
});
// show the window
window.setVisible(true);
window.requestFocus();
// setup double buffering on the display
window.createBufferStrategy(2);
bufStrat = window.getBufferStrategy();
}
/**
* Runs the game, executing game logic and rendering the current state.
*/
private void run() {
while(!finished) {
logic();
render();
sync();
}
}
/**
* Cleans up any resources and exits the program as soon as possible.
*/
private void quit() {
window.dispose();
}
/**
* Updates the game state for a frame.
*/
private void logic() {
if (keys[KeyEvent.VK_ESCAPE]) {
finished = true;
return;
}
switch(state) {
case MENU:
updateMenu(); break;
case INGAME:
updateGame(); break;
case POINTSCORED:
updatePointScored(); break;
case WINNER:
updateWinner(); break;
}
}
private void updateMenu() {
if (keys[KeyEvent.VK_1]) { // start single player game
singlePlayer = true;
resetGame();
state = GameState.INGAME;
} else if (keys[KeyEvent.VK_2]) { // start two player game
singlePlayer = false;
resetGame();
state = GameState.INGAME;
}
}
private void updateGame() {
// calculate new position for player one
if (keys[KeyEvent.VK_A] && playerLY > 20) {
playerLY -= PADDLE_SPEED;
}
if (keys[KeyEvent.VK_Z] && playerLY + PADDLE_HEIGHT < GAME_HEIGHT - 20) {
playerLY += PADDLE_SPEED;
}
// calculate new position for player two
if (!singlePlayer) {
if (keys[KeyEvent.VK_UP] && playerRY > 20) {
playerRY -= PADDLE_SPEED;
}
if (keys[KeyEvent.VK_DOWN] && playerRY + PADDLE_HEIGHT < GAME_HEIGHT - 20) {
playerRY += PADDLE_SPEED;
}
} else {
updateAI();
}
// do collision detection
updateBallCollision();
// calculate new position for the ball
ballX += ballVX;
ballY += ballVY;
}
private void updatePointScored() {
timer++;
if (timer >= INTERPOINT_DELAY) {
timer = 0;
if (playerLScore >= SCORE_TO_WIN || playerRScore >= SCORE_TO_WIN) {
// one player has one after the last point
state = GameState.WINNER;
} else {
// need to keep playing to find a winner
resetPoint();
state = GameState.INGAME;
}
}
}
private void updateWinner() {
timer++;
if (timer >= INTERGAME_DELAY) {
timer = 0;
state = GameState.MENU;
}
}
/**
* Renders the current state of the game.
*/
private void render() {
Graphics2D g = (Graphics2D)bufStrat.getDrawGraphics();
g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON);
switch(state) {
case MENU:
renderMenu(g); break;
case INGAME:
case POINTSCORED:
renderGame(g); break;
case WINNER:
renderWinner(g); break;
}
g.dispose();
bufStrat.show();
}
private void renderMenu(Graphics2D g) {
// clear the screen
g.setColor(Color.BLACK);
g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
// draw the title
g.setFont(titleFont);
g.setColor(Color.WHITE);
g.fillRect(0, 70, GAME_WIDTH, 5);
g.drawString(GAME_NAME, 55, 130);
g.fillRect(0, 140, GAME_WIDTH, 5);
// draw the instruction text
g.setFont(menuFont);
g.drawString("(1) player - (2) players - (Esc)ape", 70, GAME_HEIGHT - 10);
}
private void renderGame(Graphics2D g) {
// clear the screen
g.setColor(Color.BLACK);
g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
// draw the scores
g.setFont(scoreFont);
g.setColor(Color.DARK_GRAY);
g.drawString(String.valueOf(playerLScore), 120, 70);
g.drawString(String.valueOf(playerRScore), 155, 70);
// draw the top and bottom edges
g.setColor(Color.WHITE);
g.fillRect(GFX_SPACER, GAME_HEIGHT - 2 * GFX_SPACER, GAME_WIDTH - 2 * GFX_SPACER, GFX_SPACER);
g.fillRect(GFX_SPACER, GFX_SPACER, GAME_WIDTH - 2 * GFX_SPACER, GFX_SPACER);
// draw the centre line
g.setStroke(centreStroke);
g.drawLine(GAME_WIDTH / 2, 2 * GFX_SPACER, GAME_WIDTH / 2, GAME_HEIGHT - 2 * GFX_SPACER);
// draw the two paddles
g.setColor(Color.WHITE);
g.fillRect(GFX_SPACER, (int)playerLY, GFX_SPACER, PADDLE_HEIGHT);
g.fillRect(GAME_WIDTH - 2 * GFX_SPACER, (int)playerRY, PADDLE_HALFWIDTH * 2, PADDLE_HEIGHT);
// draw the ball
g.setColor(Color.WHITE);
g.fillRect((int)(ballX - BALL_RADIUS), (int)(ballY - BALL_RADIUS),
(int)(BALL_RADIUS * 2), (int)(BALL_RADIUS * 2));
}
private void renderWinner(Graphics2D g) {
// render the game in the background
renderGame(g);
Color maskBack = new Color(100, 100, 100, 128);
g.setColor(maskBack);
g.fillRect(0, 0, GAME_WIDTH, GAME_HEIGHT);
// draw the winner string
String winner = (playerLScore > playerRScore)
? "Left player wins!"
: "Right player wins!";
g.setFont(winnerFont);
g.setColor(Color.WHITE);
g.drawString(winner, 85, 120);
}
/**
* Resets the position of the ball and paddles for a new point.
*/
private void resetPoint() {
playerLY = GAME_HEIGHT / 2 - PADDLE_HEIGHT / 2;
playerRY = GAME_HEIGHT / 2 - PADDLE_HEIGHT / 2;
ballX = GAME_WIDTH / 2;
ballY = GAME_HEIGHT / 2;
ballVX = (Math.random() > 0.5) ? 2 : -2;
ballVY = (Math.random() > 0.5) ? 2 : -2;
}
/**
* Resets the game state for a new game.
*/
private void resetGame() {
playerLScore = 0;
playerRScore = 0;
resetPoint();
}
/**
* Checks for collision of the ball again the walls and player paddles. If a
* player has scored this method will change the state appropriately.
*/
private void updateBallCollision() {
// check for collision against the top and bottom
if ((ballY - BALL_RADIUS <= 2 * GFX_SPACER) ||
(ballY + BALL_RADIUS >= GAME_HEIGHT - 2 * GFX_SPACER)) {
ballVY = -ballVY;
}
// check for collision with paddles
final int PADDLE_HALFHEIGHT = PADDLE_HEIGHT / 2;
// calculate the penetration on each axis
float penRX = PADDLE_HALFWIDTH + BALL_RADIUS - Math.abs(ballX - (GAME_WIDTH - 15));
float penRY = PADDLE_HALFHEIGHT + BALL_RADIUS - Math.abs(ballY - (playerRY + PADDLE_HALFHEIGHT));
float penLX = PADDLE_HALFWIDTH + BALL_RADIUS - Math.abs(ballX - 15);
float penLY = PADDLE_HALFHEIGHT + BALL_RADIUS - Math.abs(ballY - (playerLY + PADDLE_HALFHEIGHT));
if (penRX > 0 && penRY > 0) { // hit right paddle
ballVX = -ballVX;
if (penRX < penRY) {
ballX -= penRX;
} else {
ballY += (ballY > playerRY) ? penRY : -penRY;
ballVY = -ballVY;
}
} else if (penLX > 0 && penLY > 0) { // hit left paddle
ballVX = -ballVX;
if (penLX < penLY) {
ballX += penLX;
} else {
ballY += (ballY > playerLY) ? penLY : -penLY;
ballVY = -ballVY;
}
}
// increase the speed of the ball with every hit
if ((penRX > 0 && penRY > 0) || (penLX > 0 && penLY > 0)) {
ballVX *= BALL_SPEED_INCREASE;
ballVY *= BALL_SPEED_INCREASE;
}
// check for points scored
if (ballX < 0) {
playerRScore++;
state = GameState.POINTSCORED;
} else if (ballX > GAME_WIDTH) {
playerLScore++;
state = GameState.POINTSCORED;
}
}
/**
* Runs the artificial stupidity calculations for this frame.
*/
private void updateAI() {
float paddleDest;
if (ballVX > 0) {
// ball is moving toward AI, move paddle to ball
paddleDest = ballY - PADDLE_HEIGHT / 2;
} else {
// ball is moving away, move paddle back to centre
paddleDest = GAME_HEIGHT / 2 - PADDLE_HEIGHT / 2;
}
if (playerRY > paddleDest && playerRY > 20) {
playerRY -= PADDLE_SPEED;
} else if (playerRY < paddleDest && playerRY + PADDLE_HEIGHT < GAME_HEIGHT - 20) {
playerRY += PADDLE_SPEED;
}
}
/**
* Synchrnoizes the display to the desired frame rate.
*/
private void sync() {
long timeOfNextFrame = (1000000000l / GAME_FPS) + timeThen;
timeNow = System.nanoTime();
while(timeOfNextFrame > timeNow + timeLate) {
try {
Thread.sleep(1);
} catch (InterruptedException e) { }
timeNow = System.nanoTime();
}
if (timeNow > timeOfNextFrame) {
timeLate = timeNow - timeOfNextFrame;
} else {
timeLate = 0;
}
timeThen = timeNow;
}
/**
* Entry point to the application.
*/
public static void main(String[] args) {
Pong p = new Pong();
}
}