Clicking out of a Canvas - java

I am making a Pong program, and I have a start button that begins to draw everything, and quite literally to get the ball rolling (you're welcome for the pun). Anyways, when I hit the start button, a Key Listener to move the paddles won't work unless I click the mouse somewhere on the canvas to give it priority. Is there some sort of code to automatically "click" on the canvas without the user being hassled to do so? Thanks in advance.
This Is running awt by the way. I realize I should learn swing, but never got around to it.
public class Pong extends Applet implements ActionListener, KeyListener
{
Canvas c1;
Graphics myG;
Button start;
ball ball;
paddle LPaddle;
paddle RPaddle;
public void init()
{
this.setSize(1300,700);
c1 = new Canvas();
add(c1);
c1.addKeyListener(this);
c1.setBackground(Color.pink);
start = new Button("Start");
add(start);
start.addActionListener(this);
ball = new ball();
LPaddle = new paddle();
RPaddle = new paddle();
myG = c1.getGraphics();
}
public void paint(Graphics g)
{
c1.setLocation(0,0);
c1.setSize(1251,700);
start.setLocation(1255,350);
start.setSize(40,20);
}
public void keyPressed(KeyEvent e)
{
if(e.getKeyCode()==KeyEvent.VK_UP)//up
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleUp();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_DOWN)//down
{
RPaddle.erasePaddle(myG);
RPaddle.movePaddleDown();
RPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_W)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleUp();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_S)
{
LPaddle.erasePaddle(myG);
LPaddle.movePaddleDown();
LPaddle.drawPaddle(myG);
}
if(e.getKeyCode()==KeyEvent.VK_ENTER)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}
public void keyReleased(KeyEvent e)
{
}
public void keyTyped(KeyEvent e)
{
}
public void actionPerformed(ActionEvent e)
{
if(e.getSource()==start)
{
myG.drawLine(625,0,625,700);
LPaddle.setInitial(150,0,350);
RPaddle.setInitial(150,1250,350);
LPaddle.drawPaddle(myG);
RPaddle.drawPaddle(myG);
}
}

KeyListeners are low level interfaces which have a major, significant draw back: The componet they are registered to must be focusable and be focused.
By clicking the start button, you are giving the button focus.
You could call requestFocusInWindow on the instance of the canvas, but this assumes that the the canvas is focusable in the first place.
If you can, you'd be better of using a JComponent/JPanel as the base for your game canvas and use the key bindings API.

Related

Adding a timer to KeyListener

I extended a JFrame class and have my own model and JPanel extended classes as instance variables. I implemented KeyListener to my JFrame and it works with the arrow keys but my model moves extremely slow around the frame when I hold the keys down. My question is how do I attach the KeyListener methods to a timer or do something to make my model move faster when I hold the keys. Also if it is possible, how can the model move two directions at once, say left and up?
public class GameController extends JFrame implements KeyListener,ActionListener
{
private GamePieces p;
private GamePanel panel;
private Timer timer;
public GameController()
{
super("Balls");
setSize(800, 600);
timer = new Timer(10, this);
setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Container c = getContentPane();
c.setLayout(new BorderLayout());
p = new GamePieces();
panel = new GamePanel();
p.addObserver(panel);
c.add(panel);
addKeyListener(this);
panel.update(p, null);
setResizable(false);
timer.start();
}
public void actionPerformed(ActionEvent e)
{
String actionCommand = e.getActionCommand();
if (e.getSource() == timer)
{
p.checkEat();
p.moveOthers();
panel.update(p, null);
}
}
public void keyTyped(KeyEvent e)
{
int s = 0;
}
public void keyPressed(KeyEvent e)
{
int key = e.getKeyCode();
if (key==38)
{
p.up();
panel.update(p, null);
}
else if (key==40)
{
p.down();
panel.update(p, null);
}
else if (key==39)
{
p.right();
panel.update(p, null);
}
else if (key==37)
{
p.left();
panel.update(p, null);
}
}
public void keyReleased(KeyEvent e)
{
int o = 0;
}
public static void main(String[]args)
{
GameController a = new GameController();
a.setVisible(true);
}
}
You are on right way but it should be a little more sophisticated. Follow MVC pattern. You should have model which remembers where you panel should be - up(), down() methods only update this model. You should have viewer which shows panel at current position. panel.update() belongs to viewer. And then you should have controller which changes position (invoke these up and down, left and right) and invokes viewer when necessary - to show movement or just next frame.
Your KeyListener tells controller to move position in some intervals when key is pressed. And then it tells controller to stop doing so when key is released. See, all parts move smoothly and independently. Keylistener tells controller what to do, controller does - changes model and invokes viewer. Model does nothing - only holds data. Viewer does nothing only shows current data.
P.S. Don't forget thread safety since you will press multiple keys and they will invoke controller multiple times in the same thread.

Simulate MousePress to enter MouseDragged

I am trying to achieve:
1. User performs a mouse-press on parent JFrame
2. Child JFrame becomes visible at mouse location
3. While mouse-button remains pressed, the user can drag the child JFrame across the screen by moving the mouse
The problem:
I can mimic a mouse-press but it does not 'grab' the child JFrame--hence the child JFrame is not being dragged unless the user manually clicks the child JFrame again. I want the process to be smooth without any interruptions: i.e. steps 1-3 (above) should all execute with a single mouse-press.
Failed attempts:
1. I have tried using Robot's mousePressed() to simulate an additional mouse-press on the child. This works, however, it's not clean and can be quite buggy--especially if the PC/device is slow or the user moves the mouse too quickly. This is not a good solution.
2. Using the Toolkit.getDefaultToolkit().getSystemEventQueue().postEvent(new MouseEvent()); results in the exact same issue as depicted by the current code.
3. When adding a KeyListener (for testing) to both the child and the parent, when the mouse-press is made, the child window is focused and responds to the implemented KeyListener--the parent's KeyListener is not activated..
The Code:
final JFrame parent = new JFrame(), child = new JFrame();
parent.setSize(256, 256);
child.setSize(128,128);
parent.setVisible(true);
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
int id = MouseEvent.MOUSE_PRESSED;
long time = System.currentTimeMillis();
int x = 48;
int y = 48;
int button = MouseEvent.BUTTON1_MASK;
child.dispatchEvent(new MouseEvent(child, id, time, button, x, y, 1, false));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
child.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
System.out.println("Pressed!");
}
});
Even though the child JFrame has the focus, the MousePress remains active on the parent. To resolve the issue in a stable manner, the parent should 'forward' its MouseDragged event to the child as shown below:
parent.addMouseListener(new MouseAdapter() {
#Override
public void mousePressed(MouseEvent e) {
child.setVisible(true);
child.setLocation(e.getXOnScreen()-48, e.getYOnScreen()-48);
}
});
parent.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.dispatchEvent(new MouseEvent(child, e.getID(), e.getWhen(), e.getButton(), e.getX(), e.getY(), e.getClickCount(), e.isPopupTrigger()));
}
});
child.addMouseMotionListener(new MouseAdapter() {
#Override
public void mouseDragged(MouseEvent e) {
child.setLocation(parent.getX()+e.getX()-48, parent.getY()+e.getY()-48);
}
});
Even this is not fully 'clean' because the code is relying on the parent's frame visibility. However, it is a stable solution (unlike Robot).

Why doesn't the ball show up in the frame if I add the ball after the for loop?

The program makes a ball glide across from top left to bottom right and works. But if I were to shift the line
frame.getContentPane().add(ball);
from its current position to after the for loop, why doesn't the ball show up on the frame.
I agree that the ball should no longer move, because all the shifting done in the for loop happens even before I add the ball to the JFrame,but I don't understand why the ball doesn't show up on the screen when I ultimately add it to the frame.
Here's the code of the working program, if you shift the line mentioned above to after the for loop, the ball no longer shows up
import java.awt.*;
import javax.swing.*;
import java.awt.event.*;
public class Animate
{
private JFrame frame;
private int x,y;
public static void main(String args[])
{
Animate ballRoll = new Animate();
ballRoll.go();
}
public void go()
{
frame = new JFrame();
frame.setSize(500,500);
frame.setVisible(true);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
MyRoll ball = new MyRoll();
frame.getContentPane().add(ball);
for(x = 5;x<=350;x++)
{
y=x;
try
{
Thread.sleep(50);
}
catch(Exception e)
{
System.out.println("dsfsd");
}
ball.repaint();
}
}
class MyRoll extends JPanel
{
public void paintComponent(Graphics g)
{
g.setColor(Color.BLACK);
g.fillRect(0, 0, this.getWidth(), this.getHeight());
g.setColor(Color.ORANGE);
g.fillOval(x, y, 100, 100);
}
}
}
Some points to remember:
Don't use Thread.sleep() that sometime hangs the whole swing application instead try with Swing Timer that is most suitable for swing application.
Read more How to Use Swing Timers
Don't forget to call super.paintComponent() in overridden paintComponent() method.
Call frame.setVisible(true) in the end after adding all the components.
Use frame.pack() instead of frame.setSize(500,500) that fits the components as per component's preferred size.
Override getPreferredSize() to set the preferred size of the JPanel in case of custom painting.
Use SwingUtilities.invokeLater() or EventQueue.invokeLater() to make sure that EDT is initialized properly.
Read more
Why to use SwingUtilities.invokeLater in main method?
SwingUtilities.invokeLater
Should we use EventQueue.invokeLater for any GUI update in a Java desktop application?
Sample code: (change it as per your custom painting)
private Timer timer;
...
timer = new javax.swing.Timer(50, new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
y = ++x;
ball.repaint();
if (x > 350) {
timer.stop();
}
}
});
timer.setRepeats(true);
timer.start();
public static void main(String args[]) {
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
Animate ballRoll = new Animate();
ballRoll.go();
}
});
}
class MyRoll extends JPanel {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
...
}
#Override
public Dimension getPreferredSize() {
return new Dimension(..., ...);
}
}

Java: help using KeyAdapter

I'm using java trying to make a basic game but am having some trouble with a KeyAdapter. I've used a very similar format before and thought I understood it, but this has me stumped.
Any help would be appreciated, here is the main code I'm working with
public class Board extends JPanel implements ActionListener{
Timer timer;
Tank tank = new Tank();
boolean boardset;
public Board(){
setBackground(Color.BLACK);
ImageIcon alien1ii = new ImageIcon(this.getClass().getResource("si_Alien1.png"));
Image alien1 = alien1ii.getImage();
ImageIcon alien2ii = new ImageIcon(this.getClass().getResource("si_Alien2.png"));
Image alien2 = alien2ii.getImage();
ImageIcon alien3ii = new ImageIcon(this.getClass().getResource("si_Alien3.png"));
Image alien3 = alien3ii.getImage();
timer = new Timer(5, this);
timer.start();
addKeyListener(new TAdapter());
JButton button = new JButton(new AbstractAction("hello2"){
#Override
public void actionPerformed(ActionEvent e){
boardset = false;
}
});
this.add(button);
//actual game
setFocusable(true);
setDoubleBuffered(true);
}
public void paint(Graphics g){
super.paint(g);
g.setColor(Color.WHITE);
Graphics2D g2d = (Graphics2D) g;
g2d.drawImage(tank.getTank(), tank.getx(), tank.getY(), this);
g2d.drawLine(0, (tank.getY()+25), 400, (tank.getY()+25));
Toolkit.getDefaultToolkit().sync();
g.dispose();
}
public class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e){
tank.keyPressed(e);
System.out.println("pressedddddddddddddddddd");
}
public void keyReleased(KeyEvent e){
tank.keyReleased(e);
}
}
public void setBoardset(boolean x){
boardset = x;
}
public boolean getBoardset(){
return boardset;
}
#Override
public void actionPerformed(ActionEvent e) {
repaint();
tank.move();
}
}
Seems to me like this should be pretty straightforward, right now I'm using this print statement to see if the class is actually recognizing key strokes at all:
public class TAdapter extends KeyAdapter{
public void keyPressed(KeyEvent e){
tank.keyPressed(e);
System.out.println("pressedddddddddddddddddd");
}
However, there is no output. So I suspect it is not recognizing any keystrokes at all. But I can't figure out why. If anybody has any suggestions I would appreciate it. Obviously I have more code I can share if anybody thinks it would be useful in figuring out this bug.
1)Use KeyBindings KeyListener has 2 big issues,first you listen to all keys and second you have to have focus and be focusable. Instead KeyBinding you bind for a key and you don't have to be in focus.
Simple Example:
AbstractAction escapeAction = new AbstractAction() {
#Override
public void actionPerformed(ActionEvent e) {
//code here example
((JComponent)e.getSource()).setVisible(Boolean.FALSE);
}};
String key = "ESCAPE";
KeyStroke keyStroke = KeyStroke.getKeyStroke(key);
component.getInputMap(JComponent.WHEN_IN_FOCUSED_WINDOW).put(keyStroke, key);
component.getActionMap().put(key, escapeAction);
You can use these JComponent constants
WHEN_ANCESTOR_OF_FOCUSED_COMPONENT
WHEN_FOCUSED
WHEN_IN_FOCUSED_WINDOW
2) Don't use concrete inheritance if it isn't necessary at all.
3) Don't implement ActionListener in top classes, see Single Responsability Principle
Example
Change this:
public class Board extends JPanel implements ActionListener{
to:
public class Board{
private JPanel panel;
private class MyActionListener implements ActionListener{
//code here
}
}
4) Don't use inheritance if it's just the same for example in your KeyAdapter , you don't add nothing to it, just use KeyAdapter (Now you are gonna to use keybinding so this is useless but to know :) ).
5) Add #Override annotation when you do overriding , also you should override paintComponent(..) instead of paint(..) in swing.
KeyListener suffers from focus issues. The component needs to be both focusable and have focus in order for the listener to be notified of key events.
A better solution would be to use Key Bindings which don't suffer from these constraints.

Using awt Paint in Multi threading

I have a school project and i want to dedicate this for New Year, the project that i came up is a Text-Firework, i am using characters and symbols as the Explosion particles, and just constantly changing their X and Y position inside Paint().
I am confused on how to use Paint and Thread together. The problem is it's not painting on the screen or maybe the thread is not starting. (i cant really tell, im sorry). the problem is i dont get any error, it just doesn't work :(
the code is a little bit long i think, thank you for reading it.
How it should Works: When a user click, a Firework Thread will be started on the mouse position,
this Firework class has a paint loop for recreating the incremental explosion. so basically, i want the user to create multiple explosions thats why i made it a Thread.
here is the main applet:
public class TBFireworks extends Applet implements MouseListener
{
public void init()
{
setBackground( Color.black );
addMouseListener( this );
}
public void mouseEntered( MouseEvent e ) { }
public void mouseExited( MouseEvent e ) { }
public void mousePressed( MouseEvent e ) { }
public void mouseReleased( MouseEvent e ) { }
public void mouseClicked( MouseEvent e )
{
new Firework( e.getX(),e.getY(), this);
}
}
and the Firework Thread class:
class Firework extends Thread
{
Point center = new Point(0,0);
int blastRadius = 10;
Point posIncrement = new Point(0,0);
Applet applet;
public Firework(int positionX, int positionY, Applet apple)
{
center.x = positionX;
center.y = positionY;
applet = apple;
new Thread(this).start();
}
public void run()
{
while(blastRadius > 0)
{
applet.paint(applet.getGraphics());
try {
this.sleep(1000/20);
} catch (InterruptedException e) { ; }
}
}
public void paint(Graphics g)
{
if(blastRadius > 0)
{
Point[] fakeFire = {new Point(20,20),new Point(20,30),new Point(30,20)};
ApplyNextPos(fakeFire,posIncrement);
g.setColor(Color.red);
for(int xaa=1; xaa<5; xaa++) // draw the formation
{
for(int zaa=0;zaa<fakeFire.length;zaa++)
{
fakeFire[zaa] = GetQuadrant(xaa,center,fakeFire[zaa]);
}
for(int yaa=0;yaa<fakeFire.length;yaa++)
{
g.drawString("*",fakeFire[yaa].x,fakeFire[yaa].y);
}
}
posIncrement.incrementPos(5);
blastRadius--;
}
}
}
First, you seem not to be using your paint-method in the FireWork thread, you call the applet's paint method instead.
I'm a bit rosty in the applet and AWT stuff, but if it were Swing (I guess it not that different), I would suggest another approach. Painting should (can?) only be done in the EDT (Event Dispatch Thread). When the user clicks, you create a similar object to FireWork, and add that to a list. Then you start ONE thread (if not already started, that continously calls repaint on some panel. Then in the paint-method of your panel you loop the list of all fireworks and draw them.
This will also be more memory efficient, since you only use one thread.
The paint method should (normaly) only be called by the GUI when the corresponding component or part of it needs to be (re-)painted. It should not be called by the application (if not from inside another paint method).
The paint method is called to draw the actual state of the component. The state should be changed by another method/thread. The repainting of the component is forced by calling repaint.
An incomplete, untested example:
public class Animation extends Canvas {
private final List<Firework> fireworks = new ArrayList<Firework>();
public void start() {
Thread t = new Thread(new Runnable() {
#Override
public void run() {
while (true) {
step();
Thread.sleep(STEP_TIME); // InterruptedException ?
}
}
});
t.start();
}
#Override
public void paint(Graphics g) {
super.paint(g);
for (Firework fw : fireworks)
fw.draw(g); // draw the Firework
}
private void step() {
for (Firework fw : fireworks)
fw.step(); // update/move the Firework
repaint();
}
// methods for adding/deleting Fireworks, synchronization?
}

Categories