0

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);
  }
}
  • 1
    Are you _actually_ using the Processing programming language? If so, please update your title to summarize the problem you have (because right now it sounds like you just want to process a string, which is not a problem description). As for "I want a single character": you're showing code that very intentionally adds letters to `character`. If you don't want that, don't use the `+=` operator. – Mike 'Pomax' Kamermans Dec 23 '21 at 17:38

2 Answers2

2

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

apodidae
  • 1,988
  • 2
  • 5
  • 9
  • nice solution (+1). what I like about your approach is that because each character is rendered indepedently which would make it easier for example to render a single char in a brighter shade of green or make other per character changes. – George Profenza Dec 27 '21 at 12:27
1

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);
  }
}
George Profenza
  • 50,687
  • 19
  • 144
  • 218
  • When I add the String setChartAt, processing crashes... I don't know why this isn't working in my code, it's weird. But your solution makes sense to me and it should work when I put it onto my code – Maria Nolasco Dec 27 '21 at 12:03
  • The Processing editor crashes completely (or the sketch stops with a runtime error) ? Would you be able to post an update to your question including the sketch as you describe it above, crashing ? (maybe there's something in the mix of code that isn't 100% yet) – George Profenza Dec 27 '21 at 12:08
  • 1
    I'll post the update.. But, basically, the code doesn't even start to run – Maria Nolasco Dec 27 '21 at 12:22
  • @MariaNolasco I think the error was due to attempting to change a character at an index that didn't exist (since the string was empty). I've posted an example above. – George Profenza Dec 27 '21 at 12:48
  • 1
    It's working now! It's not perfect, and think it's doubling the characters, but that's probably due to the other classes I have, so I will take a look into it. But thank you very much! – Maria Nolasco Dec 27 '21 at 12:56