3

I am writing a roguelike in Elm, where there is a discrete 50x50 grid (see share-elm.com snippet). A roguelike is a video game, where objects (like enemies, items, walls, etc) are represented by ASCII characters. Therefore I should be able to have hundreds of different ASCII characters, aligned in a rectangular grid. Every character should be strictly within its grid cell.

To create this grid, I put every character in a square container (1/50 size of the actual game container). This means I can have 2500 containers in the game maximum. Elm creates <div> elements for containers, even if I convert these containers to Form and put them inside a collage. This makes my Firefox 39.0 very slow in performance.

How do I create a rectangular grid with nicely aligned ASCII characters (and possibly some other graphical elements) within its grid cells, so that no matter how many elements I have at the same time, the collage still stays quick and responsive? And what is the general idiomatic approach every time I'm writing a program with lots of containers and other elements inside a collage? Or maybe there is a completely different approach to creating snappy rectangular grids in Elm?

Mirzhan Irkegulov
  • 17,660
  • 12
  • 105
  • 166
  • I don't have a real answer because my experience is lacking in the optimization area. However, `elm-svg` might help. I honestly don't know if this will have better performance. – Joe Aug 09 '15 at 19:08

2 Answers2

1

One possibility (if you don't mind writing some HTML instead of using collage/container) would be to use the Html.Lazy module. You could, for example, wrap the rendering of each "row" of the display in a lazy and it would only re-render the rows that changed (which should only be 1-2 per timestep/movement).

robertjlooby
  • 7,160
  • 2
  • 33
  • 45
  • Thanks for the tip. However, check the share-elm.com snippet. I don't have any timestep yet, only 2500 characters inside containers on screen, yet the browser already becomes unbearably slow. How to solve this problem? – Mirzhan Irkegulov Aug 09 '15 at 19:14
  • If you look at the generated HTML, every single character is wrapped in 2 nested divs, each with CSS transforms to position it in the correct place. I have to think that is what is making it so slow. Even 2500 elements with more normal grid positioning markup I would expect to be much quicker. – robertjlooby Aug 09 '15 at 19:23
  • You're absolutely right, the performance has nothing to do with the number of divs, but with CSS transforms to each element. I'll add my solution to the problem as an answer, once I finish the code more or less. – Mirzhan Irkegulov Aug 09 '15 at 19:39
1

What you're looking for here is Graphics.Collage.text. When you turn an Element into a Form Elm will take the general approach that can place any Element like a Form, but it doesn't actually draw it on the canvas. (Yay, implementation details). If you instead go straight from Text to Form, it's statically known that it's text, so the faster method of drawing text on a canvas can be used. This is a simple change:

view : (Int, Int) -> Element
view (w,h) =
  let
    s = min w h -- collageSize
    forms = List.map (\(x,y) -> move (s,s) (x,y) playerForm)
               <| cartesian 0 (screenSize-1) 0 (screenSize-1)
    playerForm = "@"
               |> Text.fromString
               |> Text.height ((toFloat s) / screenSize)
               |> C.text
             --  |> E.centered
             --  |> E.container (s//screenSize) (s//screenSize) E.middle
             --  |> C.toForm
  in
    E.color Color.lightGray
       <| E.container w h E.middle
            <| E.color Color.white
                 <| C.collage s s forms

Instead of the three lines in comments, it's just the C.text. You can see the responsiveness in the updated share-elm snippet.
Note that you can no longer select the text! But otherwise it should be much better.

Apanatshka
  • 5,958
  • 27
  • 38