10
\$\begingroup\$

I'd like to build a webapp for my game website that involves using text characters to represent animals and people, and have them move around on map squares with independent (server driven) AI.

So essentially, a dwarf-fortress map in the browser: dwarf-fortress-example With moving creatures, mobs, npcs and pcs. Though not that I'm not looking to acheive this scale, I'd probably start of showing a quarter of this content or so at any time.

Probably some of the background/immobile tiles could be loaded statically. But for creatures/animals and things that can move, I'm not sure what technology solutions would be most effective.

I'm aware of <canvas> though I don't know whether its capabilities fit this use case. Certainly some amount of javascript is going to be necessary.

Are there javascript libraries or canvas libraries out there that fit this use case? Another technology that I am not aware of? Anyone know of any examples of websites that have done anything similar to this, so that I can crib ideas from them?

\$\endgroup\$
2
  • 1
    \$\begingroup\$ Take a look at rot.js ondras.github.com/rot.js/hp \$\endgroup\$
    – Alayric
    Mar 26, 2013 at 23:15
  • \$\begingroup\$ Well, rot.js may be exactly what I was looking for, but didn't know it. \$\endgroup\$
    – Kzqai
    Mar 27, 2013 at 4:10

5 Answers 5

5
\$\begingroup\$

I have actually made a character display library for the web, Unicodetiles.js, which I have not only spent some time optimizing, but it also explores different ways of presenting the text; it has three renderers:

  1. DOM, which uses a matrix of <div> elements to render each glyph with a customizable foreground and background colors.
  2. Canvas, which draws the characters using the <canvas> element. This is much faster, and there are performance tests to back that up: http://tapiov.net/unicodetiles.js/tests/
  3. WebGL, which uses a canvas-element to create a font texture and then renders using WebGL, which is even faster and very scalable to big viewport sizes, but not as well supported in browsers.

Note that the linked performance tests can be pretty extreme, changing every character every frame. In practice, even the DOM renderer is fast enough for most purposes.

If you decide to make your own library, I'd still recommend using canvas because it seems to perform better, allowing bigger scenes. Using only WebGL will limit user base and it is complex to implement (Unicodetiles has an automatic fallback mechanism).


Another library, which I've heard suggested a lot recently is rot.js. It is specifically geared towards roguelikes as it comes with e.g. FOV system and dungeon generators. If you want a complete package, this might the way to go.

\$\endgroup\$
2
  • \$\begingroup\$ Nice. I can use that. Or at least learn from the way someone else did it to inform my own approach, since I want to make a roguelike, but not that like a roguelike. :D \$\endgroup\$
    – Kzqai
    Mar 27, 2013 at 21:29
  • \$\begingroup\$ @Tapio I am trying to implement pacman with unicodetiles, the problem is player always centered in the map, it's undesirable for pacman, can I disable it somehow, or can I get around without specifying the player. \$\endgroup\$ Oct 24, 2014 at 16:07
6
\$\begingroup\$

I think the most effective way would be just to fake it. Rendering to some target element using your own built in sprite font as if you're rendering a normal 2D screen. This approach makes sure no strange stuff happens when people are missing fonts, or are using a very different language (Chinese, Russian).

Fonts and texts are one of the most difficult thing to get pixel perfect in all locales on all browsers. Even when embedding a font and using some CSS magic browser and usability settings can still override and alter it. For normal websites pixel-perfect text isn't an issue but in games like Dwarf Fortress a few pixels can lead to an extremely incoherent view. Even when not using a browser but a normal application there are issues with text rendering. So even Dwarf Fortress itself uses the approach I described.

http://en.wikipedia.org/wiki/Dwarf_Fortress

On-screen displays use slightly modified code page 437 characters in 16 different colors implemented as bitmaps, rendered with OpenGL

Edit: because I got some comments I extended the answer a bit

\$\endgroup\$
6
  • \$\begingroup\$ Are other languages not extensions rather than replacements of ASCII? Why don't usual text-containing websites "fake it"? \$\endgroup\$
    – Anko
    Mar 26, 2013 at 15:11
  • 1
    \$\begingroup\$ Most character encoding problems can be prevented by using UTF-8 encoding. \$\endgroup\$
    – Philipp
    Mar 26, 2013 at 15:15
  • \$\begingroup\$ Russian has a different font than ASCII, and Chinese is even more different. Usual website care about aesthetics, which includes stuff like variable sized glyphs, kerning, nice "flow" of text etc. A DF-like game would care about regular placement of letters, which cannot be reliably done in a web browser. \$\endgroup\$
    – Liosan
    Mar 26, 2013 at 15:15
  • 1
    \$\begingroup\$ @Liosan sure it can. Just use the CSS declaration font-family:monospace; and the web browser will use the default monospace font. \$\endgroup\$
    – Philipp
    Mar 26, 2013 at 15:30
  • \$\begingroup\$ I'm not actually sure how this would be of benefit vs. an embedded css monospace-family web font? I mean, it would take more rendering, maaaybe it would allow for spacing issues to be solved more reliably, but with an embedded css font it wouldn't matter if people were missing the font or using a different language? Though I guess that would restrict operations to just the embedded web font's character set, hmmm. \$\endgroup\$
    – Kzqai
    Mar 27, 2013 at 1:14
3
\$\begingroup\$

To find out the number of lines and columns you need to output, you should check the window width and height and change it accordingly. Remember to listen to onResize events and modify the width and height accordingly.

When you want to do this the textual way, you could do this using text with a monospaced font and a table where each cell contains one character.

To address individual characters you could create a <table> with the correct number of rows and columns, where each <td> has an ID consisting of its x- and y- coordinates. That way you can address individual cells by ID and change their innerHTML to change the letter and change their css class to change their color.

Using a canvas, however, might be faster because you don't have to manipulate a large DOM tree for each character you have to replace. Dwarf Fortress is doing a similar thing, by the way. The characters which are used to represent objects are actually bitmaps, not true text output, and they are drawn using 2d graphic APIs. The HTML5 canvas is well-equipped for this. It has the context.fillText method which allows you to draw text on the canvas. This can be used to draw individual characters. You can change the size and font face by manipulating the variables context.font and the color of each letter by calling context.fillStyle.

Note that calling fillText hundreds of times per frame could be slow, because rasterizing fonts is expensive and no browser I know of uses caching. That means that when you render the same letter with the same settings a hundred times, it will be re-rasterized a hundred times. To increase the performance you could cache the rasterized appearance of each letter with each color on a hidden canvas and then draw these hidden canvases using context.drawImage. Copying from one canvas to another is usually a lot faster than font rasterization.

I am currently developing a 2d game using canvas, and noticed that the largest FPS eater was the font drawing. When I added a cache for rasterized text, it improved performance a lot.

\$\endgroup\$
4
  • \$\begingroup\$ Bitmap fonts are true text output too! I use them in a terminal all the time. Also, if a canvas is faster, why isn't StackExchange's text rendering canvas-based? \$\endgroup\$
    – Anko
    Mar 26, 2013 at 15:16
  • \$\begingroup\$ damn, now I want to write a library for that. \$\endgroup\$
    – Philipp
    Mar 26, 2013 at 15:25
  • \$\begingroup\$ @Anko terminal != web browser. What text rendering exactly are you referring to? \$\endgroup\$
    – Philipp
    Mar 26, 2013 at 15:26
  • 1
    \$\begingroup\$ Anko's saying that as bitmapped fonts are true-text, he can use them in his terminal - and that's thereby his proof that bitmapped fonts are true-text. \$\endgroup\$
    – Polar
    Mar 26, 2013 at 16:27
0
\$\begingroup\$

OK this is just a stab in the dark and I don't know how that renders.

Basically you use the same trick consoles (aka terminal) did in the olden days. First you start with a monospace font. You have M lines with N characters. So you simply dump the text into a div that is wide enough (width: N em?) and put every N characters a line break; in this case a <br/> instead of a \n.

The trick is to replace the buffer, either char by char or the entire contents at once with java script.

If you want to get really specific you can use the @font-face to ensure you have the same monospace font everywhere.

\$\endgroup\$
1
  • \$\begingroup\$ I also thought about doing that, but then I realized that it would not be a good architecture when you want to control the color and background-color of each individual character. \$\endgroup\$
    – Philipp
    Mar 26, 2013 at 16:32
0
\$\begingroup\$

Think in terms of glyphs. Decouple the display of the text from the meaning behind it. For example:

(pseudo code)

if (display.hitGlyph)
    glyph = Glyph.Asterisk;

display(glyph);

And then in your underlying code for defining the glyph atlas, simply do something as:

Glyph.Asterisk = "*";

The glyph atlas can in fact be a lookup into a ascii table with various encodings. The point here is merely to separate when to display, with what is to be displayed. I would recommend making a framework from scratch. It would give you more freedom.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged .