Fungustris - University of Notre Dame
Transcription
Fungustris - University of Notre Dame
Fungustris Robert Bruggner and Matt Corgan University Of Notre Dame, Notre Dame, IN 46556, USA Abstract. Fungustris started as an effort to mimic the famous arcade-style game Tetris. Our inspiration was an assignment for a Data Structures class which required that we make use of data structures and algorithms to implement a program of our choice. Suggested genres for programs included databases, networking applications, string processing, and video games. Thinking about video games, we noticed that many of our favorites were written in the late 1980’s and early 1990’s when objectoriented programming was not nearly as dominant as it is today. With this in mind we wondered if we could create a game such as Tetris in an object-oriented fashion, unlike most of its original versions. The following details that attempt. Keywords: Fungustris, Tetris, SDL 1 Introduction The authors delved headlong into the task of making a video game without really knowing how to implement the graphics. The closest either of us had come to programming outside of a console window was with the Java Swing GUI builder. We realized that this would not be adequate for an arcade game. Browsing the Internet we came across the Simple DirectMedia Layer, or SDL for short. We describe it in further detail below, but it is essentially a set of libraries written in C for accepting real time input and drawing graphics to the screen. 2 2.1 Tools SDL For implementing the graphical layer of Fungustris, we chose to use the free, cross platform libraries of SDL (Simple DirectMedia Layer / www.libsdl.org). We decided to go with SDL because it supported all the tools we needed to implement Fungustris: direct drawing to the screen (SDL BlitSurface/SDL LowerBlit), real-time updating of specified screen areas (SDL UpdateRect/SDL UpdateRects), timing mechanisms to create the effect of frames (SDL GetTicks, SDL Delay), etc. The SDL Libraries were designed to be used with C which altered our programming style a tad. 2 2.2 C++ C++ was the programming language of choice for our implementation of Tetris. It has majestic support for encapsulation enabling us to build upon smaller concepts such as an individual piece, to larger ones such as a game board, and finally to the full concept of the game. C++ is also backwards compatible with C, allowing us to use the SDL libraries mentioned above. 3 3.1 Procedures Program Breakdown Because Fungustris was a team effort, we needed to find a way to split up the programming between the two authors. To do this we decided that we could break the program into three logical pieces. The first piece deals with input acceptance. The second piece is responsible for receiving the current state of the game and translating it to the graphical representation that is seen by the user. The third and final piece sits between the first two. It could be considered the brains of the program, receiving the input from the first part and passing the output to the second part. 3.2 An Event Driven Program Unlike the console applications we were most familiar with, Tetris needed to be an event driven program. We designated certain keys to activate different events. The SDL provides a keyboard monitoring function. With this function we were able to map the control keys to functions that performed their corresponding actions. These actions are the standard Tetris moves: move left, move right, rotate, move down, and drop. The keys we chose to activate these events were the left arrow, right arrow, up arrow, down arrow, and space-bar, respectively. There is no standard for the keys used to perform these functions because of the wide variety of authors’ preferences and the many different control modules for various gaming systems. One of the main functions of our game was the event listener. Using the SDL Event struct, we were able to watch for an SDL Event, in this case a keystroke, and appropriately respond to the pressed key. We declared an SDL Event, ’keystroke’ , and continually monitored it for an SDLK KEYDOWN action. In the event of SDLK KEYDOWN, the SDL libraries place a ’1’ in an array of all the keyboard keys. By looking at this array, we were able to determine which keys were pressed and call the appropriate game functions (i.e. move the piece left in the event of the left arrow key being pressed, quit the game if ’q’ is pressed, etc.). 3 3.3 The SDL Layer Class One of Fungustris’ two most important classes was the SDL Layer class. This class was designed and written to be the translator between the game class (the other most important class) and the screen using a multitude of functions and data. The basic idea of the SDL Layer class is that it creates a new window, dfunctionsraws the game background, reads an array received from the game class that indicates where the pieces are, and updates the screen accordingly. In a little more detail: Every tetris board consists of an 18 x 10 grid of blocks. We created a base data structure, ’square’, that contained information such as the the square’s image, its position, and whether it was alive or not. We then declared an 18 x 10 array of squares and turned each one on and off, based on the array received from the game class. Each time the SDLK KEYDOWN event occurred, the SDL Layer class synchronized with the game class board array, drew squares in their appropriate position, and then updated the screen. 3.4 The Game Class The third piece of the program is invisible to the user. Its purpose is to encapsulate the logic of the game and hold its current state. It needs to provide interface functions to the event listener and to the graphics drawing functions. All of this is incorporated into a class called Game. In its most abstract sense, class Game provides start, moveLeft, moveRight, moveDown, rotate, and drop. Each of these functions does just as its name implies. On the output end, class Game provides functions getScreenData, getLevel, and getScore. Function getScreenData returns an array of integers 18 high and 10 wide. Each integer corresponds to what block should be placed in each grid position. A 0 indicates that no block is currently in the grid position, and the numbers 1 through 7 indicate that there should be a block whose color is decided by the number. Function getLevel returns the level of play that the player has worked up to. Completing 10 rows moves the player to the next level. The getLevel function is used by the event listener to determine how quickly to automatically drop the pieces, and it is used by the graphics drawing functions to indicate the level to the left of the playing board. Function getScore is used only by the graphics drawing functions so that they may post the score to the left of the playing board. 3.5 More On the Game Class publicThere is much more to class Game than just its interface functions. It contains all the functionality of the game of tetris. Take a look at the header file for Game found below in order to see all of the private methods and data members used to implement the video game’s internal structure. Class Game is composed of a Board object, a pointer to a Shape object, and some other data members such as the row and column where the active 4 piece is located. The function isValidMove( Shape*, int, int ) is of special interest. When the user requests a move, the move must be validated before allowing it to happen and propgating its effect on the rest of the game. If the attempted move is invalid, such as moving the active shape off the side of the screen, the program would surely malfunction. So, if a move is requested, the function performing that move queries the isValidMove function. It passes the shape, which knows its own orientation, and the row and column of the upper left hand corner of the shape in the move under inspection. It also has access to the Game’s Board. The function applies the shape to the board and checks a number of things such as the overflow on the left, right, and bottom borders, and a collision with squares that are already occupied. 3.6 The Header File: game.h class Game{ p: Game(); //functions called by the keyboard listener void start(); void stop(); void rotate(); void moveLeft(); void moveRight(); void moveDown(); void drop(); //function called bythe screen drawer int** getScreenData(); //returns the screenData array bool getGameOver(); //returns whether the game is over int getLevel(); //how fast should the pieces fall? int getScore(); void reset(); private: void updateScreenData(); //updates the screeData array for use by SDL //call these when a piece is finished falling void addShape(); //generate new shape and put at top of grid void removeRows(); //delete the complete rows and shift everything down bool isValidMove( Shape *, int, int );//tests the shape on the board void printTextScreen(); void printScore(); //used for console based game //prints scoring data in the console 5 private: Board b; Shape *s; int row, col; int** screenData; //private data members // the intelligent board class // the active shape // top left grid position of the shape s // contains the board and the active Shape s int score; int* rowCounts; //player’s score // 0-total rows, 1-single rows, 2-double rows, etc... bool shapeIsActive; bool gameOver; bool textVersion; int errorType; bool commentsOn; }; // true if a shape is falling // becomes true when board fills up // set to true in constructor for text game //indicates the type of out-of-bounds error //print console information while running 3.7 The Shape Class Another integral part of the program’s structure is its reliance on the Shape class. Class Shape is an abstract base class which contains most of the characteristics and functionality of a shape. There are seven classes derived from Shape: bar, bird, box, left dog, right dog, left periscope, and right periscope. Each of them implements its own setOrientation function as a virtual function. In the program the specific shapes are referred to polymorphically through a Shape pointer. This functionality was one of the biggest benefits of using C++ over C to program Tetris. 4 Conclusion Fungustris was a definite success! The three segments of the program work together seamlessly and provide for a top quality Tetris clone. Fungustris has basically all the functionality of the true game: all of the same pieces, the same controls, the same scoring and disappearing rows, and the same increase in speed as the levels progress. It is also quite visually pleasing thanks to the SDL’s capabilities in displaying regular bitmapped images which we were able to design using standard image editing software. The object-oriented design made the game a pleasure to create. C++ was the perfect language thanks to its encapsulation techniques, its virtual functions, and especially its backward compatibility with the SDL libraries. 6 Fig. 1. A screenshot of Tetris implemented with the SDL 5 Version Two One of the biggest strengths of our program is its ease of modification. If we want to go back and change some of the logic, we must simply add or modify functions in the Game class. If we want to add new shapes, something that might make the game a little more interesting and unique, we must simply add new classes descending from class Shape and add their ”new” declarations to the switch statement in Game. They would then function properly throughout the whole game because it is coded so as to be independent of whatever shapes it might be asked to use. Modifying the appearance of the game would probably be the simplest and most noticeable change. We can edit the images used to build the board, the pieces, and the score and level digits with the most basic of image editing programs. Version two will most likely be Piratetris. 7 Adding sound to the game could make it much more enjoyable. It might be as simple as playing a background song constantly, adding in short clicks when keys are pressed, and adding friendly error sounds when a move cannot be completed. Piratetris would also have an ”Arghh!” when the player scored a ”Tetris”. 6 References I. The SDL Documentation Project: http://sdldoc.csn.ul.ie/ Provides basic documentation for the SDL libraries and their functions. II. Bjarne Stroustrup. The C++ Programming Language. AT&T Labs, 1997. Core C++ information and methods. III. SDL Library - Video: http://www.chs.rit.edu/ benjamin/desktop/webpages/video/video.html Provides a closer look at the SDL Video Library. IV. H. & P. Deitel. How To Program C++. Prentice Hall, 2001. In depth look at C++ class programing. V. John Hall. ”A Crash Course in SDL”. Specialized Systems Consultants. Seattle, WA. 2001. Introduction to programming with Simple DirectMedia Layer. 8 7 About the Authors Fig. 2. Authors Matt and Robert Matt Corgan is currently studying Computer Engineering as a junior at the University of Notre Dame. He was born and raised in Gaithersburg, Maryland and enjoys skiing, biking, road trips, Bruce Springsteen’s music, and eating crabs for hours. Matt currently works in the Management Information Systems computer laboratory at Notre Dame. In the past he has done lawn-service, catering, and has spent two summers with the Heat Transfer and Alternate Energy Group at the National Institute of Standards and Technology. At present his plans for the future lean towards software engineering in the Rocky Mountains. Robert Bruggner is a native of Havana, Fl. and constantly asks himself how he ended up in South Bend, In. He’s studying Computer Engineering but his real intrests lie in sound and audio work (recording, mixing, playing). When he’s not working (and even when he should be ), Robert can usally be found playing frisbee (ultimate and/or golf ) with those crazy arts and letters hippies and loves every minute of it. Classically trained in piano and an avid fan of talking in third person, Robert hopes to live on a coast somewhere after graduation.