Java Hangman GUI not properly displaying [closed]

2019-09-25 06:30发布

问题:

I'm having a couple of different problems with my code, most of them are GUI based problems but i do have one actionevent problem. I will post my code in sections first then I will point out what the issue is specifically with each section. *note all of my code will be in order of how is actually in my IDE.

If you wish to copy my code without all of the other stuff here it is on pastebin: http://pastebin.com/HHjRRtGZ

My imports:

import java.awt.*;
import java.awt.event.*;

import javax.swing.*; //Makes it a Japplet

import java.util.Random;

My Program Starts here:

public class Hangman extends JApplet implements ActionListener {

    // How many times you can guess the wrong letter till you lose
    static final int DEAD = 7;   

    private int errors;        // amount of errors
    private String message;   // Message displaying either Error or Victory
    private String information; // Secondary Message
    private String RealWord;      // The Real word
    private StringBuffer GuessWord;// The Guessed word
    private Button StartBtn;      // The Restart Button
    private Button GoBtn;         // The Go Button
    private TextField LetterBox; // The LetterBox

My ActionEvent:

    public void actionPerformed(ActionEvent e){

        if (e.getSource() == StartBtn){
            initGame();
        }

        if (e.getSource() == GoBtn){

            processTurn();

            LetterBox.setText("");
            repaint();
        }
    }

Problem One:

The problem that I am having with my Action event is that when I hit the StartBtn it does not reinitialize everything. Nothing is cleared. Nothing is done. It doesn't do anything. So that's the issue there.

My init() function (This is where one of my problems lie.

    public void init() {

        // Create a "Textbox" for the letter guessing
        LetterBox = new TextField();

        //Create my buttons and labels
        StartBtn = new Button("Restart");
        GoBtn = new Button("Go");

        //Add the elements to the applet

        JPanel p = new JPanel();
        p.setLayout(new FlowLayout());

        p.add(StartBtn);
        p.add(new Label("Guess a letter"));
        p.add(LetterBox);
        p.add(GoBtn);

        add(p, BorderLayout.SOUTH);

        //Make buttons event listeners
        StartBtn.addActionListener(this);
        GoBtn.addActionListener(this);

        //Startup the Game
        initGame();

    }

Problem 2 (MAIN PROBLEM):

The problems I have with my Init code that affect my GUI is that the actual word guess area and the hangman area are kind of messed up. It's hard to explain, so i'll show you an image. The Backdrop for almost all of the form is completely transparent. So it just uses a still image of whatever it was on top of as it's back ground.

Problem 3: There are some other issues with the image as you can see. The code is further down for those parts however. But as you can tell, the messages do not clear and just write over each other, even though I specify them to (which you will see further down).

Problem 4: Now before you guess any letters, the word is hidden with a "" but when you guess a correct letter for the word it's supposed to replace the "" with the correct letter guessed. However it just put's it on top of it (you will see the code for this below as well).

This is the Game initializer

    public void initGame() {
        //Set the errors to 0
        errors = 0;

        //Enter the wordslist, separated by a | here
        String str = "write|program|receive|positive|variables|temporary|good|bad|test";        
        String[] temp;


        //delimiter
        String delimiter = "\\|";

        // given string will be split by the argument delimiter provided.
        temp = str.split(delimiter);

        //Create the Random Seed Generator
        Random RandGenerator = new Random();

        //Generate my Random Number
        int randomInt = RandGenerator.nextInt(temp.length);

        RealWord = new String(temp[randomInt]);

        char positions[] = new char[RealWord.length()];

Right here is where it replaces the unguessed characters on screen with a "*".

        for (int i = 0; i < RealWord.length(); i++) {
            positions[i] = '*';
        }


        String s = new String(positions);
        GuessWord = new StringBuffer(s);

        LetterBox.setText("");

        //Delete Messages
        message = "";
        information = "";
        repaint();
    }

My Painting function

    @Override
    public void paint(Graphics g) {

        int BaseY = 250;

        //THE HANGING STAND
        if (errors > 0) { //1 Error
            g.drawLine(90, BaseY, 200, BaseY); //The ground
            g.drawLine(125, BaseY, 125, BaseY-100); //The bar going up
            g.drawLine(125, BaseY-100, 175, BaseY-100); //The sidebar
            g.drawLine(175, BaseY-100, 175, BaseY-75); //The Rope
        }

        //THE PERSON
        if (errors > 1) { 
           g.drawOval(170, BaseY-75, 10, 12); // The Head       
        }

        if (errors > 2) { 
           g.drawLine(175, BaseY-62, 175, BaseY-45); // The Body    
        }

        if (errors > 3) { 
           g.drawLine(165, BaseY-65, 175, BaseY-55); // Left Arm  
        }

        if (errors > 4) { 
           g.drawLine(185, BaseY-65, 175, BaseY-55); // Right Arm 
        }

        if (errors > 5) { 
           g.drawLine(170, BaseY-30, 175, BaseY-45); //Left Leg       
        }

        if (errors > 6) {  //7 Errors
           g.drawLine(175, BaseY-45, 180, BaseY-30); // Right Left 
        }


        //Show Messages/Errors
        g.drawString(message, 40, BaseY+25);
        g.drawString(information, 25, BaseY+45);
        g.drawString(new String (GuessWord), 140, BaseY-120);

        g.drawString(new String("WELCOME TO HANGMAN!"), 75, 40);
    }

This is where alot of the magic happens. This is the processTurn Function

    private void processTurn() {

        String s, t;
        char a;

        s = LetterBox.getText();
        a = s.charAt(0);

        if (!Character.isLetter(a)) {
            message = "Only enter letters please.";
            return;
        }

        if (s.length() > 1) {
            message = "One letter at a time please.";
            return;
        }

        //Check if letter has been used already
        t = new String(GuessWord);
        if (t.indexOf(s) != -1) {
            message = "You have already guessed with that letter!";
            return;
        }

        //If the letter you guessed does not occur in the Real Word
        if (RealWord.indexOf(s) == -1) {

            message = "";
            errors++;

            if (errors==DEAD) {
                message = "Sorry, you lose";
                information = "Click restart to try again!";

                //INSERT MOVING HANGMAN HERE.
            }

            return;
        }

This is where the "*" is supposed to be replaced with the correctl guessed letter but it doesn't work correctly!

        //Replace stars in the Guessed Word with the found letter
        for (int i = 0; i < RealWord.length(); i++) {
            if (RealWord.charAt(i) == a) {
                GuessWord.setCharAt(i, a);
            }
        }

        t = new String(GuessWord);

        //If all of the stars have been filled, then you win!
        if (t.indexOf('*') == -1) {
            message = "You have won!";
            return;
        }

        //Delete the Message
        message = "";
        repaint();
    }

Main Function

    public static void main(String[] args) {

        JFrame frame = new JFrame();
        JApplet applet = new Hangman();

        applet.init();
        applet.start();
        frame.add(applet);

        frame.setSize(300, 400);
        frame.setLocationRelativeTo(null); // Center the frame
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);


    }
}

Any and all assistance will be greatly appreciated :) Thanks in advance!

回答1:

This is caused by the basic fact that you have broken the paint chain.

Painting in AWT/Swing is made of a chain of method calls. If you neglect to maintain this chain, you start ending up with paint artifacts, in the form you are seeing.

The Graphics context is a shared resource, that is, every component painted during any given paint cycle will be given the same Graphics resource. Each component is expected to prepare the Graphics context before painting to it...

To fix the problem, in all your paint methods, you should be calling super.paint(g) to ensure that the paint chain is maintained and that the Graphics context is preapred for the individual components painting needs.

Having said that. It is not recommended to override paint of top level containers. Instead, it is recommended that you perform your custom painting using something like a JPanel and override it's paintComponent method instead (ensuring that you call super.paintComponent as well!)

This has a least two basic benefits. The first is, you can know decide where the panel should be displayed, for example, you could add it to an JApplet or JFrame or other container as you see fit. It is double buffered by default. This means you won't see any flicker when the painting is updated.

You should consider breaking your application down into small panels, focusing on each of the components needs separately. This will help reduce the complexity of your application and make it easier to manage the individual needs of each section of your application.

Take a look at Performing Custom Painting and Painting in AWT and Swing for more details



回答2:

The problem is that you're painting directly on JApplet. You should not paint directly on top level container such as JFrame or JApplet. Instead, use JComponent or JPanel. Override paintComponent() for painting rather than paint() and don't forget to call super.paintComponent(g).

Take a look at Performing Custom Painting tutorial for more information.

Consider refactoring your code by moving all the current logic and painting into a new JPanel which will be used as applet content. Then, add this panel to the applet.

EDIT:

The source of the problem is not calling super.paint() in you painting implementation. Overriding paint() is not necessary and usually not recommended in many Swing applications. JComponent.paint() (a superclass for all Swing components) handles painting the content, borders, and children of a Swing component. And by neglecting a call to super.paint() you are disrupting the painting of all these details.

Take a looks at A Closer Look at the Paint Mechanism for more details about painting cycle.