Skip to content
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

Feedback #1

Open
wants to merge 6 commits into
base: feedback
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
93 changes: 73 additions & 20 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
[![Open in Visual Studio Code](https://classroom.github.com/assets/open-in-vscode-718a45dd9cf7e7f842a935f5ebbe5719a5e09af4491e668f4dbf3b35d5cca122.svg)](https://classroom.github.com/online_ide?assignment_repo_id=14588401&assignment_repo_type=AssignmentRepo)

:warning: Everything between << >> needs to be replaced (remove << >> after replacing)
# Cuff Final Project
## CS110 Final Project Spring 2024

# << Project Title >>
## CS110 Final Project << Semester, Year >>

## Team Members

<< List team member names >>
## Team Members

Patrick Cuff
***

## Project Description

<< Give an overview of your project >>

My project is meant to resemble the game "Crossy Road" to an extent. The player is meant to move vertically up the screen as enemies move horizontally across the screen. If you bump into an enemy, the game is over. The goal is to reach the end of the level(top of the screen) as fast as possible without dying, and the fastest time is the high score recorded. If you collide with an enemy, it is game over and you have the option of playing again. If you win, your best time is recorded and you have the option to play again to get the best score possible!
***

## GUI Design
Expand All @@ -30,20 +27,76 @@

### Features

1. << Feature 1 >>
2. << Feature 2 >>
3. << Feature 3 >>
4. << Feature 4 >>
5. << Feature 5 >>
1. start button on main screen that starts the game
2. player moves left, right, up, down by means of arrow key
3. player dies and ends game as they collide with moving enemy
4. player wins when they reach the top of the screen, win page appears
5. game over screen appears once you collide with enemy, and the play again option is available setting you back to the starting position
6. win screen displays the high score, or "best time" in which the player has finished the game. This time can also be found in the top left of the screen as you play.



### Classes

- << You should have a list of each of your classes with a description >>

1. player_controller: This class handles the players movement in the window, using each movement(up,right,left,down) with its respective arrow key. It also initializes the players size, position, and score.

2. enemy: This class handles the "enemies" or obstacles that move horizonatally across the screen and are to be avoided by the player. It also initializes the generation of these enemies' position, size, and their movement speed.

3. crossy_road_controller: This class handles the games main loop of the game itself. It takes in user inputs such as "start" and "play again" buttons. It also handles the detection of the player making contact with enemies, and will change the screen to the respective game over or winner screen depending on the games outcome. This class also handles the best score time dynamic by keeping count of the current run time, and comparing it with the overall kept best score.



## ATP

| Step |Procedure |Expected Results |
|----------------------|:--------------------:|----------------------------------:|
| 1 | Run Counter Program |GUI window appears with count = 0 |
| 2 | click count button | display changes to count = 1 |
etc...
Test Case 1: Player Movement

a. Test Description: Verify that the player moves left, right, up, and down as expected
b. Test Steps:
1. Press the start game button
2. Press the arrow keys (up, down, left, right) to verify the player moves in their respective direction.
c. Expected Outcome: The player should move in the corresponding direction according to the arrow key inputs.


Test Case 2: Collision Detection with Enemy

a. Test Description: Ensure that collisions between the player and enemies are detected correctly.
b. Test Steps:
1. press the start game button
2. Use the arrow keys to move the player towards an enemy.
3. Verify that when the player collides with an enemy, the game over screen appears.
c. Expected Outcome: When the player collides with an enemy, the game over screen should appear.


Test Case 3: Start Button/Main Menu Works

a. Test Description: Test the functionality of the start button or main menu.
b. Test Steps:
1. Launch the game.
2. Click on the start button located on the main menu
3. Verify that the game starts once you press the button.
c. Expected Outcome: Clicking on the start button should start the game.


Test Case 4: Player wins

a. Test Description: Confirm that once the player reaches the end of the level, they win and are taken to the win screen.
b. Test Steps:
1. press the start game button
2. Move the player upward using the up key without hitting any enemies
3. Touch the top of the screen with your player, reaching the maximum height
4. Verify that the game takes you to the winner page, listing your best time.
c. Expected Outcome: The player is taken to the win page after they reach the maximum height of the level.


Test Case 5: Play Again

a. Test Description: Confirm that the play again button appears when the player collides with an enemy on the game over screen, and also appears when the player wins on the win screen.
b. Test Steps:
1. press the start game button
2. use the arrow keys to move the player into an enemy
3. verify that the play again button is offered and restarts the game for the player
4. use the arrow keys to avoid enemies and reach the end of the level at the top
5. verify that the play again button is offered on the win screeen and restarts the game for the player.
Click on the play again button.
c. Expected Outcome: The play again button should restart the game and allow the player to play again, offered when they die and when they win.
Binary file not shown.
Binary file added __pycache__/enemy.cpython-310.pyc
Binary file not shown.
Binary file added __pycache__/player_controller.cpython-310.pyc
Binary file not shown.
Binary file added assets/finalgui.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified assets/gui.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions besttime.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
20.008684873580933
176 changes: 176 additions & 0 deletions crossy_road_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,176 @@
import pygame
import sys
import time
import random
from player_controller import PlayerController
from enemy import Enemy

class CrossyRoadController:
def __init__(self):
pygame.init()
self.screen_width = 800
self.screen_height = 600
self.screen = pygame.display.set_mode((self.screen_width, self.screen_height))
pygame.display.set_caption("Crossy Road")
self.clock = pygame.time.Clock()
self.player_size = 50
self.player_speed = 5
self.enemy_size = 50
self.enemy_speed = 3
self.enemies = []
self.player_controller = PlayerController(self.screen_width, self.screen_height, self.player_size,
self.player_speed)
self.running = False
self.game_over = False
self.best_time = None
self.start_time = 0

def load_best_time(self):
try:
with open("besttime.txt", "r") as file:
return float(file.read())
except FileNotFoundError:
return None

def save_best_time(self):
with open("besttime.txt", "w") as file:
if self.best_time is not None:
file.write(str(self.best_time))
else:
file.write("N/A")

def generate_enemy(self):
initial_y = random.randint(0, self.screen_height - self.enemy_size)
direction = random.choice(["LEFT", "RIGHT"])
enemy = Enemy(self.screen_width, self.screen_height, self.enemy_size, self.enemy_speed, direction, initial_y)
self.enemies.append(enemy)

def handle_keyboard_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
self.player_controller.move_up()
elif event.key == pygame.K_DOWN:
self.player_controller.move_down()
elif event.key == pygame.K_LEFT:
self.player_controller.move_left()
elif event.key == pygame.K_RIGHT:
self.player_controller.move_right()

def handle_mouse_events(self):
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
elif event.type == pygame.MOUSEBUTTONDOWN:
if not self.running and not self.game_over:
if 300 <= event.pos[0] <= 500 and 400 <= event.pos[1] <= 450:
self.start_game()
elif self.game_over:
if 300 <= event.pos[0] <= 500 and 400 <= event.pos[1] <= 450:
self.start_game()

def start_game(self):
self.running = True
self.game_over = False
self.enemies.clear()
self.player_controller.player.topleft = self.player_controller.initial_player_position
self.player_controller.score = 0
self.start_time = time.time()

def move_enemies(self):
for enemy in self.enemies:
enemy.move()

def check_collisions(self):
for enemy in self.enemies:
if self.player_controller.player.colliderect(enemy.rect):
self.game_over = True
self.running = False

def check_win_condition(self):
if self.player_controller.player.top <= 0:
self.running = False
self.game_over = True
time_taken = time.time() - self.start_time
if self.best_time is None or time_taken < self.best_time:
self.best_time = time_taken
self.save_best_time()

def draw_best_time(self):
font = pygame.font.Font(None, 24)
if self.best_time is None:
best_time_text = font.render("Best Time: N/A", True, pygame.Color("black"))
else:
best_time_text = font.render("Best Time: " + "{:.2f}".format(self.best_time) + " seconds", True,
pygame.Color("black"))
self.screen.blit(best_time_text, (10, 30))

def draw_start_screen(self):
self.screen.fill((255, 255, 255))
font = pygame.font.Font(None, 36)
main_menu_text = font.render("Main Menu", True, pygame.Color("blue"))
self.screen.blit(main_menu_text, (330, 100))
adaptation_text = font.render("Crossy Road Adaptation: By Pat Cuff", True, pygame.Color("blue"))
self.screen.blit(adaptation_text, (180, 150))
start_button = pygame.Rect(300, 400, 200, 50)
pygame.draw.rect(self.screen, pygame.Color("green"), start_button)
start_text = font.render("Start", True, pygame.Color("black"))
self.screen.blit(start_text, (370, 410))

def draw_game_over_screen(self):
self.screen.fill((255, 255, 255))
font = pygame.font.Font(None, 36)
game_over_text = font.render("Game Over", True, pygame.Color("red"))
self.screen.blit(game_over_text, (340, 200))
final_score_text = font.render("Best Time: " + ("N/A" if self.best_time is None else "{:.2f}".format(self.best_time)) + " seconds", True,
pygame.Color("black"))
self.screen.blit(final_score_text, (300, 250))
play_again_button = pygame.Rect(300, 400, 200, 50)
pygame.draw.rect(self.screen, pygame.Color("green"), play_again_button)
play_again_text = font.render("Play Again", True, pygame.Color("black"))
self.screen.blit(play_again_text, (340, 410))

def draw_win_screen(self):
self.screen.fill((255, 255, 255))
font = pygame.font.Font(None, 36)
win_text = font.render("You Win!", True, pygame.Color("green"))
self.screen.blit(win_text, (340, 200))
final_score_text = font.render("Best Time: " + ("N/A" if self.best_time is None else "{:.2f}".format(self.best_time)) + " seconds", True,
pygame.Color("black"))
self.screen.blit(final_score_text, (300, 250))
play_again_button = pygame.Rect(300, 400, 200, 50)
pygame.draw.rect(self.screen, pygame.Color("green"), play_again_button)
play_again_text = font.render("Play Again", True, pygame.Color("black"))
self.screen.blit(play_again_text, (340, 410))

def run_game(self):
while True:
if self.running:
self.handle_keyboard_events()
else:
self.handle_mouse_events()

if self.running:
if random.random() < 0.02:
self.generate_enemy()
self.move_enemies()
self.check_collisions()
self.check_win_condition()
self.screen.fill((255, 255, 255)) # Clear screen before drawing
self.draw_best_time()
pygame.draw.rect(self.screen, pygame.Color("blue"), self.player_controller.player)
for enemy in self.enemies:
pygame.draw.rect(self.screen, pygame.Color("red"), enemy.rect)
else:
if self.game_over:
if self.player_controller.player.top <= 0:
self.draw_win_screen()
else:
self.draw_game_over_screen()
else:
self.draw_start_screen()

pygame.display.update()
self.clock.tick(60)
26 changes: 26 additions & 0 deletions enemy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import pygame
import random

class Enemy:
def __init__(self, screen_width, screen_height, size, speed, direction, initial_y):
self.screen_width = screen_width
self.screen_height = screen_height
self.size = size
self.speed = speed
self.direction = direction
self.initial_y = initial_y
self.rect = self.initialize_position()

def initialize_position(self):
if self.direction == "LEFT":
x = self.screen_width
else:
x = -self.size
y = self.initial_y
return pygame.Rect(x, y, self.size, self.size)

def move(self):
if self.direction == "LEFT":
self.rect.x -= self.speed
else:
self.rect.x += self.speed
16 changes: 4 additions & 12 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,5 @@
import pygame
#import your controller
from crossy_road_controller import CrossyRoadController

def main():
pygame.init()
#Create an instance on your controller object
#Call your mainloop

###### NOTHING ELSE SHOULD GO IN main(), JUST THE ABOVE 3 LINES OF CODE ######

# https://codefather.tech/blog/if-name-main-python/
if __name__ == '__main__':
main()
if __name__ == "__main__":
game = CrossyRoadController()
game.run_game()
29 changes: 29 additions & 0 deletions player_controller.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import pygame

class PlayerController:
def __init__(self, screen_width, screen_height, player_size, player_speed):
self.screen_width = screen_width
self.screen_height = screen_height
self.player_size = player_size
self.player_speed = player_speed
self.initial_player_position = (self.screen_width // 2 - self.player_size // 2,
self.screen_height - self.player_size - 10)
self.player = pygame.Rect(*self.initial_player_position, self.player_size, self.player_size)
self.score = 0

def move_up(self):
if self.player.top > 0:
self.player.y -= self.player_speed
self.score += 1

def move_down(self):
if self.player.bottom < self.screen_height:
self.player.y += self.player_speed

def move_left(self):
if self.player.left > 0:
self.player.x -= self.player_speed

def move_right(self):
if self.player.right < self.screen_width:
self.player.x += self.player_speed