Nice! Mind telling how you did this? I'm currently working with some dialogue stuff myself, so I'm kind of curious
Thanks!
It's not perfected, still. I used a couple of sequenced variables, together with some constants. Every bubble has a TTL (time to live), computed as:
ttl = line_count * (KEEPALIVE + 18) + INTROTIME + FADETIME;
Where line_count * (KEEPALIVE + 18) represents the time (more precisely, number of logic cycles) required to scroll the text (18 represents line spacing), INTROTIME is time required to begin scrolling and FADETIME is time required for the bubble to wait until it is faded away.
So basically, the bubble gets created (it does some simple word wrapping and calculates a few variables in the process), does its thing, and gets destroyed once ttl is equal to 0.
This is the bubble logic function, it moves the offset sequentially:
void OxScene :: SpeechBubble :: DoLogic()
{
ttl--;
if (ttl >= FADETIME)
{
if (seqintro > 0)
seqintro--;
else
{
seq++;
if (seq > KEEPALIVE + 18)
seq = 0;
else if (seq > KEEPALIVE && offset < max_offset)
offset++;
}
}
}
This is the rendering function. It uses my engine's API to draw stuff but the code should be self-explanatory. I have two surfaces, bubbleSurface (containing pre-rendered bubble graphics) and textSurface (containing pre-rendered text).
void OxScene :: SpeechBubble :: DoDrawing(OxSurface* canvas, int x, int y)
{
OxSurface* buffer = new OxSurface(WIDTH, h);
buffer->ClearToMask();
buffer->render->Surface(bubbleSurface, 0, 0);
OxSurface* textDisplay = new OxSurface(WIDTH-20, h-26);
textDisplay->ClearToMask();
textDisplay->render->Surface(textSurface, 0, -offset);
buffer->render->Surface(textDisplay, 10, 10);
delete textDisplay;
if (ttl > 127)
canvas->render->Surface(buffer, x, y);
else
canvas->render->BlendSurface(buffer, x, y, 128, 128, 128, (unsigned char)(ttl*2));
delete buffer;
}
The way I ensure that the same bubble is not re-created is a bit naive. I check if there already is a bubble that uses the same x and y as origin. It's a bit dumb but since my NPC's won't be able to walk around, it sort of makes sense. And it is a lot easier to code than using some ID system.
This is the code that adds a new bubble. I have an "array of bubbles" to keep track of them.
void OxScene :: AddSpeechBubble(OxPrimitive* primitive, std::string text)
{
if (!text.size())
return;
for (SpeechBubbleArray::iterator it = bubbles->begin(); it != bubbles->end(); ++it)
{
if ((*it)->originalX == primitive->x && (*it)->originalY == primitive->y)
{
// bubble already exists, refresh it
(*it)->Regenerate();
return;
}
}
// add a new bubble
bubbles->push_back(new SpeechBubble(primitive, text));
}
And here is the "outer" logic, taking care of proper disposal:
for (SpeechBubbleArray::iterator it = bubbles->begin(); it != bubbles->end(); )
{
(*it)->DoLogic();
if (!(*it)->ttl) // destroy the bubble
{
delete *it;
it = bubbles->erase(it);
}
else // increment the iterator
++it;
}
(They are also destroyed in the Scene destructor, in case any of them are left on quit.)