Trying to display speech and narration in a non-blocking, transparent box SOLVED

Started by Akril15, Thu 06/08/2009 11:57:02

Previous topic - Next topic

Akril15

Hello,

Several times over the last few months, I've searched the forums in an attempt to find a simple way to display speech as opaque white text on top of a semi-transparent black box near the bottom of my game's screen (like in Torin's Passage and LSL7). I want the characters to be talking and/or moving in other ways as their dialogue is displayed as well.

The LSL7 GUI won't work with any of the AGS versions that I thought would support it, so I've tried to find other methods of imitating it. Obviously, the Sierra style with background option and the Display line won't work for what I have in mind, and there's no way that I know of to put a transparent background behind Lucasarts style text. I've tried working with SSH's Hypertext module, but unfortunately it proved to be much more complex and challenging than I first thought, and it looks like I won't be able to go with that option either. I've even tried displaying Lucasarts speech over a transparent GUI with the same text in it that I set to appear at the same time the dialogue did, but that method turned out to be too cumbersome.

Is there any other method that anyone can recommend? I would greatly appreciate some advice on how I might go about doing this.

Khris

You can use a transparent GUI with two or three labels and manually line-break the text.
In repeatedly_execute_always, add a few lines to display/hide, resize and reposition a second, black, semi-transparent GUI.

Code: ags
function repeatedly_execute_always() {
  GUI*g = gSpeech;
  gBlack.Visible = g.Visible;
  if (g.Visible) {
    gBlack.SetPosition(g.X, g.Y);
    gBlack.SetSize(g.Width, g.Height);
  }
}

GarageGothic

Use LucasArt speech mode, substitute the speech font with an invisible one (tolworthy posted a link to one just days ago).

Then write a custom speech command, that:

1) Displays the spoken text String on a GUI label at the bottom of the screen.
2) Calls Character.Say with the same text.
3) Removes the label at the bottom of the screen.

Since Character.Say is blocking, you don't need to add any timers or anything.

Akril15

GarageGothic: I had trouble figuring out how to implement your suggestion, but finally figured most of it out. Unfortunately, the speech is always displayed under the GUI. Is there any easy way to fix that problem? I'm also not sure how I can get each box and line of speech lined up correctly without a lot of trial and error, unless there's something I can do to simplify that, e.g., inserting a line of code that commands that every line of speech and every appearance of that text box be at specific coordinates and a specific width.

Also, I'm not sure how I should create a command like the one you described without having the first step be written out in every instance it occurs for each different message. I looked through the manual in an attempt to find how something like this could be done, and variables were the only thing that looked remotely like a solution, but I'm still drawing a blank.


And Khris, I tried using your solution as well, but I was a bit confused by it. Am I supposed to create GUIs called Speech, Black and G, then make G visible right before a character says something and hide it afterwards? I tried to figure out what the code meant in the manual, but I still wasn't able to completely grasp its meaning, or how to properly make use of it.

Apologies in advance for my incompetence. By the way, sorry for not mentioning this sooner, but I'm using AGS 2.72.

Khris

You'll just need two GUIs, gSpeech and gBlack. I'm using the local pointer "g" so I don't have to write "gSpeech" six times, plus you need to replace "gSpeech" with the name you're using only once, too.

All my code does is make sure that all the time, the black semi-transparent background duplicates the actual speech GUI in respect to its visibility, dimension and size.

Btw, you don't need to use manual line-breaking and labels if you use a global DynamicSprite, draw wrapped text onto it, then use it as background for the GUI.

I've tested the following and it seems like it works:

Code: ags
DynamicSprite*messagetext;

#define GWIDTH 300
#define GHEIGHT 30
#define GTIMER 1

void Sax(this Character*, String message) {
  if (messagetext == null) messagetext = DynamicSprite.Create(GWIDTH, GHEIGHT);  
  DrawingSurface*ds = messagetext.GetDrawingSurface();
  ds.Clear();
  ds.DrawingColor = this.SpeechColor;
  int w = GWIDTH - 20;
  int wt = GetTextWidth(message, Game.SpeechFont) + 5;
  if (wt < w) w = wt;
  gSpeech.Width = w + 20;
  gSpeech.X = (System.ScreenWidth - w - 20) / 2;
  int y = (GHEIGHT - GetTextHeight(message, Game.SpeechFont, w)) / 2;
  ds.DrawStringWrapped(10, y, w, Game.SpeechFont, eAlignCentre, message);
  ds.Release();
  gSpeech.BackgroundGraphic = messagetext.Graphic;
  gSpeech.Visible = true;
  SetTimer(GTIMER, ((message.Length / game.text_speed) + 1) * GetGameSpeed());
}

function repeatedly_execute_always() {
  GUI*g = gSpeech;
  gBlack.Visible = g.Visible;
  if (g.Visible) {
    gBlack.SetPosition(g.X, g.Y);
    gBlack.SetSize(g.Width, g.Height);
  }

 if (IsTimerExpired(GTIMER)) gSpeech.Visible = false;

}


No guarantees that this'll work with 2.72.

Akril15

Okay, I understand your first piece of code. I understood the gist of it, but the pointer confused me. Thank you for clarifying things for me.

I tried copying the new code you wrote up into the global script in my game in AGS 3.1.2. I set gSpeech as the text window GUI and made it a transparent window, but when I tested the game, using the Display command resulted in the appearance of a transparent window with opaque text that wouldn't eventually disappear like the code seems to imply it shoudl have. Am I doing something wrong?

Khris

You need to call the code e.g. like this:
  player.Sax("O RLY?");
I called it Sax because it's a letter before Say.

Don't use a Text window GUI for either of the two, btw.
Their Visibilty needs to be set to "Normally, initially off", and gSpeech should have the correct Y-Position and height.

Akril15

All right, I tried doing what you said, and unfortunately, I got this error message when I tried to test the game:

room1.asc(182): Error (line 182): '.Sax' is not a public member of 'Character'. Are you sure you spelt it correctly (remember, capital letters are important)?

I double-checked to make sure that I hadn't forgotten to capitalize the "S" in Sax, and since I was in a grasping-at-straws moment, I attempted typing "import void Sax();" into the script header, which made no difference. What am I missing this time?

Again, thank you very much for your help.

Khris

Apologies, you have to add this line to GlobalScript.ash:
import void Sax(this Character*, String message);

Akril15

Darn. I thought I was so close to success, but when I added (this Character*, String message); to the line I entered in Globalscript.ash, I got a new error message for the line in Globalscript.asc that reads:

function Sax(this Character*, String message) {

The error reads:

GlobalScript.asc(194): Error (line 194): Size of identifier does not match prototype

I couldn't find the terms "identifier" and "prototype" in the manual, and though I found this same error message brought up elsewhere on the forums, none of the proposed solutions seemed applicable to this particular case.

Khris

You changed "void" to "function" in the Globalscript.asc. Either change both or none.
(I used void instead of function because Sax doesn't return anything.)

Akril15

Ah, I see. I guess I shouldn't try tinkering with scripts at 3:00 AM.

That fixed the prototype error, but unfortunately, I've encountered yet another batch of problems. No matter what both GUIs' coordinates are, they are always displayed in the lower right corner. The text window won't disappear until the timer runs out, not responding to mouse clicks or keyboard key hits, and worst of all, my character's mouth doesn't move when the speech is displayed.

I'm trying to see if I can get GarageGothic's suggestion to work, but I'm not sure how I would go about that. I tried modifying the code that you provided me with to create another custom speech command, but it's mostly guesswork, and consequently it's a non-functional mess so far:

Code: ags
void Trans(this Character*, String message) {
  gLabel.Visible = true;
  labeltext.Text = (String message);
   gLabel.Visible = false;
}


I've created a GUI named gLabel with a label called "labeltext" on it. I'm trying to get it to replicate the character's speech in an invisible font in the third line of this code ( labeltext.Text = (String message); ) , but unsurprisingly, it doesn't work.

Khris

What resolution are you using?

I didn't incorporate any of the other stuff; this was meant to get you started, not to provide a full-fledged solution.

Akril15

Sorry, I'm using 800X600. Though I do appreciate your support, I'm starting to think that using DynamicSprite may not be the best method for me, though.

Akril15

I finally managed to find a solution to this problem with the help of GarageGothic. I used the suggestion he gave earlier in this thread, having the speech text be an invisible font and making a command to have a GUI with a label on it appear when speech is displayed and disappear after it times out.

For anyone else who is interested, this is the code he came up with that worked for me (inserted at the end of the GlobalScript.asc):

Code: ags
int trans_maxtextwidth = 300;
int trans_textmargin = 5;
int trans_guibottommargin = 5;

void Trans(this Character*, String message) {
 if (GetTextWidth(message, labeltext.Font) < trans_maxtextwidth) labeltext.Width = GetTextWidth(message, labeltext.Font) + 1;
  else labeltext.Width = trans_maxtextwidth;
  labeltext.Height = GetTextHeight(message, labeltext.Font, labeltext.Width);
  gLabel.Width = labeltext.Width + (2*trans_textmargin);
  gLabel.Height = labeltext.Height + (2*trans_textmargin);
  labeltext.X = trans_textmargin; //redundant if you set these coordinates in the GUI editor
  labeltext.Y = trans_textmargin; //redundant if you set these coordinates in the GUI editor
  labeltext.Text = message;
  gLabel.X = (System.ViewportWidth-gLabel.Width)/2;
  gLabel.Y = (System.ViewportHeight-gLabel.Height)-trans_guibottommargin;
  gLabel.Visible = true;  
  this.Say(message);
  gLabel.Visible = false;
 }


Also, be sure to import this by pasting this line into the Global Script header (GlobalScript.ash):
Code: ags
import void Trans(this Character*, String message);

SMF spam blocked by CleanTalk