Maria Nolasco
Maria Nolasco

Reputation: 71

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);
  }
}

Upvotes: 0

Views: 889

Answers (2)

George Profenza
George Profenza

Reputation: 51847

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);
  }
}

Upvotes: 1

apodidae
apodidae

Reputation: 2733

Alternate method which uses an IntList to hold integers used to create letters. Numbers and corresponding letters in list are sequentially changed with each draw() cycle; frameRate may be slowed to 1 to see changes.

IntList  charNum;
int y = 0;
int index = 0;

void display () {
  background(0);
  y = 30;
  for (int i = 0; i < charNum.size(); i++) {
    char a = char(charNum.get(i));
    fill(0, 192, 0);
    text(a, 60, y);
    y+=20;
  }
}

void setup() {
  size(200, 300);
  background(209);
  charNum = new IntList();
  for (int i = 0; i < 12; i++) {
    charNum.append(int(random(65, 65+24)));
  }
  display();
}

void draw() {
  frameRate(60); // Slow this to 1 to see changes
  charNum.set(index, int(random(65, 65+24)));
  display();
  index += 1;
  if (index > charNum.size() - 1) {
    index = 0;
  }
}

Upvotes: 2

Related Questions