Skip to content
This repository was archived by the owner on Aug 24, 2018. It is now read-only.
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
12 changes: 12 additions & 0 deletions Pipfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
pygame = "*"

[dev-packages]

[requires]
python_version = "3.6"
46 changes: 46 additions & 0 deletions Pipfile.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

150 changes: 105 additions & 45 deletions src/ball.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import math
# import math

from pygame.math import Vector2
from pygame import Rect
from block import BreakableBlock

from block import KineticBlock

import sys


class Ball:
"""
base class for bouncing objects
"""

def __init__(self, bounds, position, velocity, color, radius):
self.position = position
self.velocity = velocity
Expand All @@ -18,23 +23,26 @@ def __init__(self, bounds, position, velocity, color, radius):
self.collision_rectangle = self.update_rectangle()

def update_rectangle(self):
return Rect(self.position.x - self.radius,
self.position.y - self.radius,
self.radius*2, self.radius*2)
return Rect(
self.position.x - self.radius,
self.position.y - self.radius,
self.radius * 2,
self.radius * 2,
)

def update(self, **kwargs):
if self.position.x <= 0 + self.radius: # screen width
if self.position.x <= 0 + self.radius: # screen width
self.position.x = self.radius + 1
self.velocity.x *= -1
if self.position.x >= self.bounds[0] - self.radius:
self.position.x = self.bounds[0] - self.radius - 1
self.velocity.x *= -1
if self.position.y <= 0 + self.radius: # screen height
self.position.y = self.radius + 1
self.velocity.y *= -1
if self.position.y <= 0 + self.radius: # screen height
print("You win!")
sys.exit()
if self.position.y >= self.bounds[1] - self.radius:
self.position.y = self.bounds[1] - self.radius - 1
self.velocity.y *= -1
print("You lose!")
sys.exit()

self.position += self.velocity
self.collision_rectangle = self.update_rectangle()
Expand All @@ -45,20 +53,27 @@ def check_collision(self):

def draw(self, screen, pygame):
# cast x and y to int for drawing
pygame.draw.circle(screen, self.color, [int(self.position.x), int(self.position.y)], self.radius)
pygame.draw.circle(
screen,
self.color,
[int(self.position.x), int(self.position.y)],
self.radius,
)


class GameBall(Ball):
"""
A ball that collides with blocks
"""

def __init__(self, mass, object_list, bounds, position, velocity, color, radius):
self.object_list = object_list
self.mass = mass
super().__init__(bounds, position, velocity, color, radius)

def collide_with_ball(self, object, relative_vector):

#TODO: Calculate the correct position and move there directly
# TODO: Calculate the correct position and move there directly
while relative_vector.length() <= self.radius + object.radius:
self.position += relative_vector.normalize()
object.position -= relative_vector.normalize()
Expand All @@ -78,90 +93,135 @@ def collide_with_ball(self, object, relative_vector):

def collide_with_rectangle(self, object):
# This function is called after a first-pass test, that is the collision
# rectangles overlap.
# rectangles overlap.

left, right, top, bottom = False, False, False, False
# TODO: This can probably be optimized
if (
object.position.x > self.position.x and
object.position.x - object.rectangle.width/2 <= self.position.x + self.radius and
self.position.y <= object.position.y+object.rectangle.height/2 and
self.position.y >= object.position.y - object.rectangle.height/2
object.position.x > self.position.x
and object.position.x - object.rectangle.width / 2
<= self.position.x + self.radius
and self.position.y <= object.position.y + object.rectangle.height / 2
and self.position.y >= object.position.y - object.rectangle.height / 2
):
left = True

if (
object.position.x < self.position.x and
object.position.x + object.rectangle.width/2 >= self.position.x - self.radius and
self.position.y <= object.position.y+object.rectangle.height/2 and
self.position.y >= object.position.y - object.rectangle.height/2
object.position.x < self.position.x
and object.position.x + object.rectangle.width / 2
>= self.position.x - self.radius
and self.position.y <= object.position.y + object.rectangle.height / 2
and self.position.y >= object.position.y - object.rectangle.height / 2
):
right = True

if (
object.position.y > self.position.y and
object.position.y - object.rectangle.height/2 <= self.position.y + self.radius and
self.position.x <= object.position.x+object.rectangle.width/2 and
self.position.x >= object.position.x - object.rectangle.width/2
object.position.y > self.position.y
and object.position.y - object.rectangle.height / 2
<= self.position.y + self.radius
and self.position.x <= object.position.x + object.rectangle.width / 2
and self.position.x >= object.position.x - object.rectangle.width / 2
):
top = True

if (
object.position.y < self.position.y and
object.position.y + object.rectangle.width/2 >= self.position.y - self.radius and
self.position.x <= object.position.x+object.rectangle.width/2 and
self.position.x >= object.position.x - object.rectangle.width/2
object.position.y < self.position.y
and object.position.y + object.rectangle.width / 2
>= self.position.y - self.radius
and self.position.x <= object.position.x + object.rectangle.width / 2
and self.position.x >= object.position.x - object.rectangle.width / 2
):
bottom = True

test = left + right + top + bottom

if test == 1:
object.touched_by_ball = True
if isinstance(object, BreakableBlock):
object.got_hit()
if object.hp == 0:
self.object_list.remove(object)
# the ball has collided with an edge
# TODO: # fix sticky edges
if left or right:
self.velocity.x *= -1
if left:
self.position.x = object.position.x - object.rectangle.width/2 - self.radius - 1
self.position.x = (
object.position.x - object.rectangle.width / 2 - self.radius - 1
)
else:
self.position.x = object.position.x + object.rectangle.width/2 + self.radius + 1
self.position.x = (
object.position.x + object.rectangle.width / 2 + self.radius + 1
)

if top or bottom:
self.velocity.y *= -1
if top:
self.position.y = object.position.y - object.rectangle.height/2 - self.radius - 1
self.position.y = (
object.position.y
- object.rectangle.height / 2
- self.radius
- 1
)
else:
self.position.y = object.position.y + object.rectangle.height/2 + self.radius + 1
self.position.y = (
object.position.y
+ object.rectangle.height / 2
+ self.radius
+ 1
)

elif test == 4:
# TODO: Better error handling
print('error: ball inside rectangle')
print("error: ball inside rectangle")

elif test == 0:
# We are at a corner. Either it narrowly missed, or it hit the corner
corners = [
Vector2(object.position.x - object.rectangle.width/2, object.position.y - object.rectangle.height/2),
Vector2(object.position.x + object.rectangle.width/2, object.position.y - object.rectangle.height/2),
Vector2(object.position.x - object.rectangle.width/2, object.position.y + object.rectangle.height/2),
Vector2(object.position.x + object.rectangle.width/2, object.position.y + object.rectangle.height/2)
Vector2(
object.position.x - object.rectangle.width / 2,
object.position.y - object.rectangle.height / 2,
),
Vector2(
object.position.x + object.rectangle.width / 2,
object.position.y - object.rectangle.height / 2,
),
Vector2(
object.position.x - object.rectangle.width / 2,
object.position.y + object.rectangle.height / 2,
),
Vector2(
object.position.x + object.rectangle.width / 2,
object.position.y + object.rectangle.height / 2,
),
]

for corner in corners:
relative_vector = self.position - corner
if relative_vector.length() <= self.radius:
object.touched_by_ball = True
# Create a dummy object to make use of ball to ball collision, because the math is the same
# Give it a velocity of the same magnitude as the current ball to cause it to reflect at
# the same speed
stand_in = Ball(self.bounds, corner, Vector2(0, self.velocity.length()), [0,0,0], 0)
if isinstance(object, BreakableBlock):
object.got_hit()
if object.hp == 0:
self.object_list.remove(object)
# Create a dummy object to make use of ball to ball collision,
# because the math is the same. Give it a velocity of the
# same magnitude as the current ball to cause it to reflect at the
# same speed
stand_in = Ball(
self.bounds,
corner,
Vector2(0, self.velocity.length()),
[0, 0, 0],
0,
)
self.collide_with_ball(stand_in, relative_vector)

def check_collision(self):
# Warning!: This is a primitive method of collision detection
# Consider time complexity when adding more of this type
index = self.object_list.index(self)
for object in self.object_list[index+1:]: # TODO: Check effeciency
for object in self.object_list[index + 1 :]: # TODO: Check effeciency
# Balls colliding with blocks
if issubclass(type(object), KineticBlock) and object != self:
# Do a first round pass for collision (we know object is a KineticBlock)
Expand Down
54 changes: 47 additions & 7 deletions src/block.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
import pygame

from pygame.math import Vector2
from pygame import Rect
# from pygame.math import Vector2
# from pygame import Rect


class Block:
"""
Expand All @@ -12,14 +13,13 @@ def __init__(self, position, width, height, color):
# Create a rectangle centered around the x and y
self.position = position
self.rectangle = pygame.Rect(
position.x - (width/2),
position.y - (height/2),
width,
height)
position.x - (width / 2), position.y - (height / 2), width, height
)
self.width = width
self.height = height
self.color = color
self.touched_by_ball = False


def update(self, **kwargs):
self.touched_by_ball = False

Expand All @@ -29,9 +29,49 @@ def check_collision(self):
def draw(self, screen, pygame):
pygame.draw.rect(screen, self.color, self.rectangle)


class KineticBlock(Block):
"""
Constructor takes position, width, height, color
"""

# No custom code needed here, just want to be able to differentiate
# KineticBall will handle the collison
pass


class Paddle(KineticBlock):
"""
Class for the player paddle
"""

def move_paddle(self, direction):

if direction == "l" and self.position.x > self.width / 2:
self.position.x -= 3
elif direction == "r" and self.position.x < 400 - self.width / 2:
self.position.x += 3
self.rectangle = pygame.Rect(
self.position.x - (self.width / 2),
self.position.y - (self.height / 2),
self.width,
self.height,
)


class BreakableBlock(KineticBlock):
"""
Class for blocks that can be destroyed by the paddle.
Expects RGB values of 0-63, which it will then multiply by its number of hp.
Expects 1-4 hp.
"""

def __init__(self, position, width, height, color, hp):
self.hp = hp
self.orig_color = color
color = [i * hp for i in color]
super().__init__(position, width, height, color)

def got_hit(self):
self.hp -= 1
self.color = [i * self.hp for i in self.orig_color]
Loading