Creative Switch

Concept

The main concept is to create a switch that isn’t necessarily a physical switch but a gate that returns true when a requirement is fulfilled. I created a switch that can provide an alert based on the water level of a specific environment. The circuit looks like this:

The two switches at the bottom part of the board indicate the water levels. Whatever the water level is, the rightmost LED always shines. If the water level is above a threshold, the second LED shines too. If another threshold is met, the third LED shines. This serves as an alert system for the water level of a specific environment. The actual functional circuit is shown below:This is what happens when there is no water in the cup.This is what happens when the water in the cup only reaches the first thresholdThis is what happens when the water level reaches the maximum value.This is what happens when I disconnect the second LED, because the power the red LED needs is more than the yellow one needs, so it might not be obvious that the red one is shining. This image is to show that the red one is shining when the water level is high.

Midterm Project

Fullscreen link: https://editor.p5js.org/fhgggtyf/full/_GaqN8_Sa

Overall Concepts

The concepts didn’t change much compared to what I thought of when planning. However, instead of staring with randomly generated maps, I chose to use the original map for the first few levels so that the difficulty won’t get too insane at the beginning. Then, after the user is familiar with the game, they will encounter randomly generated maps which are insanely difficult. The main gameplay is the same with the original Pac-Man version, but the success condition for finishing a level no longer is to eat all pellets but also reaching the portals to get to the next level. Each level has a theme which is rotated in a pool of three: fire, wood, and water. Each theme has its own sets of environment and music. This is because I do not want this game to only feel like a game to the user, but feel like a real adventure in various landscapes. The player also gets a record of their final score after their 3 health is depleted. The four ghosts each have a different logic system of pursuing the player, and the mode changes of the ghosts are coherent with the original game. As levels increase, scatter mode and chase mode ratios will change to make difficulties higher.

Implementation

The game is mostly separated into 3 modules: Game, Player, Ghosts. It could be divided to make the structure clearer but since its not such a big project I decided to keep it simple.

The game module is responsible for controlling how the game progresses. There are 3 game states, which are start, in game, and game over. In the in game state, it is responsible for the game interactions, level states, and sending signals to the ghosts making them go to different modes. There should be a game effect controller to do this but I integrated the elements into the game module instead. Basically, this module controls all in-game state changes and the program state changes.

the player module is responsible for taking in inputs and converting it into actions of the player object. it also contains stats of the player.

The ghosts module is used for calculating the actions of the ghosts. each ghost have its own rules in pursuit of the player. Also the ghosts have different states, which would effect their destination points. Overall, All ghosts have the same rules pursuing a certain destination point. However, different ghosts usually have different destination points due to their unique behaviors. Therefore I have subclasses inheriting the Ghosts class so they can determine their own destinations. These squares refers to their different destination points based on their behaviors. The squares in the following image stands for the destinations of each of the ghosts.

The code is shown below.

class Ghosts{
  constructor(){
    this.position=[1,1];
    this.img=blinkyImage;
    this.vulnerable=false;
    this.addPointFlag=false;
    this.absPos=[round(this.position[0]),round(this.position[1])];
    this.prevAbsPos=this.absPos;
    this.state=0;//prepare state
    this.dir=[1,0];//no direction
    this.speed=ghostSpeed[game.gameLevel];
    this.dest=[0,0];
    this.next=[0,0];
    this.sur=[[this.absPos[0],this.absPos[1]-1],
              [this.absPos[0]+1,this.absPos[1]],
              [this.absPos[0],this.absPos[1]+1],
              [this.absPos[0]-1,this.absPos[1]]];
    this.choices=[];
  }
  
  checkState(){ // Check ghost's state to determin action
    if(this.state==0&&this.absPos[1]>11){
      this.resetMode();
    }
    else if(this.state==9){
      if(this.absPos[0]==13&&this.absPos[1]==15){
        this.state=0;
        this.addPointFlag=false;
      }
    }
    else{
      if(game.frightened && game.frightenedTimer<=7){
        this.frightenedMode();
      }
      else{
        this.vulnerable=false;
        game.frightened=false;
        game.frightenedTimer=0;
        if(game.cycleTimer/(int(chaseTime[game.gameLevel])+int(scatterTime[game.gameLevel]))<=5 && game.cycleTimer%(int(chaseTime[game.gameLevel])+int(scatterTime[game.gameLevel]))<scatterTime[game.gameLevel]){
          this.scatterMode();
        }
        else{
          this.chaseMode();
        }
      }
    }

    
  }
   
  defeatedMode(){
    this.state=9;
    this.img=eyesImage;
    this.dest=[13,15];
  }
  
  resetMode(){
    this.dest=[13,11];
  }
  
  checkMoved(){ // check if the ghost moved a whole block, if moved calculate new destination
    this.absPos=[round(this.position[0]),round(this.position[1])];
    this.sur=[[this.absPos[0],this.absPos[1]-1],
              [this.absPos[0]+1,this.absPos[1]],
              [this.absPos[0],this.absPos[1]+1],
              [this.absPos[0]-1,this.absPos[1]]];
    if(this.absPos[0]!=this.prevAbsPos[0] || this.absPos[1]!=this.prevAbsPos[1]){
      this.calcMovement();
      this.prevAbsPos=this.absPos;
    }
  }
  
  calcMovement(){ // calculate new destination and how to get there
    this.choices=[];
    this.sur.forEach(element => {
      if((element[0]!=this.prevAbsPos[0] || element[1]!=this.prevAbsPos[1]) && mapData[game.mapNum][element[1]][element[0]]!=1){
        if((this.state != 0 && this.state != 9)&& mapData[game.mapNum][element[1]][element[0]]==3){
        }
        else{
          this.choices.push(element);
        }

      } 
    });
    
    if(this.choices.length==0){
      if(this.absPos[0]==1 && this.absPos[1]==14){
        this.position=[26,14];
        this.absPos=[round(this.position[0]),round(this.position[1])];
        this.choices.push([25,14]);
      }
      else if(this.absPos[0]==26 && this.absPos[1]==14){
        this.position=[1,14];
        this.absPos=[round(this.position[0]),round(this.position[1])];
        this.choices.push([2,14]);
      }
    }
    
    let closest = Infinity;
    
    this.choices.forEach(element => {
      
      let difference = sq(element[0]-this.dest[0])+sq(element[1]-this.dest[1]);

      // Check if the current element is closer than the previous closest element
      if (difference < closest) {
        closest = difference;
        this.next = element;
      }
    });
    
    this.dir=[this.next[0]-this.absPos[0],this.next[1]-this.absPos[1]];
  }
  
  moveToNext(){ // move
    if(this.dir[0]!=0){
      this.position[0]+=this.dir[0]*this.speed*deltaTime/1000;
      this.position[1]=this.absPos[1];
    }
    else{
      this.position[1]+=this.dir[1]*this.speed*deltaTime/1000;
      this.position[0]=this.absPos[0];
    }
  }
  
  frightenedMode(){
    this.vulnerable=true;
    this.img=vulImage;
    if(this.choices.length>1){
      this.dest=this.choices[floor(random(this.choices.length))];
    }

  }
  
}

class Blinky extends Ghosts{
  constructor(state,dest,position,img){
    super(state,dest,position,img);
    this.position=[13.5,11]
    this.img=blinkyImage;
  }
  
  scatterMode(){ // Scatter mode determine destination
    if(this.state!=2){
      this.dir[0]=-this.dir[0];
      this.dir[1]=-this.dir[1];
      this.state=2;
    }
    this.dest=[27,0];
  }
  
  chaseMode(){ // Chase mode determine destination
    if(this.state!=1){
      this.dir[0]=-this.dir[0];
      this.dir[1]=-this.dir[1];
      this.state=1;
    }
    this.dest=player.position;
  }
  
  display(){
    if(this.state!=9 && this.vulnerable==false){
      this.img=blinkyImage;
    }
    fill("red");
    // rect(this.dest[0]*40,this.dest[1]*40,40,40);
    image(this.img,this.position[0]*40,this.position[1]*40,40,40);
    fill(0); // Set the text fill color to black  
  }

}

In the code above I only shown one subclass so that it won’t be too long. The basic concept of the subclasses are similar. I am pretty proud of this part because the structure is clear and it made the development progress so much easier.

Areas for Improvement

Some areas of improvements could be the aesthetics of the different scenes. I planned to do a pixelated low-poly tile-map styled map design but it turned out to look very unnatural and dirty. If I have more knowledge in graphic design I might be able to do better. Another thing is that the program has a few minor rule bugs that may allow the user to gain points incredibly fast (with a great risk). Maybe I’ll fix them in the future. Also, the random maps may cause the ghosts to behave abnormally because of the limitations of a 43-year-old algorithm. It could also be refined. Also a user login system could be applied to store people’s personal bests online. Apart of random maps, I could also add one way areas or blockages that would slow the player down, etc. But that would require refines in the ghosts’ AI, which I didn’t have time to do in this project.

Week 5 – Midterm Concept

Concept

For people who know me, I was a gamer my entire life, and I loved the concept of video games as art. I always believed that games themselves can convey meaning like any other artwork, and for some their aesthetic values might even exceed what people traditionally identify as art. Also due to games’ nature of being an interactive medium, it can achieve much more that traditional art forms cannot. For example, the immersion that games bring to people playing them goes far beyond just viewing them, but actually experiencing them yourselves. Therefore, I decided to look for these artistic values in the earliest games, played on the arcades. When I was reading the webpage on The Art of Video Games exhibition in 2012 on the Smithsonian Art Museum website, I came across this game that I had heard of but never played before (because it was too old, obviously), Pac-Man. I knew it was a famous game and almost as old as Tetris, and then soon found out it was one of the first video games that “took this leap beyond the arcade, beyond the screen, into the broader popular culture. That changed the way the world looked at video games and understood that there was something bigger happening here than just a place to waste a quarter.” I took a great interest in this and decided to modify it by combining some other game elements into it and upgrading its interface for my midterm assignment.

I plan to leave the core gameplay of Pac-Man alone and focus on other aspects of the game. In the original version of the game, The map is always the same. I want to combine some roguelike game elements into my project by randomly generating the map every time the player resets the game. therefore the player would always get a new experience when in the game. I also want to make it an endless mode, therefore the player can play to update their personal records. I plan to achieve that by using the tunnels on the left and right sides of the map. Once the player finishes eating all the dots in one map, the tunnels turn into portals that teleport them to other maps with different backgrounds and different theme songs.

Functions and Classes

In this game, I think the main classes are Ghosts, Map, Player, Dots, and Game.

For ghosts, the four types of ghosts (Blinky (red), Pinky (pink), Inky (cyan), and Clyde (orange)) inherit the Ghost class. The Ghost class has certain attributes: intposition for identifying its absolute position, also responsible for calculating destination position; realposition for identifying the true position of the ghost and for determining collisions; targetposition for identifying the target the ghost tries to reach; speed for the speed of the ghosts, and state for determining the current state the ghost is in for it to have different actions. The functions the Ghost class includes are checkCollision, updateTarget, stateChange, directionUpdate, etc. In these functions, updateTarget is different for different types of ghosts.

For the map, it will only have a few attributes. One is a 2D array used to represent the map, and one is its state. it will only have one function, that is mapGen, called every time it is initialized.

For Player, it will have attributes: intposition, realposition, direction, and speed. The only functions it will have are movement and checkDot.

For Dots, the only attribute is its position and type, and the only function is to destroy itself after being touched.

Lastly, for the game class, will have attributes such as points, lives, gamestate(pause, ongoing, in between, etc). It will take care of loading each state of the game and contain data necessary to keep the game going.

This is just a brief outlook of what the system would be like. In practice, some parts may be modified.

Uncertain Part of Project

I find the most uncertain part to be the random map generator. I need to come up with a set of rules so that the map is playable and won’t include too many long corridors the player can be cornered, long dead ends, or areas the player cannot access. However, I have made a preliminary generator that follows one set of rules. This set of rules makes sure that the map is different every time and will not have corridors with width 2 in it. However, this set of rules could result in inaccessible areas. Right now I have an idea about another set of rules to be applied after this so that the inaccessible areas and dead ends can be connected to the other corridors so that the map could be more reasonable. I plan to make any tile that should be accessible with only one or fewer connections with other accessible tiles have at least two connections with other accessible tiles. There will be tiles that cannot find two connection points. Then this tile will be discarded into a wall tile. This process will repeat until no action can be done, and a usable map is then created. I haven’t implemented the second rule set yet, but I have a prototype of the first rule set and it yields satisfying results. It generates grids shown below:

Therefore I am confident that this map generator will be achievable, and I only need to tweak some details.

Week 5 – Reading Reflection

After reading the paper, what I find most interesting is the methods used for computer vision and how simple they actually are. I always thought computer vision requires a significant amount of different complex algorithms, but it turns out to be as simple as comparing pixels one by one at different times. However, I still have no idea how arrays of pixels (how algorithms interpret images or videos) can be used to gain advanced information such as “tracking the orientation of a person’s gaze.”

I also find it interesting how these techniques can be applied to interactive media art. The way it makes artwork interactive is not making the viewer touch buttons whatsoever, but only move around and see what happens. This method of interactivity gives the viewer more freedom to explore what lies beyond the artwork, instead of just playing within the set of rules the artist sets. It also gives viewers an urge to explore the artwork more because they feel freedom when participating in the artwork. They would want to try out different ways of interaction and see what happens, and that’s what I find most interesting about computer vision used in interactive art pieces.

Week 4 – Reading Reflection

After reading the chapter, I feel the author said exactly what I thought. Some designs are inhumane. NYUAD is a great example. I remember the designer of NYUAD was famous and great. However, some aspects of the design really piss me off. For instance, I understand the doors are automatic so that disabled people can enter. However, It also led to many other problems, including maintenance issues, slow reactions (leading to traffic), etc. Many different ways could fix this issue, for example, changing the opening method of the doors from swinging to sliding, adding specific gateways for people with disabilities, and so on. However, it might negatively influence the aesthetics of the design itself. I guess that is why it hasn’t been fixed.

Another thing about the article that interests me is that the author said the engineers are too logical. I immediately thought of a concept in design called “affordance.” This term generally refers to an object’s aspects that define its uses and how it could be used. If this concept can be applied to designing and making it evident how it should be used, it should become a good design. However, I also understand the difficulties in doing this, especially in machines with multiple functions or complicated properties. Still, it should be applied to many daily items since they are not that complex, and usually, people using it are not professionals.

Data Visualization

This is the visualization of the data about the popularity of a set of popular songs in 2023. The red line stands for the number of times it was streamed, the green line stands for the number of Spotify playlists it was included, and the blue line stands for the number of Apple playlists it was included. I couldn’t find any WEBGL fonts, and it was almost impossible to detect which ball was clicked in WEBGL (at least for my Javascript abilities), so I couldn’t label the axis nor display information of the song when its representative ball was clicked.

OOP P5.js Artwork

Design Concept

This project portrays that in a box, there are various balls flying around and another rather distinctive ball floating inside. Every other ball experiences a force when approaching the yellow ball in the center. No matter how the yellow ball moves, other balls are driven away from it. My original intention is to remind people that there are bright spots in people, even if most reject or isolate them. However, it is also open to other interpretations since it only vaguely represents my intent.

Code I am Proud of

I am proud of the part where I implemented the particle effects of the balls traveling around. Originally, I wanted to implement an algorithm that makes the particles fly out in a certain cone range behind the ball. However, the effects are rather unsatisfactory. The particles fly out sequentially in an over-orderly manner. It was almost like the particles were in a line when the range of the cone was too narrow. Then I thought of the movements the balls are making. If the ball is moving, perhaps I could make the particles fly in all directions instead of only in a cone behind the ball. It will still form a coned trail as long as the particles have less velocity than the ball. The particles with the same direction as the ball will have less relative speed to the ball. Therefore, it will disappear close to the ball, and vice versa. This makes the trail look more chaotic and more like a trail instead of a projector.

class Particle{
  //xyz be the coordinates of the ball
  //dir is random plus direction of the ball
  constructor(x,y,z,dir){
    this.x=x;
    this.y=y;
    this.z=z;
    this.r=random(255);
    this.g=random(255);
    this.b=random(255);
    //Generate random cold color
    if(this.r>this.b){
      this.clr=color(this.b,this.g,this.r);
    }
    else{
      this.clr=color(this.r,this.g,this.b);
    }
    this.direction=p5.Vector.add(dir,p5.Vector.random3D());
    this.opacity=0.5;
  }
  
  //Displaying color
  display(){
    push();
    if(this.r>this.b){
      this.clr=color(this.b,this.g,this.r,this.opacity*255);
    }
    else{
      this.clr=color(this.r,this.g,this.b,this.opacity*255);
    }
    stroke(this.clr);
    translate(this.x,this.y,this.z);
    sphere(3);
    pop();
  }
  
  //Movement and change in opacity
  updateLocation(){
    this.x=this.x+0.5*this.direction.x;
    this.y=this.y+0.5*this.direction.y;
    this.z=this.z+0.5*this.direction.z;
    this.opacity-=0.015;
  }
}

Improvements

I wanted to do lighting on the balls, especially the central ball. However, I cannot understand the usage of shaders or other methods that can imitate lighting and light halos. Therefore, this project is not yet complete, and it should have lighting effects to indicate the size of the repulsion field of the central ball. Without the lighting, the theme that “even the most isolated people shine” becomes not obvious.

Week 3 – Reading Reflection

I find this reading very interesting because it was not until now I realized that interactivity is related to interaction. It never occurred to me that one-way interaction might not even count towards being interactive at all. When I actually think about it, I realize how reasonable the author’s claims are. If anything that moves when you interact with it is interactive, then anything could be interactive. You could potentially pick up a rock, throw it away, and say it is interactive. Then, there would be no meaning to the word “interactive.” The author’s definition of interactivity really gave me a lot of new insight into the term”interactive media.” This framework clearly separates and distinguishes interactive media from traditional media. Being interactive requires the media to somehow respond to the audience and improvise. Regarding media, it could only be done with algorithms and sensors, which traditional media doesn’t include.

Another thing I find interesting is the distinction between interactive design and traditional interface design. I always thought that interactive design is based on interface design and that there must be an interface basis to implement interactivity on top of it. It only occurred to me now that interactivity design could be much broader than interface design. There is much more to play with in interactive design, not just thinking about the aesthetics and alignment of elements on the page.

Week 2 – Graphic Art

Design Concept

At first, I wanted to make something that spirals around a pivot point. To make it look less monotonous, I made the spiral only the trajectory. The actual thing going along that spiral consists of lines of increasing length with the same distance to each other with various colors normal to the spiral. But simple or random colors seemed too messy, so I made the lines rainbow-colored. Inspired by Casey Reas’ works, I decided to add a touch of randomness to the project. Therefore, I randomized the spawn points of the spiral while fixing the pivot point to the center. I also wanted the audience to have some control or involvement in the project, so I made the spirals detect the pivot point in real time, and the audience could change the pivot point themselves with a click of their mouse.

Code I am Proud of

The part that I am really satisfied with is the trajectory algorithm. This outcome was, in fact, unexpected. I spent a lot of time calculating angles so that the angles align. I gave the trajectory an original angle relative to the x-axis and calculated the angle that the x-axis, the pivot point, and the trajectory head make. then I used these two angles to determine the direction of the trajectory head should turn to. I made it turn 1/15 of the angle between each frame. Because it is a percentage change, the trajectory will never touch the pivot point. Also, the distance traveled by the trajectory head between each frame is fixed, when it is very close to the pivot point, the relative angle between the pivot and the trajectory head will change dramatically between the frames, making it leave the pivot point even further. I wasn’t sure what this would bring, and it really surprised me when I saw this interwoven structure with four corners. The code is shown below:

class Dot{
  constructor(x,y,ang,len){
    this.x=x;
    this.y=y;
    this.ang=ang;
    this.len=len;
  }
  
  display(){
    // Rainbow color collection
    let firstColor=color("red");
    let secondColor=color("orange");
    let thirdColor=color("yellow");
    let fourthColor=color("green");
    let fifthColor=color("blue");
    let sixthColor=color("indigo");
    let seventhColor=color("violet");
    let actualColor;
    
    // Interpolated color generation
    for(let i=0;i<this.len;i++){
      if(3*i/this.len<=1){
        actualColor=lerpColor(firstColor,secondColor,3*i/this.len);
        stroke(actualColor);
        point(this.x-i*cos(this.ang),this.y-i*sin(this.ang));
      }
      else if(3*i/this.len>1 && 3*i/this.len<=2){
        actualColor=lerpColor(secondColor,thirdColor,3*i/this.len-1);
        stroke(actualColor);
        point(this.x-i*cos(this.ang),this.y-i*sin(this.ang));
      }
      else if(3*i/this.len>2 && 3*i/this.len<=3){
        actualColor=lerpColor(thirdColor,fourthColor,3*i/this.len-2);
        stroke(actualColor);
        point(this.x-i*cos(this.ang),this.y-i*sin(this.ang));
      }
      if(3*i/this.len<=1){
        actualColor=lerpColor(fourthColor,fifthColor,3*i/this.len);
        stroke(actualColor);
        point(this.x+i*cos(this.ang),this.y+i*sin(this.ang));
      }
      else if(3*i/this.len>1 && 3*i/this.len<=2){
        actualColor=lerpColor(fifthColor,sixthColor,3*i/this.len-1);
        stroke(actualColor);
        point(this.x+i*cos(this.ang),this.y+i*sin(this.ang));
      }
      else if(3*i/this.len>2 && 3*i/this.len<=3){
        actualColor=lerpColor(sixthColor,seventhColor,3*i/this.len-2);
        stroke(actualColor);
        point(this.x+i*cos(this.ang),this.y+i*sin(this.ang));
      }

    }
  // Calculating angle
  updateParam(){
    this.x=this.x-10*cos(this.ang+PI);
    this.y=this.y+10*sin(this.ang+PI);
    this.len=sqrt(sqrt(sqrt(fibRec1)));
    let navAng =atan2(navdot.y-this.y, navdot.x-this.x);
    if(navAng<0){
      navAng=abs(navAng);
    }
    else{
      navAng=2*PI-navAng;
    }
    let angleDifference = navAng-this.ang;
    while (angleDifference > PI) {
      angleDifference -= 2 * PI;
    }
    while (angleDifference < -PI) {
      angleDifference += 2 * PI;
    }
    let clockwise = angleDifference < 0;
    let rotationAngle = abs(angleDifference);
    if (clockwise) {
      this.ang -= rotationAngle/15;
    } else {
      this.ang += rotationAngle/15;
    } 
  }

Improvements

The lengths of the lines were a Fibonacci sequence, down to its 8th root. I wanted the increase of the lines to become gradually bigger, while not exploding to a number too huge. I haven’t experimented with other sequences due to limited mathematical abilities, but I believe there can be better sequences that will produce smoother increases in the length of the lines.

Reading Reflection – Week 2

Casey’s talk at the Eyeo Festival 2012 first introduced the usage of randomness in modern art starting from the 20th century, then showed some of his work that used the concept of chance within algorithmic rules. I find this concept very interesting because, intuitively, chaos and art don’t belong together. Just as random alphabets combined don’t convey information, random pixels featuring random colors also shouldn’t have artistic value. However, Casey and various other artists created artistic pieces by combining chaos with some constraints, which is simply amazing. Also, these artworks are closely related to algorithms and technology, which I am particularly interested in.

When I heard about combining randomness with art, I immediately thought of the infinite monkey theorem. This theorem states that, given an endless amount of time, a monkey randomly typing on a typewriter can type out anything, including Shakespeare’s “Hamlet” word for word. Changing to computed art instead of typing would mean randomly distributed pixels on a canvas can produce artworks like “Mona Lisa” to a specific resolution. Casey’s works are like using programming algorithms to give instructions to the “monkey” while maintaining its freedom to an extent so that it could produce abstract pieces of art. The algorithms also provide interactivity to artworks that are otherwise impossible in traditional art pieces. Therefore, the artist’s job is no longer creating art itself, but generating rules for the “monkey” to enter the art piece’s basic building blocks randomly. This is also very intriguing to me.