Skip to content

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

Open
wants to merge 30 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
f96afcc
preliminary fruit ninja files
Oct 18, 2017
79be113
Preliminary tests in PyGame
alakmazaheri Oct 18, 2017
6460494
adding random generating fruit function
Oct 18, 2017
d7109bf
Make fruit and sword objects using sprites
alakmazaheri Oct 18, 2017
e250751
Merge branch 'master' of https://github.com/ewesterhoff/InteractivePr…
alakmazaheri Oct 18, 2017
5f5abaa
Add collision detection
alakmazaheri Oct 19, 2017
c3cddd7
Add mvp
Oct 19, 2017
5617b65
add third fruit and scoreboard
Oct 19, 2017
1b1e4bf
Prep for projectile motion
Oct 19, 2017
15d2635
Update projectile motion and larger window
Oct 19, 2017
cef7827
clean object all_fruits file
Oct 19, 2017
8d36632
add new apples
Oct 20, 2017
666fd74
Add OpenCV detection and integration
alakmazaheri Oct 20, 2017
dd1b663
cut fruit in half
Oct 21, 2017
b7e163a
Integrate hand detection with game interface
alakmazaheri Oct 22, 2017
cfc8f47
Launch fruit higher
alakmazaheri Oct 22, 2017
a61a414
Fix sword off screen error
alakmazaheri Oct 26, 2017
71ec663
Fix merge conflict
Oct 26, 2017
deb4578
Fix merge conflict
Oct 26, 2017
dfed9fd
Update preliminary readme and reflection files
Oct 26, 2017
b9cad3f
Clean and comment code
alakmazaheri Oct 26, 2017
ab9ca76
Merge branch 'master' of https://github.com/ewesterhoff/InteractivePr…
alakmazaheri Oct 26, 2017
6fdb082
Update README
alakmazaheri Oct 28, 2017
01bada2
Update README
alakmazaheri Oct 28, 2017
0447eaf
Update README
alakmazaheri Oct 28, 2017
01ce5f7
Update reflection
Oct 28, 2017
1beb904
Merge branch 'master' of https://github.com/ewesterhoff/InteractivePr…
Oct 28, 2017
fc3f565
Add uml diagram
Oct 28, 2017
7f1ffb9
Add reflection
alakmazaheri Oct 28, 2017
83c6bdb
Remove reflection draft
Oct 28, 2017
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
34 changes: 32 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,33 @@
# InteractiveProgramming
# Interactive Programming
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good readme!


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)
Binary file added Reflection.pdf
Binary file not shown.
34 changes: 34 additions & 0 deletions Scoring.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import random, pygame, sys, pygame.font
Copy link

Choose a reason for hiding this comment

The 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)
Copy link

Choose a reason for hiding this comment

The 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.

Copy link
Contributor

Choose a reason for hiding this comment

The 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 NAVY_BLUE.

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))
163 changes: 163 additions & 0 deletions all_fruits.py
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)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can use super().__init__(self) instead of pygame.sprite.Sprite.__init__(self). This makes the code less fragile (you can can change class Fruit(pygame.sprite.Sprite) to class Fruit(MySprite):, where MySprite is some intermediate subclass of pygame.sprite.Sprite, without needing to search Fruit's methods for occurrences of pygame.sprite.Sprite).

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)
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

typo :D

border = screen.get_width()/2
Copy link

Choose a reason for hiding this comment

The 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:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Handle a specific exception.

In this specific case, use math.atan2 instead of math.atan. Then there's no chance of an exception, and there's no numerical instability when distance_from_border is close, but not equal, to 0.0.

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):
Copy link

Choose a reason for hiding this comment

The 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
Copy link

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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
Copy link
Contributor

Choose a reason for hiding this comment

The 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):
Copy link

Choose a reason for hiding this comment

The 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)
Binary file added apple.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apple_half1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added apple_half2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ban1.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added ban2.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added banana.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading