GameMaker has a built-in keyboard_string
variable that lets you do simple text entry,
but what if you also want a caret
(so that the user can enter/erase text in the middle of the string)?
This is a post about that - a pretty reasonable input field.
Originally posted on Mar 16, 2013 (archive), revised in 2023.
The idea
When the user presses keys that have a character associated with them,
these characters are added to keyboard_string
, generally with a limit of 1024 characters.
And pressing backspace removes the last character from the string, surely enough.
So if we were to set keyboard_string
to
====
and the user typed abc
, it would become
====abc
and if the user pressed Backspace, it would instead become
===
we can use this (with a less-common prefix-character, of course) to figure out the newly entered characters / number of erased ones.
The code
You might replace var name; name = ...
by var name = ...
if you are using GameMaker: Studio or newer.
Create event:
input_string = ""; // current text caret_pos = 0; // caret position (0 being before the first letter) caret_next_move = current_time; // when's the next time you can move the caret left/right caret_move_rate = 170; // delay between such movements caret_flash_start = current_time; // when the caret will start flashing caret_flash_delay = 700; // delay before flashing caret_flash_rate = 600; // flashing rate // we figure out the number of erased characters based on how many filler // characters went missing from the string since we have last checked fill_char = chr(27); // that's an ESC. It's not very typable as-is fill_count = 16; fill_string = string_repeat(fill_char, fill_count);
Step event:
// cursor movement: var caret_delta; caret_delta = keyboard_check(vk_right) - keyboard_check(vk_left); if (caret_delta != 0) { if (caret_next_move <= current_time) { caret_next_move = current_time + caret_move_rate; caret_pos = clamp(caret_pos + caret_delta, 0, string_length(input_string)); caret_flash_start = current_time; } } else caret_next_move = current_time; // keyboard_shortcuts: if (keyboard_check_pressed(vk_home)) caret_pos = 0; if (keyboard_check_pressed(vk_end)) caret_pos = string_length(input_string); if (keyboard_check_pressed(vk_delete)) { input_string = string_delete(input_string, caret_pos + 1, 1); } if (keyboard_check(vk_control)) { if (keyboard_check_pressed(ord("C"))) clipboard_set_text(input_string); if (keyboard_check_pressed(ord("V"))) { input_string = clipboard_get_text(); caret_pos = string_length(input_string); } } // actual input: var nstr; nstr = keyboard_string; if (nstr == "") { // `keyboard_string` is set to `""` when the window loses focus. keyboard_string = fill_string; } else if (nstr != fill_string) { // new user input! // the number of missing "filler" characters is the number of characters // that user wants to erase this frame var numBksp; numBksp = fill_count - string_count(fill_char, nstr); numBksp = min(numBksp, caret_pos); // can't erase what doesn't exist if (numBksp > 0) { caret_pos -= numBksp; input_string = string_delete(input_string, caret_pos + 1, numBksp); } // the rest are the newly typed characters: nstr = string_replace_all(nstr, fill_char, ""); nstr = string_replace_all(nstr, chr(127), ""); // ctrl+bksp types a DEL for some reason if (nstr != "") { input_string = string_insert(nstr, input_string, caret_pos + 1); caret_pos += string_length(nstr); } caret_flash_start = current_time; keyboard_string = fill_string; }
Draw event:
// we don't want the user to type line breaks (in GM:S or earlier) so we'll replace those: var draw_string; draw_string = string_replace_all(input_string, "#", "\#"); draw_text(x, y, draw_string); // draw the caret: var caret_offset; caret_offset = string_width(string_copy(draw_string, 1, caret_pos)); if (current_time < caret_flash_start + caret_flash_delay || (current_time - caret_flash_start) mod (caret_flash_rate * 2) > caret_flash_rate ) { draw_line(x + caret_offset, y, x + caret_offset, y + string_height("Q")); }
Other Things
You might be wondering "But what about text selection? And mouse interaction? And multi-line input? And a dozen other common keyboard shortcuts?" and all of those are valid desires, but also slightly out of scope for this little post.
For example, the chat system that you see in some of my multiplayer mods counts a thousand-something lines of code and it's not even feature-complete!
Downloads
You can download
the GameMaker 8.1 project that was used for the screenshot in the beginning of this post.
It contains the exact same code that you can see above and a very basic "message log"
(a ds_list
and a few draw_text
calls).
How to open this file using GMS2?
It supports only *.yyp for project files
Import to GMS1 first, then pick Import in GMS2 and choose the GMS1 GMX project.
Hey,
I have been messing around a bit with your work but I a afraid of messing with this piece of code because I don’t understand how it works. I think i am getting close to completing it so that you can change the lines after pressing enter.
I would appreciate your help.
// actual input:
if (keyboard_string != filltext && keyboard_string != “”) {
var n, l, t;
t = keyboard_string
l = string_length(t)
n = 1
// find where actual input starts
while ((n 0 && caret > 0) {
caret -= c
text = string_delete(tex, caret[0] + 1, c)
}
c = l – n + 1
if (c > 0) {
text = string_insert(string_copy(t, n, c), text, caret + 1)
caret += c
}
}
Instead of messing with that code, it would probably be a better idea to change the string being edited to that of the new line, and store the current one for display on-screen. There are a few complete text field examples on marketplace if I remember right.
Any updated location for this file? I’m browsing different text input methods, and i ran into this one (by recommendation on the forums)
Should be fixed now.
Can’t download, but need it so bad, Vadim :)
Fixed for sure now? There was just an extra “http://” in the URL, should always try taking such out on first guess.
yep, thanks a lot!
Do you know a code I can add so if I were to say “game_restart’ in the input box, the game would find that function I added and restart the game? :/
I like the style! No scripts, just some coding.
Hello,
Thanks for the input field, really helped me with my project.
My mother language and the language that will be used in it is Hebrew, and it’s a right-to-left language.
I added support for the Hebrew characters but the text comes out mirrored.
Can you help me please?
Sincerely,
Ofek
שלום אופק!!
נתקלתי בתגובתך לאתר בנוגע לכתיבת עברית בגיימייקר
(קלט עברית וכד’)
?האם הצלחת לפתור את הבעיה
אודה לך מאוד אם תשתף אותי בקוד מסוים שמאפשר קלט-פלט בעברית ללא אפקט מראה.
תודה (:
שחר
Remember that HTML5 binds UK keyboard, so the @ sign wont work with SHIFT-2 in HTML5, but will in windows. So good idea to add this to functionality.
For HTML5 it would generally be a wiser decision to write an extension that would place an actual <input> element over the game instead of trying to write a keyboard handler yourself (which will not be able to consider every possible keyboard layout anyway)
Pretty good and short for script but you cannot go up or down nor erase once you have created a new line so multiple lines are not yet supported.
Correct, this is currently geared towards single-line fields. Adding multi-line support would be possible, but would require increasing complexity of code quite a bit. Will try to look into that sometime.
Just saw this coming from yoyo forum, I was wondering if you could help a newbie like me to GM and suggest a way to integrate this code into an existing prj. I’d like to ask the user his name when the game finished (i.e. lives = 0) so I can write it up in a file and use it in the leaderboard at startup screen. I was using the get_string() function but I don’t like the ugly window pop-up at all, your solution it’s much more elegant from a look’n’feel point of view.
I’ve tried to create the object (the same that’s in your project) with the same code, and used a create_instance inside my code in place of the get_string but it just doesn’t do anything… I guess I’m too much of a newbie to GM though.
any help/suggestion is much appreciated.
thanks
I was excited to see this text input example because as the previous commenter said it’s something that’s serious lacking from GameMaker. That said, I’m disappointed it doesn’t invoke the virtual keyboard on mobile devices when used with HTML5. That’s not the fault of your example, but rather a continued inefficiency of GameMaker. Nevertheless, thanks for all your hard work!
Indeed, while bringing up virtual keyboard is possible, it is completely out of scope of this example.
It’s not even just about the GameMaker – it’s also about how HTML5 works: system keyboard will not pop up unless the user has tapped an according element (input/textarea). Thus, to bring up the keyboard, you have to trick the user into tapping such an input field. And that, in turn, means that all further event capturing must be done via that element.
That, in combination with current lack of a proper JS API for interacting with engine, makes achieving of such task fairly problematic.
Thanks, if it wasnt for your sniplet Game Maker would have been uninstalled already. Seems silly something that is used to make HTML5 applications has no form fields.
Thanks very much for this.