I want to make Visual Novel Style conversations

Started by The Creator, Fri 18/01/2013 19:38:50

Previous topic - Next topic

The Creator

Hello, i'm trying to make VN-like conversations.

Something similar to this horrible drawn image i found:




To put it simple, i want to have a text box on the bottom part of the screen (if you could please tell me how to customize those, it would help me a lot too) with the image for the main character on top of the box on the left, and the other character on the right. I'm using Sierra-like conversations, but the talking view appears at top-left at the screen (for both characters).

Also, while the characters are indeed talking, i would like to be able to put some shadow effect on the background so that it gets darker and the main focus are the character who's speaking and the textbox. If the character stops stalking and the conversations switch to the other character talking, i want to put the shadow effect on the now listening character.

Thanks

Snarky

#1
To do this you'll have to write your own conversation system. I did something similar once:

[imgzoom]http://i55.tinypic.com/23m7o1i.png[/imgzoom]

You need to create a GUI that covers the entire screen. This is your "foreground" with the characters. Make the background 50% (or some suitable number) transparent black to give you the shadow effect you want. Create buttons (just because that's the simplest way to put sprites on GUIs) with the character images. Then create another GUI for the text window, with a button for the frame graphic and a label for the text.

Then you just write an extender function that puts up the GUIs with the right graphics and text when you want your character to speak. In my case the code looks a little like this:

Speech.ash:
Code: AGS

// Based on the Speech closeup module by Mazoliin
import function Speak(this Character*, String toSay);


Speech.asc:
Code: AGS

#define TEXT_LABEL SayLabel // The label where the text will be written
#define TEXT_TITLE SpeakerNameLabel // The label where the name caption is written
#define TEXT_FRAME textBkgrdButton // The button used for the background of the text
#define TEXT_GUI gCloseUp // The gui the text is on
#define CHARACTER_BUTTON closeupButton// The button where the speech animation will be drawn
#define CHARACTER_GUI gCloseupChar // The GUI the actual closeup is on

function Speak(this Character*, String toSay)
{
   ...
}


In the simplest case, where you just want the text to appear, you just assign the character speech views to the button graphics (in your case, you will need another parameter for the second character), and the text to the text label, then make the GUI visible, block waiting for user input, then make the GUI invisible again. If you want "typewriter" text, character speech animation, etc., there's a bit more coding to make all that work.

The Creator

#2
Thanks for the answer.

I do want to have a text animation similar to the typewriter text you mention and i also want to be able to switch the sprites in certain moments of the dialog (i don't want them to loop, just a static sprite that changes whenever i want it to change). I would also like to have dialog options/branches.

I've created the foreground GUI to give the shadow effect, and another one for the dialog box.  I've searched the tutorial/manual that comes with AGS and the forums a bit, but i can't find a newbie guide to buttons and have no idea how to use them. I've the same problem with labels. If i select a GUI and look in the properties panel, they have a label nor a button option so i'm guessing i've to do those with code.

Would you mind explaining a little bit more? If you could give me examples on how to code those things, that would be awesome.

Thanks a lot.

EDIT: I've been poking around and found how to add a button to the GUI. But i'm still a bit confused. I'm going to have many characters, so the sprites must be able to be changed dynamically. If i create two buttons on that GUI without an image assigned, will i be able to assing/change it's image/sprite dynamically whenever i need too?

ddq

If instead of making an adventure game with VN-style conversations, you plan on making a full-fledged visual novel, use renpy. Coding a VN in renpy is *literally* trivial.

The Creator

#4
I knew about it, but since my main focus is a character moving through different backgrounds and picking up items; AGS seemed like a good choice.


EDIT: I've been poking around and found how to add a button to the GUI. But i'm still a bit confused. I'm going to have many characters, so the sprites must be able to be changed dynamically. If i create two buttons on that GUI without an image assigned, will i be able to assing/change it's image/sprite dynamically whenever i need too?

ddq

Yes, Button.NormalGraphic. You can still create it with one assigned, it matters not.

Snarky

#6
Yup, it can all be done, Creator, but it's going to take a bit of coding. I'm happy to help you out, but I recommend you poke around in the manual (F1 from within the AGS editor) for the scripting reference, since you'll need to look up a lot of stuff sooner or later. (In fact, the whole thing isn't that long, so once you have the basics down, you might as well skim the whole thing.)

To set the graphic sprite that will appear on a button, you just assign a sprite number to its NormalGraphic property:

Code: AGS
myButton.NormalGraphic = 99; // Where 99 is the sprite slot of the character portrait


To make it play an animation, you call Animate with a view, a loop, a delay and a repeat option:

Code: AGS
myButton.Animate(99, 0, 4, eOnce); // View 99, loop 0


(You should of course code it so that you get the right character graphic from the character instead of having to hardcode it for each method call.)

As for the typewriter text effect, I'd leave it out for now, until you've got the rest of it working. Branching dialog is built into AGS (see the end of the thread I linked to for a discussion of how to integrate dialog scripting with these custom conversation styles), but if you don't want the default look and feel there'll be some further customization and coding involved. There's an explanation of that in the manual.

So it's going to take a fair bit of work, I'm afraid. If you get stuck, I could post some more sample code to help you out, but I'm not sure I'll have the time to adapt my code to your specific requirements.

The Creator

#7
I've adapted the function Sephiroth posted in your thread. This is what i have now:

Code: AGS
function Speak(int namecolour,  String name, String line, int portraitLeft, int portraitRight,  bool waitforakey)
{
     player.StopMoving();
   //PauseGame();
  
   String tmp2 = "";

   DialogName.Text = name; //The label i created so that i can show who's talking
   DialogName.TextColor = namecolour;
   DialogText.Text=""; //The label for the dialog text
   AvatarLeft.NormalGraphic = portraitLeft; //The avatar/sprite on the left
   AvatarRight.NormalGraphic = portraitRight; //The on the right

   gGui1.Visible = true;
   gGui2.Visible = true;
   gGui3.Visible = true;

   int i = 0;
   while (i < line.Length)//this function writes in typewritter mode or at least letter by letter.
   {
     if(line.Chars!= ' ' && line.Chars!= '*') // In this line i've this error: "Speech.asc(30): Error (line 30): Expected array index after 'String::Chars'"
     {
      Wait(2);
     } 
      
     if(line.Chars == '*')
     {
      Wait(6);
     }
     else
       tmp2 = tmp2.AppendChar(line.Chars);
     Label2.Text = tmp2;
     i++;
   }
   Wait(15);
   if (waitforakey == true)
    while (!IsKeyPressed(eKeySpace)) { Wait(5); }
   gGui1.Visible = false;
   gGui2.Visible = false;
   gGui3.Visible = false;
  // UnPauseGame();
}



In this line "if(line.Chars!= ' ' && line.Chars!= '*')" i've this error: "Speech.asc(30): Error (line 30): Expected array index after 'String::Chars'". I don't understand why's wrong =s.

I've a couple of other questions. If i wanted the function to take the character's name from it's own properties, could i replace "string name" with something like "Character.name*"? If so, what's the correct way of using those pointers?

Also, once i've finished this function. Where and how do i have to call it? Must i import it to GlobalScript.ash/asc? How do i replace the call to the original dialog way with the new one?


Thanks a lot.

Snarky

The error is because you copied it directly from Sephiroth, but his code was mangled because the forums interpreted an array index as a bbcode formatting tag. Notice how his code goes italic right there? If you look up String.Chars in the manual (a good tip in general!), you'll see that it is an array of characters (logically enough). So to compare it to a single character, you'll need to provide an index:

Code: AGS
line.Chars[i] != ' ' && line.Chars[i] != '*'


To use the character name, there are two options: (1) the function takes a string parameter, and you just call it with the character name, (2) the function just takes the character as a parameter, and then extracts the name internally. That's the better option. You don't need to add it to the global script; add it as another script module and just put an import statement in the header, and it will be available universally. To make it a clean replacement of Character.Say, write it as an extender function (make the first function parameter "this Character*").

If you take another look at my example in previous response, you'll see how all of this looks.


The Creator

Thanks.

I've correted the array index, and i added the character* to be able to use character.speak. I went to one of my dialogs, and after one response i tried to call the function. It looks like this:

Quote// Dialog script file
@S  // Dialog startup entry point
return
@1
NPC: "Hello there!"
cEgo.Speak(this Character*, 255,  "This is the name", "This is the text", 7, 10);
return

Whenever i try to compile i  get this error: "Dialog 0(13): Error (line 13): Type mismatch: cannot convert 'const string' to 'int'".

This is the original function parameters:
Quotefunction Speak(this Character*, int namecolour,  String name, String line, int portraitLeft, int portraitRight, bool waitforakey);

I believe that for some reason, it's taking the color integer as a string; but i've got no clue if it's really that.

Is the color number ok like that? I wanted to use white so i thought i had to use (255,255,255) but this is not the case since it's only one number. Or have i screwed up?

Also, i don't quite understand how to replace the normal dialog with the one i've created. Whenever i talked to an npc, instead of calling a dialog, must i call my function every single time i want one of my characters to talk?
If that's the case, when i want the player to choose an option to answer o respond to something the npc has said, how should i manage that?

Thanks a lot, i now i'm asking lots of newbie questions but i haven't code in ages and i'm really rusty plus i find the way AGS codes a little bit funky, hehe.

Snarky

#10
I see that you're using Sephiroth's code as a basis. I actually think Mazolin's example is probably a better starting point.

And again I urge you: read the documentation! Especially when you don't quite understand the meaning of the code you're using. (I've underlined some terms that have their own entries in the manual.)

In my example, Speak() is an extender function of the Character class, which in AGS means that it becomes like any standard class method. That's why the first parameter is "this Character*". So you call your code with just:

Code: AGS
character.Speak(nameColour, name, line, portraitLeft, portraitRight, waitforakey);


In other words, the first argument in the function prototype is used as the object you call the method on.

I should also add that depending on how you want them to work, the arguments nameColour, name and portraitLeft may be redundant. Name is just the name of the character, right? So you don't need to pass it, the function can just look up character.Name itself. If the namecolour is always the same for a particular character, it's better to either just use the character.SpeechColour property, or if you need to use a different color from "normal" speech for some reason, create a custom property and store it as part of the character data (which the function can then look up). And if portraitLeft is always the portrait of the character speaking, you probably don't need that either.

Text color in AGS is set as a palette index (for legacy reasons). I think white is 15, but the easiest way to find out is to look in the character editor of the AGS Editor under SpeechColor and find the text color you want there.

Quote from: The Creator on Wed 23/01/2013 19:11:00
Also, i don't quite understand how to replace the normal dialog with the one i've created. Whenever i talked to an npc, instead of calling a dialog, must i call my function every single time i want one of my characters to talk?
Yes. That's why you want the function to be as simple as possible, without unnecessary arguments.
QuoteIf that's the case, when i want the player to choose an option to answer o respond to something the npc has said, how should i manage that?
You just create them as topics and options in the dialog designer as usual. (See "Tutorial|Setting up the game|Conversations" in the manual.) For each option, uncheck the Say option, and instead under that topic call your method with the same text.

The Creator

Thanks.

I do read the manual but it's a little bit weird how you have to search. Sometimes i tried searching for the objetc.propertie and nothing would come up. Now i know i must look up just for the object alone, and then manually look for the propertie. Sorry for the stupid questions, but as i said i'm really rusty (haven't code in 2-3 years, haven't used pointers in more than 5). I spent 45 minutes triying to understand why couldn't i found the character.name propertie till i remembered that i was supposed to use this. alone.


I was able to summon the GUI, and the text appear correctly, the name was ok too, and everything was in place. Now i have a little problem when the text has finished appearing. The mouse remains as a little watch and i can't click on anything. I haven't had the time to check everything but i'm assuming that i must re-enable the mouse or something like that. Tomorrow i'll look more carefully but if you have some tip, i'd more than welcome to hear it.

Again thanks, i know it's really annoying answering to the questions that are on the manual.

Snarky

I'm not really annoyed. Anyone going into a task like this without much experience with AGS coding is going to have a lot of questions; the reason I'm replying is because I've been through this particular challenge myself. I just believe that you're genuinely better off not just being told the answer to each individual problem you come across, but instead learning how to deal with them yourself.

The watch cursor means the game is blocking. Most likely reasons for this are either a PauseGame() or some sort of Wait() command.

Khris

It's near the end of the Speak function. The game is waiting for the user to press space.
You'll probably want to replace this
Code: ags
   if (waitforakey == true)
    while (!IsKeyPressed(eKeySpace)) { Wait(5); }


with

Code: ags
  int waitloops = (line.Length / game.text_speed + 1) * GetGameSpeed();
  WaitMouseKey(waitloops);


This is the official formula for how long text stays on screen, and the user can press any key or click any button to remove it prematurely.

The Creator

Thanks. Today i did discovered that keyPressed thing but i didn't know that function. I was using isMouseButton to move the text, but that one is much better.

I can call the dialogs perfect now. I fixed the grey square that appeared over the GUIs and i even add a little option to write the whole setence of whatever the character is saying by clicking. I can switch the sprites and stuff.

Thanks a lot guys =D.


I only have (hopefully xP) one more question. I am now calling my speak function whenever i talk to the npc, instead of calling the normal dialog. Is there a way to customize the normal topic-selection box? Because i would like to use the same dialog box without having the enourmous pain of having to make another GUI and having gigantics functions with dialog options, variable to check if X has been said, the roads the player has chosen, etc.


Snarky

Customize how? If you mean the visual appearance of the option list, you can look for the game setting "Dialog options on GUI," which lets you put them on a GUI you choose. If that doesn't give you enough control over the appearance, look into "custom dialog options rendering" (which has its own page in the manual).

The Creator

I just wanted to be able to put them in the X-Y i wanted, choose the color (and if possible, the font too) of the text, being able to print that text over my GUI.

I've already been able to print the dialog's options over my GUI and by incrementing "Gap between the options" i was able to separate them a little bit because they were too close. I was also able to change the color of the text since it's the character's SpeechColor.

I still need to make them appear inside the box (the default location is very close to the left edge) and under the name of the person who's speaking. The DialogOptionsRenderingInfo is able to set this, but i must make my own custom dialog option rendering. The manual has an example of how to make a custom dialog but it seems a little bit too complex just to print the text a little to left and couple of pixels down.

I could always go the easy way, create another GUI for the dialog itself, and putting the options over that one but if there is some way to modified where the options appear (without having to create my own custom dialog), it would be much better.

Slasher

#17
To position dialog options in your custom GUI

Example (In Global asc):
Code: AGS

// Called when the game starts, before the first room is loaded

//Scroll down to:
function game_start() 
{  
//add 
 game.dialog_options_x = 10; // 1O Pixels from the left edge
 game.dialog_options_y = 14; // 14 pixels down from top edge
}


Adjust numbers if required.


The Creator

Thanks. I had already created a new GUI and was printing it there, but it's much better to just move the starting point.


I've finished the talking module. I even created a little function to "clean" the dialog box, so that i can print the dialogue options.

Now i've a different issue. I want to have many dialogue options, so i need to be able to scroll through those options. I've searched a little bit and found than most threads recommend using abstauber's module. I checked it out, but it's way too complex for what i want to do. I just need to put some arrow buttons, and scroll through the differents text options. No icons, no extra pop-up window, no special nothing. Just being able to print and hide dialogue options at will. I tried looking for the code that actually prints the options when you call a Dialog but couldn't find it.

Should i creat a new thread for this?

SMF spam blocked by CleanTalk