简体   繁体   English

Java和Swing游戏循环

[英]Java and Swing with a game loop

(Not so) short question here. (不是)这里的简短问题。 I am working on making a simple roguelike using Swing as the UI rather than the console (Which makes it easier to work with in Eclipse, among other things) but I seem to have hit a roadblock. 我正在使用Swing作为UI而不是控制台来进行简单的roguelike操作(这使得在Eclipse中使用起来更容易),但我似乎遇到了障碍。

What I am having trouble with is that when I enter the game loop, the UI will not display properly. 我遇到的麻烦是,当我进入游戏循环时,UI将无法正确显示。 I get an ugly window frame that gives me the 'solitaire' effect during the entire thing and while running it quickly grows in RAM usage. 我看到一个难看的窗口框架,在整个过程中都给了我“纸牌”效果,并且在运行时,它的RAM使用量迅速增加。

Am I missing something critical about Swing here? 我是否在这里缺少有关Swing的批评? Is it mandatory that I use Swing's concurrency setup to do this? 我必须使用Swing的并发设置来做到这一点吗? If so, what would be the best approach? 如果是这样,最好的方法是什么?

The full code is below: 完整的代码如下:

package roguelike;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Font;
import java.awt.FontMetrics;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.util.LinkedList;

import javax.swing.*;
import javax.swing.text.*;

public class roguelike {
    Color c_black = new Color(0x000000);
    Color c_white = new Color(0xffffff);
    Color c_red = new Color(0xff0000);
    Color c_blue = new Color(0x0000ff);
    Color c_green = new Color(0x00ff00);

    UI ui = null;
    Player me = null;
    Map gamemap = null;
    LinkedList<Character> keyqueue = new LinkedList<Character>();

    int charheight = 20;
    int charwidth = 80;

    public static void main(String[] args){
        SwingUtilities.invokeLater(new Runnable(){
            public void run(){
                roguelike game = new roguelike();
                game.run();
            }
        });
    }
    public roguelike(){
        gamemap = new Map();
        ui = new UI(charheight,charwidth,"Monospaced");
        me = new Player();
    }
    private class UI extends JFrame implements KeyListener{
        private static final long serialVersionUID = 9065411532125953842L;
        JPanel disp_screen = null;
        JTextPane disp_screen_text = null;
        StyledDocument disp_doc = null;
        Font mono_norm = null;
        int pxheight = 0;
        int pxwidth = 0;
        String[][] ctemp = new String[charheight][charwidth];
        String[][] stemp = new String[charheight][charwidth];


        public UI(int h,int w,String fontname){
            setVisible(true);
            charheight = h;
            charwidth = w;
            addKeyListener(this);

            this.setResizable(false);
            mono_norm = new Font(fontname,0,25);
            initScreen(h,w);
            add(disp_screen);
            makeStyles();
            try {
                disp_doc.insertString(0, "12345678901234567890123456789012345678901234567890123456789012345678901234567890\n2\n3\n4\n5\n6\n7\n8\n9\n10\n11\n12\n13\n14\n15\n16\n17\n18\n19\n20", disp_doc.getStyle("Default"));
            } catch (BadLocationException e) {
                e.printStackTrace();
            }

            Dimension temp = disp_screen.getSize();
            this.setSize(temp);
            this.setDefaultCloseOperation(EXIT_ON_CLOSE);           


            pack();
            try {
                disp_doc.remove(0, disp_doc.getLength());
            } catch (BadLocationException e) {
                e.printStackTrace();
            }
            renderMap(gamemap.getText(),gamemap.getStyles());

        }
        @Override
        public void keyPressed(KeyEvent e) {
            // TODO Auto-generated method stub

        }
        @Override
        public void keyReleased(KeyEvent e) {
            // TODO Auto-generated method stub

        }
        @Override
        public void keyTyped(KeyEvent e) {
            keyqueue.add(e.getKeyChar());
        }
        private void initScreen(int height, int width){
            FontMetrics metric = getFontMetrics(mono_norm);
            String temp = null;

            //generate a string of the right width from which to grab metrics
            for(int x=0;x<=width;x++){
                temp += '.';
            }
            pxwidth = SwingUtilities.computeStringWidth(metric, temp);
            pxheight = (metric.getHeight())*charheight;

            disp_screen = new JPanel();
            disp_screen_text = new JTextPane();
            disp_screen_text.setEditable(false);
            disp_screen_text.setAlignmentX(LEFT_ALIGNMENT);
            disp_screen_text.setAlignmentY(TOP_ALIGNMENT);
            disp_screen_text.setFont(mono_norm);
            disp_screen_text.setBackground(c_black);
            disp_screen_text.setForeground(c_green);
            disp_doc = disp_screen_text.getStyledDocument();


            disp_screen.add(disp_screen_text);
            disp_screen.setAlignmentX(LEFT_ALIGNMENT);
            disp_screen.setAlignmentY(TOP_ALIGNMENT);
            disp_screen.setLayout(new BoxLayout(disp_screen,BoxLayout.Y_AXIS));
        }
        private void makeStyles(){
            //The default style removes all special formatting and returns the text to standard nonsense
            Style sty_default = disp_doc.addStyle("Default", null);
            StyleConstants.setFontFamily(sty_default, "Monospaced");
            StyleConstants.setFontSize(sty_default, 18);
            StyleConstants.setForeground(sty_default, c_green);
            StyleConstants.setBackground(sty_default, c_black);
            StyleConstants.setItalic(sty_default, false);
            StyleConstants.setBold(sty_default, false);
            //StyleConstants.setSpaceAbove(sty_default, 0);
            //StyleConstants.setSpaceBelow(sty_default, 0);

            //The following styles apply certain effects. They are meant to be set without replacing styles
            Style sty_bold = disp_doc.addStyle("Bold", disp_doc.getStyle("Default"));
            StyleConstants.setBold(sty_bold,true);

            Style sty_ital = disp_doc.addStyle("Italic", disp_doc.getStyle("Default"));
            StyleConstants.setItalic(sty_ital, true);

        }
        private void clearMap(){
            try {
                disp_doc.remove(0, disp_doc.getLength());
            } catch (BadLocationException e1) {
                e1.printStackTrace();
            }
            try {
                //CLEAR THE MAP
                //For every row...
                for(int y=0;y<charheight;y++){
                    //For every column location in a row...
                    for(int x=0;x<charwidth;x++){
                        disp_doc.insertString(disp_doc.getLength(),".", disp_doc.getStyle("Default"));
                    }
                    disp_doc.insertString(disp_doc.getLength(),"\n", disp_doc.getStyle("Default"));
                }
            } catch (BadLocationException e){
                    e.printStackTrace();
            }
        }
        public void renderMap(String[][] chars, String[][] styles){
            System.out.print("Rendering map...");
            clearMap();
            ctemp = chars;
            stemp = styles;

            try {

                //For every row...
                for(int y=0;y<charheight;y++){
                    //For every column location in a row...
                    for(int x=0;x<charwidth;x++){
                        if(ctemp[y][x] != null){
                            if(stemp[y][x] == "D"){
                                disp_doc.remove((y*charwidth)+x, 1);
                                disp_doc.insertString((y*charwidth)+x,ctemp[y][x], disp_doc.getStyle("Default"));
                            } else if(stemp[y][x] == "B"){
                                disp_doc.remove((y*charwidth)+x, 1);
                                disp_doc.insertString((y*charwidth)+x,ctemp[y][x], disp_doc.getStyle("Bold"));
                            } else if(stemp[y][x] == "I"){
                                disp_doc.remove((y*charwidth)+x, 1);
                                disp_doc.insertString((y*charwidth)+x,ctemp[y][x], disp_doc.getStyle("Italic"));
                            } else{
                                disp_doc.remove((y*charwidth)+x, 1);
                                disp_doc.insertString((y*charwidth)+x,ctemp[y][x], disp_doc.getStyle("Default"));
                            }
                        }
                    }
                }
            } catch (BadLocationException e){
                e.printStackTrace();
                System.err.print(e.getCause());
            }
        }
    }

    //Handles the virtualized information of characters on the map. Does NOT handle display of map on screen
    //Displaying the screen happens by passing required data to the ui member
    private class Map{
        //Holds an array of map characters
        String[][] text = new String[charheight][charwidth];
        //Holds an array of styles associated with each map character
        String[][] styles = new String[charheight][charwidth];


        public Map(){
        }
        public String[][] getText(){

            return text;
        }
        public String[][] getStyles(){
            return styles;
        }
        public void putch(String thing, String styledef,int y, int x){
            text[y][x] = thing;
            styles[y][x] = styledef;
        }
    }

    private class Player{
        //Player location, [y][x]
        int[] location = {0,0};
        String sym = "@";
        public Player(){
        }
        public int[] getLocation(){
            return location;
        }
        public String getSymbol(){
            return sym;
        }
        public void setLocation(int y, int x){
            location[0]=y;
            location[1]=x;
        }
        public void setSymbol(String newsym){
            sym = newsym;
        }
        public void move(int dir){
            //Movement will be handled in an 8 directional fashion
            //North is 1, like below
            //     812
            //     703
            //     654
            //////////////////////
            switch(dir){
                case 0:
                    break;
                case 1:
                    location[0] += 1;
                    break;
                case 2:
                    location[0] += 1;
                    location[1] += 1;
                    break;
                case 3:
                    location[1] += 1;
                    break;
                case 4:
                    location[0] -= 1;
                    location[1] += 1;
                    break;
                case 5:
                    location[0] -= 1;
                    break;
                case 6:
                    location[0] -= 1;
                    location[1] -= 1;
                    break;
                case 7:
                    location[1] -= 1;
                    break;
                case 8:
                    location[0] += 1;
                    location[1] -= 1;
                default:
                    System.err.print("ERROR! "+dir+" is not a valid direction!");
                    break;
            }
        }
    }

    /////////////////////////////////////////////////////
    /////////////FUNCTIONS///////////////////////////////
    /////////////////////////////////////////////////////
    public void run(){
        boolean running = true;
        while(running){
            //Render Map
            gamemap.putch(me.getSymbol(), "D", me.getLocation()[0], me.getLocation()[1]);
            ui.renderMap(gamemap.getText(), gamemap.getStyles());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            //Wait for player input
            if(keyqueue.isEmpty()){
                continue;
            }
            //Act on input
            else{
                char input = keyqueue.getFirst();
                keyqueue.removeFirst();
                if(input == 'w'){
                    me.move(1);
                }
                if(input =='a'){
                    me.move(7);
                }
                if(input == 's'){
                    me.move(5);
                }
                if(input == 'd'){
                    me.move(3);
                }
            }
        }

    }
}

Swing apps must be event-driven - your game.run() call will block Swing from doing anything else useful. Swing应用程序必须是事件驱动的-您的game.run()调用将阻止Swing执行其他有用的操作。 Note that "events" could be user events, timers, or probably other things. 请注意,“事件”可以是用户事件,计时器或其他可能的东西。

If your design needs game.run() or something like it to run and never return, it could be done on another thread (or even a SwingWorker background thread), but all UI components must be accessed on the Swing thread. 如果您的设计需要game.run()或类似的东西运行并且永远不会返回,则可以在另一个线程(甚至是SwingWorker后台线程)上完成它,但是必须在Swing线程上访问所有UI组件。

A recommendation: your keyboard handling could easily be done with a KeyListener you write, attached to an event handler on the window or other component, and then will be called whenever the user presses the key - ie you do not need to poll with this design. 建议:您可以使用编写的KeyListener轻松完成键盘处理,将其附加到窗口或其他组件上的事件处理程序,然后在用户每次按下该键时都将调用它-即,您无需使用此设计进行轮询。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM