Java-Gaming.org    
Featured games (91)
games approved by the League of Dukes
Games in Showcase (581)
games submitted by our members
Games in WIP (500)
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  
  formatting a string in java2d, adding new lines  (Read 1798 times)
0 Members and 1 Guest are viewing this topic.
Offline Kova

Senior Member





« Posted 2007-02-28 21:14:32 »

Hello.
Characters in my 2d game can say stuff and then text appears above their heads, like in a comic. So far I've been only able to figure out how to center text, but not how to divide it into multiple lines if text is too long. I'm using Java2d, drawString(). So I guess I could count the words, calculate their width and then do multiple drawString() since "\n" dosen't work in Java2d, but I guess this has all been automized somewhere.  Also it would be nice if I can get it justified. Can you show me where to look?
Thank you.
Offline irreversible_kev

Junior Member





« Reply #1 - Posted 2007-02-28 21:29:03 »

Take a look at LineBreakMeasurer - http://java.sun.com/javase/6/docs/api/java/awt/font/LineBreakMeasurer.html
Offline Kova

Senior Member





« Reply #2 - Posted 2007-03-01 15:16:12 »

Thank you, it is just what I needed.
API is very functional but that makes it complex also, much to be done for a simple thing such as breaking string for writing across multiple lines.

Anyway I figured out most of it, but still what I wrote doesn't work as expected. Here's the code. I want to start showing text 100 pix left of player and then warp it into new line after 100 pix right of player. The left border works just fine, but I don't understand what is wrong with the right one, it depends where player is!?  When I move player left (towards left side of screen) then warping border shirinks,  comes closer to player, shrinkin the text, and when I move player to right then it expands, goes away from player thus streatching the text. Please someone tell me what I'm missing

1  
2  
3  
4  
5  
6  
7  
8  
9  
10  
11  
12  
13  
14  
15  
16  
17  
...
// getPosX(), getPosY() return (x,y) coordinates of Player, RADIUS is radius of visible part of image of player (player image resembles to a circle)
...
            AttributedString text = new AttributedString(say);
            AttributedCharacterIterator char_iterator= text.getIterator();
            LineBreakMeasurer measurer = new LineBreakMeasurer(char_iterator, g2d.getFontRenderContext());
           
            Point2D.Float pen = new Point2D.Float(getPosX()-100, getPosY()-RADIUS-g2d.getFontMetrics().getHeight());
            float wrappingWidth = getPosX()+100;
           
            while(measurer.getPosition() < char_iterator.getEndIndex()) {
                TextLayout layout = measurer.nextLayout(wrappingWidth);
                pen.y += layout.getAscent();

                layout.draw(g2d, pen.x, pen.y);
                pen.y += layout.getDescent() + layout.getLeading();
            }


EDIT: sorry everyone, stupid me, I figured it out ... I thought that nextLayout() takes x coordinate as argument, didn't noticed it was actually width, even if variable was clearly named to imply that Smiley
Now I'm working to get the text centered... any ideas? Smiley

EDIT2: got it, for centered text use this while in loop for drawing:
1  
2  
3  
...
pen.x += (wrappingWidth - layout.getBounds().getWidth()) / 2;
...
Games published by our own members! Check 'em out!
Legends of Yore - The Casual Retro Roguelike
Offline Kova

Senior Member





« Reply #3 - Posted 2007-03-02 00:26:33 »

ok I've got a problem with this... I can't figure out how to get the text printed always above my player. Like if there is one line of text then print it right above him, if there are more then print the last line right above him. I should move pen.y up but I can't know how much lines of text there will be at the end. LineBreakMeasurer class doesn't seem to have any indication about that. One thing I can do is create a measurer object, iterate over it just for counting lines, then recreate measurer object and iterate for drawing them... but this is ultra stupid. There's got to be a way how to do this normally, anyone? Thank you.
Offline purpleguitar

Junior Member





« Reply #4 - Posted 2007-03-02 13:52:18 »

LineBreakMeasurer can give you a TextLayout object, which can give you all sorts of nifty information, including the text's bounding box.  Collect all of the TextLayouts prior to rendering them, then do your y-coordinate subtraction as appropriate.
Offline Kova

Senior Member





« Reply #5 - Posted 2007-03-02 15:52:17 »

I would need to store layouts in array, and either way I must first finish iteration over LineBreakMeasurer... what you suggest is as good as what I'm doing now, iterating over it and count the lines, then recreate LineBreakMeasurer and iterate again to display the text.
Shame that there isn't something like "countLayouts(width)" to see how much layouts would you get.
Offline Coinerson

Junior Member




Introducing the world's cutest zombie, Timmy


« Reply #6 - Posted 2007-03-08 04:16:57 »

So did you get this all figured out, if so can I see your  finished code. I did the same thing you are doing or did, and it seems I over-complicated the matter a lot if I am reading this right.. Ill go find it and post what I did.
Offline Kova

Senior Member





« Reply #7 - Posted 2007-03-08 16:36:33 »

well don't know exactly what do you need, but this draws text above some 2d point, and breaks it into multiple lines if length exceeds TEXT_AREA_WIDTH value... like in a comic when characters talk.
There are 2 problems I haven't studied yet: First one is that you need to know how much lines you have in order to move text up (normally it's written downwards) so, as I said, I iterate over LineBreakMeasurer twice, first is for counting the lines and second is for drawing the text. For now this seems to be the only way to do this. Second problem is that in first usage it stalls my simple game, noticeable glitch ~50ms, after that all fine, so you need to warm it up. Call it once when stall isn't noticeable before you start using it. If someone could explain this to me I would appreciate it Smiley

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  
    /** draws text above player, if is too wide splits it into multiple
     * lines, last line is always same amount of pixels above player
     * @param g2d - graphics to draw to
     * @see it seems this has something heavy to create and requires
     * one warmup before it is used, or it will stall the game ~50ms
     * first time it is used
     * @author Kova
     */

    void drawWhatIsSaid(Graphics2D g2d) {
        if (System.nanoTime() < say_time) {
            final int TEXT_AREA_WIDTH = 200;
            final boolean TEXT_CENTERED = true;
           
            AttributedString text = new AttributedString(say);
            AttributedCharacterIterator char_iterator= text.getIterator();
            LineBreakMeasurer measurer = new LineBreakMeasurer(char_iterator, g2d.getFontRenderContext());
            Point2D.Float pen = new Point2D.Float(getPosX()-TEXT_AREA_WIDTH/2, getPosY()-RADIUS-g2d.getFontMetrics().getHeight());
           
            int i=0; // TODO change this stupid counting of lines and do it the right way
           while(measurer.getPosition() < char_iterator.getEndIndex()) {
                measurer.nextLayout(TEXT_AREA_WIDTH);
                i++;
            }
            measurer = new LineBreakMeasurer(char_iterator, g2d.getFontRenderContext());
            while(measurer.getPosition() < char_iterator.getEndIndex()) {
                pen.x = getPosX()-TEXT_AREA_WIDTH/2;
                pen.y = getPosY()-RADIUS-g2d.getFontMetrics().getHeight();                
                TextLayout layout = measurer.nextLayout(TEXT_AREA_WIDTH);
                pen.y -= --i*layout.getBounds().getHeight();
                pen.y += layout.getAscent();
                if (TEXT_CENTERED)
                    pen.x += (TEXT_AREA_WIDTH - layout.getBounds().getWidth()) / 2;
                layout.draw(g2d, pen.x, pen.y);
                pen.y += layout.getDescent() + layout.getLeading();
            }
   
        }
    }
Offline Coinerson

Junior Member




Introducing the world's cutest zombie, Timmy


« Reply #8 - Posted 2007-03-10 07:03:05 »

Hrm thats way different than what I did. Gotta swithc partitins twice just to get it so I havn't bothered yet. I supose I can do it right now. Strange glichy things though.


here is mine which works 100% of the time and is not very re-usable. I should re-write it someday so its more flexible.

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  
125  
126  
127  
128  
129  
130  
131  
132  
133  
134  
135  
136  
137  
138  
139  
140  
141  
142  
143  
144  
145  
146  
147  
148  
149  
150  
151  
152  
153  
154  
155  
156  
157  
158  
159  
160  
import java.awt.*;
import java.util.Iterator;
import java.awt.image.BufferedImage;
 
/**
    AlertWindow is the window that pops up when you pick up a new Animal.
*/

public class AlertWindow {
    public static final int PORTRAIT_X_OFFSET = 30;
    public static final int PORTRAIT_Y_OFFSET = 22;
 
    public static final int PORTRAIT_STRING_X_OFFSET = 74;
    public static final int NORMAL_STRING_X_OFFSET = 30;
 
    public static final int START_STRING_Y_OFFSET = 25;
 
    public static final int STRING_OFFSET_BETWEEN_LINES = 15;
    public static final int FONT_SIZE = 12;
    public static final int BOX_IMAGE_TRANSPARENCY_OFFSET = 27;
 
    private static String text;
    private static BufferedImage portrait;
    private static BufferedImage image;
    public static boolean drawing;
 
    static int x, y, boxWidth;
 
 
    /**
        This sets the background box image, in case I ever want to change it
 
        @param i BufferedImage for background box
    */

 
    public static void setImage(BufferedImage i)   {
        image = i;
        x = (RunnerApp.WIDTH / 2) - (getWidth() / 2);
        y = (RunnerApp.HEIGHT / 2) - (getHeight() / 2);
        boxWidth = (RunnerApp.WIDTH / 2) + (getWidth() / 2)
            - BOX_IMAGE_TRANSPARENCY_OFFSET;
    }
 
    /**
        Gets the current background box image
 
        @return BufferedImage returns box image
    */

    public static BufferedImage getImage()  {
        return image;
    }
 
    /**
        Sets the animal portrait of the window
 
        @param np BufferedImage to reprent animal
    */

    public static void setPortrait(BufferedImage np)   {
        portrait = np;
    }
 
    /**
        Portrait of animal
 
        @return BufferedImage Portrait of animal
    */

    public static BufferedImage getPortrait()  {
        return portrait;
    }
 
    /**
        Returns what the animal says
 
        @return String animal quote
    */

    public static String getText()    {
        return text;
    }
 
    /**
        Sets what the animal says.
 
        @param t String for what the animal will say
    */

    public static void setText(String t)   {
        text = t;
    }
 
    /**
        Returns the height of the window
 
        @return Height of window
    */

    public static int getHeight()  {
        if (image == null)   {
            return 0;
        }   else    {
            return image.getHeight();
        }
    }
 
    /**
        Returns the width of the window
 
        @return Width of window
    */

    public static int getWidth()  {
        if (image == null)   {
            return 0;
        }   else    {
            return image.getWidth();
        }
    }
 
    /**
        Draw the window IF drawing is true
 
        @param g Graphics area to draw in
    */

    public static void draw(Graphics g)    {
        if (!drawing)   return;
 
        g.drawImage(image, x, y, null);
        g.drawImage(portrait, x + PORTRAIT_X_OFFSET,
                    y + PORTRAIT_Y_OFFSET, null);
 
        Font font = new Font("Helvetica Neue", Font.PLAIN, FONT_SIZE);
        g.setFont(font);
        g.setColor(Color.WHITE);
        FontMetrics metrics = g.getFontMetrics(font);
 
        int height = metrics.getHeight();
        int stringX, stringY;
        stringX = x + PORTRAIT_STRING_X_OFFSET;
        stringY = y + START_STRING_Y_OFFSET;
 
        int lastWhitespace = 0;
        int lastNewlineMarker = 0;
 
        for (int i = 0; i < getText().length(); i++)  {
            String string = getText().substring(lastNewlineMarker, i);
            if (getText().charAt(i) == ' ')   {
                lastWhitespace = i;
            }
            if (x + PORTRAIT_STRING_X_OFFSET
                  + metrics.stringWidth(string) >= boxWidth)   {
                    String s = getText().substring(lastNewlineMarker,
                                                    lastWhitespace);
                    lastNewlineMarker = lastWhitespace;
                    g.drawString(s, stringX, stringY);
 
                    stringY += STRING_OFFSET_BETWEEN_LINES;
 
            }
            if (i == getText().length() - 1)   {
                g.drawString(getText().substring(lastNewlineMarker,
                    getText().length()), stringX, stringY);
            }
        }
    }
}



And a link to the much more readable rafb paste.

http://rafb.net/p/9n4nH073.html

That code is very specific, a great feature is that my artist decided to leave random bits of transparency around the talky box window thing. So drawing it looked werid. And I was too lazy to trim it so I just added an offset. Thats the last one in the list.

Otherwise I think its fairly straightforward.
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.

xsi3rr4x (54 views)
2014-04-15 18:08:23

BurntPizza (52 views)
2014-04-15 03:46:01

UprightPath (66 views)
2014-04-14 17:39:50

UprightPath (49 views)
2014-04-14 17:35:47

Porlus (66 views)
2014-04-14 15:48:38

tom_mai78101 (90 views)
2014-04-10 04:04:31

BurntPizza (150 views)
2014-04-08 23:06:04

tom_mai78101 (246 views)
2014-04-05 13:34:39

trollwarrior1 (204 views)
2014-04-04 12:06:45

CJLetsGame (211 views)
2014-04-01 02:16:10
List of Learning Resources
by SHC
2014-04-18 03:17:39

List of Learning Resources
by Longarmx
2014-04-08 03:14:44

Good Examples
by matheus23
2014-04-05 13:51:37

Good Examples
by Grunnt
2014-04-03 15:48:46

Good Examples
by Grunnt
2014-04-03 15:48:37

Good Examples
by matheus23
2014-04-01 18:40:51

Good Examples
by matheus23
2014-04-01 18:40:34

Anonymous/Local/Inner class gotchas
by Roquen
2014-03-11 15:22:30
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!