3
\$\begingroup\$

I have a little 2D game made with Allegro, and I want to introduce strings during gameplay. For example, I've just made it so a key is needed to open a door, so I'd need some tutorial text to appear when the player collides with the door that tells them they need the key first.
This is just a learning experience for me, really - the key is pretty visible but I would like to learn how to handle this sort of messages especially for tutorial purposes in a bigger, better game.

Right now, during gameplay, I have running game_loop() (which handles player movement, collisions etc) and game_render(), which obviously runs after it. To introduce these messages, what I have successfully done so far is to add a global std::vector<const char*> to which I push_back messages whenever appropriate.

My message_renderer() then goes like so:

static double t = 0, c = 0;

    if (!messages.empty())
    {

    for (unsigned int i = 0; i < messages.size(); i++)
    {

        al_draw_text(font18, al_map_rgb_f(255, 255, 255), wWidth / 2, wHeight - 50, ALLEGRO_ALIGN_CENTRE, messages[i]);
        if (t < 2)
         {
           t += get_dt();
           c += get_dt();
            if (c > 1)
                  c = 1;
          }
        else
        {
        t = 0;
        c = 0;
        messages.pop_back();
        }

    }
    }

I then call this message_renderer() in my game_render() after I've drawn everything else, to ensure that the message appears on top.

Now, again, this does work - the message displays for 2 seconds at render time once it's added to the vector. However, this solution doesn't really seem particularly neat to me, since as far as I know there are standardised ways of dealing with these sort of events - I just don't know what they are!

So my question is - is my current method efficient enough? Can it be improved, in Allegro (or generally, in logic!) and if so, how?

Thank you in advance!

EDIT: I've modified the code since posting the question, so it should now behave like expected - showing a message for 3 seconds. It does so, but when I have two strings in the vector at the same time they overlap - I thought the timer would work as "pause" between each iteration but I guess not... If someone could point me in the right direction for that too, it'd be much appreciated :)

\$\endgroup\$
  • \$\begingroup\$ Why not use std::string instead of const char* ? You have the benefits of the standard C++ class, and you can retrieve the pointer needed by Allegro with std::string::c_str(). \$\endgroup\$ – teh internets is made of catz Aug 2 '14 at 21:48
  • \$\begingroup\$ Thanks for your reply! So as you say, I would have the text as std::strings (in my "text file"), then I'd add them to a vector of strings, and retrieve them with c_str()? Wouldn't that clash with Allegro's draw text function which needs a const char (as far as I'm aware)? \$\endgroup\$ – Nicholas Aug 3 '14 at 9:20
  • \$\begingroup\$ That's perfectly fine, C++ is able to construct std::strings from const char* and the c_str() function is here to get access to the underlying null-terminated character string, for cases like yours. \$\endgroup\$ – teh internets is made of catz Aug 3 '14 at 10:08
  • \$\begingroup\$ Fantastic, thanks very much - I'll give that a go :) \$\endgroup\$ – Nicholas Aug 3 '14 at 10:10
1
\$\begingroup\$

You can use std::ostringstreams to push anything (even non-char variables) into a stream and convert it to a string:

std::ostringstream ss;
ss << "Some text: " << some_variable;
//...
al_draw_text(font28, al_map_rgb_f(255, 255, 255), wWidth / 2, (wHeight / 2) - 28, ALLEGRO_ALIGN_CENTRE, ss.str().c_str());
ss.str(""); //Clear stream contents.
\$\endgroup\$
  • \$\begingroup\$ Thanks for your reply! Can I ask - did you suggest this because it's better or easier to use strings than chars? \$\endgroup\$ – Nicholas Aug 2 '14 at 19:48
  • \$\begingroup\$ @Nicholas std::strings are indeed easier to use, and much more powerful. But Casey's answer is about string streams, which are a different thing. Check out the documentation and don't forget to add ss.clear() to cleanly reset your stream (see stackoverflow.com/a/2848109/1119972). \$\endgroup\$ – teh internets is made of catz Aug 2 '14 at 21:53
  • \$\begingroup\$ I suggested using std::ostringstreams because if you ever wanted to output a number into a string it is a lot less of a hassle than trying to do it using a std::string. \$\endgroup\$ – Casey Aug 2 '14 at 23:34
  • \$\begingroup\$ Thank you both! I didn't know of the actual relationship between char and string in C++... I think it's starting to make sense now. I'll try ostringstream - cheers! \$\endgroup\$ – Nicholas Aug 3 '14 at 10:12
0
\$\begingroup\$

This is to address your overlapped strings described in your edit

al_draw_text contains arguments where you specify the position to start writing your string. Normally that means the first character will be at the specified position and the rest of the characters continue to the right (although this - the text alignment - can be changed via the flags argument).

However if you reuse this function to draw a second string without changing the position, it will not draw the second string to the right, or below the first string. It will draw the second string at the same starting position as the first string, resulting in overlapping text.

You have two options:

  • If you simply want to draw the two strings as if they were one, join the strings first and draw the joined strings in one go.
  • If you want to control the positioning yourself, specify a different position to draw the second string. If you want to make sure the text doesn't overlap, or for the layout to adapt to different fonts and sizes, you can use the drawn size of the first string by using al_get_text_dimensions or the related functions.
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.