I need to make an animation with a part controlled with arrows. I created JPanel which draws the "background" simply with timer and repaint(). Then I'm trying to add a canvas with key listener.
public class MyPanel extends JPanel implements ActionListener{
MyPanel(){
... creating other objects ...
MyCanvas canv = new MyCanvas();
this.add(canv);
Timer timer = new Timer(30, this);
}
...actionPerformed and other functions for background animation...
}
public class MyCanvas extends Canvas implements ActionListener, KeyListener{
int rX;
int rY;
Color color;
KeyEvent e;
int code;
Timer timer;
MyCanvas() {
rX = 400;
rY = 400;
color=Color.red;
this.setSize(1220, 840);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(5, this);
timer.start();
}
public void KeyPressed(KeyEvent e){
code = e.getKeyCode();
}
public void paint (Graphics g)
{
g.setColor(color);
g.fillOval(this.rX, this.rY, 30, 30);
}
public void actionPerformed(KeyEvent evt) {
int keyCode = evt.getKeyCode();
if(keyCode == KeyEvent.VK_LEFT){
rX-=2;
}
...and so on...
public void keyPressed(KeyEvent e) {}
public void keyReleased(KeyEvent e) {}
public void keyTyped(KeyEvent e) {}
public void actionPerformed(ActionEvent e) {
repaint();
}
}
The background animation works and moves just fine, but canvas is added to JPanel, it completely covers it up. Also the key control doesn't work at all.
How to fix it?
Related
I'm makiing a game and when I was introduciing a user input I came across this problem. I add a KeyListener to JPanel but it doesn't work at all. The program is made of JPanel (GameWindow) inside a JFrame(WindowManager). Game is started in GameLoop object which immplements Runnable. Level contains all the information how the map look like. I read that KeyListener works if the Component is focusable so I checked if the panel is focusable and the program returned true. Maybe it is a thread issue but i dont know why.
public class GameWindow extends JPanel {
private Level level;
private GameLoop gameLoop;
private LevelLoader levelLoader = new LevelLoader();
private WindowManager windowParent; // A JFrame
public GameWindow(WindowManager windowParent){
super();
this.windowParent = windowParent;
setBackground(Color.BLACK);
gameLoop = new GameLoop(this);
level = levelLoader.loadLevel(1);
setFocusable(true);
requestFocusInWindow();
addKeyListener(new KeyListener() {
#Override
public void keyTyped(KeyEvent e) {
System.out.println("typed");
}
#Override
public void keyPressed(KeyEvent e) {
System.out.println("pressed");
}
#Override
public void keyReleased(KeyEvent e) {
System.out.println("released");
}
});
start();
}
private void start(){
Thread thread = new Thread(gameLoop);
thread.start();
}
#Override
public Dimension getPreferredSize() {
return new Dimension(600, 600);
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D graphics2D = (Graphics2D) g;
graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,RenderingHints.VALUE_ANTIALIAS_ON);
level.paintLevel(g,getSize());
}
}
Problem solved i need to request for focus after the JPanel is added to JFrame and become visible.
I'm trying to move a dot by pressing the right-left keys.
Here is my main:
import javax.swing.JFrame;
public class Main {
public static void main(String[] args) {
JFrame frame = new JFrame();
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(null);
frame.setSize(500,500);
Graphic graphic=new Graphic();
frame.add(graphic);
}}
This is the graphic class where i created the dot and I implemented KeyListener and ActionListener:
import java.awt.Color;
import java.awt.Graphics;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import javax.swing.JPanel;
import javax.swing.Timer;
public class Graphic extends JPanel implements ActionListener, KeyListener {
private int posX = 220;
private int posY = 300;
private Timer timer;
private int delay = 8;
private int width = 500;
private int height = 500;
public Graphic() {
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
timer = new Timer(delay, this);
timer.start();
this.setSize(width, height);
}
public void paint(Graphics g) {
g.setColor(Color.black);
g.fillRect(0, 0, width, height);
g.setColor(Color.GREEN);
g.fillOval(posX, posY, 20, 20);
}
#Override
public void keyPressed(KeyEvent e) {
if (e.getKeyCode() == KeyEvent.VK_LEFT) {
if (posX <= 20) {
posX = 20;
} else {
moveLeft();
}
}
if (e.getKeyCode() == KeyEvent.VK_RIGHT) {
if (posX >= 460) {
posX = 460;
} else {
moveRight();
}
}
repaint();
}
private void moveRight() {
posX += 20;
}
private void moveLeft() {
posX -= 20;
}
#Override
public void actionPerformed(ActionEvent e) {
timer.start();
repaint();
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void keyTyped(KeyEvent e) {
}
}
In my opinion, this should work...but it doesn't. When I press the left-right keys nothing happens and it looks like it's not "listening" my commands. So, what is wrong with my code?
KeyListener works if the component which has the listener has the focus. JFrame has by default the focus when you display it but not a JPanel.
In the Graphic constructor add simply grabFocus() :
public Graphic() {
addKeyListener(this);
setFocusTraversalKeysEnabled(true);
setFocusable(true);
grabFocus();
timer = new Timer(delay, this);
timer.start();
this.setSize(width, height);
}
EDIT
I have tested on my machine. The problem is that it works randomly as the JFrame needs to be visible if we want that the JPanel grab the focus. Sometimes it is, other times it is not.
SwingUtilities.invokeLater() may solve the problem.
After adding the panel to the JFrame, invoke the code which grabs the focus in a invokeLater() method.
frame.add(graphic);
...
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
graphic.grabFocus();
}
});
You call repaint method of Component class instead of your paint method.
So when trying to create a new game and importing a png via BufferedImage the JFrame becomes gray and the various objects I painted on it in other classes disappear. I can share other classes if needed but I don't see a reason this shouldnt work.
public class Panel extends JPanel implements ActionListener, KeyListener {
private Teeto game;
private Player player;
private Shrooms shroom;
BufferedImage img;
public Panel(Teeto game) {
setBackground(Color.WHITE);
this.game = game;
player = new Player(100, (game.HEIGHT / 2) - 100, 200, KeyEvent.VK_UP, KeyEvent.VK_DOWN);
shroom = new Shrooms(100);
Timer timer = new Timer(5, this);
timer.start();
addKeyListener(this);
setFocusable(true);
try {
img = ImageIO.read(new File("C:\\Users\\Patrick\\Desktop\\Teeto\\Yasuo.png"));
} catch (IOException e) {}
}
public void update() {
player.update();
shroom.update();
checkIntersection();
}
public void checkIntersection() {
if (player.getBounds().intersects(shroom.getBounds())) {
player.health = player.health - 20;
}
}
public void actionPerformed(ActionEvent e) {
update();
repaint();
}
public void keyPressed(KeyEvent e) {
player.pressed(e.getKeyCode());
}
public void keyReleased(KeyEvent e) {
player.released(e.getKeyCode());
}
public void keyTyped(KeyEvent e) {}
#Override
public void paintComponent(Graphics g) {
super.paintComponent(g);
player.paint(g);
shroom.paint(g);
g.drawImage(game.getPanel().img, 0, 0, null);
}
}
It appears your application can't find the file "Yasuo.png". Ensure it resides in the same folder as your class files.
The happy face Im using----)This project wants me to Modify the Rebound program from this chapter such that when the mouse button is clicked the animation stops, and when its clicked again the animation resumes.
When I click on the screen with the moving smiley face, it doesnt stop when I click it nor start up again because I couldnt stop the smiley face from moving What am I doing wrong? Here is the problem area.------) |
private class ReboundMouseListener implements MouseListener {
public void mouseClicked(MouseEvent event) {
if (timer.isRunning())
timer.stop();
else
timer.start();
}
}
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}
Here is the rest of the code:
public class ReboundPanel extends JPanel {
private final int WIDTH =300, HEIGHT= 100;
private final int DELAY= 20, IMAGE_SIZE=35;
private ImageIcon image;
private Timer timer;
private int x, y, moveX, moveY;
//---------------------------------------------------------
// Sets up the panel,including the timer for the animation.
//---------------------------------------------------------
public ReboundPanel(){
timer= new Timer(DELAY, new ReboundListener());
image= new ImageIcon("happyFace.gif");
x=0;
y=40;
moveX=moveY=3;
setPreferredSize(new Dimension(WIDTH, HEIGHT));
setBackground(Color.black);
timer.start();
}
//---------------------------------------------------------
// Draws the image in the current location.
//---------------------------------------------------------
public void paintComponent(Graphics page)
{
super.paintComponent(page);
image.paintIcon(this, page, x, y);
}
//*********************************************************
// Represents the action listener for the timer.
//*********************************************************
private class ReboundListener implements ActionListener
{
//--------------------------------------------------------
// Updates the position of the image and possibly the direction
// of movement whenever the timer fires an action event.
//--------------------------------------------------------
public void actionPerformed(ActionEvent event)
{
x += moveX;
y += moveY;
if (x <=0 || x >= WIDTH-IMAGE_SIZE)
moveX =moveX * -1;
if (y <=0 || y >= HEIGHT-IMAGE_SIZE)
moveY = moveY * -1;
repaint();
}
}
private class ReboundMouseListener implements MouseListener {
//--------------------------------------------------------------
// Stops or starts the timer (and therefore the animation)
// when the mouse button is clicked.
//--------------------------------------------------------------
public void mouseClicked(MouseEvent event) {
if (timer.isRunning())
timer.stop();
else
timer.start();
}
//--------------------------------------------------------------
// Provide empty definitions for unused event methods.
//--------------------------------------------------------------
public void mouseEntered(MouseEvent event) {}
public void mouseExited(MouseEvent event) {}
public void mousePressed(MouseEvent event) {}
public void mouseReleased(MouseEvent event) {}
}
}
public class Rebound {
public static void main(String[] args) {
JFrame frame = new JFrame("Rebound");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.getContentPane().add(new ReboundPanel());
frame.pack();
frame.setVisible(true);
}
}
Looks like you are missing your addMouseListener() calls:
public ReboundPanel() {
// Other initializations ...
addMouseListener(new ReboundMouseListener()); // <-- add
timer.start();
}
I'm creating a java game. In the game there are a hero and a bubble. The hero is supposed to move when I press the arrow keys and the bubble is supposed to have continuous diagonal movement. When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both I just get a very small square! I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working. Probably I have to define some type of layout to the JPanels.
What am I doing wrong?
Code:
public class Pang {
public static void main(String[] args) {
JFrame f=new JFrame();
JPanel gamePanel=new JPanel();
gamePanel.setPreferredSize(new Dimension(800, 600));
DrawHero d=new DrawHero();
DrawBubble bubble=new DrawBubble();
gamePanel.add(d);
gamePanel.add(bubble);
f.add(gamePanel);
f.setVisible(true);
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.setSize(800, 600);
}
}
public class DrawHero extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 0, y = 0, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawHero() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
imageHero = getHeroImage();
g2.drawImage(imageHero, x, y, 40, 40, null);
}
public Image getHeroImage() {
Image image = null;
image = getImage("hero.png");
return image;
}
public Image getImage(String path) {
Image tempImage = null;
try {
URL heroiURL = DrawHero.class.getResource(path);
tempImage = Toolkit.getDefaultToolkit().getImage(heroiURL);
} catch (Exception e) {
System.out.println("Error loading hero image! - "
+ e.getMessage());
}
return tempImage;
}
public void actionPerformed(ActionEvent e) {
repaint();
}
public void moveUp() {
y = y - step;
}
public void moveDown() {
y = y + step;
}
public void moveLeft() {
x = x - step;
}
public void moveRight() {
x = x + step;
}
public void keyPressed(KeyEvent e) {
int keyCode = e.getKeyCode();
if (keyCode == KeyEvent.VK_UP) {
moveUp();
}
if (keyCode == KeyEvent.VK_DOWN) {
moveDown();
}
if (keyCode == KeyEvent.VK_LEFT) {
moveLeft();
}
if (keyCode == KeyEvent.VK_RIGHT) {
moveRight();
}
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
public class DrawBubble extends JPanel implements ActionListener, KeyListener {
Timer myTimer = new Timer(5, this);
int x = 100, y = 200, dx = 0, dy = 0, step = 10;
private transient Image imageHero = null;
public DrawBubble() {
myTimer.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
}
public void paintComponent(Graphics g) {
super.paintComponent(g);
Graphics2D g2 = (Graphics2D) g;
g2.fill(new Ellipse2D.Double(x, y, 40, 40));
}
public void actionPerformed(ActionEvent e) {
x=x+dx;
y=y+dy;
repaint();
}
public void moveBubble() {
dy=2;
dx=2;
}
public void keyPressed(KeyEvent e) {
moveBubble();
}
public void keyTyped(KeyEvent e) {
}
public void keyReleased(KeyEvent e) {
}
}
I recommend that neither the DrawHero nor DrawBubble (which should be called Hero nor Bubble respectively) should extend any JComponent. Instead each should simply know how to draw itself to a Graphics object passed to it, when requested to do so.
Then a single GameField or PlayingArea class should keep references to all the Bubble objects and the Hero and draw call the draw(Graphics) method of those objects.
Using this approach it is not necessary to worry about layouts within the GameField component (they become irrelevant).
That is the basic strategy I pursue for rendering the stationary objects in this answer to [Collision detection with complex shapes.
When I add the hero or the bubble directly into to the JFrame separately I get the desired behavior, but when I add them both i just get a very small square!
The default layout manager for a JFrame is a BorderLayout. When you use add(component) without a constraint the component goes to the CENTER. Only one component can be added to the CENTER, so only the last one added is displayed.
I tried to add them to the same JPanel and after add that JPanel to the JFrame but it is not working.
The default layout manager for a JPanel is the FlowLayout which respects the preferred size of component. The problem is you don't override the getPreferredSize() method so the size is (0, 0) and there is nothing to paint.
Probably I have to define some type of layout to the JPanels.
Actually since you want random motion you need to use a null layout on the panel and then use the setSize() and setLocation() method of your components to position the components.
Then when you do this the custom painting should always be done at (0, 0) instead of (x, y) since the location will control where the component is painted on the panel.