I am trying to to set up a KeyListener to respond to my keystrokes. I have already setup a mouselistener but for some reason I am un-able to get the keylistener to respond to any keystrokes.
I have created a class that implements KeyListener and overridden the functions. I then created an instance of the new class and added the handler to the JPanel and JFrame. Still no dice
public class main_program extends JFrame {
private int mX_cord, mY_cord,prior_selected_vertex, current_selected_vertex;
private int verticies_to_edge1, verticies_to_edge2;
private int radius =10;
private boolean vertex_selected1 = false, vertex_selected2 = false, edge_ready = false,delete_vertex_ready = false;
private Edge tempEdge = null;
private ArrayList<Integer> vertex_xcord = new ArrayList<Integer>();
private ArrayList<Integer> vertex_ycord = new ArrayList<Integer>();
private ArrayList<Edge> edge = new ArrayList<Edge>();
HandlerMouse handler = new HandlerMouse();
HandlerKey keyhand = new HandlerKey();
private JPanel masterPanel;
private JTextArea masterTextArea;
private JScrollPane masterScrollPane;
private Point point1, point2;
Graphics g;
public main_program(){
setTitle("Graph");
setSize(600, 400);
setDefaultCloseOperation(new JFrame().EXIT_ON_CLOSE);
//this must be set for custom layout of components
setLayout(null);
masterPanel = new JPanel();
masterPanel.setSize(600,300);
masterPanel.setLocation(0, 0);
masterPanel.setBackground(Color.WHITE);
masterPanel.addMouseListener(handler);
masterPanel.addMouseMotionListener(handler);
masterPanel.addKeyListener(keyhand);
masterTextArea = new JTextArea();
masterTextArea.setBackground(Color.green);
masterScrollPane = new JScrollPane();
masterScrollPane.add(masterTextArea);
masterScrollPane.setSize(600, 100);
masterScrollPane.setLocation(0, 300);
masterScrollPane.addMouseListener(handler);
masterScrollPane.addMouseListener(handler);
masterScrollPane.addKeyListener(keyhand);
add(masterPanel);
add(masterScrollPane);
setLocationRelativeTo(null);
setVisible(true);
}
public void paint(Graphics g){
super.paint(g);
for(int i = 0 ; i < vertex_xcord.size(); i++){
g.fillOval(vertex_xcord.get(i), vertex_ycord.get(i), radius, radius);
}
for(int i = 0 ; i<edge.size(); i++){
tempEdge = edge.get(i);
g.drawLine(vertex_xcord.get(tempEdge.vertex1), vertex_ycord.get(tempEdge.vertex1), vertex_xcord.get(tempEdge.vertex2), vertex_ycord.get(tempEdge.vertex2));
}
//g.fillOval(mX_cord, mY_cord, radius, radius);
//repaint();
}
private class HandlerKey implements KeyListener{
public void keyPressed(KeyEvent evt){
System.out.println("key pressed");
if(evt.getKeyCode() == KeyEvent.VK_ENTER && edge_ready){
edge.add(new Edge(prior_selected_vertex, current_selected_vertex));
edge_ready = false;
repaint();
}
}
public void keyReleased(KeyEvent evt){
System.out.println("key rel");
}
public void keyTyped(KeyEvent evt){
System.out.println("key type");
}
}
private class HandlerMouse implements MouseListener, MouseMotionListener{
public void mouseClicked(MouseEvent evt){
mX_cord = evt.getX()-5;
mY_cord = evt.getY()+15;
if( evt.getClickCount() == 1){
//mX_cord = evt.getX();
//mY_cord = evt.getY();
vertex_xcord.add(mX_cord);
vertex_ycord.add(mY_cord);
repaint();
}
else{
for(int i = 0 ; i < vertex_xcord.size(); i++){
if(Math.abs(vertex_xcord.get(i) - mX_cord ) < 15 && Math.abs(vertex_ycord.get(i) - mY_cord ) < 15 ){
if(vertex_selected1 == false){
prior_selected_vertex = i;
vertex_selected1 = true;
}
else{
current_selected_vertex = i;
vertex_selected2 = true;
}
System.out.println("YOU HAVE SELECTED A VERTEX: " + i);
break;
}
}
}
if(vertex_selected2 == true){
edge_ready = true;
verticies_to_edge1 = prior_selected_vertex;
verticies_to_edge2 = current_selected_vertex ;
vertex_selected1 = vertex_selected2 = false;
System.out.println("Ready for edge!");
}
else{
delete_vertex_ready = true;
}
}
public void mouseEntered(MouseEvent arg0)
{
}
public void mouseExited(MouseEvent arg0)
{
}
public void mousePressed(MouseEvent evt)
{
}
public void mouseReleased(MouseEvent arg0)
{
}
public void mouseDragged(MouseEvent e)
{
}
public void mouseMoved(MouseEvent e)
{
}
}
class Edge {
int vertex1, vertex2;
public Edge(int v1, int v2){
vertex1 = v1;
vertex2 = v2;
}
}
public static void main(String[] args){
main_program circle = new main_program();
}
}
You've got several problems with that program, and it suggests that you'd do well to read many of the Swing Q&A's on this site, because these problems (and your main problem) are quite common, and solutions are often posted.
As to your main problem, the problem again is very common: KeyListeners only work if the listened to component has focus. If it loses focus or is not focusable, then you're out of luck. The best solution is often not to use a KeyListener but rather the higher level and more flexible Key Bindings. Google will find the tutorials for you for this.
As for other problems with your code:
You're using null layout, a layout that leads to inflexible GUI's that are very difficult to upgrade and enhance, and that look terrible on all but your current platform and screen resolution. Solution: study and use the layout managers.
You're drawing directly into a JFrame's paint(Graphics g) method, which has risks as you risk messing up the painting of any and all of the JFrame's constituents by doing this. Much better to draw in a JPanel's paintComponent(Graphics g) method, and gain the benefit of Swing (rather than AWT) graphics including automatic double buffering.
Your class has a Graphics variable, g, which suggests that you're contemplating using a stored Graphics object either from a component or from the JVM. This increases the risk of your program throwing a NullPointerException when that Graphics object no longer exists, either that or drawing with it and but not seeing any effect. Solution: draw inside the painting method (again better with a JComponent's paintComponent method) only, or with the Graphics object from a BufferedImage.
You're adding a MouseListener directly to a JPanel but are drawing in a different component, the JFrame, and by doing so without taking insets into account, are placing points in the wrong location. Use the MouseListener on the same component that you're drawing on.
Again, please have a look around here, as I think you'll find a lot of treasures on this site that can be used to enhance your program.
Related
So I am making a space invaders clone. Originally I had no problem getting my game to work with a simple main class that created the frame, created the gameplay and started the thread.
But then I tried to implement a start menu and it all went to crap. The menu appears with success but the gameplay does not appear when I press start.
I am running out of ideas and I am completely stumped. I am somewhat new as well to SO, so if there is anything I left out, I appreciate any help.
Here is the original with no menu that worked fine:
public static void main(String[] args) {
JFrame frame = new JFrame("SpaceRaiders");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
Gameplay gameplay = new Gameplay();
frame.add(gameplay);
frame.setVisible(true);
Thread t1 = new Thread(gameplay);
t1.start();
}
However, the moment I tried to implement a menu to then play the game, I am running into all sorts of trouble. I created a UI class as well as an actual "game" class like so:
public class UI {
JFrame frame, f2;
JPanel titrePanel, startButtonPanel, loadButtonPanel, p2;
JLabel nomJeu;
JButton startButton, loadButton;
Font fontTitre, fontStart;
Gameplay gameplay;
public void createUI(ChoixJeu cj) {
frame = new JFrame("SpaceRaiders");
frame.setSize(600, 600);
frame.setLocationRelativeTo(null);
frame.setResizable(false);
frame.setLayout(null);
frame.getContentPane().setBackground(Color.black);
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
//------------------ECRAN MENU---------------------
//Titre
titrePanel = new JPanel();
titrePanel.setBounds(100, 100, 400, 100);
titrePanel.setBackground(Color.BLUE);
Font fontTitre = new Font("Times New Roman", Font.BOLD, 50);
Font fontStart = new Font("Times New Roman", Font.PLAIN, 20);
nomJeu = new JLabel("SpaceRaiders");
nomJeu.setForeground(Color.white);
nomJeu.setFont(fontTitre);
titrePanel.add(nomJeu);
//Start button
startButtonPanel = new JPanel();
startButtonPanel.setBounds(200, 400, 200, 40);
startButtonPanel.setBackground(Color.BLACK);
startButton = new JButton("START");
startButton.setBackground(Color.BLACK);
startButton.setForeground(Color.WHITE);
startButton.setFont(fontStart);
startButton.setFocusPainted(false);
startButton.addActionListener(cj);
startButton.setActionCommand("start");
startButtonPanel.add(startButton);
//Load Button
loadButtonPanel = new JPanel();
loadButtonPanel.setBounds(200, 440, 200, 100);
loadButtonPanel.setBackground(Color.BLACK);
loadButton = new JButton("LOAD");
loadButton.setBackground(Color.BLACK);
loadButton.setForeground(Color.WHITE);
loadButton.setFont(fontStart);
loadButton.setFocusPainted(false);
titrePanel.add(nomJeu);
loadButtonPanel.add(loadButton);
frame.add(startButtonPanel);
frame.add(titrePanel);
//------------------ECRAN MENU FIN---------------------
frame.setVisible(true);
}
And the game class...
public class Jeu {
ChoixJeu cj = new ChoixJeu();
UI ui = new UI();
Ecrans e = new Ecrans(ui);
Gameplay gp;
public static void main(String[] args) {
new Jeu();
}
public Jeu() {
ui.createUI(cj);
Gameplay gameplay = new Gameplay();
this.gp = gameplay;
}
public class ChoixJeu implements ActionListener {
#Override
public void actionPerformed(ActionEvent ae) {
String yourChoice = ae.getActionCommand();
switch (yourChoice) {
case "start":
e.montrerEcranJeu();
new Thread(gp).start();
ui.frame.add(gp);
break;
default:
break;
}
}
}
}
I also tried to make a class/method that hides the menu panels
public void montrerEcranJeu() {
//Cache Menu
ui.titrePanel.setVisible(false);
ui.startButtonPanel.setVisible(false);
//Montre Jeu
// ui.frame.add(gameplay);
}
And just in case the Gameplay class. The run() method is at the bottom
public class Gameplay extends JPanel implements KeyListener, ActionListener, Runnable {
private Ship player = new Ship(new Point(200, 555));
Timer t = new Timer(5, this);
private ArrayList<Laser> lasers = new ArrayList<Laser>();
private int laserNb;
private boolean readytofire;
private boolean shot = false;
private ArrayList<Invader> invaders = new ArrayList<Invader>();
private boolean pause;
public Gameplay() {
super();
t.start();
addKeyListener(this);
setFocusable(true);
setFocusTraversalKeysEnabled(false);
for (int j = 0; j < 80; j += 20) {
for (int i = 0; i < 20; i++) {
invaders.add(new Invader(5 + i * 30, j));
}
}
}
public boolean addLaser(Laser a) {
lasers.add(a);
return true;
}
public boolean addPlayer(Ship p) {
this.player = p;
return true;
}
#Override
public void keyTyped(KeyEvent ke) {
}
public void keyPressed(KeyEvent e) {
if (KeyEvent.VK_RIGHT == e.getKeyCode()) {
moveRight();
}
if (KeyEvent.VK_LEFT == e.getKeyCode()) {
moveLeft();
}
if (KeyEvent.VK_SPACE == e.getKeyCode()) {
shoot();
System.out.println("Space Action from Gameplay is working");
}
}
#Override
public void keyReleased(KeyEvent e) {
}
#Override
public void actionPerformed(ActionEvent ae) {
repaint();
}
public void moveRight() {
if (player.getCentre().getX() >= 580) {
player.setX(580);
} else {
double movement = player.getCentre().getX();
movement += 10;
player.setX(movement);
}
this.repaint();
}
public void moveLeft() {
if (player.getCentre().getX() <= 20) {
player.setX(20);
} else {
double movement = player.getCentre().getX();
movement -= 10;
player.setX(movement);
}
this.repaint();
}
public void shoot() {
shot = true;
if (readytofire) {
Point top = new Point(player.getTopX(), player.getTopY());
Laser laser = new Laser(top);
addLaser(laser);
}
}
public void moveShot() {
if (shot) {
for (Laser l : lasers) {
l.setY(l.getTopLeft().getY() - 1);
}
}
}
#Override
public void paint(Graphics g) {
setBackground(Color.black);
super.paint(g);
player.draw(g);
for (Laser l : lasers) {
l.draw(g);
}
for (Invader i : invaders) {
i.draw(g);
}
}
// public void paintComponent (Graphics g){
// Controle Thread
public void run() {
while (true) {
moveShot();
for (Invader i : invaders) {
i.moveAndUpdate();
}
// for (Invader i : invaders) {
// if (){
// System.out.println("YOU ARE DEAD!");
// }
// }
try {
Thread.sleep(10);
readytofire = true;
} catch (InterruptedException ex) {
Logger.getLogger(Gameplay.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
So, using null layouts is the beginning of your problems. I might recommend using CardLayout which is designed to help you dynamically switch between views. See How to Use CardLayout for more details. I'd also suggest taking the time to read through Laying Out Components Within a Container and finding one or more appropriate layouts to support your menu.
You're also making a lot of fundamental mistakes. Swing is not thread safe, so you should avoid updating the UI (or something the UI depends on) from outside the context of the EDT - see Concurrency in Swing for more information and How to Use Swing Timers for a possible solution.
As a general recommendation, you should avoid overriding paint and, in the case of classes which extend from JComponent, prefer paintComponent instead. You should also avoid call methods which might change the state of the component during a paint cycle, this can increase the number of repaint requests and degrade the performance of your program (ie, don't call setBackground inside paint).
Have a look at Performing Custom Painting and Painting in AWT and Swing for more details about how the paint system works and how best you can work with it.
You should also avoid KeyListener, this is likely to cause you issues when you introduce other, focusable, components into the picture. Instead, you should favour the Key bindings API instead
I've read through [insert link or tutorial], but it still doesn't help...
And forgive me if this doesn't happen all the time.
The point of providing you the tutorial links is to encourage you to learn something;
Learn where to find answers to your questions
Learn how the APIs work
Expand your knowledge and understanding of how the APIs work
Having said that, they're not always "obvious" as to the solution. What I do when I'm in this situation is start with one or more new projects, dedicated to just working on that aspect of the API I'm trying to understand. For here I can explore the concepts in isolation and when I "think" I understand them, try and implement them into the project I'm working on. This might take a number of iterations, but once it works, I have gained a much deeper understanding and appreciation of the API then I would have gained from a simple "copy-n-paste" solution
Beginner Java developer. Trying to make a Tetris applet as part of my personal projects.
I'm at the point were I can draw tetris blocks onto the screen but I cannot make it vertically go downwards every second.
Code:
public class InitialScreen extends JApplet implements ActionListener {
public JPanel cards = new JPanel();
private JPanel introPanel = new JPanel();
public CardLayout c1 = new CardLayout();
public void init() {
initiateIntroScreen();
//game();
add(cards, BorderLayout.NORTH);
setSize(500, 100);
}
private void initiateIntroScreen() {
Frame title = (Frame)this.getParent().getParent();
cards.setLayout(c1);
JLabel centralWords = new JLabel("Click the following button options: 'Play' or 'Instructions'.");
JButton playBtn = new JButton("Play!");
JButton instructionsBtn = new JButton("Instructions!");
introPanel.add(centralWords);
introPanel.add(playBtn);
introPanel.add(instructionsBtn);
cards.add(introPanel,"1");
playBtn.addActionListener(this);
playBtn.addActionListener(new MainGame(cards,c1));
}
#Override
public void actionPerformed(ActionEvent e) {
setSize(300,410);
getContentPane().setBackground(Color.BLACK);
}
So this is the initial screen for the JApplet. Has two buttons. When you press the 'Play' button it goes to the Main Game Screen.
public class MainGame extends JApplet implements ActionListener {
private JPanel cards;
private CardLayout c1;
private JPanel gamePanel = new JPanel();
public MainGame(JPanel cards, CardLayout c1) {
this.c1 = c1;
this.cards = cards;
gamePanel.add(new Tetris_Block(new int[10][20]));
}
#Override
public void actionPerformed(ActionEvent e) {
JLabel scoreLbl = new JLabel("Score:");
gamePanel.add(scoreLbl);
cards.add(gamePanel,"game");
c1.show(cards,"game");
}
This is the game screen were Tetris is played. In the constructor it calls a Tetris Block.
public class Tetris_Block extends JComponent implements ActionListener {
static Color[] colors =
{darkGray, green, blue, red,
yellow, magenta, pink, cyan};
int[][] a;
int w, h;
static int horizontalPos, verticalPos = 0;
static int size = 20;
private int verticalPos1 = 1;
public Tetris_Block(int[][] a) {
this.a = a;
w = a.length;
h = a[0].length;
square_Block();
startTimer();
}
private void nextMove() {
verticalPos++;
verticalPos1++;
}
public void square_Block(){ //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = 3;
a[0][verticalPos1] = 3;
a[1][verticalPos] = 3;
a[1][verticalPos1] = 3;
}
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
}
public void startTimer(){
Timer timer = new Timer(1000,this);
timer.start();
}
public void paintComponent(Graphics g) {
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
g.setColor(colors[a[i][j]]);
g.fill3DRect(i * size, j * size,
size, size, true);
}
}
}
public Dimension getPreferredSize() {
return new Dimension(w * size, h * size);
}
My aim is to make the vertical position increment by 1 every second (So it goes down the window in second intervals.
I don't think the Timer function is the problem. When I print verticalPos it prints out the incremented value every second, however it's just displaying the new location onto the screen- that is the problem.
Image of window right now.
[img]http://i.imgur.com/au5fceO.png?1[/img]
Start by adding a call to repaint in you actionPerformed method of your Tetris_Block
#Override
public void actionPerformed(ActionEvent e) {
nextMove();
square_Block();
System.out.println(verticalPos);
// This is important
repaint();
}
This will schedule a paint event on the event queue which will eventually call your paintComponent method (indirectly)
This will get the block to start moving. The next problem you will have is you're not actually "removing" the block from it's previous position, so it will continue to bleed/grow down the screen
You could solve this by passing in the color of the block to square_Block, for example...
public void square_Block(int color) { //Horizontal || Vertical || Colour
//Horizontal never changes for this as I just want the blocks to go down.
a[0][verticalPos] = color;
a[0][verticalPos1] = color;
a[1][verticalPos] = color;
a[1][verticalPos1] = color;
}
And then "rest" the blocks of the current position, update the position and then set the new block colors;
#Override
public void actionPerformed(ActionEvent e) {
square_Block(0);
nextMove();
square_Block(3);
System.out.println(verticalPos);
repaint();
}
Your design here may be faulty. You need to have a game loop that runs in a separate thread. It has to be a separate thread from the main thread so the user can still click buttons. Once you have a loop in the separate thread you need to have a method that you call for every game tick. It's in that method that you update the coordinates of the blocks.
Game loop works like this:
1. Read state of the game and draw the blocks
2. Process user input.
3. Update game state
I know this is abstract but I hope it helps. Google about java games and game loops.
I'm trying to design a game like BeJeweled using java language
This is where I've reached so far :
public class Game {
public static void main(String[] args) throws InterruptedException {
final JFrame window = new JFrame();
window.setSize(508,669);
window.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
final Grid g=new Grid();
Game.obj(g);
window.add(g);
window.setVisible(true);
window.setResizable(false);
window.repaint(2);
window.addMouseListener(new MouseListener(){
#Override
public void mouseClicked(MouseEvent e) {
int x=e.getX();
int y=e.getY();
for(int i=0;i<10;i++) {
for(int j=0;j<16;j++){
if(x>i*50+5 && x<i*50+54 && y>j*40+26 && y<j*40+26+39){
g.b[i][j]=g.a[i][j];
int q = x;
int w=y;
int r =x;
int t =y;
q-=50;
w-=40;
if( i>0&&g.a[i-1][j]==g.b[i][j]){
g.a[i][j]=0;
g.a[i-1][j]=0;
}
if( j>0&&g.a[i][j-1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j-1]=0;
}
r+=50;
t+=40;
if(i<9&&g.a[i+1][j]==g.b[i][j]){
g.a[i][j]=0;
g.a[i+1][j]=0;
}
if(j<15&&g.a[i][j+1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j+1]=0;
}
}
}
}
SwingUtilities.updateComponentTreeUI(window);
}
#Override
public void mousePressed(MouseEvent e) {
}
#Override
public void mouseReleased(MouseEvent e) {
}
#Override
public void mouseEntered(MouseEvent e) {
}
#Override
public void mouseExited(MouseEvent e) {
}
});
}
public static void obj(Grid g){
Random r =new Random();
for(int k=0;k<10;k++)
for(int l=0;l<16;l++)
g.a[k][l]=1+r.nextInt(4);
}
}
class Grid extends JPanel {
private Graphics Graphics;
#Override
public void paint(Graphics g) {
final Graphics g1 = g ;
this.setGraphics(g);
for(int i=0;i<600;i+=50)
for(int j=0;j<400;j+=40)
g.drawRect(i, j, i+50, j+40);
for(int i=0;i<10;i++)
for(int j=0;j<16;j++){
if(a[i][j]== 0) g.setColor(Color.WHITE);
if(a[i][j] == 1) g.setColor(Color.ORANGE);
if(a[i][j] == 2) g.setColor(Color.red);
if(a[i][j] == 3) g.setColor(Color.GREEN);
if(a[i][j] == 4) g.setColor(Color.cyan);
g.fillRect(i*50+1, j*40+1, 49, 39);
}
// Mouselis mouseptr = new Mouselis(g);
// this.addMouseListener(mouseptr);
// this.addMouseMotionListener(mouseptr);
}
public void setGraphics(Graphics Graphics) {
this.Graphics = Graphics;
}
int [][] a= new int[10][16];
int [][] b= new int[10][16];
}
In this design only the up down left right of rectangles are being checked for same color in the method mouse clicked. How can i make it check all the near rectangles for the one having same color ?
Please help Thanks
You have four if statements that check the adjacent rectangles for the same color as the one that was clicked. Here is the last one:
if(j<15&&g.a[i][j+1]==g.b[i][j]){
g.a[i][j]=0;
g.a[i][j+1]=0;
}
j, the vertical grid coordinate, is compared with 15 to be sure that j+1 is within bounds. Also, a rectangle in the 2d-array a is checked to see if it is equal to the rectangle in the 2d-array b that got clicked. If it is, you set it to 0 for Color.WHITE. Because the indices are i and j+1 this checks the rectangle directly below the clicked one.
To check a rectangle that is diagonally adjacent, change both indices by one.
The rectangle to the bottom right is g.a[i+1][j+1]. To access this rectangle without an error you need to be sure that both i+1 and j+1 are within the bounds of the array to avoid an error.
So, the if statement is
if(j<15 && i < 9 && g.a[i+1][j+1]==g.b[i][j]){ ...
You can figure out the rest.
Java is a strong Object-Oriented programming language - especially before Java8: I think that you should really work with more Objects that would hold their own responsibilities and behaviours.
Once you'll have a class that represents a Jewel, just implement some recursive method to know if it's part of some "Jewel streak". For example:
public class Jewel {
private final JewelType type; // Could be an Enum for example
// Many other stuff to add (like a constructor at least!)
public Collection<Jewel> getStreak() {
final Set<Jewel> res = new HashSet<>();
this._getStreak(res);
return res;
}
private void _getStreak(final Set<Jewel> streak) {
// Just leave the method if we've already visited this Jewel
if(!streak.add(this)) {
return;
}
// Assuming that this method returns a Collection of the Jewels that are stored West, North, South and East from this
for(final Jewel neighbour : Grid.getInstance().getJewelsAround(this)) {
if(neighbour.getJewelType().equals(this.getJewelType())) {
neighbour._getStreak(streak);
}
}
}
}
When trying to see if some Jewel is part of a streak that is long enough to be "destroyed", just check something like this:
Jewel b = Grid.getInstance().getJewelForCoordinates(x, y);
final Collection<Jewel> streak = b.getStreak();
if(streak.size() >= Game.MINIMUM_STREAK_SIZE) {
Grid.getInstance().destroyJewels(streak);
}
I just don't even have access to any Java editor/compiler right now so please excuse me, would my code present any mistyping. Even though I recommend to do many changes, I hope it can help you :)
By the way, if you don't want to override all the methods described by the MouseListener interface, use the following:
window.addMouseListener(new MouseAdapter() {
#Override
public void mouseClicked(final MouseEvent event) {
// Code to be executed on mouseClicked().
}
});
MouseAdapter is an abstract class that implements MouseListener and does nothing when receiving any event. You just have to override the methods that handle the events you're interested in ;)
So I'm trying to make a Roguelike game with procedural generation etc. But I'm having trouble with the character handling.
The thing is when i want to move the character around with the keyboard, it is only changing its coordinates - which it should do of course, but i can't see it on the screen.
I read some answers on this site, and that helped me a little, but it didn't solve the problem entirely.
So, I'm note sure what I'm doing wrong, here's a bit of code (hope that it is not too long):
public class PanelTest extends JPanel implements KeyListener{
private static final long serialVersionUID = 1L;
private Game game;
private int width, height;
private int tileSize;
public PanelTest(Game game, int tileSize) {
super();
this.game = game;
this.tileSize = tileSize;
this.width = game.getMap().getWidth()*tileSize;
this.height = game.getMap().getHeight()*tileSize;
this.addKeyListener(this);
setPreferredSize(new Dimension(width, height));
setRequestFocusEnabled(true);
requestFocus();
setVisible(true);
}
public void paintComponent(Graphics g){
super.paintComponent(g);
for(int i=0; i < game.getMap().getWidth(); i++){
for(int j=0; j < game.getMap().getHeight(); j++){
g.setColor(game.getMap().getColor(i,j));
g.fillRect(i*tileSize, j*tileSize, tileSize, tileSize);
}
}
// print the correct coordinates
System.out.println(game.getPlayer().getX()+" "+game.getPlayer().getY());
}
#Override
public void keyPressed(KeyEvent e) {
// TODO Auto-generated method stub
switch(e.getKeyCode()){
case(KeyEvent.VK_Z) : game.getPlayer().moveUp();break;
case(KeyEvent.VK_S) : game.getPlayer().moveDown(); break;
case(KeyEvent.VK_Q) : game.getPlayer().moveLeft(); break;
case(KeyEvent.VK_D) : game.getPlayer().moveRight(); break;
}
revalidate();
repaint();
}
public void addNotify() {
super.addNotify();
requestFocus();
}
#Override
public void keyReleased(KeyEvent e) {
// TODO Auto-generated method stub
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
public static void main(String[] args){
JFrame frame = new JFrame();
frame.setBounds(0, 0, 400, 400);
Map map = MapFactory.createNewEmptyMap();
RoomMaker rm = new RoomMaker(map.getWidth()/7,map);
TunnelMaker tm = new TunnelMaker(rm, map);
rm.carveOut();
tm.carveOut();
Room r = rm.getRandomRoom();
Player p = new Player(map, r.getxCenter(),r.getyCenter());
Game game = new Game(map,p);
frame.getContentPane().add(new PanelTest(game, 10));
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.pack();
frame.setVisible(true);
}
}
Hope you can help, thanks !
By default a JPanel uses a FlowLayout.
It looks like your code is changing a players position, but then you invoke revalidate() which causes the layout manager code to be invoked and the location of your component is recalculated again.
So for games where you have random movement you want to use a null layout and you don't need to invoke revalidate() and repaint() since your code is not responsible for invoking the setLocation() method directly.
See also Motion Using the Keyboard for problems with using a keyListener.
setRequestFocusEnabled(true);
requestFocus();
setVisible(true);
The above is not needed. The two properties you are setting to true default to true. You can't request focus on a component unless the component is displayed on a visible GUI, so adding that code here does nothing. Also, the proper method to use would be requestFocusInWindow(), not requestFocus().
I am trying to flash the background colour in a textfield. My timer setup is as follows:
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
tmr.setInitialDelay(0);
tmr.setRepeats(true);
tmr.start();
My actionListener is as follows:
static class Flash implements ActionListener
{
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.white);
}
else
{
SpreademPanel.historyPnl.NameTxt.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
Now, when I put this in debug and follow the action, the program does repeatedly step through flash and toggle between the two alternatives. But onscreen, only the first toggle
occurs. After that, no action, although flash is still functioning.
What is wrong here?
Thanks in advance for any help.
This example continually varies the saturation of a panel's background color:
import java.awt.*;
import java.awt.event.*;
import java.awt.event.ActionListener;
import java.util.LinkedList;
import java.util.Queue;
import javax.swing.*;
public class FlashTest extends JPanel {
private static final Font font = new Font("Serif", Font.PLAIN, 32);
private static final String s = "Godzilla alert!";
FlashTest() {
this.setPreferredSize(new Dimension(256, 96));
this.setBackground(Color.red);
Timer t = new Timer(50, new Flash(this));
t.start();
}
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
g.setFont(font);
int xx = this.getWidth();
int yy = this.getHeight();
int w2 = g.getFontMetrics().stringWidth(s) / 2;
int h2 = g.getFontMetrics().getDescent();
g.setColor(Color.black);
g.drawString(s, xx / 2 - w2, yy / 2 + h2);
}
private static class Flash implements ActionListener {
private final float N = 32;
private final JComponent component;
private final Queue<Color> clut = new LinkedList<Color>();
public Flash(JComponent component) {
this.component = component;
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, 1 - (i / N), 1));
}
for (int i = 0; i < N; i++) {
clut.add(Color.getHSBColor(1, i / N, 1));
}
}
#Override
public void actionPerformed(ActionEvent e) {
component.setBackground(clut.peek());
clut.add(clut.remove());
}
}
static public void main(String[] args) {
EventQueue.invokeLater(new Runnable() {
#Override
public void run() {
JFrame f = new JFrame();
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
f.add(new FlashTest());
f.pack();
f.setVisible(true);
}
});
}
}
There are a couple of problems here.
The first obvious thing is that you appear to be using mutable statics. This is a really bad idea and indicates (and causes!) confusion. In this particular case, one of the problems caused is that the flasher static is shared.
Flash flash = new Flash(); //set up timer
tmr = new javax.swing.Timer(1000, new Flash());
tmr.addActionListener(flash);
We are adding two Flash actions. Ordinarily this would be bad, but just produce an undetectable "bug". The colour would be set twice.
Bring these two things together, and we have two actions without a break that perform the same toggle. Two toggles. The state does not change (although there are repaint, property change events, etc.).
So, don't use mutable statics, and keep the code clean.
tmr = new javax.swing.Timer(1000, flash);
I tried your code and it works fine.
Why do you use a static context for SpreademPanel.historyPnl.NameTxt?
EDIT
You might want to redesign your class to pass the component in the constructor.
private class Flash implements ActionListener
{
private boolean flasher = false;
private JComponent component;
public Flash(JComponent component) {
this.component = component;
}
public void actionPerformed(ActionEvent evt)
{
if (flasher)
{
component.setBackground(Color.white);
}
else
{
component.setBackground(Color.pink);
}
flasher = !flasher;
} //actionPerformed
} //Flash
and then init it with
Flash flash = new Flash(SpreademPanel.historyPnl.NameTxt);
Timer tmr = new javax.swing.Timer(1000, flash);
tmr.start();