-
-
Save PotatoPope/85965d57d2c65b03ab167e1b08f51403 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import sys | |
import random | |
import pygame | |
from precode2 import * | |
from pygame.locals import * | |
pygame.init() | |
pygame.display.init() | |
pygame.display.set_caption("Boids") | |
black = (0, 0, 0) | |
white = (255, 255, 255) | |
class Screen: | |
width = 720 | |
height = 480 | |
size = (width, height) | |
screen = pygame.display.set_mode(Screen.size) | |
class Boid(): | |
def __init__(self, screen): | |
self.bird = pygame.image.load("birdie.png") | |
self._pos = Vector2D(random.randint(0, screen.get_width()), | |
random.randint(0, screen.get_height())) | |
self._vel = Vector2D((random.randint(1, 10) / 5.0), | |
(random.randint(1, 10) / 5.0)) | |
self.speed = random.randint(1, 5) | |
self.bird_rect = self.bird.get_rect(center=(self._pos.x, self._pos.y)) | |
self._boids = [] | |
def add_boid(self): | |
keys = pygame.key.get_pressed() | |
if keys[pygame.K_LEFT]: | |
self._boids.append(Boid(screen)) | |
def move_boids(self): | |
s = Screen() | |
#self.bird_rect.move_ip(self._vel.x, self._vel.y) | |
self._pos += (self._vel * self.speed) | |
#bounds checks | |
if self._pos.x + self.bird_rect.width >= s.width: | |
self._pos.x = s.width - self.bird_rect.width | |
self._vel.x *= -1 | |
elif self._pos.x <= 0: | |
self._pos.x = 0 | |
self._vel.x *= -1 | |
if self._pos.y - self.bird_rect.height <= 0: | |
self._pos.y = self.bird_rect.height | |
self._vel.y *= -1 | |
elif self._pos.y >= s.height: | |
self._pos.y = s.height - self.bird_rect.height | |
self._vel.y *= -1 | |
def draw_boids(self): | |
keys = pygame.key.get_pressed() | |
if keys[pygame.K_LEFT]: | |
print(len(self._boids)) | |
for boid in self._boids: | |
self.boidRect = pygame.Rect(self.bird_rect) | |
self.boidRect.x = boid._pos.x | |
self.boidRect.y = boid._pos.y | |
screen.blit(self.bird, self.boidRect) | |
@property | |
def vel(self): | |
return self._vel | |
@vel.setter | |
def vel(self, value): | |
self._vel = value | |
@property | |
def pos(self): | |
return self._pos | |
@property | |
def boids(self): | |
return self._boids | |
@boids.setter | |
def boids(self, boids): | |
self._boids = boids | |
class Game: | |
def __init__(self): | |
self.clock = pygame.time.Clock() | |
def game(self): | |
boids = Boid(screen) | |
while 1: | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
sys.exit() | |
self.tick() | |
boids.add_boid() | |
boids.draw_boids() | |
boids.move_boids() | |
pygame.display.flip() | |
def tick(self): | |
self.clock.tick(60) | |
screen.fill(black) | |
if __name__ == "__main__": | |
g = Game() | |
g.game() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python | |
from math import hypot, cos, sin, radians | |
import pygame | |
class Vector2D(): | |
"""Implements a two dimensional vector. | |
:param x: First component for the vector. | |
:param y: Second component for the vector. | |
""" | |
def __init__(self, x, y): | |
self.x = x | |
self.y = y | |
def __repr__(self): | |
return 'Vector2D({vec.x}, {vec.y})'.format(vec=self) | |
def __str__(self): | |
return 'Vector(X: {vec.x}, Y: {vec.y}) Magnitude: {lng}'.format(vec=self, lng=abs(self)) | |
def __nonzero__(self): | |
""" Makes Vector2D(0,0) evaluate to False, all other vectors evaluate to True | |
:returns: Boolean evaluation of vector. """ | |
return not self.as_point == (0, 0) | |
__bool__ = __nonzero__ | |
def __add__(self, b): | |
""" Vector addition. | |
:returns: New vector where x = self.x + b.x and y = self.y + b.y | |
""" | |
return Vector2D(self.x + b.x, self.y + b.y) | |
def __sub__(self, b): | |
""" Vector subtraction. | |
:returns: New vector where x = self.x - b.x and y = self.y - b.y | |
""" | |
return Vector2D(self.x - b.x, self.y - b.y) | |
def __eq__(self, other): | |
""" Vector equality. | |
:returns: True of both components of this vector are equal to those of b. | |
""" | |
return self.x == other.x and self.y == other.y | |
def __mul__(self, b): | |
""" Vector multiplication by a scalar. | |
:param: Any value that can be coerced into a float. | |
:returns: New vector where x = self.x * b and y = self.y * b | |
""" | |
try: | |
b = float(b) | |
return Vector2D(self.x * b, self.y * b) | |
except ValueError: | |
raise ValueError("Right value must be castable to float, was {}".format(b)) | |
def __truediv__(self, b): | |
""" Vector division by a scalar. | |
:param: Any value that can be coerced into a float. | |
:returns: New vector where x = self.x / b and y = self.y / b | |
""" | |
try: | |
b = float(b) | |
return Vector2D(self.x / b, self.y / b) | |
except ValueError: | |
raise ValueError("Right value must be castable to float, was {}".format(b)) | |
def __iter__(self): | |
""" Generator function used to iterate over components of vector. | |
:returns: Iterator over components. | |
""" | |
for value in self.__dict__.values(): | |
yield value | |
def __rmul__(self, b): | |
try: | |
b = float(b) | |
return Vector2D(self.x * b, self.y * b) | |
except (ValueError, ZeroDivisionError): | |
raise ValueError("Scalar must be castable to float, was {}".format(b)) | |
def __abs__(self): | |
""" Returns the magnitude of the vector. """ | |
return hypot(self.x, self.y) | |
def normalized(self): | |
""" Returns a new vector with the same direction but magnitude 1. | |
:returns: A new unit vector with the same direction as self. | |
Throws ZeroDivisionError if trying to normalize a zero vector. | |
""" | |
try: | |
m = abs(self) | |
return self / m | |
except ZeroDivisionError as e: | |
raise Exception("Attempted to normalize a zero vector, return a unit vector at zero degrees") from e | |
# return Vector2D(1, 0) | |
def copy(self): | |
""" Returns a copy of the vector. | |
:returns: A new vector identical to self. | |
""" | |
return Vector2D(self.x, self.y) | |
@property | |
def as_point(self): | |
""" A tuple representation of the vector, useful for pygame functions. | |
:returns: A tuple of the vectors components. | |
""" | |
return round(self.x), round(self.y) | |
def rotate(self, theta): | |
""" Vector rotation. | |
:param theta: The angle of rotation in degrees. | |
:returns: A new vector which is the same length, but rotated by theta. """ | |
cos_theta, sin_theta = cos(radians(theta)), sin(radians(theta)) | |
newx = round(self.x * cos_theta - self.y * sin_theta, 6) | |
newy = round(self.x * sin_theta + self.y * cos_theta, 6) | |
return Vector2D(newx, newy) | |
def intersect_rectangle_circle(rec_pos, sx, sy, circle_pos, circle_radius, circle_speed): | |
""" Determine if a rectangle and a circle intersects. | |
Only works for a rectangle aligned with the axes. | |
Parameters: | |
rec_pos - A Vector2D representing the position of the rectangles upper, | |
left corner. | |
sx - Width of rectangle. | |
sy - Height of rectangle. | |
circle_pos - A Vector2D representing the circle's position. | |
circle_radius - The circle's radius. | |
circle_speed - A Vector2D representing the circles speed. | |
Returns: | |
Exception if no intersection. If the rectangle and the circle intersect, returns | |
a normalized Vector2D pointing in the direction the circle will move after | |
the collision. | |
""" | |
# Position of the walls relative to the ball | |
top = (rec_pos.y ) - circle_pos.y | |
bottom = (rec_pos.y + sy) - circle_pos.y | |
left = (rec_pos.x ) - circle_pos.x | |
right = (rec_pos.x + sx) - circle_pos.x | |
r = circle_radius | |
intersecting = left <= r and top <= r and right >= -r and bottom >= -r | |
if intersecting: | |
# Now need to figure out the vector to return. | |
# should be just a matter of flipping x and y of the ball? | |
impulse = circle_speed.normalized() | |
if abs(left) <= r and impulse.x > 0: | |
impulse.x = -impulse.x | |
if abs(right) <= r and impulse.x < 0: | |
impulse.x = -impulse.x | |
if abs(top) <= r and impulse.y > 0: | |
impulse.y = -impulse.y | |
if abs(bottom) <= r and impulse.y < 0: | |
impulse.y = -impulse.y | |
#print("Impact", circle_speed, impulse.normalized()) | |
return impulse.normalized() | |
raise Exception("No intersection") | |
def intersect_circles(a_pos, a_radius, b_pos, b_radius): | |
""" Determine if two circles intersect. | |
Parameters: | |
a_pos - A Vector2D representing circle A's position | |
a_radius - Circle A's radius | |
b_pos - A Vector2D representing circle B's position | |
b_radius - Circle B's radius | |
Returns: | |
Raises exception if no intersection. If the circles intersect, returns a normalized | |
Vector2D pointing from circle A to circle B. | |
""" | |
# vector from A to B | |
dp1p2 = b_pos - a_pos | |
if a_radius + b_radius >= abs(dp1p2): | |
return dp1p2.normalized() | |
else: | |
raise Exception("No intersection") | |
def example_code(): | |
""" Example showing the use of the above code. """ | |
screen_res = (640,480) | |
pygame.init() | |
ra_pos = Vector2D(320, 320) # Rectangle A position | |
ra_sx = ra_sy = 20 # Rectangle A size | |
rb_pos = Vector2D(250, 250) # Rectangle B position | |
rb_sx = rb_sy = 10 # Rectangle B stretch | |
# Tracks the mouse cursor | |
a_pos = Vector2D(10, 10) # Circle A position | |
a_radius = 6 # Circle A radius | |
a_speed = Vector2D(5,5) # Circle A speed | |
b_pos = Vector2D(150, 150) # Circle B position | |
b_radius = 10 # Circle B radius | |
b_speed = Vector2D(5,5) # Circle B speed | |
screen = pygame.display.set_mode(screen_res) | |
clock = pygame.time.Clock() | |
while True: | |
for event in pygame.event.get(): | |
if event.type == pygame.QUIT: | |
exit() | |
pygame.draw.rect(screen, (0,0,0), (0, 0, screen.get_width(), screen.get_height())) | |
time_passed = clock.tick(30) # limit to 30FPS | |
time_passed_seconds = time_passed / 1000.0 # convert to seconds | |
x, y = pygame.mouse.get_pos() | |
a_pos = Vector2D(x, y) | |
pygame.draw.rect(screen, (255,255,255), (ra_pos.x, ra_pos.y, ra_sx, ra_sy)) | |
pygame.draw.rect(screen, (255,255,255), (rb_pos.x, rb_pos.y, rb_sx, rb_sy)) | |
pygame.draw.circle(screen, (255,255,255), (b_pos.x, b_pos.y), b_radius) # other circle | |
pygame.draw.circle(screen, (255,0,0), (a_pos.x, a_pos.y), a_radius) # mouse | |
def draw_vec_from_ball(vec, col): | |
""" Draw a vector from the mouse controlled circle. """ | |
pygame.draw.line(screen, col, (a_pos.x, a_pos.y), (a_pos.x + vec.x * 20, a_pos.y + vec.y * 20), 3) | |
# Draw speed vector | |
draw_vec_from_ball(a_speed, (255,255,0)) | |
# The big rectangle | |
impulse = intersect_rectangle_circle(ra_pos, ra_sx, ra_sy, a_pos, a_radius, a_speed) | |
if impulse: | |
draw_vec_from_ball(impulse, (0, 255,255)) | |
# The small rectangle | |
impulse = intersect_rectangle_circle(rb_pos, rb_sx, rb_sy, a_pos, a_radius, a_speed) | |
if impulse: | |
draw_vec_from_ball(impulse, (0, 255,255)) | |
# The circle | |
impulse = intersect_circles(a_pos, a_radius, b_pos, b_radius) | |
if impulse: | |
draw_vec_from_ball(impulse, (0, 255,255)) | |
pygame.display.update() | |
def example2(): | |
V1 = Vector2D(300, 300) | |
V2 = Vector2D(100, 100) | |
V3 = V1 + V2 | |
print(V3) | |
if __name__ == '__main__': | |
example_code() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment