Not logged inGosu Forums
Forum back to libgosu.org Help Search Register Login
Up Topic Gosu / Gosu Exchange / RPG like message windows and Gosu
- - By kyonides Date 2010-09-14 21:35
Well, I know how I can create a Font object and display it on screen but I wonder how I could manage to show a message window text slowly not all at once as it's the case right now with Gosu. Any ideas on how I could achieve this goal of including such a feature in my Gosu game?
Parent - - By arrow Date 2010-09-14 22:32 Edited 2010-09-14 22:35
I'm currently working on a class. Basically I'm adding one character at a time.
Here it is. Might give you an idea

#public functions
#TextBox::font 'arial', 14
#TextBox::speed = 0.25
#TextBox::text = "Hello World! This is my append text awesome typewriter thingy. I'm just writing text to test this."
#TextBox::finish #Just print out the whole thing now

class TextBox
  class << self   
    def font dir, size
       
        def update
          256.times do
            break if @index.to_int < @text_index
            break if @text_index >= @text.length
            @text_index += 1
            @output << @text[@text_index-1] if @text_index <= @text.length
          end
          @index += @speed
        end
        def draw
          @font.draw @output, 10, 10, 0
        end
     
      @font = Gosu::Font::new $view, dir, size
     
    end
   
    def speed=val
      raise ArgumentError, "TextBox: speed should be less than 40" if val > 40
      raise ArgumentError, "TextBox: speed should be positive" if val < 0
      @speed = val
    end
    def text=string
      @index = 0
      @text_index = 0
      @output = ""
      @text = string
    end
    def finish
      @output = @text
      @index = @text.length
      @text_index = @text.length
    end
    
  end
 
  def initialize; raise ArgumentError, "Cannot initialize TextBox" end
end
Parent - - By lol_o2 Date 2010-09-15 12:44
Good for "slow" text is method [] of string class combined with Range
Text[(0...@index)]
It will return part of text from beginning to current char.
Parent - By arrow Date 2010-09-16 07:22
definitely more ruby like :)
Parent - - By Maytsh Date 2010-09-16 11:09
I guess the real challenge here would be to implement proper word-wrapping... :)
Parent - - By erisdiscord Date 2010-09-16 13:39
Many console RPGs don't have word wrapping! Since the text and its container are static, line breaks are hard coded. Makes editing the text later a pain, though.

The simplest word wrap algorithm is pretty straightforward but it seems like there's no good way to do it except brute force so your time complexity is probably stuck at O(n); even worse for "prettier" algorithms.
Parent - - By Maytsh Date 2010-09-17 21:57
You have to read the whole string anyway, so saying that it's O(n) doesn't mean much. The dominating cost is probably the calls to text_width - and you should get away with calling it one time for each word in the text. After that, it's simple number munging, which shouldn't take too long (even in Ruby ;P ).
Parent - - By erisdiscord Date 2010-09-17 22:24
Yeah, you're right, and I doubt an RPG message box is going to need fancy TeX-style O(n**x) wrapping besides. The simple algorithm as I implemented it seems to run fast enough on a standard Lorem Ipsum text and it looks about as good as I'd expect for a game.

The real magic would be getting a fast implementation in Haskell, which I understand is also notoriously slow. ;P
Parent - - By RavensKrag Date 2010-09-18 17:43
If speed is a problem for implementing TeX-word wrapping, couldn't you just write a script to format the text before it's used in game? Thus, you could write your dialog into a text file, and then the script would take the text from that file, apply the necessary formatting, and then save it to another file.  This second file would be something that could easily be read back with the proper formatting, whether that be a binary, YAML, or simply another text file.  In this way, could could change the original dialog file.

Hope that makes sense.  Also, on that note, aren't there ruby bindings for either TeX or LaTeX?  Could those be used for formatting?
Parent - - By erisdiscord Date 2010-09-18 19:04
Mostly I'm just idly musing. I think it would be overkill to do anything but the simplest word wrapping algorithm in this case unless you're really, really particular about your game's typesetting. In which case, Gosu's Font class is probably not your first choice for text display anyway. :) I wonder what it would take to draw a PDF from OpenGL...

Besides, TeX itself is a massive behemoth of a system. I have the full texlive distribution with installed and it apparently weighs in at a staggering 2.13 GB. The basic version alone is 276.4 MB. I'd hate to inflict that on my audience. :) If I really wanted super-fancy typsetting in my game, I'd definitely do as you suggest and ship it pre-formatted.

As to bindings, I don't think there is a library interface, but I could be wrong.

On the other hand, you've just put some terrible, awesome ideas in my head and now I'm researching PDF rendering.
Parent - - By RavensKrag Date 2010-09-18 19:31 Edited 2010-09-19 00:46
Oh, I didn't mean PDF rendering, I was assuming that there was some way to get LaTeX to format a string.  Also, you wouldn't have to ship your program with LaTeX.  You would just have to "compile" your dialog into the typeset format before deployment.  Though, now that I think about it, this would make open-source games annoying, as you would not be able to simply write new dialog (for translation purposes or similar) and expect the proper formatting.

Is there a LaTeX light version for ruby then?

EDIT:
It seems like there are some ruby libraries that should handle the specific task of line wrapping better than a full-blown installation of TeX.  In particular, this one looks promising.
http://raa.ruby-lang.org/project/text-format/

EDIT 2:
Here's a regular expression that will allow for word wrapping.
http://blog.macromates.com/2006/wrapping-text-with-regular-expressions/

Also, I just realized that the topic of word wrapping is a little off-topic, so I will stop now ^_^;
Parent - By erisdiscord Date 2010-09-18 21:59
Don't mind me; I'm just letting my imagination go wild here. You might say I'm addicted to bad ideas. :)
Parent - - By jlnr (dev) Date 2010-09-18 23:21
Pre-rendering your text also makes it possible to use fonts that you are not allowed to ship with your game. I have to do this for my current game. Let me know if you get the type-setting any better than Gosu::from_text—I'd be interested too.

Right now, I use Gosu::from_text, then RMagick to apply a color gradient and then a Photoshop batch filter to increase the saturation. Would be awesome to have pro typesetting too. :D
Parent - - By erisdiscord Date 2010-09-19 00:25
Haha, that sounds like a hell of a workflow and I'm curious what it looks like. Got a screen shot to demonstrate?
Parent - - By jlnr (dev) Date 2010-09-19 17:33 Edited 2010-09-19 17:38
Imagine this on black during level load times. I dunno why the gradient doesn't look good if I just do it with RMagick. Right now it means running a Ruby script to create the PNGs from my YAML level list, then running the Photoshop action, so it's just two manual steps—still manageable.

The paragraph-formatting would be for the in-game dialogs which are longer, but I don't have a script for those yet. On a second thought, custom fonts and TeX? I smell trouble…
Parent - By erisdiscord Date 2010-09-19 18:49
Bahaha, yeah. TeX is big trouble, always. :D
Parent - - By Maytsh Date 2010-09-21 09:52 Edited 2010-09-21 09:57

> The real magic would be getting a fast implementation in Haskell, which I understand is also notoriously slow. ;P


Uhm, where does that understanding come from? Because of static typing, no side effects and no guarantees on execution order, Haskell can be optimized much more aggressively than most languages. According to notoriously subjective benchmarks, we are talking about "half as fast as C" here. That's quite a distance to Ruby's current "40 times slower" standing.

Disclaimer: I'm currently doing a PhD roughly on making Haskell programs go fast, so be careful ;)
Parent - - By erisdiscord Date 2010-09-21 14:19
A joke, of course. Haskell and Ruby both have reputations for being "too slow" for this and that among people who don't use them.
Parent - - By Maytsh Date 2010-09-23 12:56
Oh, sorry, didn't spot it. But well, as I said - discussing performance is futile anyway when all you have to do is loop over a few dozen numbers. That's the whole point behind having a game framework: To have the expensive bits in the library and the fun bits in a language that you can write quickly...
Parent - - By jlnr (dev) Date 2010-09-23 13:07
I think the bottleneck in my, and many Ruby games, is the collision detection, and it's not even easily extractable :(
Parent - - By Maytsh Date 2010-09-24 09:57 Edited 2010-09-24 10:03
Hm, isn't it? Not claiming that it is the best solution - but the Clonk approach is to have objects have bounding boxes, chunk game fields into sections, and then provide a generic "give me all objects overlapping rect X" (plus some) mechanic. Under the assumptions that objects are generally spread out evenly, that takes the O(n^2) out of the equation - for the tunable extra cost of having thousands of tiny object lists everywhere. And you could probably still tune that using quad-trees or something. Would you need more generality?

Edit:

> int? double? 2D only, or with a depth?


Sounds like a case for parameterization... :)
Parent - - By erisdiscord Date 2010-09-24 16:10
Sounds like spatial hashing? I have used this to great success.
Parent - - By Maytsh Date 2010-09-24 18:03 Edited 2010-09-24 18:05
Well, I guess. The question is: Would it be useful to have as a library?
Parent - By erisdiscord Date 2010-09-24 18:43
Probably not unless you're making a library for a very specific kind of game world. Chipmunk uses a sparse spatial hash for its broad phase collision detection (and other spatial queries), but obviously the way it's done doesn't translate too well to a tile based game unless you're willing to calculate a set of edges from your tilemap—which is not difficult but neither is it particularly straightforward. And then most tile based games probably don't want realistic physics, so you're leaving the major focus of Chipmunk unused then.

Most game logic beyond drawing and input seems to be pretty specialised and general solutions aren't usually that useful in my experience, but maybe I'm doing it wrong.
Parent - - By jlnr (dev) Date 2010-09-25 00:14
I agree with using rects usually, if you can have composite objects, but the game map may or may not be a bottleneck too, depending on how complicated its shape is. And game maps are even more a per-game thing :)
Parent - - By RavensKrag Date 2010-09-25 02:05
As of right now, my game map is basically handled in Chipmunk, with a Chipmunk sensor object functioning as the camera.  This allows me to know what objects to render, as well as what arguments to pass to translate(). 

Seems to be working pretty well, except that I can't seem to get the the sensor to actually detect objects.  Strange because my other collisions are working.  But that's a problem for another thread.  Just wanted to get my two-cents in on how I'm doing the game map.  However, I'm not using a tile-based layout, in favor of something more organic.
Parent - - By erisdiscord Date 2010-09-25 05:01 Edited 2010-09-25 05:04
Oh, huh, using a sensor to detect which objects need to be drawn is pretty clever, but isn't there a function to do a bounding box query for that? I mean, I know the C api has cpSpaceBBQuery. The declaration looks like this:

void cpSpaceBBQuery(cpSpace *space, cpBB bb, cpLayers layers, cpGroup group, cpSpaceBBQueryFunc func, void *data);

It would also be faster than using a rectangular polygon sensor. The only downside is that if you intend to rotate the camera then the BB query is right out—but you can also do shape queries I think?

Come to think of it, I don't know whether the Ruby bindings actually expose this or not.
Parent - - By RavensKrag Date 2010-09-25 17:24 Edited 2010-09-25 17:27
The ruby bindings to in fact expose the BB Query method, though due to the object-oriented nature of ruby it's a little more complicated. 

space.active_shapes_hash.query_by_bb BB.new(0,0,5,5)

This will return an array I believe.
But I do believe that this would be a static query.  By this I mean that the coordinates would not be automatically updated as the space scrolls, like with using a sensor object.  Thus, you would have to compute what to pass to translate() in some other manner.  Thus, I'm not sure that the added performance would be worth it.

I hope my logic makes sense.  Feel free to point out any flaws.
Parent - By erisdiscord Date 2010-09-25 19:16
Well, you'd still need to move the sensor object, right? If you're using a "viewport" or "camera" object then I suppose you could use a CP::BB to hold the scrolling position and do something like this:

class Viewport
 
  def scroll_to x, y
    @bb.l, @bb.t = x, y
  end
 
  def visible_objects space
    shapes = space.active_shapes_hash.query_by_bb @bb
    magically_get_objects_from shapes
  end
 
  def apply &draw_block
    $window.translate -@bb.x, -@bb.y, &draw_block
  end
 
end

class MyWindow < Gosu::Window
 
  def draw
    @viewport.apply do
      @viewport.visible_objects(@space).each { |obj| ... }
    end
  end
 
end


Perhaps I misunderstand your problem, though!
Parent - - By erisdiscord Date 2010-09-16 15:00 Edited 2010-09-16 15:08
I hacked together a class inspired by yours that has word wrapping and padding. Speed is characters per second. I haven't actually bothered to enforce height and it will probably hang if you enter a word that's wider than the box; I haven't tested it thoroughly.

class MessageBox
 
  attr_accessor :speed
  attr_reader :text, :font, :padding
 
  def initialize text, options = {}
    @x, @y = options[:pos]  || [0, 0]
    @w, @h = options[:size] || [$window.width, $window.height / 3]
   
    @speed = options[:speed] || 40
   
    self.padding = options[:padding] || 8
    self.font    = options[:font]
    self.text    = text
   
    @last_update = Gosu.milliseconds / 1000.0
  end
 
  def text= text
    @reveal = 0
    @text   = text
    calculate_wrap
  end
 
  def font= font
    if font
      @font = font
    else
      @font = Gosu::Font.new $window, Gosu.default_font_name, 16
    end
    calculate_wrap
  end
 
  def padding= padding
    @padding = padding
    calculate_wrap
  end
 
  def finish
    @reveal = @text.length
  end
 
  def update
    old_time, new_time = @last_update, Gosu.milliseconds / 1000.0
    dt = new_time - old_time
    @last_update = new_time
   
    @reveal += @speed * dt
  end
 
  def draw
    x, y = @x + @padding, @y + @padding
    @wrap.each do |range|
      if range.last > @reveal
        trunc_range = range.first ... @reveal.floor
        @font.draw @text[trunc_range], x, y, Float::INFINITY
        break
      else
        @font.draw @text[range], x, y, Float::INFINITY
      end
      y += @font.height
    end
  end
 
  protected
 
  def calculate_wrap
    return unless @font and @text and @padding
   
    box_width = @w - @padding * 2
    @wrap = []
   
    first_index = 0
    last_index  = test_index = @text.index(/\s|$/)
   
    while test_index
      if @font.text_width(@text[first_index ... test_index]) >= box_width
        @wrap << (first_index ... last_index)
        first_index = last_index + 1
      end
     
      last_index = test_index
      test_index = @text.index /\s|$/, last_index + 1
    end
   
    @wrap << (first_index .. last_index)
    @wrap
  end
 
end


Sorry for the lack of comments. Again, quick hack. It's yours if you want it, although I'd appreciate a small credit in that case. :)
Parent - By kyonides Date 2010-09-16 21:23
Well, since I'm just taking a look at the ideas posted here, I don't think I'll implement it the same way all of you did but it helps me somewhat indeed. Thank you for your suggestions but keep them coming if there are more!
Parent - - By arrow Date 2010-09-17 18:35 Edited 2010-09-18 18:38
I worked a little further on your message box. I'm sharing it though it's really messy. I'll clean it up later. :)
Use X to speed up and continue. Use C to skip through.

Edit: Uploaded a little cleaner version.
Attachment: RPGMessageBox.zip (16k)
Parent - - By kyonides Date 2010-09-17 23:29
Oh, thanks but the idea was not to use another scripter's script but get ideas on how I could do it on my own and check a couple of simple examples. Thanks again for your interest but there's no need to make the script for me if you don't have the time to do it. As I said, a couple of examples are more than enough.
Parent - By arrow Date 2010-09-18 09:38
I'm just making it because I'll use it myself. (Quick! Conquer the thread!)
Up Topic Gosu / Gosu Exchange / RPG like message windows and Gosu

Powered by mwForum 2.29.7 © 1999-2015 Markus Wichitill