Jump to content
  • Advertisement

Design Windows-like pause menu and color computations

jb-dev

990 views

So the game we develop is growing steadily. We've already added a pause menu.

Its function is to be a... pause menu. (I don't know what you expected...)

The design

Because our game is going to be really Vaporwave, we knew that the visual had to be on point.

We ended up trying a classic Windows-like design with header bars and everything, but with a twist...

Here's the result:

image.thumb.png.524cf954b0c7af548fa73f1c55a16c4b.png

image.thumb.png.6f0cfd4e3cf5a13ccd5fe39ee3b46b96.png

(Note that not all buttons are fully functioning. Also, the seed isn't actually used in the generation)

The idea is that this window appears when the player pauses. It takes the form of a popup with fully functional tabs.

We also plan to let the player easily navigate through the tabs with keyboard (or buttons, in the case of a controller) shortcuts.

In addition, our game uses palettes, so the GUI elements are colored according to the active palette.

Here's a example with a different palette:

image.thumb.png.4c93da0a3fef1465b5f465ca5cdcec9f.png

image.thumb.png.0bd4ab51b3ee0a10568cf0482abbd069.png

LESS-like color computations

You may have noticed that there is a difference between each palette (for example, the title of the window has changed color). This is done by a beautiful library that I built for our project.

Because I was a web developer for about 2 years, I already knew (and worked with) CSS compilers like SASS and LESS. My library is strongly inspired these compilers. Especially LESS.

The luma value

For this reason, I knew there was a way to know if a text of a given color would be readable when displayed on a given background. This feature is present in vanilla LESS : it's called "contrast"

That function uses the luma values (sometimes called "relative lightness" or "perceived luminance") of colors. This small practical value describes the perceived luminance of a color, which means that particularly brightly perceived colors (such as lime green or yellow) gets higher luma values than other colors (red or brown) despite their real lightness value.

Here's how I compute a given color's luma value:

Color color = Color.GREEN; // Fictionnal class, but it stores each components as floating points values form 0 to 1

float redComponent, blueComponent, greenComponent;

if (color.r < 0.03928f){
	redComponent = color.r / 12.92f;
} else {
	redComponent = Math.pow((color.r + 0.055f) / 1.055f, 2.4f);
}

if (color.g < 0.03928f){
	greenComponent = color.g / 12.92f;
} else {
	greenComponent = Math.pow((color.g + 0.055f) / 1.055f, 2.4f);
}

if (color.b < 0.03928f){
	blueComponent = color.b / 12.92f
} else {
	blueComponent = Math.pow((color.b + 0.055f) / 1.055f, 2.4f);
}

float luma = (0.2126f * redComponent) + (0.7152f * greenComponent) + (0.0722f * blueComponent);

The actual luma computation is fully describe here.

With that luma value, we can then check and compare the contrast between 2 colors:

float backgroundLuma = getLuma(backgroundColor) + 0.05f;
float textLuma = getLuma(textColor) + 0.05f;

float contrast = Math.max(backgroundLuma, textLuma) / Math.min(backgroundLuma, textLuma);

With that, we can choose between tow color by picking the one with the lowest contrast:

Color chosenTextColor = getContrast(backgroundColor, lightTextColor) > getContrast(backgroundColor, darkTextColor) ? lightTextColor : darkTextColor;

This can give us a lot of flexibility: especially since we use many different color palettes in our game, and each with different luma values.

This, along with more nice LESS colors functions, can make coloring components a breeze.

Just for example, I've inverted our color palette texture and these are the results:

 image.thumb.png.36c458754f2d0735c809be9292cc66ca.png

Yes, it looks weird, but notice how each component is still fully readable.

Paired with our dynamic palette, color computation is now really easy and flexable.

 



2 Comments


Recommended Comments

Wow! Quite a beautiful game you got there, my friend. But did you know you could buy Skyrim VR Ultimate Edition on your smart fridge now? I wonder if your game can compete against that.

- Rod Broward, independent video game journalist

Image result for meme todd howard independent video game journalist

Share this comment


Link to comment
57 minutes ago, thecheeselover said:

Image result for meme todd howard independent video game journalist

This looks like a clean man.

Share this comment


Link to comment

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Advertisement
  • Advertisement
  • What is your GameDev Story?

    In 2019 we are celebrating 20 years of GameDev.net! Share your GameDev Story with us.

    (You must login to your GameDev.net account.)

  • Blog Entries

  • Similar Content

    • By jb-dev
      This is how a mall looks like when a punk breaks one of its window.
      What the image doesn't tell you is that a loud alarm is being blasted trough your ears, too!
    • By Uttam Kushwah
      This article is on wikipedia says that we can change the behavior of the game entity by adding or removing the component at runtime how it goes 
      i don't know.
       
      What i am doing is, I have a gameObject class which is having four components 
       class gameObject
       {
           public:
            #define MAX_COMPONENT 4
            unsigned int components[MAX_COMPONENT];
            gameObject()
             {
              for(int i=0;i<MAX_COMPONENT;i++)
               {
                 components=0;
               }
                  //parent_id=-1;
            }
      };
      i don't use inheritance, Whenever i make the new game entity i add an gameObject in the gameObjectManger class using the game entities constructor 
      with all of its components filled at the time of game entity creation like index for rigid body , mesh and etc.
      Then i use these gameObjects at individual systems to run the game like below 
      // For renderer 
      for(unsigned int i=0;i<manager->gameObjects.size();i++)
           {
              unsigned int meshIndex = manager->gameObjects.components[MY_MESH]; //mesh data
              mat4 trans=(*transforms)[bodyIndex];// The transformation matrix extracted and spitted out by the the Physics engine 
              mesh_Entries[meshIndex].init();
              GLuint lMM  = glGetUniformLocation(programHandle,"Model");
              glUniformMatrix4fv(lMM, 1, GL_FALSE,&trans[0][0]);
              mesh_Entries[meshIndex].bindTexture();
              glBindBuffer(GL_ARRAY_BUFFER, mesh_Entries[meshIndex].getVertexBuffer());
              glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, mesh_Entries[meshIndex].getIndexBuffer());
              pickColor=vec4(1.0f);
              pickScale=mat4(1.0f);
              lMM  = glGetUniformLocation(programHandle,"pick");
              glUniform4fv(lMM,1,&pickColor[0]);
              lMM  = glGetUniformLocation(programHandle,"pickScale");
              glUniformMatrix4fv(lMM,1,GL_FALSE,&pickScale[0][0]);
              // This is very basic rendering since all object's indices are treated as
              // indexed based. Stored in element buffer with indices and vbo with vertices
              // for each single gameObject having their own VAO and IO. Optimization need to be done later on.
              glDrawElements(
                              GL_TRIANGLES,                                // mode
                              mesh_Entries[meshIndex].getIndicesSize(),    // count
                              GL_UNSIGNED_SHORT,                           // type
                              (void*)0                                     // element array buffer offset
                            );
           }
      but i am not getting it how to add a new component like LogicalData to the gameObject not about how to point from gameObject but how to build one 
      is this should be my approach 
      struct LogicalData 
      {
       float running_speed;
      vec3 seek_position;
      vec3 attack_point;
      };
      Character : public CharacterController
      {
       private:
      gameObject* me;
      public:
      // methods to manipulate gameObject content using the component id for each component in their container
      // i.e is to update component[LOGICAL_DATA] in the gameLogicContainer 
      };
      and then a global container which hold this logical data and every entity has a id to it using gameobjects  
      or i should not be doing this at all. i can just put all logical data  into the any game entity like this and push the physics and rendering data back to gameobject
      Character : public CharacterController
      {
       private:
         float running_speed;
         vec3 seek_position;
         vec3 attack_point;
      public:
      // methods to manipulate above
      };
      Any comments will be greatly appreciated.
       
       
    • By elviras9t
      I created this post due another radix sort post for CPU. This is Radix Sort for GPU. Able achieve 900 Mkeys/S, and sorting 8 million elements in 9.3ms (on RTX 2070). Written on C++ with Vulkan API and GLSL. Based on bitfield and warp hacks. For understand this shader code, need very good knowledge of bitfields and GPU subgroup. For NVIDIA, also need knowledge of subgroup partition extension. 
      Github source code: https://github.com/world8th/RadX
      Classification: Radix Sort (LSD)
      Stable: yes
      Parallel: yes, vector supported
      Bit width: 8-bit (Turing), 2-bit (other), can be changed
      Device type: GPU
      Also, how to get fastest GPU radix sort ever?
       
    • By nadyakzmn
      I'm making a research about using fonts in video games.
      Could you, please, help me and share your experience.
      What are possible font formats that are used in games? What are their technical advantages and disadvantages?
      Thank you for the help!
    • By Uttam Kushwah
      i am confused what i am doing is right or wrong?
      currently storing the state of the each key and checking it in the game loop like this 
      eHandler->getKey(EVENT_KEYS::KEY_LEFT_CTRL);
      if it returns true than do something.
      //this function is called by the function, which is passed as callback to the glfw library 
      void eventHandler::keyBoardHandler(int key , int scancode, int action, int mods)
       {
          if(action!=GLFW_KEY_UNKNOWN)
          switch (key)
           {
              case GLFW_KEY_A:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_A=true;
                              if(currentCamera->TP_PERSPECTIVE)
                              character_controller->moveLeft(currentCamera);
                         }
                        break;
                      }
              case GLFW_KEY_B:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_B=true;
                         }
                        break;
                      }
              case GLFW_KEY_C:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_C=true;
                         }
                        break;
                      }
              case GLFW_KEY_D:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_D=true;
                              if(currentCamera->TP_PERSPECTIVE)
                              character_controller->moveRight(currentCamera);
                         }
                        break;
                      }
              case GLFW_KEY_E:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_E=true;
                         }
                        break;
                      }
              case GLFW_KEY_F:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_F=true;
                         }
                        break;
                      }
              case GLFW_KEY_G:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_G=true;
                         }
                        break;
                      }
              case GLFW_KEY_H:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_H=true;
                         }
                        break;
                      }
              case GLFW_KEY_I:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_I=true;
                         }
                        break;
                      }
              case GLFW_KEY_J:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_J=true;
                         }
                        break;
                      }
              case GLFW_KEY_K:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_K=true;
                         }
                        break;
                      }
              case GLFW_KEY_L:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_L=true;
                         }
                        break;
                      }
              case GLFW_KEY_M:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_M=true;
                         }
                        break;
                      }
              case GLFW_KEY_N:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_N=true;
                         }
                        break;
                      }
              case GLFW_KEY_O:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_O=true;
                         }
                        break;
                      }
              case GLFW_KEY_P:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_P=true;
                         }
                        break;
                      }
              case GLFW_KEY_Q:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_Q=true;
                         }
                        break;
                      }
              case GLFW_KEY_R:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_R=true;
                         }
                        break;
                      }
              case GLFW_KEY_S:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_S=true;
                              if(currentCamera->TP_PERSPECTIVE)
                              character_controller->moveBackward(currentCamera);
                         }
                        break;
                      }
              case GLFW_KEY_T:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_T=true;
                         }
                        break;
                      }
              case GLFW_KEY_U:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_U=true;
                         }
                        break;
                      }
              case GLFW_KEY_V:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_V=true;
                         }
                        break;
                      }
              case GLFW_KEY_W:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                              my_keys.KEY_W=true;
                              if(currentCamera->TP_PERSPECTIVE)
                              character_controller->moveForward(currentCamera);
                         }
                        break;
                      }
              case GLFW_KEY_X:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_X=true;
                         }
                        break;
                      }
              case GLFW_KEY_Y:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_Y=true;
                         }
                        break;
                      }
              case GLFW_KEY_Z:
                      {
                        if(action==GLFW_PRESS)
                         {
                              my_keys.KEY_Z=true;
                         }
                        break;
                      }
              case GLFW_KEY_UP :
                      {
                        my_keys.KEY_UP = true;
                        currentCamera->panUp();
                        break;
                      }
              case GLFW_KEY_DOWN :
                      {
                        my_keys.KEY_DOWN = true;
                        currentCamera->panDown();
                        break;
                      }
              case GLFW_KEY_LEFT :
                      {
                        my_keys.KEY_LEFT = true;
                        currentCamera->panLeft();
                        break;
                      }
              case GLFW_KEY_RIGHT :
                      {
                        my_keys.KEY_RIGHT = true;
                        currentCamera->panRight();
                        break;
                      }
              case GLFW_KEY_ESCAPE:
                      {
                        my_keys.KEY_ESCAPE = true;
                        glfwSetWindowShouldClose(window, GLFW_TRUE);
                        break;
                      }
              case GLFW_KEY_SPACE:
                      {
                        if(action==GLFW_PRESS||action==GLFW_REPEAT)
                         {
                           my_keys.KEY_SPACE = true;
                           if(currentCamera->TP_PERSPECTIVE)
                           character_controller->jump(currentCamera);
                         }
                        break;
                      }
              case GLFW_KEY_ENTER:
                      {
                        if(action==GLFW_PRESS)
                         {
                           my_keys.KEY_ENTER = true;
                         }
                        break;
                      }
              case GLFW_KEY_TAB:
                      {
                        if(action==GLFW_PRESS)
                         {
                           my_keys.KEY_TAB = true;
                         }
                        break;
                      }
              case GLFW_KEY_LEFT_CONTROL:
                      {
                        if(action==GLFW_PRESS)
                         {
                           my_keys.KEY_LEFT_CTRL = true;
                         }
                        break;
                      }
              default:{}
           }
       }
      sorry that i have pasted this thing here but this is what i am really doing.
      i have done this like above because i don't want to pass other pointers to event handler class example
      camera,object_manger,game_world_physics and that all
      so my question is 
      what is the correct way of doing event handling? what you people do for optimization?
      😓😓😓😓😓😓😓😓😓😓😓😓
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

GameDev.net is your game development community. Create an account for your GameDev Portfolio and participate in the largest developer community in the games industry.

Sign me up!