-
Notifications
You must be signed in to change notification settings - Fork 8
Turning in MP4 #1
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 all commits
f96afcc
79be113
6460494
d7109bf
e250751
5f5abaa
c3cddd7
5617b65
1b1e4bf
15d2635
cef7827
8d36632
666fd74
dd1b663
b7e163a
cfc8f47
a61a414
71ec663
deb4578
dfed9fd
b9cad3f
ab9ca76
6fdb082
01bada2
0447eaf
01ce5f7
1beb904
fc3f565
7f1ffb9
83c6bdb
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 |
---|---|---|
@@ -1,3 +1,33 @@ | ||
# InteractiveProgramming | ||
# Interactive Programming | ||
|
||
This is the base repo for the interactive programming project for Software Design at Olin College. | ||
Software Design MP4 <br /> | ||
Hand-Tracking Fruit Ninja | ||
|
||
In this immersive gaming experience, Fruit Ninja moves off of your phone screen and into 3D! Swipe your hand through space and slice the fruit on the screen in real time. The game is infinite, and so is the fun. | ||
|
||
## Getting Started: | ||
|
||
### Installing Packages | ||
This game makes use of OpenCV and Pygame libraries. For everything you need, run the following. <br /> | ||
|
||
pip install cv2, pygame, numpy, imutils, argparse | ||
|
||
### Downloading Files | ||
Download the following files, as well as all fruit images, from the repository: <br /> | ||
|
||
ninja.py: main game file <br /> | ||
all_fruits.py: holds fruit class and apple, banana, strawberry subclasses <br /> | ||
Scoring.py: holds scoreboard class <br /> | ||
|
||
### Usage | ||
To run the game, run ninja.py with Python 3. | ||
|
||
### Calibration | ||
The game requires a still image from the webcam to begin hand detection. A camera display will pop up when you run ninja.py, in which the captured frame is contained within the blue rectangle. Make sure your hand is not in this area! Click on the webcam screen and press "b" on the keyboard when you're ready to take a picture. <br /> | ||
|
||
When the image has been taken, you're ready to play! Navigate to the game screen and slice the fruit by gesturing. Try to keep the rest of the background as static as possible for the best results (i.e., don't move your computer, have people walking behind you, etc.) <br /> | ||
|
||
Enjoy! | ||
|
||
## Links: | ||
[Project Reflection](Reflection.pdf) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import random, pygame, sys, pygame.font | ||
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. It's good practice to have a header comment at the top of every file to explain what this file does. That way when people are cruising through all the files they can easily read a summary of the code. |
||
from pygame.locals import * | ||
|
||
navyblue = (60,60,100) | ||
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. Avoid global variables when you can. It seems like you could make these class attributes. 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. A special case of a variable that doesn't actually vary – that's used as a constant. Python-the-languge doesn't distinguish between constants and other variables, but Python-as-used-by-humans does. The convention for a constant is all uppercase |
||
white = (255,255,255) | ||
|
||
class Scoreboard(pygame.sprite.Sprite): | ||
def __init__(self, screen): | ||
pygame.sprite.Sprite.__init__(self) | ||
self.fruit_sliced = 0 | ||
self.fruit_missed = 0 | ||
self.screen = screen | ||
|
||
self.sb_height, self.sb_width = 50, self.screen.get_width() | ||
self.rect = pygame.Rect(0,0, self.sb_width, self.sb_height) | ||
self.bg_color = navyblue | ||
self.text_color = white | ||
self.font = pygame.font.SysFont('Arial', 18) | ||
|
||
self.x_sliced_position, self.y_sliced_position = 20.0, 10.0 | ||
self.x_missed_position, self.y_missed_position = self.screen.get_width()-100, 10.0 | ||
|
||
def prep_scores(self): | ||
self.sliced_string = "Sliced: " + str(self.fruit_sliced) | ||
self.sliced_image = self.font.render(self.sliced_string, True, self.text_color) | ||
|
||
self.missed_string = "Missed: " + str(self.fruit_missed) | ||
self.missed_image = self.font.render(self.missed_string, True, self.text_color) | ||
|
||
def show(self): | ||
self.prep_scores() | ||
self.screen.fill(self.bg_color, self.rect) | ||
self.screen.blit(self.sliced_image, (self.x_sliced_position, self.y_sliced_position)) | ||
self.screen.blit(self.missed_image, (self.x_missed_position, self.y_missed_position)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,163 @@ | ||
import random, pygame, sys, math | ||
from pygame.locals import * | ||
|
||
white = (255, 255, 255) | ||
green = (0, 255, 0) | ||
|
||
class Fruit(pygame.sprite.Sprite): | ||
def __init__(self, screen, image_name): | ||
# Call the parent class (Sprite) constructor | ||
pygame.sprite.Sprite.__init__(self) | ||
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. You can use This also allows you to eliminate the comment, since the code is now self-documenting. |
||
self.speed = .3 | ||
self.screen = screen | ||
self.total_time = 0 | ||
self.time = 0 | ||
|
||
self.image = pygame.image.load(image_name).convert_alpha() | ||
self.image.set_colorkey(white) | ||
self.image.convert_alpha() | ||
self.rect = self.image.get_rect() | ||
|
||
self.x = random.randint(0, self.screen.get_width()-int(self.image.get_width()/2)) | ||
self.y = self.screen.get_height()+self.image.get_width() | ||
|
||
self.rect.centerx = self.x + (self.image.get_width()/2) | ||
self.rect.centery = self.y + (self.image.get_height()/2) | ||
|
||
#figure out direction to fruit to move based on spawn position (left or right) | ||
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. typo :D |
||
border = screen.get_width()/2 | ||
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. The rest of this init function could almost be it's own method that init calls. It's determining how the fruit will move so it's can be sectioned off in a comprehensible manner and there are only two class attributes that are created in 15 lines of code. |
||
#if starts to the left, move over right | ||
if (self.x < border): | ||
self.direction = 1 | ||
#if starts over on the right, move left | ||
else: | ||
self.direction = -1 | ||
|
||
#determine realistic trajectory angle that will clear minimum height and not exceed maximum | ||
min_height = .6*screen.get_height() | ||
max_height = .9*screen.get_height() | ||
|
||
start_distance_from_border = abs(self.x - border) | ||
distance_from_border = random.randint(0, int(start_distance_from_border)) | ||
|
||
try: | ||
min_angle = math.atan(min_height/distance_from_border) | ||
max_angle = math.atan(max_height/distance_from_border) | ||
|
||
self.angle = random.randint(int(min_angle), int(max_angle)) | ||
except: | ||
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. Handle a specific exception. In this specific case, use |
||
self.angle = math.pi/2 | ||
|
||
def move(self, x, y): | ||
self.x = x | ||
self.y = y | ||
self.rect.centerx = x + (self.image.get_width()/2) | ||
self.rect.centery = y + (self.image.get_height()/2) | ||
|
||
def toss(self, time_passed): | ||
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. There's a lot going on in this function that result in tossing. While the name of the function kind of explains what the function does, it might be worth having a docstring to explain the specifics. |
||
#self.y -= self.speed*time_passed | ||
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. There shouldn't be any lines of code that are commented out |
||
self.total_time = self.total_time+time_passed | ||
self.time = self.total_time/800 | ||
|
||
x_speed = random.randint(0,25) | ||
add_x = math.sin(self.time) * x_speed | ||
self.x += self.direction*add_x | ||
|
||
y_speed = random.randint(30,43) | ||
add_y = math.cos(self.time) *y_speed | ||
self.y -= add_y | ||
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. Going beyond: there's a lot of code that does something to x, and then the same thing to y. You can use numpy vectors to reduce the redundancy (at the cost of using a more complicated data structure). |
||
|
||
def draw(self): | ||
self.screen.blit(self.image, (self.x, self.y)) | ||
|
||
def checkCollision(self, other): | ||
# returns True or False if apple has collided with other object | ||
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. Since this comment describes the specification of the method, it could actually be a docstring. Advantages: signals to the reader that this describes what the code does, not how it works; will show up in generated documentation. |
||
col = pygame.sprite.collide_rect(self, other) | ||
return col | ||
|
||
def fall(self, time_passed): | ||
add_x = .15*time_passed | ||
self.x += self.direction*add_x | ||
|
||
add_y = .7*time_passed | ||
self.y += add_y | ||
|
||
class Apple(Fruit): | ||
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. These fruit classes/half fruit classes could have been one class with an extra variable in the init call that determines the name of the file. It would have brought you down to 2 more classes rather than 9 more classes. |
||
def __init__(self, screen): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'apple.png') | ||
|
||
class Banana(Fruit): | ||
def __init__(self, screen): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'banana.png') | ||
|
||
class Strawberry(Fruit): | ||
def __init__(self, screen): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'strawberry.png') | ||
|
||
class Half_Apple1(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'apple_half1.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Half_Apple2(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'apple_half2.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Half_Banana1(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'ban2.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Half_Banana2(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'ban1.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Half_Strawberry1(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'straw1.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Half_Strawberry2(Fruit): | ||
def __init__(self, screen, start_x, start_y): | ||
# Call the parent class (Fruit) constructor | ||
super().__init__(screen, 'straw2.png') | ||
self.x = start_x | ||
self.y = start_y | ||
|
||
class Sword(pygame.sprite.Sprite): | ||
def __init__(self, x, y, screen): | ||
# Call the parent class (Sprite) constructor | ||
pygame.sprite.Sprite.__init__(self) | ||
self.x = x | ||
self.y = y | ||
self.screen = screen | ||
|
||
self.image = pygame.image.load("sword.png") | ||
self.rect = self.image.get_rect() | ||
|
||
def move(self, x, y): | ||
self.x = x | ||
self.y = y | ||
self.rect.centerx = x | ||
self.rect.centery = y | ||
|
||
def draw(self): | ||
self.screen.blit(self.image, self.rect) | ||
|
||
def printrect(self): | ||
print(self.rect.centerx, ", ", self.rect.centery) |
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.
Good readme!