• 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 way to implement basic text input

Grez1

Member
Ok, n00b to the forums here. I've cranked out a couple of simple games so far and am enjoying GameMaker (note: I'm still on 1.499999 - evaluating as to whether to invest in 2.x). The one thing that I'm finding completely confounding is what would be the most simple of functionality way back the days when I was a kid and first learned to program: text input.

I get it -- GMS is designed in a way such that devs can take their code and port it to multiple platforms, especially mobile where the normal, simple solution -- a pop-up text box for input -- doesn't really work, as the concept of multiple windows is invalid there. But it's still fairly infuriating to see how much work it takes to get this done.

I've watched and played with multiple tutorials regarding drawing text and user-input text...but they mostly just show how to spit it out in an empty room. I watched tutorials on views, thinking that I could leverage a view to fake a text box in the room, but they primarily focused on a "zoomed-in" slice of a very large room in order to create a scrolling game.

I have a simple shooter game done, and I want the user to be able to input their name to be displayed with the high score. Ideally, I'd like for them to just be able to click on the shown name ("Player" by default), and have it pop up a little box to input the text with which they'd like to replace it. Should be simple, right? This is literally 1-2 lines of code in any of the other 10-12 languages in which I've coded in the past, so you can imagine that this is confusing and frustrating for me in this scenario.

So does anyone have a best practice as to a simple way to implement this type of functionality? I keep searching for answers and coming up empty. I'm hoping that the community here can help. I'm ready to get over this hurdle and start moving on to some other more advanced (well, intermediate :D) topics.

Thanks in advance for the help!
 

CloseRange

Member
This is literally 1-2 lines of code in any of the other 10-12 languages in which I've coded in the past
Were you running those programs through command lines? Because of course that's easy it's built into the system not the language.

Anyway best practices? Well get_string actually does work on mobile as it doesn't actually create a second window. At least it works on android where I've tested it.
What you want to look into is keyboard_string
it's a read/write variable that stores a string of all the last hit characters (up to 1024).
Code:
// create event
text = "";
type = false;
Code:
/// draw event
if(keyboard_check_pressed(vk_enter)) {
      type = !type;
      keyboard_string = text;
}
if(type) text = keyboard_string;
var t_ = text;
if(t_ == "") t_ = "Player";
draw_text(0, 0, t_);
 

NightFrost

Member
I haven't written a text input in a while, but I recall I used keyboard_string. When the game starts to expect player input, initialization would clear it of all old keypresses:
Code:
keyboard_string = "";
And when the game waits for player to finish writing, it would capture the input doing:
Code:
if(keyboard_string != ""){
    Player_Input += keyboard_string;
    keyboard_string = "";
}
That's the basics, then you'd probably add sniffing for enter button or some other action to end input process, and if you're using bitmap fonts you've drawn yourself (I normally do) filter out keys that you don't have a drawn font for - that is, you have a whitelist of allowed keys and only those get added to input. (That is why I grab the keys one by one by clearing the variable after every press; I can easily process individual keys and decide what to do with each.)
 

Grez1

Member
Hi, everyone! First of all, thank you for the responses! I apologize for being slow to get back to the group, but I've had a lot on my plate lately (looking for my next career step, very time-consuming), and it took a while before I was able to try out any code ideas.

The basics of what I ended up doing are something like this:
1) put a little "Click to edit player name" button under where the player name is displayed
2) When clicked, I create an "edit box" object (just basically a big, colored box) that displays over the top of the current name and edit button (interesting note...even when defining depth of objects and making the object containing the text the lowest depth, the actual text wouldn't hide behind the box, only the button. I fixed this with a global variable defining the edit state, and put an if statement around the code that draws the player name in the first place).
3) I then create an instance of the containing object with the actual draw code that's ready to capture keyboard input
4) I create a "done" button
5) when the done button is clicked, I check the string length, and if it's > 0 then the global player name variable is reassigned to this
6) I write the player name out to the ini file
7) I destroy the edit objects as appropriate
8) edit state is set back to false, so the player name now appears again, edited, in its original place in the room.

My containing object for the draw logic is called "objPlayerName" and looks something like this:

Create Event:
Code:
playername = "";
enabled_keys = "ABCDEFGHIJKLMNOPQRSTUVWXYZ 1234567890";
draw_set_font(fntStencil);
blink = true;
blink_speed = 15;
alarm[0] = blink_speed;
Alarm 0:
Code:
blink = !blink;
alarm[0] = blink_speed;
Draw:
Code:
draw_text(x,y,playername);

if blink
{
    var length = string_width(playername);
    var height = string_height(playername);
 
    draw_line(x + length, y, x+length, y+height);
 
    if height < 1 { draw_line(x, y, x, y+50);
    }
}
Press Any Key:
Code:
// ensure that the key pressed is in our list of approved characters
if (string_count(chr(keyboard_key), enabled_keys)) and (string_length(chr(keyboard_key)) == 1)
{
    blink = true;
    alarm[0]=blink_speed;
    switch(keyboard_lastchar)
    {
     case "#": playername += "\#"; break;
     default: playername += keyboard_lastchar; break;
    }
}

// see if the name has exceeded the box width - if so, remove last character
if string_width(playername) > objEditBox.sprite_width
{
    playername = string_copy(playername, 0, string_length(playername) - 1);
}

if !keyboard_check(vk_control)
{
    switch(keyboard_key)
    {
        case vk_backspace: switch(string_char_at(playername, string_length(playername)))
                            {
                            case "#": playername = string_copy(playername, 0, string_length(playername) - 2); break;
                            default: playername = string_copy(playername, 0, string_length(playername) - 1); break;
                            }
        default: break;
    }
}
else
{
 switch(keyboard_key)
 {
  case ord("V"): playername += clipboard_get_text(); break;
  default: break;
 }
}

All of this allows for input of only approved characters, a working backspace, the ability to paste in text, and limiting the user to the size of the edit box. Additionally, it has a blinking "edit line" cursor, so that the user knows that the space is active for input. I didn't bother to try to reposition things via mouse click or allow for editing in the middle of the string...too much work for the simple task here.


As for the button code, the left-click event of my "edit player name button" looks like this:
Code:
// Only execute if we're not already in editing mode
if global.NameEditMode = false {

    // set var to establish we're in editing mode
    global.NameEditMode = true;
 
    // Create edit rectangle object
    instance_create(20, 74, objEditBox);
 
    // Create player name text edit object
    instance_create(30, 80, objPlayerName);
 
    // Create done button
    instance_create(30, (objEditBox.y + objEditBox.sprite_height + 10), objPlayerNameDoneBtn);
}
It's merely ensuring that we're not already in edit mode (because it was possible to click the button even with the edit box object drawn on top of it), setting edit mode to true, and then creating the appropriate objects. If I were writing this to be used in more than one place, then I obviously wouldn't hardcode locations the way that I did. But with it just being to capture the player name and knowing that I have a fixed, single instance, etc., I went with the easy solution in this case.


The click event for the "Done" button is simple too, and looks like this:
Code:
// Assign global player name variable to string in box if it's not null,
// write new name to ini file,
// then destroy all objects used for editing
if string_length(objPlayerName.playername) > 0 {
    global.player_name = objPlayerName.playername;
    var settingsfile = file_ini_open("settings.ini",true);
    file_ini_write_string(settingsfile,"playername","player_name",global.player_name);
    file_ini_close(settingsfile);
}

instance_destroy(objPlayerName);
instance_destroy(objEditBox);

global.NameEditMode = false;

instance_destroy(objPlayerNameDoneBtn);
It just checks to be sure that the user actually typed in a name, and if this is true, assigns it to the global variable, writes the updated name to the ini file, sets the edit mode flag back to false, then destroys the editing objects.


Sooooo, that's it. I wanted to post most of the code that I used to accomplish this in the hopes that it may help another new GML dev later on. I have to say that this is the most work I've done in well over 30 years of programming to allow a user to input some text and update an object with it! :D

Thanks to the group for all of the help...it lead me down a path to getting this done. As I improve the code and make it more flexible later, I'll try to re-post if it becomes something easily reusable.

_____________

Footnote: to answer the earlier question regarding languages I've used in the past (almost all of which have simple coding structures for capturing user input), I have written production code in :
  • BASIC (GWBASIC, BASICA, Advanced BASIC, TurboBASIC)
  • C
  • C++
  • C#
  • Visual BASIC 3, 4, 5, and 6
  • ASP classic
  • Siebel script
  • AIX script
  • PL/SQL
  • T-SQL
  • ASP.NET
  • VB.NET (.NET 2.x-4.x)
  • C#.NET (.NET 2.x-4.x - and C#.NET is now version...what? 7? Yeah, that sounds right. :D )

I have converted code from (i.e., I can read and translate):
  • COBOL 85
  • JCL

I have a working knowledge of (i.e., I have actively debugged and approved others' code):
  • java
  • javascript
  • python
  • PHP
  • Fortran (yep, for real...let me tell you stories about NASA [no, I did not work there] some time...lol)
  • LISP

There's probably some other crap in there somewhere, but you get the idea. Lots of years of code, and then a lot of time doing design/strategy/blah blah blah and letting younger, smarter people do most of the coding. I'm kind of brushing off an old skill for fun with GML. Pretty cool so far. :)
 
Top