• Hey Guest! Ever feel like entering a Game Jam, but the time limit is always too much pressure? We get it... You lead a hectic life and dedicating 3 whole days to make a game just doesn't work for you! So, why not enter the GMC SLOW JAM? Take your time! Kick back and make your game over 4 months! Interested? Then just click here!

Simple dialog boxes?

H

Hudsonius

Guest
So I was wondering if there was an easy and simple dialog box system that I can use. Not one where a text bubble appears and floats over said person's head, just one kinda like Undertale's where a text box with text appears at the bottom of the screen. If anyone could help or at least link a video it would be great.
 

Tsa05

Member
XD I've been working on such a system for the past 19 months. Almost done!
One of the core scripts is here on the forum:
https://forum.yoyogames.com/index.p...timization-the-mother-of-all-textboxes.35901/

Using that script, you can cause text to fit within a rectangular area.
So, you could draw a box on the screen, then use that script to display wrapped text within that area.

My full engine, with graphical editor, will be ready in a month ;D But it sounds like you're looking for just something like the text part.
 
H

Hudsonius

Guest
XD I've been working on such a system for the past 19 months. Almost done!
One of the core scripts is here on the forum:
https://forum.yoyogames.com/index.p...timization-the-mother-of-all-textboxes.35901/

Using that script, you can cause text to fit within a rectangular area.
So, you could draw a box on the screen, then use that script to display wrapped text within that area.

My full engine, with graphical editor, will be ready in a month ;D But it sounds like you're looking for just something like the text part.
Hello!

*cough*

I'm going to sound stupid saying this but here we go. I saw your thread and put it to use, although I'm pretty sure I did something wrong.

First I took the script and y'know, created a script thingamajig with the given code in it. This part:
Code:
/// @description draw_wrapped_colored_text(x, y, text, width, [length], [height], [align, mode])
/// @arg {real}        x        X-position
/// @arg {real}        y        Y-position
/// @arg {string}    text    Text to draw
///    @arg {real}        width    Width to wrap within
/// @arg {real}        length*    Number of characters to draw
///    @arg {real}        height*    Height in pixels to draw within
///    @arg {real}        align*    0=left, 1=center
/// @arg {real}        [mode]    Mode is used internally
/*
*
*   Draws text at position x,y. Limits text drawn to [length] characters
*   If text height exceeds [height], truncates.
*
*   This script uses web hex code colors instead of
*   c_color constants to keep me sane.
*
*    [FFFFFF] Hex sets color
*   [f=fontID] changes the font
*   [r:index] draws index value
*
*   REQUIRES:
*   variable typewriterDelay, if | appears in the string
*
*   RETURNS:
*   An array containing three values:
*   0: The number of the last character printed
*   1: Whether the text was truncated due to height (1) or not (0);
*    2: Whether the box is "done."
*
*    RETURNS [if mode is 1]:
*    An array containing the width of each line of text
*
*    Created by Tsa05 on the GMC forums, possibly with help from GMCers
*    Terms: Use at will. Do not sell as a textbox script.
*    Feel free to sell your own assets that happen to use this for the text part.
*
*/
var typewriterAlarm = 1;  // The alarm number used in your object to advance typewriter text
var debug=0;
draw_set_halign(fa_left);
draw_set_valign(fa_top);

var x1 = argument[0];
var y1 = argument[1];
var text = argument[2];
var boxW = argument[3];
var x2 = x1+boxW;
var font = -1;

var maxLength = 0;

if(!is_undefined(text)){
    if(is_string(text)){
        maxLength = string_length(text);
    }else if(is_array(text)){
        maxLength = array_length_1d(text)-1;
    }
}

var maxHeight = -1;
if(argument_count>5){
    maxHeight = argument[5];
}
var length = maxLength;
var retval = 0;
retval[0]=0; retval[1]=0; retval[2]=0;

if(argument_count>4){
    if(argument[4]>=0){
        length = argument[4];
    }
}
var align = 1;
if(argument_count>6){
    align = argument[6];
}
var mode = 0;
if(argument_count>7){
    mode = argument[7];
}
var lines = 0;
var currentLine = 0;
var line_height = string_height("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
var cc = draw_get_colour();
var cx = x1;
var cy = y1;
var skip = false;
var c;

var newLine = chr(10);
var    i = 1;
var removal = 0;
brackets = false; // Instance variable; calling instance can indicate tag

/*
*    Since this function must draw 1 character at a time,
*    string_char_at becomes a significant source of lag.
*    An array is created to accomodate the problem.
*    This method is 14% faster than using string_char_at in testing.
*/
var textArray = 0;
if(is_array(mode)){
    textArray = mode;
    mode = 1;
}else{
    if(is_array(text)){
        // If text was provided as a character array...
        textArray = text;
    }else{
        for(var z=1;z<=maxLength;z+=1){
            textArray[z]=string_char_at(text,z); //Slower
            //textArray[z]=chr(string_byte_at(text,z)); //Faster, less character support
        }
    }
}
if(align==1 && mode==0){
    // If centering, run this script once for spacing
    // textArray is passed because why not save computation time
    lines = draw_wrapped_colored_text(x1, y1, text, boxW, -1, maxHeight, align, textArray); // Mode 1
}

while (i<=length){
 
    if(debug) show_debug_message("mode:"+string(mode)+" i:"+string(i));
 
    //c = string_char_at(text,i);
    c = textArray[i];
 
    var nextChar = ""; var prevChar = "";
    if(i<array_length_1d(textArray)-1){
        //var nextChar = string_char_at(text,i+1);
        nextChar = textArray[i+1]
    }
    if(i>1){
        //var prevChar = string_char_at(text,i-1);
        prevChar = textArray[i-1];
    }
 
    if(debug) show_debug_message("i:"+string(i)+" prevChar:"+prevChar+" nextChar:"+nextChar);
 
    // ANALYSE CURRENT CHARACTER //
 
    if (c == "\\"){
        if(debug) show_debug_message("escape character or pause");
        if ( nextChar == "[" ||
            nextChar == "|" ){
          
            i += 1;
            //if (i > string_length(text)){
            if (i > maxLength){
                break;
            }
        }
    }else if (c == newLine){
        // IF LINE-BREAK CHARACTER
        if(debug) show_debug_message("newline character");
        if (prevChar != "\\"){
            if(debug) show_debug_message("newline committed");
            if(mode){
                lines[currentLine] = cx-x1;
            }
            currentLine+=1;
            cy += line_height;
            cx = x1;
            //if (i > string_length(text)) break;
            if (i > maxLength) break;
          
        }else{
            if(debug) show_debug_message("newline was escaped");
            c = newLine; // In case you escaped newline?
        }
    }else if (c == "["){
        if(debug) show_debug_message("tag begins");
        // IF THE START OF A TAG
        if (prevChar == "\\"){
            if(debug) show_debug_message("tag escaped");
            c = c;
        }else{
            brackets = true;
            code = "";
            i += 1;
            //c = string_char_at(text,i);
            if(i<array_length_1d(textArray)){
                c = textArray[i];
            }else{ c=""; }
          
            if(debug) show_debug_message("Within tag, i:"+string(i)+" c:"+string(c));
          
            while (c != "]"){
                code += c;
                i += 1;
              
                if(debug) show_debug_message("code:"+code+" i:"+string(i));
              
                //if (i > string_length(text)) break;
                if (i > maxLength) break;
              
                //c = string_char_at(text,i);
                if(i<array_length_1d(textArray)){
                    c = textArray[i];
                }else{ c=""; }
                if(debug) show_debug_message("next character of code:"+c);
            }
          
            if (c == "]"){
                brackets = false;
                c="";
                // Analyze code
                if(string_pos("f=",code)==1||string_pos("font=",code)==1){
                    var fontName = string_delete(code,1,2);
                    if(asset_get_index(fontName)!=-1){
                        if(asset_get_type(fontName)==asset_font){
                            font = asset_get_index(fontName);
                        }
                    }
                }else if(string_pos("r:",code)==1){
                    var num = string_delete(code,1,2);
                    var txt = "[Register "+string(num)+"]";
                    var tag = "[r"+string(num)+"]";
                    c=txt;

                    length+=string_length(txt)-string_length(tag);
                }else{
                    if(mode==0){ //Don't swap colors when measuring
                        // Convert...
                        code = string_upper(code);
                        var dec = 0;
                       var h = "0123456789ABCDEF";
                       for (var p=1; p<=string_length(code); p+=1) {
                           dec = dec << 4 | (string_pos(string_char_at(code, p), h) - 1);
                       }
                        var cc = (dec & 16711680) >> 16 | (dec & 65280) | (dec & 255) << 16;
                        ///////////////////////////
                      
                    }
                }
                // Increase length to display, since we're not
                // Showing these characters.
                length+=string_length(code)+2; //Plus 2 for brackets
                code = "";
            }else{
                // if no end bracket was found, don't draw this
                c="";
            }
        }
    }else
    // IF PAUSE CHARACTER
    if (c == "|"){
        //if (i==1 || string_char_at(text,i-1) != "\\"){
        if (i==1 || textArray[i-1] != "\\"){
            i += 1;
            c="";
            if(i==length && alarm[typewriterAlarm]==-1){
                // Add a delay...
                if(variable_instance_exists(id,"typewriterDelay")){
                    alarm[typewriterAlarm]=2*typewriterDelay;
                }
            }
        }
    }
 
    // IF BEGINNING OF NEW WORD //
    while(i>array_length_1d(textArray)){
        if(debug) show_debug_message("trimming i:"+string(i));
        i-=1;
    }
    prevChar = "";
    if(i>1 ){
        //var prevChar = string_char_at(text,i-1);
        prevChar = textArray[i-1];
    }
 
    /* The string_char_at version... */
    /*
    if( (i == 1) or
    ( (string_char_at(text,i-1) == " ") || (string_char_at(text,i-1) == "-") ) or
    ( (string_char_at(text,i-1) == newLine) && (string_char_at(text,i - 2) != "\\") ))
    */
 
    if( (i == 1) or
    ( (prevChar == " ") || (prevChar == "-") ) or
    ( (prevChar == newLine) && ( array_length_1d(textArray)>3 && textArray[i - 2] != "\\") ) )
    {     
        // CHECK WIDTH //
        if(debug) show_debug_message("determine if linebreak is needed");
        var w  = 0;
        var ii = i;
        var n  = 0;
        var ci  = c;
      
        while ((ci != " ") && (ci != "-") && ci != newLine){
            if (ci == "[") n = 1;
            //if ((ci == "\\") && (string_char_at(text,ii + 1) == newLine))
            if ((ci == "\\") && (textArray[ii + 1] == newLine))
                {
                ii += 1;
                //if (ii > string_length(text)) break;
                if(debug) show_debug_message("ii:"+string(ii));
                if (ii > maxLength){
                    if(debug) show_debug_message("breaking");
                    break;
                }
                continue;
              
                }
          
            if ((!n) && (ci != "\\")) w += string_width(ci);
          
            if (cx + w > x2){
                if(debug) show_debug_message("new line found");
                if(i!=1){
                    if(mode){
                        lines[currentLine]=cx-x1;
                    }
                    currentLine+=1;
                    cy+=line_height;
                    cx=x1;
                }
                w=0;
            }
          
            ii += 1;
            //if (ii > string_length(text)) break;
            if (ii > maxLength) {
                if(debug) show_debug_message("breaking here:"+string(ii));
                break;
            }
          
            //ci  = string_char_at(text,ii);
            ci = textArray[ii];
            if(debug) show_debug_message("ci:"+string(ci));
            if (ci == "]") n = 0;
        }
    }
 
    var cW = string_width(c);
    if(cx+cW>x2){ 
        // Check the width again, in case word wrap found no words
        // This means your "word" is bigger than the whole box.
        if(mode){
            lines[currentLine]=cx-x1;
        }
        currentLine+=1;
        cy+=line_height;
        cx=x1;
    }
 
    if(maxHeight && cy+line_height>y1+maxHeight){
      
        retval[0] = i-removal-1;
        retval[1] = 1;
        if(mode) retval = lines;
        return retval;
        exit;
    }else{
        draw_set_color(cc);
        if(font_exists(font)) draw_set_font(font);
        var newX = cx;
        if(mode==0){
            if(align==1){
                if(is_array(lines)&&array_length_1d(lines)>=currentLine){
                    var lineW = lines[currentLine];
                    var d = cx-x1;
                    var centerX = x1+boxW/2;
                    var newX = centerX-lineW/2+d;
                }
            }
            draw_text(newX,cy,c);
        }
        draw_set_halign(fa_left);
        cx += cW;
      
        i += 1;
      
        //if (i > string_length(text)){
        if (i > maxLength){
            retval[0] = maxLength;
            retval[1] = 0;
            retval[2] = 1;
            if(mode){
                lines[currentLine]=cx-x1;
                retval = lines;
            }
            return retval;
            break;
        }
    }   
}

retval[0] = i;
if(mode) retval=lines;
return retval;
Then I saw in the comments you posted this
I think you're entirely correct, as long as the characters are Unicode...right? Check out this sample in a Step event:
Code:
Code:
/// @description Insert description here
// You can write your code in this editor

str = "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc ultricies, felis sed interdum porttitor, metus enim porta ligula, ut ultricies ante odio sed nunc. Etiam in dictum sem. Pellentesque volutpat eleifend nisl non eleifend. Pellentesque mi ipsum, consectetur quis enim eget, euismod dignissim velit. Pellentesque id turpis at nibh vehicula rhoncus a non nisi. Integer lacinia rutrum pharetra. Fusce cursus efficitur risus non euismod. Aliquam in lacus ornare, laoreet mauris eu, tempus nisl. Vivamus id vulputate dui, sit amet fringilla nulla. Cras congue semper sem, nec molestie nunc iaculis nec. Maecenas consectetur mauris vel bibendum facilisis. Morbi dignissim neque dolor, ac semper neque dignissim id. Quisque dapibus non massa a placerat.";
var L = string_length(str);

for(var z=1;z<=L;z+=1){
   var c = string_char_at(str,z);
   textArray0[z] = c;
}

for(var z=1;z<=string_length(str);z+=1){
   var c = string_byte_at(str,z);
   textArray1[z] = chr(c);
}
Two loops, adding all characters to an array. One requires string_char_at(), and the other requires string_byte_at() and also chr() to turn it back into a character.

Looks like byte_at plus chr is less than char_at!!!

I'll plug it into the larger context this evening and see what I save there. :D Let's see what else we can streamline!
So I thought, "Ah, I just have to create a click event (I want to try to have you click on the person and text appears or you get close to the person and press space and text appears, preferably the latter) and have the person speaking use the code above so that he'll say what I want." It didn't work. If you could help me, y'know, getting it working it would be great.
 

Tsa05

Member
@Hudsonius Hi there! Sure!

The easiest way to get it working is to make an object in your game for the dialog--maybe call it like obj_dialog or something, and then in the Draw event for that object, you call the script.

Let's start simple and build up a bit.

obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
Draw Event:
Code:
draw_wrapped_colored_text(x, y, text, width)
Now, you have something already, where you have a Person object, right? And you want to click on them?
Person Object:
Left Mouse Pressed Event:

Code:
nnn = instance_create_depth(x, y, -100, obj_dialog);
nnn.text = "Hello there, this is text that I am saying to you";
If you want the box to appear somewhere relative to the Player Object's position, use x and y there. If you always want text at a fixed position on the screen, use the coordinates there.

That ought to do it! When you click the player, the dialog object is created, and then it gets some text to draw, and draws that.

Can you get that working? If so, next step:
Typewriter text. Let's add some stuff that controls how the script works.
obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
delay = 5;  //How many frames to "wait" when typewriting
amount = 0; //Draw zero characters at first
alarm[0] = delay;
obj_dialog:
ALARM 0 Event:

Code:
amount+=1; // Show 1 more character
alarm[0] = delay; // Reset the alarm
obj_dialog:
Draw Event:

Code:
draw_wrapped_colored_text(x, y, text, width, amount);
Done!
The script accepts an optional parameter that tells it how much to draw, out of the total text. So, you set amount to Zero, and then increment amount every certain number of game steps. The script prints out one additional character each time the alarm goes off.

If all of that makes sense, there's a few extra things I can talk about--the script does a ton. But let's see if that all gets working first?
 
Last edited:
H

Hudsonius

Guest
@Hudsonius Hi there! Sure!

The easiest way to get it working is to make an object in your game for the dialog--maybe call it like obj_dialog or something, and then in the Draw event for that object, you call the script.

Let's start simple and build up a bit.

obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
Draw Event:
Code:
draw_wrapped_colored_text(x, y, text, width)
Now, you have something already, where you have a Person object, right? And you want to click on them?
Person Object:
Left Mouse Pressed Event:

Code:
nnn = instance_create_depth(x, y, obj_dialog);
nnn.text = "Hello there, this is text that I am saying to you";
If you want the box to appear somewhere relative to the Player Object's position, use x and y there. If you always want text at a fixed position on the screen, use the coordinates there.

That ought to do it! When you click the player, the dialog object is created, and then it gets some text to draw, and draws that.

Can you get that working? If so, next step:
Typewriter text. Let's add some stuff that controls how the script works.
obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
delay = 5;  //How many frames to "wait" when typewriting
amount = 0; //Draw zero characters at first
alarm[0] = delay;
obj_dialog:
ALARM 0 Event:

Code:
amount+=1; // Show 1 more character
alarm[0] = delay; // Reset the alarm
obj_dialog:
Draw Event:

Code:
draw_wrapped_colored_text(x, y, text, width, amount);
Done!
The script accepts an optional parameter that tells it how much to draw, out of the total text. So, you set amount to Zero, and then increment amount every certain number of game steps. The script prints out one additional character each time the alarm goes off.

If all of that makes sense, there's a few extra things I can talk about--the script does a ton. But let's see if that all gets working first?
Sorry it took so long for me to respond, but on the click event for the person, instance_create_depth doesn't register as a command and I get an error. Is this an incorrect thing entered or should I be using GM2?

[EDIT] So I looked it up, and realized I needed GM2. Is there any work around or different code I could use?
 
Last edited by a moderator:
W

Wraithious

Guest
So I looked it up, and realized I needed GM2. Is there any work around or different code I could use?
Just drop the _depth off instance_create_depth, then set the depth either in the object properties or in code, you'll probably want a high depth (like -1000 or something) so it will be visible over your backgrounds and sprites.

also there's no such function
draw_wrapped_colored_text(x, y, text, width, amount); in GMS 1x.
you will have to use
draw_set_color(color);
draw_text_ext(x, y, string, height in pixels between lines of text, max allowed width);
 
H

Hudsonius

Guest
Just drop the _depth off instance_create_depth, then set the depth either in the object properties or in code, you'll probably want a high depth (like -1000 or something) so it will be visible over your backgrounds and sprites.

also there's no such function
draw_wrapped_colored_text(x, y, text, width, amount); in GMS 1x.
you will have to use
draw_set_color(color);
draw_text_ext(x, y, string, height in pixels between lines of text, max allowed width);
Thanks, it seems to be working :) although when you said
also there's no such function
draw_wrapped_colored_text(x, y, text, width, amount); in GMS 1x.
you will have to use
draw_set_color(color);
draw_text_ext(x, y, string, height in pixels between lines of text, max allowed width);
draw_wrapped_colored_text is referring to a script
 
H

Hudsonius

Guest
@Hudsonius Hi there! Sure!

The easiest way to get it working is to make an object in your game for the dialog--maybe call it like obj_dialog or something, and then in the Draw event for that object, you call the script.

Let's start simple and build up a bit.

obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
Draw Event:
Code:
draw_wrapped_colored_text(x, y, text, width)
Now, you have something already, where you have a Person object, right? And you want to click on them?
Person Object:
Left Mouse Pressed Event:

Code:
nnn = instance_create_depth(x, y, obj_dialog);
nnn.text = "Hello there, this is text that I am saying to you";
If you want the box to appear somewhere relative to the Player Object's position, use x and y there. If you always want text at a fixed position on the screen, use the coordinates there.

That ought to do it! When you click the player, the dialog object is created, and then it gets some text to draw, and draws that.

Can you get that working? If so, next step:
Typewriter text. Let's add some stuff that controls how the script works.
obj_dialog:
Create Event:

Code:
text = "";
width = 500; //Use a width that makes sense for you
delay = 5;  //How many frames to "wait" when typewriting
amount = 0; //Draw zero characters at first
alarm[0] = delay;
obj_dialog:
ALARM 0 Event:

Code:
amount+=1; // Show 1 more character
alarm[0] = delay; // Reset the alarm
obj_dialog:
Draw Event:

Code:
draw_wrapped_colored_text(x, y, text, width, amount);
Done!
The script accepts an optional parameter that tells it how much to draw, out of the total text. So, you set amount to Zero, and then increment amount every certain number of game steps. The script prints out one additional character each time the alarm goes off.

If all of that makes sense, there's a few extra things I can talk about--the script does a ton. But let's see if that all gets working first?
Ok, I got that done, but I still need proper text placing, text boxes, and a slightly smaller font (in my game most sprites are 32 pixels and the view is 250 by 250, so... yeah)
 

Tsa05

Member
Is the script I provided working now? I wrote it for GMS2, but I *think* it does not use any non GMS1 stuff.

If so, then the rest is easy.
Add a font to your game using GameMaker's resources. Set the size to what you need.
Just before you draw using my function, use draw_set_font(nameOfYourFontResource);

Positioning...just set x and y in the script call to the coordinates where you'd prefer the text to be drawn.

To draw a background box, import a sprite image into your game, and, just before calling my script, do this:
draw_sprite(nameOfYourBackgroundSprite, -1, x, y);

That will draw the picture, then add the box on top.

Remember, my script can do word-wrapping, so you might draw in 2 different places. In the illustration below, you can see how a "dialog box graphic" might be drawn at different coordinates than the textbox:
 
H

Hudsonius

Guest
Is the script I provided working now? I wrote it for GMS2, but I *think* it does not use any non GMS1 stuff.

If so, then the rest is easy.
Add a font to your game using GameMaker's resources. Set the size to what you need.
Just before you draw using my function, use draw_set_font(nameOfYourFontResource);

Positioning...just set x and y in the script call to the coordinates where you'd prefer the text to be drawn.

To draw a background box, import a sprite image into your game, and, just before calling my script, do this:
draw_sprite(nameOfYourBackgroundSprite, -1, x, y);

That will draw the picture, then add the box on top.

Remember, my script can do word-wrapping, so you might draw in 2 different places. In the illustration below, you can see how a "dialog box graphic" might be drawn at different coordinates than the textbox:
So I got a smaller font, created a text box,etc. I wanted the textbox to be at the bottom of the screen so I have plenty of space to put text and stuff. I managed to get the box their but the text was still remaining where it started in an weird place near the player, if you could help with that, it would be appreciated.


Also, if you know any way to keep the text small but still be High Quality (I'm using a pixely font) it would be awesome
 

Tsa05

Member
text was still remaining where it started in an weird place near the player
It'll draw the text where you tell it to:
Code:
draw_wrapped_colored_text(x, y, text, width, [length])
If you use the player's x and y coordinates as the text's x and y coordinates, that's where it will appear. If you use coordinates similar to the ones used when you draw the background box image, it will appear near there.
 
H

Hudsonius

Guest
It'll draw the text where you tell it to:
Code:
draw_wrapped_colored_text(x, y, text, width, [length])
If you use the player's x and y coordinates as the text's x and y coordinates, that's where it will appear. If you use coordinates similar to the ones used when you draw the background box image, it will appear near there.
Thanks! Everything seems to be working great except two things.
1. With the Resolution my game is at, to make my text fit in the box, it is so pixely you have to strain to read it. Is there a way to keep the high quality text while still getting it to fit in the box?
2. I want to be colliding with the person, then press "ENTER" to activate the text. And then I can press "ENTER" again to close the text OR leave within 10 pixels of him.
 

Tsa05

Member
@Hudsonius erm, well, we're straying outside the question of drawing text inside a box and into some other game things, but let's see...

1) With regards to the quality of the font, that's really rather dependent upon the font, rather than GameMaker. It sounds as though you've got some really small text? Or have I got that wrong? Basically, some fonts are designed to be readable at small sizes, and some are not. GameMaker just draws whatever the font looks like, at the size you specify.
Two things to try:
a) If you need a small font, try searching up some so-called "tiny" fonts to use https://www.dafont.com/tiny.font
b) If your fonts look blurry in GameMaker, and you're using tiny pixel fonts already, try turning off the aliasing setting in the font resource box. (Or turn it down to a lower number). Aliasing is a technique for smoothing the edges of things by blurring them a bit, which is usually great for fonts--it removes rough, jaggy edges--but terrible if you want small fonts where every pixel must be precisely drawn,

2) Oki. Break it into tasks.
a) When you press Enter, you want the dialog to appear, right? So change the mouse left pressed event into a keyboard press Enter event.
b) You only want this to happen if the Person object is "able" to speak--in other words, when the player character is near them. So, you need to set up the concept of "not able to speak" and then the conditions for "becoming able to speak"
c) You want speaking to go away when the player is more than 10 px away, so the dialog box object needs to know which Person object it is connected to.

In your create event for the person, just put something like...
ableToSpeak = 0;

Then, in your step event for the Person, put something like:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     ableToSpeak = 0;
}
(Replace PlayerCharacterObject with whatever your player character's object name is)
Is it clear what that does?
The Person object checks, every game step, to see whether or not the distance between its own x,y coordinates and the player character coordinates are less than 10. If so, then ableToSpeak is 1. In all other cases, ableToSpeak is zero.

Now, hook it up to the code that makes the dialog box object.

Remember, only make the dialog box if this Person Object is "ableToSpeak" and tell the dialog object which Person Object controls its destiny.

Event key pressed Enter
Code:
if( ableToSpeak==1 ){
    nnn = instance_create_depth(x, y, -100, obj_dialog);
    nnn.text = "Hello there, this is text that I am saying to you";
    nnn.whoMadeMe = id;
}
So, the working dialog box will only appear if ableToSpeak == 1, and we know that ableToSpeak will only equal 1 when the player character touches the Person object.

Notice that I also added a value called whoMadeMe. This lets the dialog box know the ID of the Person object that created it.
This is important because you wanted the dialog box to go away when the player moves more than 10 px away from the Person object that created the text box.

Let me add one more thing to that, too:
Code:
if( ableToSpeak==1 and !instance_exists(obj_dialog) ){
    nnn = instance_create_depth(x, y, -100, obj_dialog);
    nnn.text = "Hello there, this is text that I am saying to you";
    nnn.whoMadeMe = id;
}
In programming, the ! means "not". So, in plain English:
"If ableToSpeak is equal to one and there's not already an instance of a dialog box on the screen....make one"
That'll keep you from getting multiple copies of the box (if the player presses Enter repeatedly).

c) So at this point, you have:
  • A Person object that regularly updates "ableToSpeak" based on how close the player is to the Person.
  • A key press event that creates a dialog box IF the Person is ableToSpeak at that moment and IF there's not already dialog on the screen.
  • A dialog box object that knows who made it, and that draws a nice box with text.
Only thing missing is the part that makes the dialog box go away. Easy.
ADD THIS TO Create event for obj_dialog:
Code:
whoMadeMe = noone;
(Initially, a box doesn't know its maker. "noone" is a built-in GameMaker value for that concept).

Step event for obj_dialog:
Code:
if( !instance_exists(whoMadeMe) or whoMadeMe.ableToSpeak == 0 ){
    instance_destroy();
}
The code above checks whether the "whoMadeMe" Person still exists, and whether they are still "ableToSpeak."
So, when the player moves away, ableToSpeak becomes zero, and thus, the dialog box destroys itself.
 
Last edited:
H

Hudsonius

Guest
@Tsa05
Sorry for your trouble, but, when I first used the code, I could access the speaking ability from anywhere. Then I realized it was set to, "if PlayerCharacterObject is <10 pixels away from PlayerCharacterObject then to start the code. So with a few changes, it became this:
Code:
with obj_player
{
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     speaking = 0;
}
}
However, now I am getting this error:

############################################################################################
FATAL ERROR in action number 1 of Key Press Event for E-key Key for object obj_lostdude:
Variable obj_lostdude.ableToSpeak(100010, -2147483648) not set before reading it. at gml_Object_obj_lostdude_KeyPressed_E_1 (line 1) - if( ableToSpeak==1 and !instance_exists(obj_dialog) ){ ############################################################################################"
Also, if you noticed, I changed the key to "E" for easier controls
 
H

Hudsonius

Guest
@Flip
@Tsa05

So I missed the create event for the person, and the text pops up when it should now! Unfortunately, the text won't go away. I've tampered around with it a bit, but nothing seems to work.
 
H

Hudsonius

Guest
@Hudsonius erm, well, we're straying outside the question of drawing text inside a box and into some other game things, but let's see...

1) With regards to the quality of the font, that's really rather dependent upon the font, rather than GameMaker. It sounds as though you've got some really small text? Or have I got that wrong? Basically, some fonts are designed to be readable at small sizes, and some are not. GameMaker just draws whatever the font looks like, at the size you specify.
Two things to try:
a) If you need a small font, try searching up some so-called "tiny" fonts to use https://www.dafont.com/tiny.font
b) If your fonts look blurry in GameMaker, and you're using tiny pixel fonts already, try turning off the aliasing setting in the font resource box. (Or turn it down to a lower number). Aliasing is a technique for smoothing the edges of things by blurring them a bit, which is usually great for fonts--it removes rough, jaggy edges--but terrible if you want small fonts where every pixel must be precisely drawn,

2) Oki. Break it into tasks.
a) When you press Enter, you want the dialog to appear, right? So change the mouse left pressed event into a keyboard press Enter event.
b) You only want this to happen if the Person object is "able" to speak--in other words, when the player character is near them. So, you need to set up the concept of "not able to speak" and then the conditions for "becoming able to speak"
c) You want speaking to go away when the player is more than 10 px away, so the dialog box object needs to know which Person object it is connected to.

In your create event for the person, just put something like...
ableToSpeak = 0;

Then, in your step event for the Person, put something like:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     speaking = 0;
}
(Replace PlayerCharacterObject with whatever your player character's object name is)
Is it clear what that does?
The Person object checks, every game step, to see whether or not the distance between its own x,y coordinates and the player character coordinates are less than 10. If so, then ableToSpeak is 1. In all other cases, ableToSpeak is zero.

Now, hook it up to the code that makes the dialog box object.

Remember, only make the dialog box if this Person Object is "ableToSpeak" and tell the dialog object which Person Object controls its destiny.

Event key pressed Enter
Code:
if( ableToSpeak==1 ){
    nnn = instance_create_depth(x, y, -100, obj_dialog);
    nnn.text = "Hello there, this is text that I am saying to you";
    nnn.whoMadeMe = id;
}
So, the working dialog box will only appear if ableToSpeak == 1, and we know that ableToSpeak will only equal 1 when the player character touches the Person object.

Notice that I also added a value called whoMadeMe. This lets the dialog box know the ID of the Person object that created it.
This is important because you wanted the dialog box to go away when the player moves more than 10 px away from the Person object that created the text box.

Let me add one more thing to that, too:
Code:
if( ableToSpeak==1 and !instance_exists(obj_dialog) ){
    nnn = instance_create_depth(x, y, -100, obj_dialog);
    nnn.text = "Hello there, this is text that I am saying to you";
    nnn.whoMadeMe = id;
}
In programming, the ! means "not". So, in plain English:
"If ableToSpeak is equal to one and there's not already an instance of a dialog box on the screen....make one"
That'll keep you from getting multiple copies of the box (if the player presses Enter repeatedly).

c) So at this point, you have:
  • A Person object that regularly updates "ableToSpeak" based on how close the player is to the Person.
  • A key press event that creates a dialog box IF the Person is ableToSpeak at that moment and IF there's not already dialog on the screen.
  • A dialog box object that knows who made it, and that draws a nice box with text.
Only thing missing is the part that makes the dialog box go away. Easy.
ADD THIS TO Create event for obj_dialog:
Code:
whoMadeMe = noone;
(Initially, a box doesn't know its maker. "noone" is a built-in GameMaker value for that concept).

Step event for obj_dialog:
Code:
if( !instance_exists(whoMadeMe) or whoMadeMe.ableToSpeak == 0 ){
    instance_destroy();
}
The code above checks whether the "whoMadeMe" Person still exists, and whether they are still "ableToSpeak."
So, when the player moves away, ableToSpeak becomes zero, and thus, the dialog box destroys itself.
Thanks! Although still more errors... :(, but I got it working with approaching the person and pressing "ENTER" (Yay)
1) Moving away doesn't delete text
2) Weird space on start of second line..?
3) Text doesn't read... Just appears.
Thanks!
 
Last edited by a moderator:

Tsa05

Member
@Hudsonius
Hi there~ I'm a little worried that you aren't quite understanding the code as I'm explaining it `~` There was unfortunately a typo in the code that I posted--easy to fix, conceptually, but if you pasted it in without a thorough read-through, then yea, there's an error :)

For the Step event, I gave you this:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     speaking = 0;
}
What's that going to do?
When the player is within 10 px, it sets ableToSpeak equal to 1. When the player moves away, it sets speaking equal to zero.
That's 2 different variable names. Since ableToSpeak is not changed when the player moves away, the dialog object will never disappear.

I simply forgot what I was naming things and typed "speaking." I've corrected the code above. The Person's Step event should have this:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     ableToSpeak = 0;
}
As for the text just appearing, well, that makes sense. The script draws however much text it has been told to draw, word wrapped, with advanced display features like changing colors and such. We haven't talked about code that would tell the script how much text to draw just yet, so it shows everything.

Let me know if the player step code issue is fixed first, and we'll modify the dialog object one more time for the typewriter effect.
(As for the "weird space" issue, not sure. Is there a double-space anywhere in your text? I haven't encountered the issue)
 
H

Hudsonius

Guest
@Hudsonius
Hi there~ I'm a little worried that you aren't quite understanding the code as I'm explaining it `~` There was unfortunately a typo in the code that I posted--easy to fix, conceptually, but if you pasted it in without a thorough read-through, then yea, there's an error :)

For the Step event, I gave you this:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     speaking = 0;
}
What's that going to do?
When the player is within 10 px, it sets ableToSpeak equal to 1. When the player moves away, it sets speaking equal to zero.
That's 2 different variable names. Since ableToSpeak is not changed when the player moves away, the dialog object will never disappear.

I simply forgot what I was naming things and typed "speaking." I've corrected the code above. The Person's Step event should have this:
Code:
if( point_distance(x, y, PlayerCharacterObject.x, PlayerCharacterObject.y) <10 ){
     ableToSpeak = 1;
}else{
     ableToSpeak = 0;
}
As for the text just appearing, well, that makes sense. The script draws however much text it has been told to draw, word wrapped, with advanced display features like changing colors and such. We haven't talked about code that would tell the script how much text to draw just yet, so it shows everything.

Let me know if the player step code issue is fixed first, and we'll modify the dialog object one more time for the typewriter effect.
(As for the "weird space" issue, not sure. Is there a double-space anywhere in your text? I haven't encountered the issue)
Thanks! It seems to go away when you leave now, but there are still a few problems.
1) I would like the text to read instead of instantly popping up. I saw this and I was wondering if editing this helps:
Code:
delay = 10;  //How many frames to "wait" when typewriting
2) The last line has a weird indent unlike the other lines
3) Person talking to change sprite while text is being read
4) Sound of text being read (I have the sound, just need to implement it)
5) Way to execute code once player closes text (person to do something)
6) Pressing "E" again to be another way of closing it (for cut-scenes)
 
Last edited by a moderator:

Tsa05

Member
I'm sorry, but we're really well beyond your request. I feel as though you're not really learning anything from this--you're just pasting code in and reporting back the next thing your game should do...waiting for me to write it.

You wanted a way to flexibly display text inside a textbox on a screen. You've got one. Now, you're just writing the rest of your game without putting in the effort. You've really got to read some of the documentation, the comments, the examples...you have to understand the code that you're putting into your game or you will never be able to customize it. Part of the goal of programming is to be able to become self-sufficient. You want to learn to make the computer do what you tell it, rather than what we tell you to write.

You already have all that you need and far more.
Your dialog box object needs to use an Alarm--built into GameMaker--and a counting variable. The script very clearly explains that it can accept a "length" input:
Code:
draw_wrapped_colored_text(x, y, text, width, length)
So, think it through.
When the object is created, you want how much text? None? What should the value of "length" be in the dialog object's Create event?
After a certain amount of time has passed, you want length to increase, right? GameMaker has "Alarms" for this. Set an alarm in the Create event. Every time the alarm goes off, increase length and re-set the alarm. Every time the alarm goes off, play a sound. Every time the player presses "E", delete the dialog box.

This stuff isn't fancy textbox drawing, it's absolutely basic events in GameMaker.
 
Top