Page 1 of 1

Text typeout effect

Posted: Sat Aug 11, 2012 04:37
by N_K
Hello all.

I'm not sure if this has been asked before, I didn't find anything on this forum, but this could be because I have no idea what is this effect supposed to be called...

I'd like to achieve a delayed text output effect, similiar to the system found in visual novels and oldschool RPG games. (Here's a video of a visual novel engine prototype showing this kind of text output.) However, I'm not sure where to start. I know CEGUI has an animation system, but after reading through the wiki and the documentation, it seems that it has no support for text effects yet. How is this supposed to be done? Store the desired string in an array, then tell CEGUI to draw it as a static text, element by element, with a specific delay?

Or even better, has anyone did this before, and willing to share some bits of code to give me some clues about this? :)

Thank you in advance.

Re: Text typeout effect

Posted: Sat Aug 11, 2012 12:57
by MorbidAngel
You could just create a layout with a DefaultWindow type and a StaticText control. If you want it delayed like in your video then you would need to create some custom class for it with a timer on it.

I haven't looked much into the Animation feature in CEGUI yet, haven't had a need to use it for any of my projects.

Re: Text typeout effect

Posted: Mon Aug 13, 2012 09:04
by CrazyEddie
I believe that text can be animated in that way with the animation system.

See also what Ident is doing here:


Ident's GSoC project is not finished (and so not merged yet), but you can see his code here: https://bitbucket.org/Ident8/cegui-gsoc ... s/GameMenu

HTH

CE.

Re: Text typeout effect

Posted: Tue Jan 26, 2016 02:46
by HeeM
Hello, I have made a code with making customized text typeout animation in runtime

it's made with lua. and using some global variable for managing created animation instances.

and.. have fun :D

Code: Select all

-- from https://forums.coronalabs.com/topic/42019-split-utf-8-string-word-with-foreign-characters-to-letters/
local UTF8ToCharArray = function(str)
    local charArray = {};
    local iStart = 0;
    local strLen = str:len();
   
    local function bit(b)
        return 2 ^ (b - 1);
    end
 
    local function hasbit(w, b)
        return w % (b + b) >= b;
    end
   
    local checkMultiByte = function(i)
        if (iStart ~= 0) then
            charArray[#charArray + 1] = str:sub(iStart, i - 1);
            iStart = 0;
        end       
    end
   
    for i = 1, strLen do
        local b = str:byte(i);
        local multiStart = hasbit(b, bit(7)) and hasbit(b, bit(8));
        local multiTrail = not hasbit(b, bit(7)) and hasbit(b, bit(8));
 
        if (multiStart) then
            checkMultiByte(i);
            iStart = i;
           
        elseif (not multiTrail) then
            checkMultiByte(i);
            charArray[#charArray + 1] = str:sub(i, i);
        end
    end
   
    -- process if last character is multi-byte
    checkMultiByte(strLen + 1);
 
    return charArray;
end

g_alreadyGenerated = {};
g_typingAnimationID = 0;   -- for unique animation ID
function StartTypingAnimation(win, text, typingSpeed, typingPostfix)
   -- setting
   local typingSpeed = (typingSpeed or 0.1) / 2;   -- by default, it shows out a text per 0.1 sec
   local typingPostfix = typingPostfix or '_';
   -----------------
   
   -- delete previous animation
   local prevInfo = g_alreadyGenerated[win];
   if prevInfo then
      local connection = prevInfo.Connection;
      local prevAnimation = prevInfo.Animation;
      connection:disconnect();
      CEGUI.AnimationManager:getSingleton():destroyAllInstancesOfAnimation(prevAnimation);
      CEGUI.AnimationManager:getSingleton():destroyAnimation(prevAnimation);
      g_alreadyGenerated[win] = nil;
   end
   
   -- make animation
   g_typingAnimationID = g_typingAnimationID + 1;
   local animation = CEGUI.AnimationManager:getSingleton():createAnimation('_TypeoutAnimation'..g_typingAnimationID);
   animation:setReplayMode(CEGUI.Animation.RM_Once);
   animation:setAutoStart(true);
   local affector = animation:createAffector('Text', 'String');
   affector:setApplicationMethod(CEGUI.Affector.AM_Absolute);
   
   local timePos = 0;
   local textInTime = '';
   local skipBrackets = win:isTextParsingEnabled();
   local bracketDepth = 0;
   local remainTextArray = UTF8ToCharArray(text);
   local totalCharCount = #remainTextArray;
   
   affector:createKeyFrame(0, '');   -- first frame;
   for i, c in ipairs(remainTextArray) do
      textInTime = textInTime .. c;
      if c == '[' then
         bracketDepth = bracketDepth + 1;
      elseif c == ']' then
         bracketDepth = math.max(0, bracketDepth - 1);
      end
      if not skipBrackets or bracketDepth == 0 then
         timePos = timePos + typingSpeed;
         local showText = textInTime;
         if i < totalCharCount then
            showText = showText .. typingPostfix;
         end
         affector:createKeyFrame(timePos, showText);
      end
   end
   if skipBrackets and bracketDepth > 0 then   -- ends with backets not matched just add last frame to show text
      timePos = timePos + typingSpeed;
      affector:createKeyFrame(timePos, textInTime);
   end
   animation:setDuration(timePos);
   
   local instance = CEGUI.AnimationManager:getSingleton():instantiateAnimation(animation);
   instance:setTargetWindow(win);
   
   -- subscribe a destruction event for make sure the animation to be deleted with window destroy
   local connection = win:subscribeEvent('DestructionStarted', function(e)
      local we = CEGUI.toWindowEventArgs(e);
      g_alreadyGenerated[we.window] = nil;
      CEGUI.AnimationManager:getSingleton():destroyAllInstancesOfAnimation(animation);
      CEGUI.AnimationManager:getSingleton():destroyAnimation(animation);
      return true;
   end);
   
   -- register global variable
   g_alreadyGenerated[win] = {Connection = connection, Animation = animation};
   
   -- clear up window
   win:setText('');
end


ps. you can just call like 'StartTypingAnimation(window, 'ABCD')' to use this