"automatic" typewriter effect

Trying to create a function to create a typewriter effect on the “Text” property of any object/multiple objects.
I know how to do this with a predefined variable. It’s something along the lines of this:


textindex = 1
...
own["Text"] += string[textindex]
textindex += 1

How do I take the Text property from the object, set it as a “static” variable, then type it out?

Idea I had was to get the string length of the property then subtract 1 until the length is 0. By then the word will be spelled out. Is this a good way to do this?
Is there a better way/how do I do this?

Nope, getting the length and using that to append is the way I’ve done it in the past.

Actually, I find (and suspect other people do to), that watching text type is annoying.

So what’s the problem you have?

I wouldn’t append because there’s one more handy python string trick, namely string[:n] which returns n first characters of the string.


own["TextIndex"] = 1
text = "Foobar"

own["Text"] = text[:own["TextIndex"]]
own["TextIndex"] += 1

The good thing is the script is less prone to system hickups and you can actually control text visibility with index number as 0 returns empty string.

And also, I find the scrolling text much more interesting to read than suddenly appearing text. Also delivers sense of motion and flow (not unlike speech does IRL) in the game. The key is to find the right speed and when in doubt make it rather too fast than too slow, adjustable is best OFC. But you don’t want the typewriter effect actually slowing down anybodys reading.

Thanks for the feedback :slight_smile:

Got this:


import bge


def setwrite(cont):
    own = cont.owner
    own["textindex"] = 1
    own["Text2"] = own["Text"]


def write(cont):
    own = cont.owner
    text = own["Text2"]
    
    own["Text"] = text[:own["textindex"]]
    own["textindex"] += 1

It kind of works, but not when editing the value of “Text” property. Is there a better way to do this?
Thanks for the help :slight_smile:

There are a lot of options.

Usually you have a source and a destination. You copy from source to destination. As this is a long lasting process you need to decide who to design the timing and if you want to manipulate the destination or completely recreate it with every step.

Here are some options:

  • as you do with the separate “textindex” by storing the current position in the source text
  • you can even count the length of the destination to see what is missing
  • remove the already processed parts from the source and add it to the destination (better have a copy of the source before hand ;))
  • to create larger “chunks” e.g. words you can add more than a single character. or the opposite, you skip skip frames to slow he “typing” down. With some random timing, it might look like natural typing.

I actually write this effect in my Dialog system tutorial: http://www.youtube.com/watch?v=_3x8TWs6wJc

I don’t think it’s “the best way” (and that would depend on the circumstances), but I think it’s good enough to be helpful.

Yeah I guess it depends on the situation… However I’m not too sure what mine is.
Well, perhaps it is a bit much to ask, but could you help me implement this into this script?:


import GameLogic as g
cont = g.getCurrentController()
own = cont.owner
scene = g.getCurrentScene()
dialogue1 = scene.objects["dialogue"]
dialogue2 = scene.objects["dialogue.001"]
dialogue3 = scene.objects["dialogue.002"]
dialogue4 = scene.objects["dialogue.003"]
dialogue5 = scene.objects["dialogue.004"]
dialogue6 = scene.objects["dialogue.005"]
dialogue7 = scene.objects["dialogue.006"]
dialogue8 = scene.objects["dialogue.007"]
dialogue9 = scene.objects["dialogue.008"]
dialogue10 = scene.objects["dialogue.009"]
def line(text):    
    dialogue10["Text"] = dialogue9["Text"]
    dialogue9["Text"] = dialogue8["Text"]
    dialogue8["Text"] = dialogue7["Text"]
    dialogue7["Text"] = dialogue6["Text"]
    dialogue6["Text"] = dialogue5["Text"]
    dialogue5["Text"] = dialogue4["Text"]
    dialogue4["Text"] = dialogue3["Text"]
    dialogue3["Text"] = dialogue2["Text"]
    dialogue2["Text"] = dialogue1["Text"]
    dialogue1["Text"] = text




def dialogue():
    clear = cont.sensors["clear"]
    if clear.positive:
        dialogue1["Text"] = ""
        dialogue2["Text"] = ""
        dialogue3["Text"] = ""
        dialogue4["Text"] = ""
        dialogue5["Text"] = ""
        dialogue6["Text"] = ""
        dialogue7["Text"] = ""
        dialogue8["Text"] = ""
        dialogue9["Text"] = ""
        dialogue10["Text"] = ""
        
    dialogue = cont.sensors["dialogue"]
    if dialogue.positive:
        id = int(dialogue.bodies[0])
        sys = msgarray[id][0]
        if str(msgarray[id][1]) .find(">") > 1:
            line(str(msgarray[id][1]))
            line(str(msgarray[id+1][1]))
        else: line(str(msgarray[id][1]))


        
        
#messages ~80 characters
#1001+ = dialogue
msgarray = [
(0,"Single Line),
(0,"Single Line"),
(1,"\"Really Really Really Really Really Really Really Really Really Really Really... >"),
(1,"Really Really Really Really Really Really Really Really Really Long Message\"")]

It’s really a simple, self explanatory dialogue system I set up. There isn’t any input or anything yet, but I’m working on the output first.

The effect I need: when the line updates to one-by-one remove a character and replace it with one from the previous line.
So I guess you could use the same function to do that?


#add
own["Text"] = text[:own["textindex"]]
own["textindex"] += 1

#subtract
own["Text"] = text[:own["textindex"]]
own["textindex"] -= 1


The problem is that the function line() requires a tap, or else it just keeps scrolling lines…

Any ideas (I’m probably doing this entirely incorrectly)?

please … use a collection!

It should really hurt you to write the same line of code more than two times (even than someone should slap you on our fingers :wink: )

Use a collection (e.g. a list).
Then you can loop over the collection to perform tasks on each single item.

The name “messages” is much better than the artificial “msgarray”.

So you do not want the single characters to appear over time, but complete lines?
Or a combination of that?

I haven’t learned about collections yet… Still getting used to python. Is that the same idea as importing another functions page?

What I mean – should have been more clear – is for something like this to happen:


kind of visual idea:

time - string1 - string2
  0     hello    world
  1     wello    horld
  2     wollo    herld
  3     worlo    helld
  4     worlo    helld
  5     world    hello

So it takes the value of the previous line then overwrites it character by character.


def sswap(t, s1, s2):
    return s2[:t] + s1[t:], s1[:t] + s2[t:]

print(sswap(2, "hello", "world")) # ('wollo', 'herld')

EDIT: got it to work. Set global dict index to always add 1 then set 0 when dialogue is added. Still getting error KeyError: ‘index’
what is this caused by?

Thanks for the help :slight_smile: