How do I make a fading in/out transition when switching gameStates/images?

edited May 2017 in Library Questions

Currently making a rhythm game and I want to create a fade to black/fade out animation for when I switch between the menu and the song selection screen. I also want to use this animation for when I'm trying to pick between songs in the song selection screen.

I know it's not entirely necessary and the game will still work without it, but I kinda wanna make it look prettier. Some ideas/code would be nice!

The animation would kinda look like this:

-fade to black

-change image behind it (hidden from user)

-start playing music of the song selection screen (hidden from user)

-fades out from black with entirely new background/music playing

Tagged:

Answers

  • edited May 2017

    @kurtsnafu --

    Here is an example sketch demonstrating a timed cross-fade. It is for two videos, but works the same way for two images:

    ...and here is a more recent discussion on how to program timed fading for different timing sequences:

    https://forum.processing.org/two/discussion/comment/96657/#Comment_96657

    See also other past discussions:

  • Please have a look at the mask documentation for PImage in the reference. In your case, you need to setup an array as big as your image. Then you change the value of the array from 255 (fully opaque) to 0 (fully transparent) and then you apply this array to your image via mask() function.

    The other extra step is to figure the timing. Trigger event applies mask, then change and apply the mask at some fixed intervals until you get full transparency (background is black).

    For curiosity, are you using the default renderer or P2D?

    Kf

  • edited May 2017

    @jeremydouglass: I'm aware of those threads, most of the ones I've seen after searching were just crossfades between two videos. I've read most of them, but I still cannot wrap my head on how to transition to another gameState while still having a tint/black-rectangle on top of everything. I feel like it is easier on a video because it switches automatically.

    @kfrajer: I'm kinda confused by this. So you propose a solution that involves only masking an image? I will be covering up the whole gameState, so that means the whole menu (background, flashing lights, and etc). and not just a single image. Sorry if I'm not getting something.

    Thank you for your replies.

  • @kurtsnafu -- re:

    most of the ones I've seen after searching were just crossfades between two videos

    As I said, it works the same with two images as with two videos -- or with two rects -- or with two anything!

    The important thing is that a fader is really just this a simple map() function wrapped in a constrain() call. That's it -- one long line of code. You can use a fader to map anything -- for example, times to transparency levels. Time goes in, alpha color comes out.

    void draw() {
      background(0);
      noStroke();
      // update fader
      float f = fade(millis(), 3000, 6000, 255, 0); //3->6secs = 255->0 alpha
      // draw red fade
      fill(255,0,0, f);
      rect(0, 0, 75, 75);
      // draw green cross-fade
      fill(0,255,0, 255-f);
      rect(25, 25, 75, 75);
    }
    float fade (float amt, float start1, float stop1, float start2, float stop2) {
      return constrain(
        map ( amt, start1, stop1, start2, stop2 ), 
        min(start2, stop2), 
        max(start2, stop2)
        );
    }
    

    In the sketch above, f is changing the fill() transparency of two rectangles. However, the fader code has nothing to do with rectangles. Instead of fill(255,f); rect() this could be tint(255,t); image() for an image, or a PGraphics surface, or a movie frame. Or it could be a shape. The fader code is the same -- shift a variable, then use the output as an argument to the change visibility while drawing some object -- any object!

    Want to add keyboard control? Calculate the amt argument based on the last time a key was pressed. Want multiple faders? Call with different sets of arguments. Want something a bit more concise for those sets of arguments? Create a CrossFader class -- this is essentially still just a wrapper around a single line of constrain(map()) code:

    class CrossFader {
      float amt;
      float start1, stop1, start2, stop2;
      CrossFader(float start1_, float stop1_, float start2_, float stop2_) {    
        start1 = start1_;
        stop1 = stop1_;
        start2 = start2_;
        stop2 = stop2_;
      }
      float fade() {
        return fade(amt);
      }
      float fade(float amt_) {
        amt = amt_;
        return constrain(
          map ( amt_, start1, stop1, start2, stop2 ), 
          min(start2, stop2), 
          max(start2, stop2)
          );
      }
    }
    

    Now you can initialize a fader and then just call fader(millis()) or fader() -- but it is still just a single map() wrapped in constraint().

    CrossFader

  • edited May 2017

    This is my version.

    Keywords: Duotransition, timed image transition, fade, fading, fade out fade in

    Kf

    //INSTRUCTIONS: Set fisrtImg, secImg and fading time.
    //              Mouse event starts the effect
    //              Effects starts with first image, and it fades to background color in time defined by fading time
    //              Then, the second image shows up in transition, from background color to full opaque in time defined by fading time.
    //              Total time for transition: twice fading time
    //              At the end of effect, the program is ready to start a new transition starting from the second image and back to the first.
    //              Notice that a mouse event in the middle of the operation resets the transition so the opposite set of images get transited.
    //              If you don't want the mouse effect to affect the current transition operation, change the lockTransition to true.
    
    PImage bck, pic;
    PImage firstImg, secImg;  //start, stop
    int[] mask;
    
    boolean activateEffect=false;
    boolean back_pic_order=true;
    boolean startFadeRecover=false; //After fading out, recover second image as fade reverse (fade in)
    
    final int kTimeLapse=2000; //msecs for fading each image 
    int timeTarget=0;
    
    final boolean lockTransition=true;
    final int kMinMask=0;
    final int kMaxMask=256;
    int fadingStep=0;
    
    
    void settings() {
    
      bck= loadImage("http://"+"wanderingdanny.com/oxford/images/p/b4242667-wytham-woods-avenue.jpg");
      pic=loadImage("http://"+"jquery-custom-scrollbar.rocketmind.pl/images/lena.png");
      size(bck.width, bck.height, P2D);
    }
    
    void setup() {  
      rectMode(CENTER);
      textAlign(CENTER,CENTER);
    
      pic.resize(bck.width, bck.height);
      mask=new int[bck.width*bck.height];
      image(pic, 0, 0);
      drawMyGUI();
    
      //surface.setTitle("Ready to start transition... ");
    }
    
    void draw() {
    
      PImage p;
      int mkVal;
    
      if ( activateEffect==false) {
        surface.setTitle("Ready to start transition... ");
        return;
      }
    
      background(0, 0, 150);
    
    
      if (startFadeRecover==false) {
        p=firstImg.get();
        mkVal=(int)map(timeTarget-millis(), 0, kTimeLapse, kMinMask, kMaxMask);
      } else {
        p=secImg.get();
        mkVal=(int)map(timeTarget-millis(), 0, kTimeLapse, kMaxMask, kMinMask);
      }
    
    
    
      if ( mkVal<(kMinMask+fadingStep) && startFadeRecover==false) {
        startFadeRecover=true;
        timeTarget=millis()+kTimeLapse;
        surface.setTitle("Fading back");
      } else {
        if ( mkVal>(kMaxMask-fadingStep) && startFadeRecover==true) {
          activateEffect=false;
          image(p, 0, 0);
          drawMyGUI();
          return;
        }
      }
    
    
      setMask(mkVal);
      p.mask(mask);
      image(p, 0, 0);
    
      drawMyGUI();
    }
    
    void mouseReleased() {
      prepareTransition();
    }
    
    void setMask(int val) {
      for (int i=0; i<mask.length; i++) {
        mask[i]=val;
      }
    }
    
    void prepareTransition() { 
    
      //Block mouse event (if lock flag enabled) until transition op is done
      if(lockTransition==true && activateEffect==true)
      return;
    
      fadingStep=ceil((kTimeLapse)/frameRate);  
      if (back_pic_order==true) {
        firstImg=pic;
        secImg=bck;
        back_pic_order=false;
      } else {
        firstImg=bck;
        secImg=pic;
        back_pic_order=true;
      }
      timeTarget=millis()+kTimeLapse;
      startFadeRecover=false;
      surface.setTitle("Fading out");
      activateEffect=true;
      //back_pic_order=!back_pic_order;
    }
    
    void drawMyGUI() {
      pushStyle();
      fill(0);
      rect(width/4, height/2, 100, 50);
      rect(width/2, height/2, 100, 50);
      rect(3*width/4, height/2, 100, 50);
      fill(255);
      text("Java",width/4, height/2);
      text("JS",width/2, height/2);
      text("Python",3*width/4, height/2);
    
      popStyle();
    }
    
Sign In or Register to comment.