• Announcements

    • khawk

      Download the Game Design and Indie Game Marketing Freebook   07/19/17

      GameDev.net and CRC Press have teamed up to bring a free ebook of content curated from top titles published by CRC Press. The freebook, Practices of Game Design & Indie Game Marketing, includes chapters from The Art of Game Design: A Book of Lenses, A Practical Guide to Indie Game Marketing, and An Architectural Approach to Level Design. The GameDev.net FreeBook is relevant to game designers, developers, and those interested in learning more about the challenges in game development. We know game development can be a tough discipline and business, so we picked several chapters from CRC Press titles that we thought would be of interest to you, the GameDev.net audience, in your journey to design, develop, and market your next game. The free ebook is available through CRC Press by clicking here. The Curated Books The Art of Game Design: A Book of Lenses, Second Edition, by Jesse Schell Presents 100+ sets of questions, or different lenses, for viewing a game’s design, encompassing diverse fields such as psychology, architecture, music, film, software engineering, theme park design, mathematics, anthropology, and more. Written by one of the world's top game designers, this book describes the deepest and most fundamental principles of game design, demonstrating how tactics used in board, card, and athletic games also work in video games. It provides practical instruction on creating world-class games that will be played again and again. View it here. A Practical Guide to Indie Game Marketing, by Joel Dreskin Marketing is an essential but too frequently overlooked or minimized component of the release plan for indie games. A Practical Guide to Indie Game Marketing provides you with the tools needed to build visibility and sell your indie games. With special focus on those developers with small budgets and limited staff and resources, this book is packed with tangible recommendations and techniques that you can put to use immediately. As a seasoned professional of the indie game arena, author Joel Dreskin gives you insight into practical, real-world experiences of marketing numerous successful games and also provides stories of the failures. View it here. An Architectural Approach to Level Design This is one of the first books to integrate architectural and spatial design theory with the field of level design. The book presents architectural techniques and theories for level designers to use in their own work. It connects architecture and level design in different ways that address the practical elements of how designers construct space and the experiential elements of how and why humans interact with this space. Throughout the text, readers learn skills for spatial layout, evoking emotion through gamespaces, and creating better levels through architectural theory. View it here. Learn more and download the ebook by clicking here. Did you know? GameDev.net and CRC Press also recently teamed up to bring GDNet+ Members up to a 20% discount on all CRC Press books. Learn more about this and other benefits here.
Sign in to follow this  
Followers 0
tom_mai78101

How to do running text efficiently?

8 posts in this topic

This is what a running text look like. The term itself could mean anything else, so I decided to put a GIF here for context.

 

1rYszXm.gif

 

And this is what I'm currently doing, using the code given below:

 

cI3JRSZ.png

 

I have created an inflexible running text code. It's not only bloated with variables, but also this is the only way I could think up of for the past few hours. But, it runs very fine. And now I want to optimize it.

	private String[] tokens;
	private int tokenPointer;
	private int beginningPointer;
	private int firstLinePointer;
	private int secondLinePointer;
	private int totalStringPointer;
	private boolean firstLineFull;
	private boolean secondLineFull;
	private Map<Integer, Boolean> dialogs;
	private boolean next;
	private boolean nextTick;
	private int stringPointer;
	private byte tickSpeed;
	private byte arrowTickSpeed;
	private String dialogueText;
	private boolean showDialog;

	public void renderTextGraphics(Graphics g) {
		if (this.dialogueText != null && !this.dialogueText.isEmpty()) {
			//The game uses 8f FONT when shown on the screen. It is scaled by GAME_SCALE.
			//Text are drawn with positive X = RIGHT, positive Y = UP. Not the other way around.
			g.setColor(Color.black);
			g.setFont(Art.font.deriveFont(Font.PLAIN, 24f));
			if (this.totalStringPointer <= this.dialogueText.length()) {
				//Handles one word sentences only.
				if (this.stringPointer > this.dialogueText.length()) {
					this.stringPointer = this.dialogueText.length();
					this.next = true;
					g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.stringPointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
					return;
				}
				
				if (this.totalStringPointer == this.dialogueText.length()) {
					this.next = true;
					g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
					g.drawString(this.dialogueText.substring(this.beginningPointer + this.firstLinePointer, this.dialogueText.length()), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextSecondLineStartingY());
					return;
				}
				
				if (this.firstLineFull && this.secondLineFull) {
					this.next = true;
					g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
					g.drawString(this.dialogueText.substring(this.beginningPointer + this.firstLinePointer, this.beginningPointer + this.secondLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextSecondLineStartingY());
					return;
				}
				
				//Handles more than one word.
				String text = this.tokens[this.tokenPointer];
				if (this.firstLinePointer + text.length() <= MAX_STRING_LENGTH) {
					if (this.stringPointer > text.length()) {
						this.firstLinePointer += this.stringPointer;
						this.secondLinePointer = this.firstLinePointer;
						if (this.tokenPointer < this.tokens.length - 1) {
							this.tokenPointer++;
							this.stringPointer = 0;
						}
						//Short sentences.
						if (this.totalStringPointer >= this.dialogueText.length() - 1) {
							g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
							return;
						}
					}
				}
				else {
					this.firstLineFull = true;
				}
				if (this.firstLineFull) {
					if (this.secondLinePointer + text.length() < MAX_STRING_LENGTH * 2) {
						if (this.stringPointer > text.length()) {
							this.secondLinePointer += this.stringPointer;
							if (this.tokenPointer < this.tokens.length - 1) {
								this.tokenPointer++;
								this.stringPointer = 0;
							}
						}
					}
					else {
						this.secondLineFull = true;
					}
					g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
					if (!this.secondLineFull) {
						if (this.secondLinePointer + text.length() < MAX_STRING_LENGTH * 2) {
							g.drawString(this.dialogueText.substring(this.beginningPointer + this.firstLinePointer, this.beginningPointer + this.secondLinePointer + this.stringPointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextSecondLineStartingY());
						}
					}
					else {
						g.drawString(this.dialogueText.substring(this.beginningPointer + this.firstLinePointer, this.beginningPointer + this.secondLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextSecondLineStartingY());
						this.next = true;
					}
				}
				else {
					g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer + this.stringPointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
				}
			}
			else {
				this.next = true;
				g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());
				g.drawString(this.dialogueText.substring(this.beginningPointer + this.firstLinePointer, this.beginningPointer + this.secondLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextSecondLineStartingY());
				return;
			}
		}
	}

For the background context: The problem for me is the way the running text works.

 

For each game update ticks, it draws a new letter at the end of the string. Once it reaches the end of the dialog box, it should automatically move itself to the next line. In order to accomplish this, I need to use three pointers. One for keeping track of the beginning of a string, because the area of the dialog box is limited, and dialogues are rarely just 2 lines at the most. The other two pointers keep track of each line, checking to make sure the text are staying in bounds of the dialog box.

 

When the text is more than 1 dialog box big, I swap the ones keeping track of the beginning of a sentence and the end of the sentence, so the beginning is now 1 sentence ahead, and erase the end of the old sentence so that it can keep track of the new sentence.

 

I thought using 3 variables is enough for me, but it turns out I need like 10 of them just to get the auto-newline part implemented.

 

--------------------------------------------------

 

I don't know how to optimize it more as I have been thinking alone on this part. Could anyone provide tips on how I should "optimize" the codes a bit? Thanks in advance.

 

EDIT: Apparently, there are still bugs in that portion of code. I am really thinking I'm not implementing the running text as well as I thought. Ugh!

0

Share this post


Link to post
Share on other sites

I don't know how to optimize it more as I have been thinking alone on this part. Could anyone provide tips on how I should "optimize" the codes a bit? Thanks in advance.

 

Why do you feel you need to "optimize" this code?  Is it not performing fast enough?  Have you profiled the code to see where the bottleneck is, and do you know how fast the code needs to run so that it meets your needs?  

 

If you mean that the algorithm is messy and you'd like to clean it up, that's different.  Refactoring will help with this.  http://www.cs.unc.edu/~stotts/723/refactor/chap1.html

 

Start by moving the algorithm to its own class.  Then move any common code to its own method so there is no duplication.  Then find any spots that are hard to understand, and rename variables and create methods with descriptive names to help you understand the code when you revisit it 3 months from now.  Finally, rearrange things until there is as much duplication as possible, then replace the duplicate with a single method.  

 

During this process you may find that some code isn't needed or there is a simpler way to get the same results.  By making the code easy to understand, you'll make it possible to optimize it later if you really need to.

Edited by Glass_Knife
2

Share this post


Link to post
Share on other sites

Oh, it's called refactoring? Understood.

 

Isn't using lots of state variables messy and bloatwise?

 

It does the job easily, it is just messy when you at it.

0

Share this post


Link to post
Share on other sites

Yes, you're talking about refactoring, or cleaning up the code.  Some book talk about how code just "smells" funny, which is actually a good way to explain it.  There isn't anything wrong with it, and there are no bugs, but...

 

http://sourcemaking.com/refactoring

 

Check this out for examples of refactoring you can do, but most of it self explanatory.  The goal of refactoring is clean code that works.  If it looks messy, refactor until its clean, but don't go so far that you have so many levels of indirection that you can't figure out what is going on.

0

Share this post


Link to post
Share on other sites

So since you are looking at replacing the algorithm (NOT refactoring, which leaves the algorithm fundamentally intact) I immediately wonder why you are reformatting everything each time. That seems like the first issue in the code.

 

Why not just format the whole thing once, then advance which letters are visible? This is especially true with automatic word wrapping, it is painful to watch a word start get spelled out on a line only to watch it pop to the next row mid-word as it exceeds the line length.  This gives you a single pass to format everything, and a much simpler pass to advance the length of the string. 

0

Share this post


Link to post
Share on other sites

It depends how simple you want the text display.

 

You can have a lot of fun if you want.  I like to use a very old technique. I have a byte array for the text and a byte array for the attributes. I use a fixed size font (which makes formatting a lot easier) and pass both the char and the attribute into a shader.

 

Think of it a bit like an old char mapped array. 

 

The attribute controls how the char is displayed. It can be as simple as colour, or as complex as you like. 

 

For example you could map the attribute to a 1D texture, anything over 128 could be transparent, then it could fade in, go through a bright flash and settle at the display colour.

 

When you update the text. If you are not adding any text you just run through the attributes and set up rules for how they change. Could be as simple as decrement until zero, or you could have ranges, anything you can think off.

 

When all attributes are stable (none changed in the current update), you can check to see if you need to change the text.

 

I know it's a very old idea, but a lot of old ideas are still good ideas.

0

Share this post


Link to post
Share on other sites


Why not just format the whole thing once, then advance which letters are visible? This is especially true with automatic word wrapping, it is painful to watch a word start get spelled out on a line only to watch it pop to the next row mid-word as it exceeds the line length.  This gives you a single pass to format everything, and a much simpler pass to advance the length of the string. 

 

Hm, never thought of that approach before. Going to work that out for a bit.

0

Share this post


Link to post
Share on other sites

Some good suggestions here. The only thing I can add here in terms of code is improvement is to mention the "DRY" principle (Don't Repeat Yourself) [1]. According to the definition, DRY applies mostly to higher-level architecture and design, but it applies to the small details of code as well.

 

For example, I see a lot of this repeated in your code:

g.drawString(this.dialogueText.substring(this.beginningPointer, this.beginningPointer + this.firstLinePointer), Dialogue.getDialogueTextStartingX(), Dialogue.getDialogueTextStartingY());

Try and see if you can eliminate some of the duplication.

 

[1] - http://en.wikipedia.org/wiki/Don't_repeat_yourself

0

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!


Register a new account

Sign in

Already have an account? Sign in here.


Sign In Now
Sign in to follow this  
Followers 0