问题
I am making a top down 2d survival game where the player must dodge projectiles from enemies in order to survive, but I've run into an issue that may, slightly influence the gameplay of my first real java project, that issue being that the player cannot move in any way shape or form and if the projectiles had of been implemented, then the player would lose immediately after spawning into this world.
here is the code:
package maximiza;
import java.awt.*;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Game extends JFrame {
private static final long serialVersionUID = 1L;
public int xPosition = 240;
public int yPosition = 240;
public int speed = 10;
public int playerSize = 20;
public Game()
{
setTitle("Maximiza");
setSize(500, 500);
setVisible(true);
setDefaultCloseOperation(EXIT_ON_CLOSE);
}
public void paint(Graphics g)
{
g.setColor(Color.yellow);
g.fillRect(xPosition, yPosition, playerSize, playerSize);
}
public void init()
{
addKeyListener(new Input(this));
}
public static void main(String [] args)
{
boolean living = true;
Game g = new Game();
g.init();
while(living)
{
g.paint(null);
}
}
public void keyPressed(KeyEvent e) {
int key = e.getKeyCode();
if(key == KeyEvent.VK_UP) {
yPosition += speed;
} else if(key == KeyEvent.VK_DOWN) {
yPosition -= speed;
}
}
public void keyReleased(KeyEvent e) {
}
}
Input script:
package maximiza;
import java.awt.event.KeyAdapter;
import java.awt.event.KeyEvent;
public class Input extends KeyAdapter {
Game game;
public Input(Game game) {
this.game = game;
}
public void keyPressed(KeyEvent e) {
game.keyPressed(e);
}
public void keyReleased(KeyEvent e) {
game.keyReleased(e);
}
}
thank you for any help provided.
回答1:
A couple of issues first of all:
- Dont extend
JFrame
unecessarily, you can create aJPanel
and simply add it to theJFrame
- Never override
paint
instead use aJComponent
like aJPanel
and overridepaintComponent
, not forgetting to callsuper.paintComponent
as the first statement in the overriden method. Also never callpaint
orpaintComponent
directly instead useJComponent#repaint()
Here is a good read on the above - All Swing components should be created on the EDT
- Don't call
setSize
use an appropriate layout manager, and overridegetPreferredSize
where needed to provide size hints to the layout manager, then callJFrame#pack()
before setting the frame to be visible - I suggest using KeyBindings as opposed to a
KeyListener
- You cannot use a while loop on the same thread as your UI components or the UI will freeze and not update, instead use a Thread
I myself am creating my own Swing Game Library, which you might use as a reference for future endeavors or issues you might be facing, such as the addition of a game loop, separating your game objects logic from the game panel/screen, anti-aliasing etc.
Below is your code with the above suggestions implemented to help you get started, as well as some comments pointing you to what else you could add or change to make your game more re-usable as it grows:
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.KeyEvent;
import javax.swing.*;
public class Game {
private GamePanel gamePanel;
public Game() {
createAndShowUI();
}
public static void main(String[] args) {
SwingUtilities.invokeLater(Game::new);
}
private void createAndShowUI() {
JFrame frame = new JFrame("Maximiza");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
gamePanel = new GamePanel();
gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_UP, 0, false), "UP pressed");
gamePanel.getActionMap().put("UP pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
gamePanel.moveUp(); // this would be something like gameObject.moveDown()
}
});
gamePanel.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(KeyStroke.getKeyStroke(KeyEvent.VK_DOWN, 0, false), "DOWN pressed");
gamePanel.getActionMap().put("DOWN pressed", new AbstractAction() {
@Override
public void actionPerformed(ActionEvent e) {
gamePanel.moveDown(); // this would be something like gameObject.moveDown()
}
});
frame.add(gamePanel);
frame.pack();
frame.setVisible(true);
// a more approrpiate game loop can be used as we only need to call repaint 60 times per second not more or less.
// fixed step: https://stackoverflow.com/questions/13739693/java-swing-based-game-framework-any-advice/13740162#13740162
// variable step: https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/GameLoop.java
Thread gameLoop = new Thread(() -> {
boolean living = true;
while (living) {
gamePanel.repaint();
// lets sleep a bit not to eat up processor time unnecessarily needs a better implementation of a game loop but thread sleep will do okay for now
try {
Thread.sleep(16);
} catch (InterruptedException ex) {
}
}
});
gameLoop.start();
}
public class GamePanel extends JPanel {
// these properties should go into its own game object which is separated from the game panel, and can simply be added to the game panel via a list
// and in the paintComponent method you simply iterate all the game objects and draw them
// https://github.com/davidkroukamp/SwingGameLibrary-Samples/blob/main/sample1/src/sgltest/Player.java
public int xPosition = 240;
public int yPosition = 240;
public int speed = 10;
public int playerSize = 20;
@Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
// can make graphics look better too using some rendering hints https://github.com/davidkroukamp/SwingGameLibrary/blob/main/src/za/co/swinggamelibrary/Graphics2DHelper.java
// should move to game object inside a render(Graphics2D g2d) method which gets called for each game object passing in the graphics parameter
g.setColor(Color.yellow);
g.fillRect(xPosition, yPosition, playerSize, playerSize);
}
@Override
public Dimension getPreferredSize() {
return new Dimension(500, 500);
}
// these methods would go into the game object class too under a single move methid which checks booleans on whether to move up or down or whatever
public void moveUp() {
yPosition -= speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
}
public void moveDown() {
yPosition += speed; // this could probably just set a boolean which your game object will check and update its position accordingly via a move() method thats called before gamePanel.repaint in the thread
}
}
}
To re-iterate, there are many more changes you could make (I've commented around the code to give you hints), but I did not do these for you as that would just be a spoiler! :)
来源:https://stackoverflow.com/questions/65735680/my-javajframe-player-movement-script-wont-work