-
Notifications
You must be signed in to change notification settings - Fork 8
Complete interactive programming #3
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 40 commits
2de033a
7d4d56a
7881c09
a72aed8
ae5ded3
035bf5d
860cf7c
dd67c71
9f2156e
a98883f
41fc448
757dba6
7bab5b1
0ede561
3d5ef9d
6b22ee8
d55ede1
a4057a2
c6a297c
522df62
ea85407
734db7c
4edcbf7
58d7c1b
af2ed11
0f26809
c66f558
dd5ea39
f877988
a896506
20ba136
d33d9c2
4441f05
6a55531
5fb8cc9
ce5b8eb
886ec96
a8bfe17
eea96f3
b97a0c0
cea81cc
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,165 @@ | ||
| import os, sys | ||
| import pygame | ||
| from random import randint | ||
|
|
||
| from misc import * | ||
|
|
||
|
|
||
| class Game4Main: | ||
| """The Main Game4 Class - This class handles the main | ||
| initialization and creating of the Game.""" | ||
|
|
||
| def __init__(self, width=480,height=360): | ||
| # initialize pygame | ||
| pygame.init() | ||
| # set window size | ||
| self.width = width | ||
| self.height = height | ||
| # create screen | ||
| self.screen = pygame.display.set_mode((self.width, self.height)) | ||
| # create clock | ||
| self.clock = pygame.time.Clock() | ||
| self.score = 0 | ||
|
|
||
|
|
||
| def mainLoop(self): | ||
| """Main screen for game.""" | ||
| # initialize player | ||
| count = 0 | ||
| play_len = 25 | ||
|
||
| play_x = 100 | ||
| play_y = self.height - play_len | ||
| player = Player(play_x, play_y, play_len, self.screen) | ||
|
|
||
| # initialize stamina bars | ||
| P1_stamina_bar = StaminaBar(self.screen,25,"WHITE") | ||
| P2_stamina_bar = StaminaBar(self.screen,350,"RED") | ||
|
|
||
| # initialize obstacle length, x and y coordinates | ||
| obs_len = 25 | ||
| obs_x = self.width | ||
| obs_y = self.height - obs_len | ||
| obstical_height = self.height - obs_len | ||
|
|
||
| # create list of obstacles | ||
| obstacles = [] | ||
| new_obstical = False | ||
| # uncomment and place line below in [] of obstacles = [] to generate random obstacles: | ||
| # Obstacle(obs_x, obs_y, obs_len, self.screen,'BLUE') | ||
|
||
|
|
||
|
|
||
| # initialize time variables | ||
| prev_time = 0 | ||
| obs_dt = 500 | ||
|
|
||
| # main event loop | ||
| while 1: | ||
|
||
| count+=1 | ||
| for event in pygame.event.get(): | ||
| if event.type == pygame.QUIT: | ||
| sys.exit() | ||
| # check keyboard for obstacle player | ||
| if event.type == pygame.KEYDOWN: | ||
| # z to create obstacle at ground level | ||
| if event.key == pygame.K_z: | ||
| new_obstical = True | ||
|
||
| obstical_height = obs_y | ||
| # x to create obstacle at second level | ||
| if event.key == pygame.K_x: | ||
| new_obstical = True | ||
| obstical_height = obs_y-25 | ||
| # c to create obstacle at third level | ||
| if event.key == pygame.K_c: | ||
| new_obstical = True | ||
| obstical_height = obs_y-50 | ||
|
|
||
| # check keyboard for main player | ||
| pressed = pygame.key.get_pressed() | ||
| # up to jump | ||
| if pressed[pygame.K_UP] and P1_stamina_bar.bars >= player.jumpcost: | ||
| if player.play_y == (360 - player.play_len): | ||
|
||
| P1_stamina_bar.decreaseBar(player.jumpcost) | ||
| player.jump() | ||
| P1_stamina_bar.draw() | ||
| # left to move left | ||
| if pressed[pygame.K_LEFT]: | ||
| player.moveLeft() | ||
| # right to move right | ||
| if pressed[pygame.K_RIGHT]: | ||
| player.moveRight() | ||
|
|
||
| # refresh screen | ||
| self.screen.fill(colors['BLACK']) | ||
|
|
||
| # update and draw player | ||
| player.update() | ||
| player.draw() | ||
|
|
||
| # update current time | ||
| current_time = pygame.time.get_ticks() | ||
|
|
||
| # generate obstacle at random time | ||
| if (current_time - prev_time > obs_dt): | ||
| # uncomment below to generate random obstacles: | ||
| # obstacles.append(Obstacle(obs_x, obs_y, obs_len, self.screen,'BLUE')) | ||
| new_obstical = False | ||
| prev_time = current_time | ||
| obs_dt = randint(1000, 3000) | ||
| # generate obstacle player's obstacle at appropriate height | ||
| if (new_obstical == True and P2_stamina_bar.bars >= 33): | ||
| new_obstical = False | ||
| P2_stamina_bar.decreaseBar(33) | ||
| obstacles.append(Obstacle(obs_x, obstical_height, obs_len, self.screen,'RED')) | ||
|
|
||
| # move each obstacle forward | ||
| for obstacle in obstacles: | ||
| obstacle.moveForward() | ||
| # check for collision between player and obstacles | ||
| if player.isCollide(obstacle.obs_x, obstacle.obs_y, obstacle.obs_len): | ||
| self.gameOver(str(count)) | ||
| # remove obstacle from list if off screen | ||
| obstacles = [obstacle for obstacle in obstacles if not obstacle.isGone()] | ||
|
|
||
| # update stamina bars | ||
| P1_stamina_bar.increaseBar() | ||
| P2_stamina_bar.increaseBar(1.5) | ||
| P1_stamina_bar.draw() | ||
| P2_stamina_bar.draw() | ||
|
|
||
| # display score | ||
| font = pygame.font.SysFont("comicsansms", 72) | ||
| text = font.render(str(count), True, (255,255,255)) | ||
| self.screen.blit(text,[200,10]) | ||
|
|
||
| # update screen | ||
| pygame.display.flip() | ||
| self.clock.tick(30) | ||
|
|
||
|
|
||
| def gameOver(self,score): | ||
| """Game over screen.""" | ||
| # main event loop | ||
| while 1: | ||
|
||
| for event in pygame.event.get(): | ||
| if event.type == pygame.QUIT: | ||
| sys.exit() | ||
|
||
|
|
||
| # check keyboard for space key to restart game | ||
| pressed = pygame.key.get_pressed() | ||
| if pressed[pygame.K_SPACE]: | ||
| self.mainLoop() | ||
|
|
||
| # display game over screen | ||
| self.screen.fill(colors['BLACK']) | ||
| font = pygame.font.SysFont("comicsansms", 28) | ||
| text = font.render("Player 1 recived a score of " + str(score), True, (255,255,255)) | ||
|
||
| self.screen.blit(text,[50,140]) | ||
| text = font.render("Press Space Bar to play again", True, (255,255,255)) | ||
| self.screen.blit(text,[50,178]) | ||
|
|
||
| # update screen | ||
| pygame.display.flip() | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| Game4Main().mainLoop() | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,58 @@ | ||
| # Project Writeup and Reflection | ||
|
|
||
| ### Project Overview [Maximum 100 words] | ||
|
|
||
| *Write a short abstract describing your project.* | ||
|
|
||
| Blockade is competitive arcade game. It has two players. Player 1 is a white block that is continually moving forward. Player 2’s job is to stop Player 1 by throwing obstacles in its way. Player 1 can avoid obstacles by jumping over or ducking under them. Both players are limited by a stamina bar. Actions such jumping or throwing obstacles depletes player’s stamina and if the stamina gets too low players cannot perform actions. The game uses keyboard inputs as commands. | ||
|
|
||
| ### Results [~2-3 paragraphs + figures/examples] | ||
|
|
||
| *Present what you accomplished. This will be different for each project, but screenshots are likely to be helpful.* | ||
|
|
||
| The game starts with Player 1 on the bottom left corner of the screen. Player 1’s objective is simple, avoid obstacles and get as far as you can. Player 1 can avoid obstacles by moving left, right or jumping. These actions are performed by pressing the Left, Right, and Up arrow keys respectively. Player 2 does not show up on screen but can make is presence known by throwing obstacles at Player 1. These obstacles can be thrown at three heights: ground level, just over Player 1 and High in the air. Player 2 can control the height of the obstacles thrown by pressing the Z, X, and C keys. The game ends when Player 1 collides with an obstacle and dies, Player 1’s score is determined by how long they survived. The competitive nature of the game come when the human players swap roles. With both trying to ensure the other gets a lower score. | ||
|
|
||
| Player actions cost stamina. If a player’s stamina gets too low, they cannot perform any actions until it regenerates. This adds another level of strategy to the game with each player trying to force the other to deplete their stamina bar. In the game, Player 2’s strategy heavily relies on forcing Player 1 to run out of stamina. | ||
|
|
||
| We playtested the game with around 9 individuals. Each person played at least one game as both players. Reviews were generally positive with testers describing it as "fascinating" and "a few minutes of mindless fun". | ||
|
|
||
|
|
||
|  | ||
|
|
||
| Figure 1: This is how the game starts. Player 1 is the white square in the bottom left corner. PLayer 1’s distance is in the top center of the screen. The stamina bars for Player 1 and 2 are in the top left and right screen respectively. | ||
|
|
||
|  | ||
|
|
||
| Figure 2: During the game Player 1 avoids the red obstacles by jumping. Player 2 can spawn red obstacles at multiple heights. Player 2’s staminal bar (red) has almost been depleted. | ||
|
|
||
|  | ||
|
|
||
| Figure 3: Game over screen reports the distance Player 1 traveled and gives instructions for how to play again. | ||
|
|
||
| ### Implementation [~2-3 paragraphs + UML diagram] | ||
|
|
||
| *Describe your implementation at a system architecture level. Include a UML class diagram, and talk about the major components, algorithms, data structures and how they fit together. You should also discuss at least one design decision where you had to choose between multiple alternatives, and explain why you made the choice you did.* | ||
|
|
||
| Our minimum viable product was a one player game similar to Google Chrome's dino run, in which the player jumps over randomly generated obstacles. The first class we created was for the main game. Its init handled the main initialization of the game. The mainLoop was a method that ran the main loop of the game, which got keyboard inputs and used them to control the player (and later the obstacles). We created two other classes. Unsurprisingly, they were the player and the obstacle. The obstacle only needed to move forward, so it had a moveForward method aside from init and repr, as well as isGone, used to delete obstacles off screen to prevent the programming from crashing from having to keep track of too many obstacles. The player had more requirements, such as moving left, moving right, and jumping, so we created the methods accordingly. We also added a method for collision detection in order to determine when game over should happen. We then created a gameOver method inside the main class game. We included a conditional statement in the mainLoop to direct the program to gameOver when the player collides with any obstacle and a conditional in gameOver to direct the program back to mainLoop when the space key is pressed. | ||
|
|
||
| We got our MVP done by the mid-project check-in, so we added additional features to allow a second player to control the obstacles. We then created another class for the stamina bar, with one method to decrease the bar with every action (i.e. when the player jumps for the player's stamina bar and when an obstacle is generated for the second player's stamina bar) and increase the bar over time up to a certain length. | ||
|
|
||
|  | ||
|
|
||
| Figure 4: UML diagram of game | ||
|
|
||
| At one point, we considered implementing Model View Control. However, given the simplicity of our game, it wasn't worth rewriting whole sections just to split up the view and control into different classes. It would have cut down on the length of our mainLoop, but the pros just didn't outweigh the cons. Another decision that we made was to not create a subclass for each of the stamina bars since we only needed two and their only differences were their color and location, so we took those parameters as arguments instead. We also kept remnants of the code that randomized obstacles from our MVP in case we wanted to revise the code to allow the player choose their mode: one-player or two-player. | ||
|
|
||
| ### Reflection [~2 paragraphs] | ||
|
|
||
| *From a process point of view, what went well? What could you improve? Other possible reflection topics: Was your project appropriately scoped? Did you have a good plan for unit testing? How will you use what you learned going forward? What do you wish you knew before you started that would have helped you succeed?* | ||
|
|
||
| *Also discuss your team process in your reflection. How did you plan to divide the work (e.g. split by class, always pair program together, etc.) and how did it actually happen? Were there any issues that arose while working together, and how did you address them? What would you do differently next time?* | ||
|
|
||
| Overall, this project went pretty well. We were able to deliver our MVP as well as a few of our stretch goals, like allowing a second player to generate obstacles and adding stamina bars, a scorekeeper, and a display of the score on the game over screen. For the most part, we planned on and were able to divide up the work based on what we wanted to complete within a period of time. We didn't look that far ahead, but planned at the beginning of each of our meetings what we wanted accomplished and divided the work as evenly as possible. Harry developed the main player and Vivien developed the obstacles. Once we made the two players, we both fixed bugs as they came up. | ||
|
|
||
| One problem we ran into was keeping a consistent class format. We developed the two players independently and as a result each player was done a little differently. We refactored the main player into an object in order to keep them consistent with each other. For the most part, we were able to effectively work independently and met up to either discuss future features for the project or how to fix bugs. | ||
|
|
||
| For a project of this scope our teamwork strategy was appropriate. However, it is not scalable. For larger projects we should spend more time planning out the classes so they can be easily integrated. We also tried to implement a CMV structure into the game; however, by that point most of the game had been written and changing the structure was deemed not worth the effort. If we were to do this project again we would have implement CMV from the beginning. | ||
|
|
||
| Unfortunately, Vivien's family came to visit the weekend before the project was due, so she could not spend as much time adding more features to the game as Harry really wanted to do. However, since we got quite a lot done early on, Vivien was able to spend time with her family and Harry just added some of the things he wanted to add. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,3 +1,11 @@ | ||
| # InteractiveProgramming | ||
|
|
||
| This is the base repo for the interactive programming project for Software Design at Olin College. | ||
|
|
||
| [Project Writeup and Reflection](https://github.com/vivienyuwenchen/InteractiveProgramming/blob/master/Project_Writeup_and_Reflection.md) | ||
|
|
||
| Required packages: | ||
| - pip install pygame | ||
|
|
||
| To run the game: | ||
| - python Game4.py |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,128 @@ | ||
| import os, sys | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this file can be named |
||
| import pygame | ||
|
|
||
|
|
||
| colors = {'BLACK': (0, 0, 0), | ||
| 'WHITE' : (255, 255, 255), | ||
| 'GREEN' : (0, 255, 0), | ||
| 'RED' : (255, 0, 0), | ||
| 'BLUE' : (0, 0, 255), | ||
| } | ||
|
|
||
|
|
||
| class Obstacle: | ||
| """A square obstacle, defined by its top left hand coordinate, length, and color. | ||
| Also takes in screen as an argument to draw the obstacle.""" | ||
| def __init__(self, obs_x, obs_y, obs_len, screen, color): | ||
| """Initialize the instance.""" | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment isn't necessary. |
||
| self.obs_x = obs_x # top left hand x coordinate | ||
| self.obs_y = obs_y # top left hand y coordinate | ||
| self.obs_len = obs_len # side length | ||
| self.screen = screen # game screen | ||
| self.color = color # color of obstacle | ||
|
|
||
| def __repr__(self): | ||
| return 'Obstacle({}, {}, {}, {})'.format(self.obs_x, self.obs_y, self.obs_len, self.screen) | ||
|
|
||
| def moveForward(self): | ||
| """Update horizontal location and draw obstacle.""" | ||
|
||
| self.obs_x -= 20 | ||
| pygame.draw.rect(self.screen, colors[self.color], [self.obs_x, self.obs_y, self.obs_len, self.obs_len]) | ||
|
|
||
| def isGone(self): | ||
| """Check if obstacle is completely off screen.""" | ||
| return self.obs_x < -self.obs_len | ||
|
|
||
|
|
||
| class Player: | ||
| """A square player, defined by its top left hand coordinate and length. | ||
| Also takes in screen as an argument to draw the player.""" | ||
| def __init__(self, play_x, play_y, play_len, screen): | ||
| """Initialize the instance.""" | ||
| self.play_x = play_x # top left hand x coordinate | ||
| self.play_y = play_y # top left hand y coordinate | ||
| self.play_len = play_len # side length | ||
| self.screen = screen # game screen | ||
| self.speed = 10 # right/left speed | ||
| self.jumpInProgress = False # initialize to False | ||
| self.v = 7.5 # "velocity" for jump | ||
| self.m = 2.5 # "mass" for jump | ||
| self.floor = play_y # location of player before jump, used for comparison | ||
| self.jumpcost = 30 | ||
|
|
||
| def draw(self): | ||
| """Draw player based on top left hand coordinate and length.""" | ||
| pygame.draw.rect(self.screen, colors['WHITE'], [self.play_x, self.play_y, self.play_len, self.play_len]) | ||
|
|
||
| def moveRight(self): | ||
| """Update horizontal location of player after moving right.""" | ||
| if self.play_x < 300: | ||
| self.play_x += self.speed | ||
|
|
||
| def moveLeft(self): | ||
| """Update horizontal location of player after moving left.""" | ||
| if self.play_x > 0: | ||
| self.play_x -= self.speed | ||
|
|
||
| def jump(self): | ||
| """Set jumping status.""" | ||
| self.jumpInProgress = True | ||
|
|
||
| def update(self): | ||
| """Update height of player during jump.""" | ||
| if self.jumpInProgress: | ||
| # change in height = "mass" times "velocity" | ||
| dy = self.m * self.v | ||
|
|
||
| # subtract height by change in height | ||
| self.play_y -= dy | ||
| # decrease velocity | ||
| self.v -= .75 | ||
|
|
||
| # stop jumping if player has landed | ||
| if self.play_y >= self.floor: | ||
| # prevent player from falling through the floor | ||
| self.play_y = self.floor | ||
| # no longer jumping | ||
| self.jumpInProgress = False | ||
| # reset velocity | ||
| self.v = 7.5 | ||
|
|
||
| def isCollide(self, obs_x, obs_y, obs_len): | ||
| """Check collision of player with obstacle.""" | ||
| # set coordinates for top left hand corner (0) and bottom right hand corner (1) of obstacle | ||
| obs_x0 = obs_x | ||
| obs_x1 = obs_x + obs_len | ||
| obs_y0 = obs_y | ||
| obs_y1 = obs_y + obs_len | ||
| # and of player | ||
| play_x0 = self.play_x | ||
| play_x1 = self.play_x + self.play_len | ||
| play_y0 = self.play_y | ||
| play_y1 = self.play_y + self.play_len | ||
| # check if player coordinates within obstacle coordinates | ||
| if (play_x0 in range(obs_x0, obs_x1) or play_x1 in range(obs_x0, obs_x1)) and (int(play_y0) in range(obs_y0, obs_y1) or int(play_y1) in range(obs_y0, obs_y1)): | ||
|
||
| return True | ||
|
|
||
|
|
||
| class StaminaBar: | ||
| """A stamina bar, defined by its starting location and color. | ||
| Also takes in screen as an argument to draw the stamina bar.""" | ||
| def __init__(self, screen, start, color): | ||
| self.screen = screen # game screen | ||
| self.start = start # starting location of stamina bar | ||
| self.color = color # color of stamina bar | ||
| self.bars = 100 # initialize number of health bars | ||
|
|
||
| def draw(self): | ||
| """Draw stamina bar based on color, starting location, and number of health bars.""" | ||
| pygame.draw.rect(self.screen, colors[self.color], [self.start, 20, self.bars, 10]) | ||
|
|
||
| def decreaseBar(self, num_bars): | ||
| """Decrease health bar by num_bars.""" | ||
| self.bars -= num_bars | ||
|
|
||
| def increaseBar(self, speed = 1): | ||
| """Increase health bar continuously if number of bars is lower than 100.""" | ||
| if self.bars < 100: | ||
| self.bars += 1 * speed | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add a few lines of comments explaining what this cool game is about!