Java-Gaming.org Hi !
Featured games (81)
games approved by the League of Dukes
Games in Showcase (513)
Games in Android Showcase (119)
games submitted by our members
Games in WIP (576)
games currently in development
News: Read the Java Gaming Resources, or peek at the official Java tutorials
 
    Home     Help   Search   Login   Register   
Pages: [1]
  ignore  |  Print  
  My most inefficient way of implementing running texts. How to optimize?  (Read 903 times)
0 Members and 1 Guest are viewing this topic.
Offline tom_mai78101
« Posted 2014-04-05 11:36:50 »

This is the "running text" that I'm talking about:

Click to Play


I'm inefficiently implementing that running text for a few hours, and I am starting to get discouraged of refactoring the code. I bloated it up, and I know I made it completely unreadable.

When I think of the running text, I came up with using just 4 variables in the beginning. 1 that remembers the beginning of a sentence, and the other 2 remembers the end of each line, where the dialog has 2 lines of sentences that can be used. For the text drawing, I need to use a Graphics object obtained from BufferStrategy, in order to draw the custom fonts that I have packaged into my JAR file.

For example, "Happening" and "tomorrow" are two words in one sentence.



Using lots of if...else conditional checks, I managed to have it line wrap if it can't fit in one line. But just that feature alone made me implement a lot of variables. So, I thought there might be a better way of efficiently implementing this.

'Twas just 4 variables, now it's like 10 of them. And I feel as if I'm doing something that I don't know how to explain.

Here's the code, using Pastebin:   http://pastebin.java-gaming.org/acdd63b9b8c

How do you optimize? And if possible, can anyone provide tips on efficient ways of implementing the running text?

Thanks in advance.
Offline jonjava
« Reply #1 - Posted 2014-04-05 11:44:13 »

Optimizing is unnecessary. Drawing a few lines of text, no matter how, has practically zero effect on performance.

Offline tom_mai78101
« Reply #2 - Posted 2014-04-05 13:09:46 »

 Huh

Does that mean the variables I decalred and used are okay, as long as the functionality works as intended? Soesn't that makes the code bloated too much?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Stranger

Senior Duke


Medals: 7



« Reply #3 - Posted 2014-04-05 13:55:03 »

It seems to me that your task is the job for java.awt.font.LineBreakMeasurer.

Anton
Offline Danny02
« Reply #4 - Posted 2014-04-05 13:57:03 »

I did not take a good look at your code, but I see no reason why you would need so much state(class variables). So you probably can optimize your code in a way, so that the logic is more clear and easier to understand. Wouldn't you only need just one state variable? Namely the current char of the sentence you are drawing?
Offline BurntPizza
« Reply #5 - Posted 2014-04-05 14:09:08 »

Seriously, "allocating" (likely on the stack) local variables and such is nearly the cheapest operation one can do. The drawString() calls and deriveFont() will dwarf the running time of anything else there. Don't worry about it.
Offline Pauler
« Reply #6 - Posted 2014-04-05 14:20:48 »

I just wrote this. Give it a try. It's small and works well.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
public static void printText() {
      final String message = "Hello there";
     
      new Thread(new Runnable() {

         String printedMessage;
         
         @Override
         public void run() {
            for (int i = 0; i < message.length() + 1; i++) {
               
               printedMessage = message.substring(0, i);
               
               //print it
               System.out.println(printedMessage);
               
               try {
                  Thread.sleep(200);
               } catch (InterruptedException e) {
                  e.printStackTrace();
               }
            }
         }
         
      }).start();
   }
Offline jonjava
« Reply #7 - Posted 2014-04-05 17:30:32 »

Sleeping isn't really an optimal solution for creating the typing effect. See this thread: http://www.java-gaming.org/topics/would-there-be-any-thread-sleep-problems/28774/msg/262512/view.html#msg262512

Offline Stranger

Senior Duke


Medals: 7



« Reply #8 - Posted 2014-04-05 19:30:15 »

Maybe it will help you:

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
package com.company;

import javax.swing.*;

public class Main
{

    public static void main( String[] args )
    {
        JFrame frame = new TextLayoutLineBreakerMeasurer();
        frame.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE );

        frame.setSize( 200, 200 );
        frame.setVisible( true );
    }
}


1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
package com.company;

import javax.swing.*;
import java.awt.*;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.font.FontRenderContext;
import java.awt.font.LineBreakMeasurer;
import java.awt.font.TextAttribute;
import java.awt.font.TextLayout;
import java.text.AttributedCharacterIterator;
import java.text.AttributedString;

public class TextLayoutLineBreakerMeasurer extends JFrame
{
    String m = "HAPPENING TOMORROW!!";

    int i;

    final javax.swing.Timer t;

    public TextLayoutLineBreakerMeasurer() throws HeadlessException
    {
        t = new javax.swing.Timer( 200,
                new ActionListener()
                {
                    public void actionPerformed( ActionEvent e )
                    {
                        if( ++i == m.length() )
                        {
                            t.stop();
                        }
                        repaint();
                    }
                }
        );
        t.start();
    }

    public void paint( Graphics g )
    {
        String s = m.substring( 0, i );
        if( s.isEmpty() )
        {
            return;
        }
        Graphics2D graphics2D = ( Graphics2D ) g;
        GraphicsEnvironment.getLocalGraphicsEnvironment();
        Font font = new Font( "Arial", Font.PLAIN, 24 );
        AttributedString messageAS = new AttributedString( s );
        messageAS.addAttribute( TextAttribute.FONT, font );
        AttributedCharacterIterator messageIterator = messageAS.getIterator();
        FontRenderContext messageFRC = graphics2D.getFontRenderContext();
        LineBreakMeasurer messageLBM = new LineBreakMeasurer( messageIterator,
                messageFRC );

        Insets insets = getInsets();
        float wrappingWidth = getSize().width - insets.left - insets.right;
        float x = insets.left;
        float y = insets.top;

        while( messageLBM.getPosition() < messageIterator.getEndIndex() )
        {
            TextLayout textLayout = messageLBM.nextLayout( wrappingWidth );
            y += textLayout.getAscent();
            textLayout.draw( graphics2D, x, y );
            y += textLayout.getDescent() + textLayout.getLeading();
            x = insets.left;
        }
    }
}

Anton
Offline tom_mai78101
« Reply #9 - Posted 2014-04-05 22:05:21 »

Thanks. I see a lot of unfamiliar classes being used, but the font setup and attributes portion looks easy to understand.

What is Inset? What does it do?
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Abuse

JGO Knight


Medals: 13


falling into the abyss of reality


« Reply #10 - Posted 2014-04-05 22:22:10 »

Thanks. I see a lot of unfamiliar classes being used, but the font setup and attributes portion looks easy to understand.

What is Inset? What does it do?

Insets

Make Elite IV:Dangerous happen! Pledge your backing at KICKSTARTER here! https://dl.dropbox.com/u/54785909/EliteIVsmaller.png
Offline DrZoidberg

Senior Duke


Medals: 15



« Reply #11 - Posted 2014-04-05 23:12:50 »

Here is another example.
It uses double buffering.

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
27  
28  
29  
30  
31  
32  
33  
34  
35  
36  
37  
38  
39  
40  
41  
42  
43  
44  
45  
46  
47  
48  
49  
50  
51  
52  
53  
54  
55  
56  
57  
58  
59  
60  
61  
62  
63  
64  
65  
66  
67  
68  
69  
70  
71  
72  
73  
74  
75  
76  
77  
78  
79  
80  
81  
82  
83  
84  
85  
86  
87  
88  
89  
90  
91  
92  
93  
94  
95  
96  
97  
98  
99  
100  
101  
102  
103  
104  
105  
106  
107  
108  
109  
110  
111  
112  
113  
114  
115  
116  
117  
118  
119  
120  
121  
122  
123  
124  
import javax.swing.JFrame;
import javax.swing.JPanel;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.awt.Font;
import java.util.ArrayList;

public class Text extends JPanel {
    static final int CHAR_WIDTH = 7;
    static final int CHAR_HEIGHT = 14;
   
    public static void main(String[] args) {
        JFrame frame = new JFrame();
        Text text = new Text();
        frame.add(text);
        text.setPreferredSize(new Dimension(800, 600));
        frame.pack();
        frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
        frame.setVisible(true);
        text.runningText("This is a running text. Words that don't fit completely on a line will automatically be moved to the next line.", 30, 30, 15, 100);
    }
   
    private void runningText(String str, int x, int y, int maxWidth, int delay) {
        new Thread() {
            @Override
            public void run() {
                String[] lines = toLines(str, maxWidth);
                int maxLineLength = maxLength(lines);
               
                Graphics2D g = image.createGraphics();
               
                g.setFont(new Font("monospaced", Font.PLAIN, 12));
                g.setColor(Color.BLACK);
                g.drawRect(x-5, y-5, maxWidth*CHAR_WIDTH + 10, lines.length*CHAR_HEIGHT + 10);
               
                for(int j = 1; j < str.length(); j++) {
                    g.setColor(Color.WHITE);
                    g.fillRect(x, y, maxLineLength*CHAR_WIDTH, lines.length*CHAR_HEIGHT);
                    g.setColor(Color.BLACK);
                    int count = 0;
                    loop:
                    for(int i = 0; i < lines.length; i++) {
                        for(int k = 0; k < lines[i].length(); k++) {
                            g.drawString(lines[i].substring(k, k+1), x+k*CHAR_WIDTH, y+(i+1)*CHAR_HEIGHT);
                            count++;
                            if(count == j) break loop;
                        }
                    }
                    repaint();
                    try {
                        Thread.sleep(delay);
                    } catch(Exception e) {
                    }
                }
               
                g.dispose();
            }
        }.start();
    }
   
    BufferedImage image = null;
   
    public Text() {
        initImage();
    }
   
    public void initImage() {
        int w = getWidth();
        int h = getHeight();
        if(w <= 0 || h <= 0) {
            w = 800;
            h = 600;
        }
        image = new BufferedImage(w, h, BufferedImage.TYPE_INT_RGB);
        Graphics2D g = image.createGraphics();
        g.setColor(Color.WHITE);
        g.fillRect(0, 0, w, h);
        g.dispose();
    }
   
    @Override
    public void setPreferredSize(Dimension d) {
        super.setPreferredSize(d);
        initImage();
    }
   
    static String[] toLines(String str, int maxLength) {
        ArrayList<String> lines = new ArrayList<>();
        String[] words = str.split("\\s");
        String line = "";
        int length = 0;
        for(String word: words) {
            if(length+word.length()+1 > maxLength) {
                lines.add(line);
                line = "";
                length = 0;
            }
            if(length > 0) {
                line += " ";
                length += 1;
            }
            line += word;
            length += word.length();
        }
        if(line.length() > 0) lines.add(line);
        return lines.toArray(new String[lines.size()]);
    }
   
    static int maxLength(String[] lines) {
        int length = 0;
        for(String line: lines) length = Math.max(length, line.length());
        return length;
    }

    @Override
    public void paint(Graphics _g) {
        Graphics2D g = (Graphics2D)_g;
        g.scale(3,3);
        g.drawImage(image, 0, 0, null);
    }
}
Offline Stranger

Senior Duke


Medals: 7



« Reply #12 - Posted 2014-04-06 06:46:51 »

If we deal with Swing it is enough to add JPanel.setDoubleBuffered( true ).

Anton
Offline tom_mai78101
« Reply #13 - Posted 2014-04-09 07:55:15 »

I've been trying to find a way how to line wrap if there exists a word that is MAX_STRING_LENGTH long, where MAX_STRING_LENGTH = 18 (defined by how many letters the dialogue box can fit in).

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
18  
19  
20  
21  
22  
23  
24  
25  
26  
   private String[] toLines(String all) {
      ArrayList<String> lines = new ArrayList<>();
      String[] words = all.split("\\s");
      String line = "";
      int length = 0;
      for (String w : words) {
         if (w.length() >= MAX_STRING_LENGTH) {
            line += w;
            length += w.length();
            lines.add(line);
         }
         else if (length + w.length() + 1 > MAX_STRING_LENGTH) {
            lines.add(line);
            line = "";
            length = 0;
         }
         if (length > 0) {
            line += " ";
            length += 1;
         }
         line += w;
         length += w.length();
      }
      if (line.length() > 0) lines.add(line);
      return lines.toArray(new String[lines.size()]);
   }


The problem that I'm having with words of length 18 or more is the way line wrapping works in the dialogue boxes.



Look at where the red box is boxing around. You can spot the letter "a" which is the first letter of the third word, "abcdefghijklmnopqr abcdefghijklmnopqr abcdefghijklmnopqr".

Couldn't find the right way to make it stop adding text to the already-36-characters-long "line" variable (not the "lines" ArrayList variable.).
Offline Stranger

Senior Duke


Medals: 7



« Reply #14 - Posted 2014-04-09 10:25:36 »

Couldn't find the right way to make it stop adding text to the already-36-characters-long "line" variable (not the "lines" ArrayList variable.).
I think you should check max height of the text that can be inserted in the window.

Also, IIRC, if the word > MAX_STRING_LENGTH it should be split on 2 parts:
first part =< MAX_STRING_LENGTH, the remainder is moved on the next line.

Anton
Offline actual

JGO Coder


Medals: 23



« Reply #15 - Posted 2014-04-09 10:30:00 »

Do you really intend on using words of 18 letters or more? It's nice to have a complete solution for these kinds of edge cases, but it may have little to no practical impact.
Offline tom_mai78101
« Reply #16 - Posted 2014-04-09 13:35:48 »

Do you really intend on using words of 18 letters or more? It's nice to have a complete solution for these kinds of edge cases, but it may have little to no practical impact.

It may not have impact overall, but there is a bug that affects all of the words. For some reasons, I wasn't able to get a few short words on the first line when the first line isn't full. Same goes for the second line, it wraps short words over to the next dialog.
Offline tom_mai78101
« Reply #17 - Posted 2014-04-10 02:04:56 »

Update:

So I managed to fix most of the issues regarding extreme and common cases of my dialogue.

Here's the code in its entirely:   http://pastebin.java-gaming.org/dd6bb5c978e

I had to abuse the use of exception handling in order to get them working as intended. I don't know if this counteracts with the standard practices.
Pages: [1]
  ignore  |  Print  
 
 
You cannot reply to this message, because it is very, very old.

 

Add your game by posting it in the WIP section,
or publish it in Showcase.

The first screenshot will be displayed as a thumbnail.

Longarmx (38 views)
2014-10-17 03:59:02

Norakomi (28 views)
2014-10-16 15:22:06

Norakomi (24 views)
2014-10-16 15:20:20

lcass (28 views)
2014-10-15 16:18:58

TehJavaDev (53 views)
2014-10-14 00:39:48

TehJavaDev (54 views)
2014-10-14 00:35:47

TehJavaDev (43 views)
2014-10-14 00:32:37

BurntPizza (64 views)
2014-10-11 23:24:42

BurntPizza (36 views)
2014-10-11 23:10:45

BurntPizza (78 views)
2014-10-11 22:30:10
Understanding relations between setOrigin, setScale and setPosition in libGdx
by mbabuskov
2014-10-09 22:35:00

Definite guide to supporting multiple device resolutions on Android (2014)
by mbabuskov
2014-10-02 22:36:02

List of Learning Resources
by Longor1996
2014-08-16 10:40:00

List of Learning Resources
by SilverTiger
2014-08-05 19:33:27

Resources for WIP games
by CogWheelz
2014-08-01 16:20:17

Resources for WIP games
by CogWheelz
2014-08-01 16:19:50

List of Learning Resources
by SilverTiger
2014-07-31 16:29:50

List of Learning Resources
by SilverTiger
2014-07-31 16:26:06
java-gaming.org is not responsible for the content posted by its members, including references to external websites, and other references that may or may not have a relation with our primarily gaming and game production oriented community. inquiries and complaints can be sent via email to the info‑account of the company managing the website of java‑gaming.org
Powered by MySQL Powered by PHP Powered by SMF 1.1.18 | SMF © 2013, Simple Machines | Managed by Enhanced Four Valid XHTML 1.0! Valid CSS!