Search code examples
stringcharprocessing

Random character String processing


So, I want to make a vertical line with characters (letters) in order to create something similiar to the Matrix effect. I started with a number string, just to see if it worked and it did

String character = str (floor (random(10)));
this.letter = character;

Now I want it to have letters instead of numbers, but I don't now how to make it generate randomly. I tried with char and string, but it shows more than one letter

character += char (int(random(65, 65+24)));

I've tried a new method that was recommended, but processing crashes and doesn't run it

  PVector pos;
  float speed;
  String letter;
  float change_threshold = 0.1;
  color cor = color (3, 160, 98);
  String character = "";
  // maximum letters in a string
  int maxLetters = 35;
  // which character to swap
  int charIndex = 0;

  Letter (float xpos, float ypos, float vel) {

    this.pickLetter();
    pos = new PVector (xpos, ypos);
    speed = vel;
  }

  void display() {
    for (int i = 0; i < maxLetters; i++) {
      character +=getRandomLetter() +"\n";
    }
    fill(this.cor);
    text(this.letter, this.pos.x, this.pos.y);
    float p = random(1);
    if (p < this.change_threshold && this.cor != color(255)) {
      this.pickLetter();
    }
  }

  void pickLetter() {

    //String character = str (floor (random(10)));
    //String character = new String ("a");
    //character += char (int(random(65, 65+24)));
    char randomChar = getRandomLetter();
    character = setCharAt(character, randomChar, charIndex);
    charIndex = (charIndex + 2)%character.length();
    this.letter = character;
  }


  void fall() {
    this.pos.y += this.speed;
  }

  // returns a random a-z char
  char getRandomLetter() {
    return char (int(random(65, 65+24)));
  }

  // return a new string with a char swapped at the given index
  String setCharAt(String myString, char myNewChar, int myCharIndex) {
    return myString.substring(0, myCharIndex) + myNewChar + myString.substring(myCharIndex + 1);
  }
}

Solution

  • char (int(random(65, 65+24))); is indeed the right way to get a random letter.

    character += char (int(random(65, 65+24))); means you append/concatenate one letter at a time therefore your character variable will increase with a new char each iteration.

    character = char (int(random(65, 65+24))); would replace the current char with a new random each iteration.

    If you want to make vertical text you can use the new line character (\n). Unfortunately you can't easily swap a character out with the String class, but with a bit of substring() and concatenation you can simulate something similar. (The StringBuilder java class would make character swapping easier). Here's a commented example using String:

    // full string of letters
    String letters = "";
    // maximum letters in a string
    int maxLetters = 12;
    // which character to swap
    int charIndex = 0;
    
    void setup(){
      size(300, 300);
      fill(0, 192, 0);
      textAlign(CENTER);
      textFont(createFont("Courier New", 12), 12);
      // populate the string
      for(int i = 0 ; i < maxLetters; i++){
        letters += getRandomLetter() + "\n";
      }
    }
    
    void draw(){
      // pick random char
      char randomChar = getRandomLetter();
      // replace existing characters
      letters = setCharAt(letters, randomChar, charIndex);
      // increment the char index by 2 to include \n
      // use the modulo operator to loop back to 0
      charIndex = (charIndex + 2) % letters.length();
      
      // render the text
      background(0);
      text(letters, width * 0.5, height * 0.25);
    }
    
    // returns a random a-z char
    char getRandomLetter(){
      return char (int(random(65, 65+24)));
    }
    
    // return a new string with a char swapped at the given index
    String setCharAt(String myString, char myNewChar, int myCharIndex){
      return myString.substring(0, myCharIndex) + myNewChar + myString.substring(myCharIndex + 1); 
    }
    

    Update The above can be encapuslated for re-use:

    MText text = new MText();
    
    void setup(){
      size(300, 300);
      fill(0, 192, 0);
      textAlign(CENTER);
      textFont(createFont("Courier New", 12), 12);  
    }
    
    void draw(){
      background(0);
      text.draw();
    }
    
    class MText{
      // full string of letters
      String letters = "";
      // maximum letters in a string
      int maxLetters = 12;
      // which character to swap
      int charIndex = 0;
      
      float x, y;
      
      MText(){
        // populate the string
        for(int i = 0 ; i < maxLetters; i++){
          letters += getRandomLetter() + "\n";
        }
        // default position
        x = width * 0.5;
        y = height * 0.25;
      }
      
      void draw(){
        // pick random char
        char randomChar = getRandomLetter();
        // replace existing characters
        letters = setCharAt(letters, randomChar, charIndex);
        // increment the char index by 2 to include \n
        // use the modulo operator to loop back to 0
        charIndex = (charIndex + 2) % letters.length();
        // render text
        text(letters, x, y);
      }
      
      // returns a random a-z char
      char getRandomLetter(){
        return char (int(random(65, 65+24)));
      }
      
      // return a new string with a char swapped at the given index
      String setCharAt(String myString, char myNewChar, int myCharIndex){
        return myString.substring(0, myCharIndex) + myNewChar + myString.substring(myCharIndex + 1); 
      }
    
    
    }
    

    The advantage of grouping/encapuslating the functionality in a class is that mulitple instances can be easily managed:

    int numTexts = 60;
    ArrayList<MText> texts = new ArrayList<MText>();
    
    void setup(){
      size(300, 300);
      fill(0, 192, 0);
      textAlign(CENTER);
      textFont(createFont("Courier New", 12), 12);
      for(int i = 0 ; i < numTexts; i++){
        MText text = new MText();
        text.x = random(width);
        text.y = random(height);
        texts.add(text);
      }
    }
    
    void draw(){
      background(0);
      for(MText text: texts) text.draw();
    }
    
    class MText{
      // full string of letters
      String letters = "";
      // maximum letters in a string
      int maxLetters = 12;
      // which character to swap
      int charIndex = 0;
      
      float x, y;
      float vy;
      float textHeight;
      
      MText(){
        // populate the string
        for(int i = 0 ; i < maxLetters; i++){
          letters += getRandomLetter() + "\n";
        }
        // default position
        x = width * 0.5;
        y = height * 0.25;
        // default Y velocity
        vy = random(.16018, 2.1);
        textHeight = (textAscent() - textDescent()) * maxLetters;
      }
      
      void draw(){
        // pick random char
        char randomChar = getRandomLetter();
        // replace existing characters
        letters = setCharAt(letters, randomChar, charIndex);
        // increment the char index by 2 to include \n
        // use the modulo operator to loop back to 0
        charIndex = (charIndex + 2) % letters.length();
        // update position
        y += vy;
        if(y > height + textHeight){
          y = -textHeight * 2;
          vy = random(.16018, 2.1);
        }
        // render text
        fill(0, 192, 0);
        text(letters, x, y);
        text(0, 255, 0);
        text(letters.charAt(0), x, y);
      }
      
      // returns a random a-z char
      char getRandomLetter(){
        return char (int(random(65, 65+24)));
      }
      
      // return a new string with a char swapped at the given index
      String setCharAt(String myString, char myNewChar, int myCharIndex){
        return myString.substring(0, myCharIndex) + myNewChar + myString.substring(myCharIndex + 1); 
      }
    
    
    }
    

    Hopefully the above is a useful direction. There many ways of achieving a similar result. In terms of improvements, little touches such as a single brighter green character (maybe even making it glow), aligning x text so it doesn't overlap for the matrix 1 effect, fading/leaving trails and chaning camera z position for the matrix 2 effect, etc.

    Update 2 The updated code didn't include the part instantiatting Letter and rendering it. The error I encountered was due to the fact that string was initialized to be empty (String character = "";) and the random letter function couldn't replace a character at an index that didn't exist (yet). The solution in this case would be to intialize the string with a few characters first. For example moving:

    for (int i = 0; i < maxLetters; i++) {
          character +=getRandomLetter() +"\n";
        }
    

    in the constructor before pickLetter() gets called. Full example:

    Letter l = new Letter(150, 15, 1.5);
    
    void setup(){
      size(300, 300);
    }
    
    void draw(){
      background(0);
      l.display();
    }
    
    class Letter{
       PVector pos;
      float speed;
      String letter;
      float change_threshold = 0.1;
      color cor = color (3, 160, 98);
      String character = "";
      // maximum letters in a string
      int maxLetters = 35;
      // which character to swap
      int charIndex = 0;
    
      Letter (float xpos, float ypos, float vel) {
        for (int i = 0; i < maxLetters; i++) {
          character +=getRandomLetter() +"\n";
        }
        this.pickLetter();
        pos = new PVector (xpos, ypos);
        speed = vel;
      }
    
      void display() {
        
        fill(this.cor);
        text(this.letter, this.pos.x, this.pos.y);
        float p = random(1);
        if (p < this.change_threshold && this.cor != color(255)) {
          this.pickLetter();
        }
      }
    
      void pickLetter() {
    
        //String character = str (floor (random(10)));
        //String character = new String ("a");
        //character += char (int(random(65, 65+24)));
        char randomChar = getRandomLetter();
        character = setCharAt(character, randomChar, charIndex);
        charIndex = (charIndex + 2)%character.length();
        this.letter = character;
      }
    
    
      void fall() {
        this.pos.y += this.speed;
      }
    
      // returns a random a-z char
      char getRandomLetter() {
        return char (int(random(65, 65+24)));
      }
    
      // return a new string with a char swapped at the given index
      String setCharAt(String myString, char myNewChar, int myCharIndex) {
        return myString.substring(0, myCharIndex) + myNewChar + myString.substring(myCharIndex + 1);
      }
    }