
Team Members:
Steve Fox - Coder for implementation
Brandon Miller - Graphics
Anshuman Sehrawat - Coder for working with the user
Nick Wroblewski - Little bit of everything
Introduction:
Sample Program of what we will attempt to implement: http://193.151.73.87/games/lemmings/
Our idea is to implement the popular game lemmings done in many flavors of languages across the the years. It will consist of many different levels that contain a starting point where lemmings are dropped out of box, and then walk without purpose to a hopeful goal. Each lemming will have possible functions and jobs which are to be used to get the most lemmings to the end goal.
What the game looks like
| Our Loading Screen that Gives you the choice of picking which level or exiting the game. | ![]() |
The fourth level | ![]() |
| The first level | ![]() |
The fifth level | ![]() |
| The second level | ![]() |
The sixth level | ![]() |
| The third level | ![]() |
The seventh level | ![]() |
As you can see from the screenshots, only one job button is allowed to be pressed at once. If the pause button is pressed, everything is paused but the job buttons still can be pressed. As jobs are dealt out, the number on the different buttons decreases until 0 is reached in which no more jobs of that type can be used.
Problem Description:
The thing that seperates this project the most from the mp's is of course the complexity of it. More specifically however, would be the updating of the images for each lemming doing any one of the possible jobs going any possible direction. The scale of that alone is a daunting task. With that comes the logic and the numerous types of collision detection that has to be done, which never occured in past mps.
Implementation:
We used bit masking, mmx, image loading, and a lot of game loops and game environment handling to create this game.
The first thing we got to work on was bit masking. Most of our game has to load individual sprites for animation, different jobs, etc. Looking at the example code in the lab manual we were able to implement a few images onto the screen. We used that code to create a procedure in which u could send to it an image and a couple of information pieces and it would load the file and proceed to convert it to 24 bit all at once for convenience sake. Allocating memory became extremely tedious as the game implementation came about, so we created a function that simply was sent a lookup table of images , the size, etc and allocated memory for all of the variables in the look up table. This and the _LoadImages saved an immense amount of repetitive code in the program.
Once the image loading and such was taken care of, next had to come the bit masking code. Using some of the instructions suggested in class, we tried to use mmx instructions to handle multiple pixels at one time. It then occurred to us that wait, we're using 24 bits and not 32 or 16. Since mmx instructions only load dwords and such, we were going to have issues with handling the excess of 2 bytes not needed in the current calculation. So we first tried to simply load two bytes at a time from both the sprite and the buffer we were overwriting. Simply by adding all zeros to the end of the mmx register we could simply use the compare instruction followed by the pandn and the and instructions and viola we would have our overlay. Unfortunately, we forgot that when doing parallel compare, you need to have the words you are looking for to match exactly with the clear color. So we needed to split the two pixels into two double words and then compare them with a generic “clear” color that we created a variable for. So we simply loaded the entire quad word and then stored the last word of the mmx register from the buffer in a register so we don't loose information, put all 0's into the first word, shift the mmx register 8 bits to the left (since in mmx it will load the bytes right to left). This way we will have 00FF FFFF FFFF FF00 and we can simply compare that to our clear color. After doing this we realized that pandn was not working like we had assumed it would, so we then proceeded to use pxor instead which actually worked. Then by por'ing the two registers we could then proceed to set the image into the buffer. Also, we noticed that since we are always accessing two extra bytes as we load the next two pixels that we may have a segmentation fault with accessing memory that wasn't “ours” with the sprites. To solve this, we decided to add 2 to all allocated images so that way when you access the last two pixels of any image, the extra two bytes are allotted for each image.
With the images intact, next we had to work on animation. Animation was kind of tough since we would use two buffers and place one on the screen and then place the second on the screen with the correct next image followed by correct movement, deletion of incorrect pixels of where the sprite was and also keep track accordingly were the lemming is, and where it should go next. Not a simple feat, we might add. To do this we would need the aid of a timer interrupt. Using the code from the lab manual, we installed the timer and allocated the memory, which we attribute to the lab manual, but the actual timer interrupt was modified greatly. The idea in timer was to initially have a timer counter that would keep track of ticks and simply just increment it and be done. To handle this we set up a function call _gameLoop that at first waited for the just the ticks to send the appropriate buffer to the screen. First we got the lemmings to just walk by individually moving a pixel and filling behind the lemming all 0's in its place (all zeros is black as was the color of the background). Once that was working, we had to start implementing the game design.
The game design was developed behind two things: Strucs, a Level_Array, and an environment around a lemming. A lemming was basically a struc object that we used to create all the individual things a lemming needs to know “about itself”. As in its current position, its old position, its image, the count of its image, its job, activejob, etc, (look at our website for details), were just a few of the things that a lemming would need to be handled by the functions created to manipulate them. To create a lemming, we created a procedure _createLemming which was sent and x and y coordinates. It first allocated memory, put the location to the bottom of the Lemming_Array, and then initialized all of the lemmings inner variables to what they needed, its job, position, etc to start running. After lemmings were created we needed a way to keep track of their environment, so _getEnvironment was born.
Now comes the fun, we mean really crazy part of our game, to handle multiple objects at one time while keeping the game running and accessing multiple possible jobs. This was very hard to do because in order to do that you have to have an array created of what is in the level. So we had to make a method of loading all the bytes of an image into an array that contains the description of whether this pixel is empty, a wall, ground, etc. With that we could start placing the lemmings into the environment and start handling lemming movements. First we enacted walking, which was very basic, you just check if to the right, left and ground. Falling and walking were not too hard to implement. The issues came up as to when to check position and when to look at the environment followed by when you should update the lemmings' size. Unfortunately some of the lemming images were different sizes than others which made manipulating them and still checking the Level_Array extremely difficult. Making the walker, faller, and floater were not too hard. Digger took a little bit of time, but since there is only one direction to worry about modifying, down. When it came time for basher, miner, and climber, we ran into major set backs. Basher we had to figure out how to make the lemming delete to the right of the lemming, or to the left depending which direction it was going. Basher which should have been very straightforward created the most issues because of the way that it kept not doing what it was supposed to. At first we had it bash through a wall, but the issue was then that the lemming wouldn't clear out the last column of the array, and same with the first one. We played around with that so much that we eventually said, hell, if we can't delete one row, why not be safe and just simply take the entire space the lemming is in currently and just move empty into them. That worked after a lot, and we mean a lot of dumb bugs. What was worse is that as soon as we tried something, we sometimes would find something else that wasn't correct, and that could have been the reason that the object wasn't doing what it was supposed to.
Due to time constraints, we made miner really simple. Basher took forever, so we didn't want to take too much time in doing it, so one of our guys devised a way to “calculate” what the miner would do by clearing out the path that it would have taken. As in make a diagonal clearing space from the bottom right corner of the lemming. Not exactly how we would have liked to do it, but something is better than nothing. So now the lemming could bash left and right, mine left and right, and dig. Builder was made to simulate the direction it would take, as in move diagonally, but doesn't add “steps” to the _Level. That would have been nuts. Because we would have to detect that special block and on top of that make them move diagonally.
Another thing that we had to adapt was the positiong offsets around the lemming. At first we had a topleft, top, right, bottomright, bottom, bottomleft, left, which were the pixels just outside of the lemming. Since we had falling check for the middle of the lemming, it would then proceed to fall when it was halfway off the edge, not when it had actually walked off the edge. To help this we added four more offsets to the lemming: topr, topl, bottoml, bottomr. These were specifically the pixels to the left and right of the outside corners of the lemming. So when we walked left we could check bottomr for empty, and vice versa for walking right.
On the flip side, some of the guys in our group heavily worked on the button/job selection. That was a completely separate part of the project. The handling here was to use the mouse interrupt to select a button on the bottom, change its picture and of course handle making a lemming receive that specific job it handles. _FindLemming is a function that given a specific mouseX, mouseY looks through the entire _Leming_Array and checks to see if the x and y coordinates are within the lemmings width and height. If it was, then it would give the lemming the job that had been selected. We also had to implement counters for the Lemming buttons. As in you are only allowed to give a number of lemmings a specific job.
While working on our final project, we learned that you have to depend on other people to complete the task at hand and help motivate others in doing so. There is definitely no way this could have been completed without the group because there was simply too much work that had to be done. The benefit received from being in a group gave a sense of pride and teamwork during the course of this final project.
Our
final piece wasn't as quite as ironed out as we had hoped, but it was
pretty much left as a very much well playable game. In retrospect, our
choice of project might have been a bit too difficult of a task to
complete in the allocated time, but we feel we still learned enough and
got plenty accomplished to have a robust working game.
There
will be two counters running in the background, one will stand for the
amount of lemmings left on the screen as to know when to check if the
player won or lost and will decrement by 1 when a lemming dies, becomes
a blocker (they are unusable), or gets to the goal. The second counter
will increment to respresent the number of lemmings that have completed
the course, and will be used to check if the user has won or lost.
A check function for making the active struct object 0 or 1 in the lemming array for each lemming based on the current job.
| Struct Lemming created to keep track of each lemming in the game. has essential data for each specific lemming to allow us to simply create an array of strucs and therefore simply access a lemming by access the data inside the struc |
Environment Objects – All possible objects in the game that are given a specific number in an array, which gets turned into graphics based on the value. |
|
|
To handle each level a dataset must be created
|
Lemming's Jobs Procedure and Algorithms (Screenshots taken from Source Game) |
|||
Miner Becomes Active: |
![]() |
Floater | Becomes Active: |
| Blocker
Becomes Active: |
![]() |
Climber | Becomes Active: |
| Bomber
Becomes Active: |
![]() |
Builder | Becomes Active: |
Basher Becomes Active: |
![]() |
Digger | Becomes Active: |
Procedures:
| _updateinitialbuttonsimages |
|
| _Start |
|
| _updateinitiallevelimages |
|
| _pick_level |
|
| _game_over |
|
| _game_loop |
|
| _updateBuffer |
|
| _setNext_Position |
|
| _manipulateEnvironment |
|
| _bomb_count_draw |
|
| _kill_Lemming |
|
| _checkMovement |
|
| _set_miner |
|
| _set_builder |
|
| _set_basher |
|
| _set_climber |
|
| _set_blocker |
|
| _set_digger |
|
| _set_falling |
|
| _set_walking |
|
| _set_old_dimensions |
|
| _getEnvironment |
|
| _createLemming |
|
| _InstallMouse |
|
| _RemoveMouse |
|
| _MouseCallback |
|
| _Allocate_Job_Images |
|
| _LoadImage |
|
| _toScreen |
|
| _toBuffer |
|
| _installTimer |
|
| _UpdateButtons |
|
| _update_num_count |
|
| _updateButtonsBuffer |
|
| _FindLemming |
|
| _updateLemming |
|
| _Allocate_All |
|
| _Load_All |
|
| _Load_Level_* |
|
Image Processing:
-need to make the .png files and lots of them
-create a queue of image files to load for each lemming and objects in
the environment that are modified by lemmings. The queue will help
maintain fluidity of image handling.
Environmental Updating:
-with timer interrupt every time the clock rises, we need to signal for
an update of all the environmental objects as in the lemmings need to
be updated in next and current position and check all job
specifications.
Mouse Interrupts:
Screen is broken up into three sections:
-the button bar at the bottom of the screen
-The info bar above that
-The lemmings environment above
The button bar consists of buttons that will simply be specific regions of space that the mouse can click on. These buttons will be the “jobs” that you can specify lemmings to do. Also there is a pause button, a nuke button (which kills all remaining lemmings on the screen) and a speed up button.
The info bar consists of a timer clock counting down which is the allotted amount of time for the level. Also contains a space that when you click on a button it describes what the button does. The bar will also tell you how many lemmings are out, how many lemmings you have in general, how many lemmings are “in”, and how many lemmings you have to save to pass the level.
The main screen will have a cursor for the mouse that when you have selected a job and you hover over a lemming, it changes to a aiming cursor to indicate which lemming you are hovering over. Also when you click on a lemming the screen space will be broken up into designated blocks depending on the size of the images for lemmings. As in it will select the closest lemming to where you click.
Look up Table - It will contain each job which will call about 4 .gif combos which will rotate the .gifs to simulate animation, whether it's a job or just walking. One "function" for every possible job. Each lemming will be a jpg with a clear background so it can be placed anywhere and easily moved around. GIFs have
Finish - after an execution this will be called to check the counters mentioned in implementation, to see if the user has won or lost or neither.
Start - Will be called and will continuiously be throwing out a lemming until the counter reaches the total
Background - some sort image map representing the background and that can be altered when digging or building is done.
Mouse controls - this will be a pretty large job, with things that have to be clicked for the job types as well clicking the actual lemming to make them that job. We will have to keep track of the location on the screen where the cursor is to check what is clicked. It will most likely be a block of pixels that is checked and represent the location of the cursor.
Timer - Require a timer interrupt for updating movements, graphics, and values in arrays.
External Routines:
_installMouse, _removeMouse, and _installTimer