CMPSC457 Final project: Light Game

Description: Description: Description: ./1.png

Description: Description: Description: ./1.png

Description: Description: Description: ./1.png

Description: Description: Description: ./1.png

Background

The final project was supposed to put all the things we learned about OpenGL into one big cool project.

The minimum criteria

  • Animation
  • 3-D Hierarchal
  • Perspective
  • Illumination and Shading
  • Textures
  • Interaction

Light Game

For our project we made a first person 3D video game. The player is in maze and the goal of the game is to collect colored teapots and place them into their matching bin. Some features:

  • Levels can be designed and processed through a text file
    • Placement of walls
    • Placement of teapots
    • Placement of bins
    • Placement of lights
    • Placement of light switches
  • Walls can not be passed through
  • Lights can be toggled on and off with switches for visibility
  • Teapots can be picked up, moved, and dropped
    • Dropped on the floor or in the correct bin
  • Player can move with WASD keys
  • Player can look around with mouse movement

Source Code

You can download and browse the source code on github.com at joegle/Light-Game Run 'make' or compile:

 
 g++ -lglut -lGLU game.cpp -o game

How it was created

Structure and Files

  • game.cpp
    This is the core program and has all the typical GLUT functions. The run through looks like this:
    1. #include all the subparts
    2. init() initializes glut, seed random number, process the level file
    3. display() Will repeatedly call drawObjects() that will place the player and draw the world
  • globals.h
    This file only instantiates the two important objects: 'world' and 'dude'.
  • utils.cpp
    Has useful definitions that most files needs:
    1. color struct to hold colors
    2. radian to degree function
    3. distance function
    4. random color generator
    5. random number from a to b
  • init.h
    This holds the 'processfile()' function which will process the 'level.txt' file and fill in the 'world' with the data needed to represent the parts like walls, teapots, switches.
  • world.h
    This stores the 'world' class and is where most of the action happens. Everything that exists, except for the player, is contained in this object and managed through it.

A world object has a vector for each kind of thing in the world (Teapot, Bin, Switch, Light) and will loop through the vector to draw each thing by call their 'draw()'

There are 4 sub objects that all have similar structure (also check the '.h' files):

Wall information is stored in a matrix

  • dude.h
    This represents the player. The player has a pointer into the world instance so that it can access world information. The main function is 'look()' which will place and tilt the camera from the player position. It will also draw the held teapots in view.
  • controls.h
    This file only handles the mapping of keys and mouse to movement of the player.

Lessons learned / Cool Stuff

Written in Linux environment, also works in Windows

Graphical vi improved (or gvim for short) running on Ubuntu was the most used combination for writing our code. However, the project instructions made it clear for all projects to run in a windows 7 environment. Because of this, we decided to make sure that our program compiled and ran as either a linux or windows executable. We had to adjust some of the light values because of this, but Zack found some useful “#if defined” functions that allowed us to write simple conditionals account for issues.

Using git for group projects

Fun Facts

·         Don’t use ‘git revert’ loosely

·         A good order to do things is git commit –a –m, git pull, git push origin master

·         Fork a personal repository for backup

o   This means you’ll have two remotes: origin and upstream

·         Git likes linux, doesn’t like Windows too much

In the beginning we decided to use github.com to host our code. Team members can download the latest version of the code and make changes concurrently with the other team members. The version control also lets us have access to every version of the project. The website is interfaced with the git program and we learned the concepts needed to use the system.

It turned out to be a nice way to manage our group project and we can foresee ourselves using the system in the future. There was some confusion sometimes but nothing that online help couldn't help with.

Structural planning end evolution

Some thought had to put into how to logically organize the project. The choice to have a world class and dude class seemed obvious but the internals of those classes had to be giving a loose frame work. At one point the dude and world instance needed to interact and the decision was made to make the dude have a pointer to the world instance.

There may be some redundancy in our data model but the extra time it would of taken to make everything perfect would not have been would saving a little bit of memory and efficiency was kept very high.

Incremental planning and debugging

Programming could get very confusing. Sometimes you could change code and not see any difference or some thing is not showing up at all in the game when it should. With a large project like ours you can't trust that the whole chain of calls has ran smoothly prior to the call of the current function your working on. You must instead some how isolate the testing of your function and incrementally building on single parts you know work.

Using multiple source files and inheritance

Working with huge source files is really annoying and unnecessary but working with a large number of dependent sources was a little like fitting a puzzle together. It took some time to figure out the right include order and this also helped shaped our functional topology. Also, it was found that our first version would not compile in Windows because g++ is less strict (or smarter) then visual studio, so our headers and source files had to shaped up better.

Collision Detection

We implemented collision detection by checking the bitwise anding the information read in from the walls. We made sure to detect the wall at a far enough distance so that no “jumping” motions were seen. An interesting point to make is that once you write the code for forward collision detection – which you should place into a function for repeated use – you only need to rotate the user and call that function in order to detect walls in the left, right, and reverse directions.

Textures

We textured our floor and ceiling with a bathroom-tile-like material, and our walls consist of a stone texture. We wanted to produce finer textures with a higher resolution, but we simply didn’t have the time to break down/ rewrite the texture processing file given to us in class. Once all was finished, we had a fully textured level, however.

Lights are difficult, but rewarding

Fun Facts:

·         The 4th index of the GL_LIGHT_POSITION array indicates whether the light will have directional (like the sun) or positional (like a desk lamp) properties

·         Unless otherwise indicated, lights will follow the transformations of the current matrix stack, regardless of their initial position

First, we wanted to take advantage of the fact that each light has a number by making a “wrapper” function of sorts. We created a switch statement that returned a Glenum (in this case LIGHT0-LIGHT7) depending on which n is passed into the light object’s constructor. Each of these lights was then tied to a toggle switch through a 1:1 ratio. Each time the toggle switches were pressed or depressed, the light would react by being enabled or disabled. Things got more complicated as we started to make our level larger and darker…

Initially, we hit road blocks with our positional lighting system. For example, we wanted the main parts of the level to be very dark, while only certain sections showed faint light. We spent a lot of time trying to figure out exactly what we needed to do, but eventually found our answer: light attenuation. Essentially, there are two different types of light attenuation: GL_LINEAR_ATTENUATION and GL_QUADRATIC_ATTENUATION. Each will cause the light sources drawn in the scene to “drop off” relative to glLookAt() at different distances. Quadratic attenuation will drop off much more quickly than will linear, so we had to tweak our settings to look right.

Object Linking

The level that we came up with has three teapots, three bins, seven lights, and seven switches. Each teapot is linked to a bin, and each light is linked to a switch. Because of the way that we read information in from a text file, it was simple to position objects exactly where we wanted them. When the player flicks a light toggle, its corresponding light will turn on somewhere inside the level. Only one light can be turned on at once (not including the dude’s lamp). Each bin also has a corresponding teapot. When the correct teapot is placed inside the bin, the bin turns black and the teapot disappears from the scene. Once the player successfully places all teapots inside the bins, he has won and a graphical message will appear on the glut window.

Read in level from a text file

We were able to read in all of our pertinent level data by using a text file. The walls of our map were created using a bit masking technique that Joe created. Level objects were then placed inside the coordinate system using (x,y) pairs. An interesting note is that our Y and Z directions were effectively switched, so walking “deeper” into the world would actually cause the Y coordinate to increase.

Resource System / Player Hud (Of sorts)

To aid in the task of walking through the dark maze and collecting teapots, we thought it would be fun to implement a personal lamp for “the dude” to hold. This lamp would emit more ambient light than any other in the maze, but would only hold a certain amount of oil. The oil would drain as long as lamp was on, but would slowly regenerate when the lamp was off. We also decided that we wanted the % of lamp oil to update inside the glut window rather than inside the console. So, we did some research and found a way to append integers to strings using string streams. We then output this string stream as bitmap text on the glut window. This added a cool puzzle game feel to the whole project, and we really enjoyed implementing it.

Dangerous Environment

The final addition to our project was spike traps. Zack coded them, and wrote the logic so that in one position they were retracted into the floor (in the safe position), but would then shoot up (danger position). If the player came into contact with the spikes, he would “die” and would have to restart the level from scratch. These spikes acted as the animation aspect of our project, and the player could directly interact with them.