skip to Main Content

Here’s my dialogue system for the Entity class in Entity.js

 drawDialogue(context, element) {

     // Draw the dialogue box and face graphic
      context.drawImage(this.sprite.dialogueBox, 0, 90);
      context.drawImage(this.sprite.faceset, 6, 103);
    
      // Create a container for the text
      const dialogueContainer = document.createElement('div');
      dialogueContainer.className = 'dialogue-container';
  
      // Create a text element for the dialogue
      const textElement = document.createElement('p');


      // Create a container for the text
      const nameContainer = document.createElement('div');
      nameContainer.className = 'name-container';
  
      // Create a text element for the dialogue
      const nameElement = document.createElement('p');
      nameElement.innerText = this.displayName;
      nameElement.className = 'name-text';
      
      //...

      textElement.className = 'dialogue-text';
      textElement.innerText = this.text;

      // // Add the text to the dialogue container
      dialogueContainer.appendChild(textElement);
      element.appendChild(dialogueContainer);

      // // Add name to the dialogue name container
      nameContainer.appendChild(nameElement);
      element.appendChild(nameContainer);
    }
  }

I am only trying to add a typewriter effect to the textElement.innerText and nothing else. I am struggling to do so as I am not sure how to approach it and how to append each letter to the dialogue text container one by one.

This system works perfectly fine, as the dialogue is displayed correctly and with the correct text when the player interacts with this entity instance, however, I am trying to add a typewriter effect to it.

Although it may seem really seem to implement a typewriter effect an RPG game like mine, I am using a game loop which updates the state of the game at a constant frame rate, this method is directly called in the game loop as seen in the following code bloc:

 Loop() {
    const gameLoop = () => {
       
        //Check if any entity is interacting with the player
        Object.values(entities).forEach(entity => {
          //If an entity is interacting...
          if (entity.interacting) {
            //Display the dialogue
            entity.drawDialogue(this.context, this.element);
          }
        });

       //Move on to the next frame
       requestAnimationFrame(gameLoop);
    };

    //Call the game loop method again
    gameLoop();
  }

As you can see I am running calling this function when the player interacts with an NPC.

Do you have any approaches that I can pursue ensuring that my game is able to manage dialogues using a typewriter effect flawlessly? (similarly to that of Pokémon FireRed)

I have tried creating a different class for typewriter text which prints each letter one by one, but it didn’t seem to work unfortunately hence why I am asking about this issue on Stack Overflow.

2

Answers


  1. Here’s a fairly generic solution:

    async function typeWriteTextToElement(cssSelectorOrElement, text, delayPerCharMs = 50) {
      let sleep = ms => new Promise(res => setTimeout(res, ms));
      let element = cssSelectorOrElement;
      element = typeof element === 'string' ? document.querySelector(element) : element;
      for (i = 1; i <= text.length; i++) {
        element.innerText = text.slice(0, i);
        await sleep(delayPerCharMs);
      }
    }
    
    typeWriteTextToElement('body', 'Hi there! How do you like typewriters?');
    
    Login or Signup to reply.
  2. Simple typewritter animation example

    const targets = document.querySelectorAll('.typewritter')
    
    const parseIntHelper = (str) => {
      const result = parseInt(str, 10)
    
      return Number.isNaN(result) ? undefined : result
    }
    
    targets.forEach(target => {
      const text = target.getAttribute('data-text')
      const charDelayString = target.getAttribute('data-char-delay')
      const endDelayString = target.getAttribute('data-end-delay')
    
      const charDelay = parseIntHelper(charDelayString, 10)
      const endDelay = parseIntHelper(endDelayString, 10)
    
      const type = (init, text, index, charDelay = 50, endDelay = 1000) => {
        const isEnd = text.length - 1 === index
    
        if (!init && index === 0)
          target.textContent = ""
    
        target.textContent += text[index]
    
        setTimeout(() => {
          type(false, text, isEnd ? 0 : index + 1, charDelay, endDelay)
        }, isEnd ? endDelay : charDelay)
      }
    
      type(true, text, 0, charDelay, endDelay)
    })
    <p class="typewritter" data-text="Hello world, Typing..."></p>
    <p class="typewritter" data-text="Hello world2, Typing2..."></p>
    <p class="typewritter" data-text="Foo, Bar, Foo, Bar"></p>
    <p class="typewritter" data-text="Test, This is typewriter"></p>
    <p class="typewritter" data-text="Test 2 seconds end delay" data-end-delay="2000"></p>
    <p class="typewritter" data-text="Test 100 ms char delay" data-char-delay="100"></p>
    <p class="typewritter" data-text="Test 0 ms char delay" data-char-delay="0"></p>
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search