Java - Drawing Text in an Applet - java

I know there are a lot of answers for this but they don't seem to work. I don't know whether I am somehow using a different version of Java or the code for applet's is different but here's my code:
Snippet from HUD class:
public void paint (Graphics g)
{
if(g instanceof Graphics2D)
{
Graphics2D g2d = (Graphics2D)g;
g2d.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING, RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
g2d.drawString("This is gona be awesome", 200, 200);
}
Main Class:
import java.applet.Applet; (more, they just glitched with the code block)
public class Main extends Applet implements Runnable, KeyListener {
static ArrayList<Block> all_blocks = new ArrayList<Block>();
Player player;
HUD hud;
static int amount = 0;
public void init(){
setSize(700, 500);
addKeyListener(this);
}
public void start(){
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
Thread thread = new Thread(this);
thread.start();
}
#Override
public void run() {
while (true){
player.update();
repaint();
camera_scroll(this.amount);
try {
Thread.sleep(17);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public void stop(){
}
public void camera_scroll(int amount){
int new_amount;
if (amount > 30){
new_amount = 30;
this.amount -= 30;
} else {
new_amount = amount;
this.amount = 0;
}
for (Block b: all_blocks){
b.x += new_amount;
b.update();
}
player.x += new_amount;
}
public void paint(Graphics g){
for (Block block: all_blocks){
block.paint(g);
}
hud.paint(g);
player.paint(g);
}
#Override
public void keyPressed(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
player.moveLeft();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
player.moveRight();
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
player.moveUp();
break;
}
}
#Override
public void keyReleased(KeyEvent e) {
switch(e.getKeyCode()){
case KeyEvent.VK_LEFT:
case KeyEvent.VK_A:
player.stopLeft();
break;
case KeyEvent.VK_RIGHT:
case KeyEvent.VK_D:
player.stopRight();
break;
case KeyEvent.VK_UP:
case KeyEvent.VK_W:
player.stopUp();
break;
}
}
#Override
public void keyTyped(KeyEvent e) {
// TODO Auto-generated method stub
}
}
Drawing of the player and blocks work fine and their paint method is identical to HUD's but they draw images instead. Let me know if you need anything else. Thanks!

The Problem...
There are two main issues...
One...
You are creating instance of objects within the start method, when they should be created in your init method.
start can be called multiple times (normally after stop is called) to get the applet to continue. This is not a good place to try and initialize the state of your underlying applet, instead, it would be better used to restart the Thread (assuming you also stop it when stop is called)...
So, instead of...
public void init(){
setSize(700, 500);
addKeyListener(this);
}
public void start(){
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
Thread thread = new Thread(this);
thread.start();
}
you should be using something like...
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
public void start(){
Thread thread = new Thread(this);
thread.start();
}
Two...
You are creating a local instance of your objects, shadowing your instance fields, which is causing a NullPointerException (for me it was in paint...
So instead of
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
HUD hud = new HUD();
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
You should be using...
public void init(){
setSize(700, 500);
addKeyListener(this);
player = new Player(100, 100);
hud = new HUD();
// Notice that HUD has not been re-declared...
for (int i = 0; i < 75; i++){
new Grass(i*30, 400);
}
new Spikes(250, 370);
}
Side Notes
AWT was replaced by Swing some 15+ years ago, the API is no longer, generally, used. It would recomended to use JApplet instead...
Applets are notoriously difficult to develop and typically run within a very restrictive security sandbox. Unless you have a very particular need to use them, I'd recommend avoiding them for the time been and use a JFrame as you core, top level container.
It's generally a bad idea to paint directly to a top level container like an applet or frame, instead, it is generally encouraged that you move your core logic to something like a JPanel and override it's paintComponent method to perform custom painting. This is recommended for a number of reasons, including the fact that painting is actually quite involved and complicated and it is very easy to break the paint chain and top level containers aren't double buffered, which will cause flickering. Swing components (apart from there top level counterparts) are double buffered by default. Take a look at Painting in AWT and Swing and Performing Custom Painting for more details
KeyListeners are notorious for having issues related to focus. If you move to the Swing API, it is best to use the Key Bindings API
Also, Swing is not thread safe, just beware ;)

Related

How to draw random shapes into my frame with ArrayList? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 3 years ago.
Improve this question
I am keen to learn how I can finish this project. I want to draw three random shapes in random colors and positions and add them to the list (which later need to be played in the second window).
Here is what I have:
public
class Frame extends JFrame { // here was implements Runnable when i
// used Thread
public ArrayList<MyShape> shapesList = new ArrayList<MyShape>(30); // create list
public JPanel shapePanel;
public Timer timer;
public final int NEW_SHAPE_FREQUENCY = 1000;
public Rectangle myRect;
public Oval myOval;
public Line myLine;
public Frame() {
super("I do not need title, just 50%");
setSize(800, 600);
setDefaultCloseOperation(3);
setLocationRelativeTo(null);
setVisible(true);
setLayout(new BorderLayout());
shapePanel = new JPanel() {
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (MyShape s : shapesList) {
g.setColor(myOval.color); // in this line i have
// Exception: NullPointerException
g.fillRect(((int)(getWidth()*Math.random()*0.8d)),((int)
(getHeight()*Math.random()*0.8d)), (int) (myRect.x2),(int) myRect.y2);
}
}
};
add(shapePanel);
initTimer();
// Thread t0 = new Thread(this); // How can i use Threads in this case ??
// t0.start();
}
private void initTimer() {
timer = new Timer(NEW_SHAPE_FREQUENCY, e -> {
shapesList.add(new MyShape()); // Add a new shape to the arraylist
shapePanel.repaint(); // Repaint the panel, so the new shape is
// visible
});
timer.start();
}
/*
#Override
public void run() {
while (true) {
try {
Random rand = new Random();
switch (rand.nextInt(3)+1) {
case 1:
shapesList.add((Shape)myRect);
break;
case 2:
shapesList.add((Shape)myOval);
break;
case 3:
shapesList.add((Shape)myLine);
break;
}
repaint();
Thread.sleep(1000);
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
*/
}
After modification, the Frame class now looks like this. I am newbie with coding in Java sorry about this errors
First of all, take some time and read if multiple jframes is good or bad practice. After that, read how to use Swing Timers, in order to learn how you will make it paint a new shape every second (i am not sure if you want this feature, but it seems so). If you use Thread.sleep() method in a Swing application, the whole GUI will freeze since the thread is sleeping and events will not be able to take place.
The code you gave to us is not an Short, Self Contained, Correct (Compilable), Example since we will have to edit it in order to make it run. For that reason I've created an example in order to show you what i mean in practice. Do not forget to note the comments inside the code.
Code:
public class RandomShape extends JFrame {
private static final long serialVersionUID = 6617760910198114903L;
private static final int NEW_SHAPE_FREQUENCY = 1000; // every second new shape
private List<Shape> shapes = new ArrayList<>(); // Keep the shapes
private JPanel shapePanel;
private Timer timer; //A javax.swing.Timer
public RandomShape() {
super("Random shapes");
setDefaultCloseOperation(EXIT_ON_CLOSE);
setLayout(new BorderLayout()); // set border layout to the content pane
shapePanel = new JPanel() {
/*
* Override paint component in order to add custom painting to this panel. But
* of course, start by calling super.paintComponent()
*/
#Override
protected void paintComponent(Graphics g) {
super.paintComponent(g);
for (Shape s : shapes) {
g.setColor(s.color);
g.fillRect(s.x, s.y, s.width, s.height);
}
}
};
// Add it to the center of the border layout in order to take all window space
add(shapePanel, BorderLayout.CENTER);
setSize(400, 400);
setLocationRelativeTo(null);
initTimer();
}
private void initTimer() {
timer = new Timer(NEW_SHAPE_FREQUENCY, e -> {
shapes.add(new Shape()); // Add a new shape to the arraylist
shapePanel.repaint(); // Repaint the panel, so the new shape is visible
});
timer.start();
}
private static class Shape {
private int x, y, height, width;
private Color color;
private Shape() {
x = random(300);
y = random(300);
height = random(50);
width = random(50);
color = new Color(random(255), random(255), random(255));
}
private static int random(int max) {
return (int) (Math.random() * max);
}
}
public static void main(String[] args) {
// Start application in its own thread, called Event Dispatch Thread.
// For more info, read
// https://docs.oracle.com/javase/tutorial/uiswing/concurrency/initial.html
SwingUtilities.invokeLater(() -> new RandomShape().setVisible(true));
}
}
Preview:

Swing going from menu to actual gameplay

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

KeyHandler not responding to keyevents

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.

JPanel not updating after KeyPressed

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().

using Graphics g in inner class in java

Why can not I see the following output?
g2.drawString(new Date().toString(), 0, 150);
(i used g2 (global variable) for using g in paint method in inner class).
Thanks a lot in advance!
public class RedRect extends Frame
{
public static void main(String[] args){
SwingUtilities.invokeLater(new Runnable() {
#Override
public void run() {
new RedRect();
}
});
}
public static Point p;
RedRect()
{ super("RedRect");
try{
addWindowListener(new WindowAdapter()
{public void windowClosing(WindowEvent e){System.exit(0);}});
setSize (800, 600);
add("Center", new CvRedRect());
show();
}
catch(Exception ex){
}
}
}
class CvRedRect extends Canvas
{
Vector<Point> v=new Vector<Point>();
CvRedRect(){
addMouseListener((new MouseAdapter() {
public void mousePressed(MouseEvent evt){
if(v.size()<3){
v.add(evt.getPoint());
}
else{
flag=false;
//v.removeElementAt(0);
//v.add(evt.getPoint());
}
p=evt.getPoint();
repaint();
}
}));
}
Point p=new Point();
boolean flag=true;
int i=0;
public static Graphics g2;
public void paint(Graphics g)
{
try{
g2=g;
Dimension d = getSize();
int maxX = d.width - 1, maxY = d.height - 1;
g.setColor(Color.red);
g.drawRect(0, 0, maxX, maxY);
g.drawString("x ="+p.x, 10, 30);
g.drawString("y = " +p.y, 10, 60);
if(v.size()>2){
g2.drawLine(v.get(0).x,v.get(0).y,v.get(1).x,v.get(1).y);
g2.drawLine(v.get(0).x,v.get(0).y,v.get(2).x,v.get(2).y);
g2.drawLine(v.get(1).x,v.get(1).y,v.get(2).x,v.get(2).y);
Thread t=new Thread(){
public void run(){
try{
while(flag){
///The following comand//////
g2.drawString(new Date().toString(), 0, 150);
///////////////////////////
Thread.sleep(300);
System.out.println (v.size());
Thread.sleep(300);
}
}
catch(Exception ex){
System.out.println (ex.getMessage());
}
}
};
t.start();
}
//System.out.println ("size="+v.size());
if(!flag){
g.clearRect(0,0,maxX,maxY);
Thread.sleep(1000);
g.drawString("FINISH", 5, 30);
flag=true;
}
}
catch(Exception ex){
}
}
}
The reason your code isn't working is because you're using Graphics wrong. The Graphics object should not be a class field much less a static one. It is not a long-lived object, and so if you try to have it persist in this way, you'll end up with either a null reference or a non-null reference that doesn't work.
Instead if your program is an AWT program, then use the Graphics object inside of the paint method where the JVM supplies you with it when it calls paint. Otherwise if Swing do likewise with the paintComponent method.
Suggestions:
First and foremost, don't code AWT unless this is an absolute requirement for a class. Instead use Swing.
Next read the Swing graphics tutorials as they'll teach you all this and more.
Next do your drawing within the paintComponent(...) overload of a JPanel or other component that extends JComponent.
Don't forget to call the super.paintComponent(g) method within your override.
Don't forget to annotate your override with the #Override annotation.
Use a Swing Timer to do what you're currently using a while loop and Thread.sleep(...) to do.

Categories