diff --git a/Asteroid.png b/Asteroid.png new file mode 100644 index 00000000..9b574e60 Binary files /dev/null and b/Asteroid.png differ diff --git a/Asteroid.py b/Asteroid.py new file mode 100644 index 00000000..6a238097 --- /dev/null +++ b/Asteroid.py @@ -0,0 +1,396 @@ +import pygame +from pygame.locals import * +import time +import math +import random + + +class Asteroid(): + """ + Asteroid Class: + x - position + y - position + speed - speed of asteroid + direction - direction of asteroid + image - surface containing picture of asteroid + gameDisplay - the display to put the asteroid on + w, h - the width and height of the surface + """ + def __init__(self,x,y,speed,direction,gameDisplay): + self.x = x + self.y = y + self.speed = speed + self.direction = direction + self.image = pygame.image.load('Asteroid.png').convert() + self.image.set_colorkey((0,0,0)) # Sets the Asteroids Blackness to be transparent + self.w, self.h = self.image.get_size() # Gets the Asteroid's width and height + self.destroyed = False + self.gameDisplay = gameDisplay + def update(self): + """ + updates the position of the asteroid and rectangle + """ + if(not self.destroyed): # once the asteroid is destroyed, it will stop redrawing the asteroid + width, height = self.gameDisplay.get_size() # gets the display's width and length + self.x = self.x + (self.speed * math.cos(self.direction)) # Sets the Asteroid's to a small change in space + self.y = self.y + (self.speed * math.sin(self.direction)) + if(self.x >= width): # If the asteroid's coordinate goes outside of the window, set that coordinate to the other side of the map + self.x = 0 - self.w # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.x <= 0 - self.w): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.x = width + if(self.y >= height): + self.y = 0 - self.h + elif(self.y <= 0 - self.h): + self.y = height + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) # The Rect is for the hitbox + self.gameDisplay.blit(self.image,(self.x,self.y)) # draws the asteroid on the screen + #pygame.draw.rect(self.gameDisplay,(0,255,0),self.rect) # display's the asteroid's hit box in red (for testing) + +class LargeAsteroid(Asteroid): + + """ + subclass of the asteroid, for the starting asteroids + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 2,self.h // 2)) # scales the asteroid to size + self.w,self.h = self.image.get_size() + self.shrinkage = 50 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) # lessening the hitbox so the corners don't stick out + def destroy(self): + """ + destroys the asteroid and returns the asteroids that should take it's place. + """ + if(not self.destroyed): + self.destroyed = True + MedAster = [] + for i in range(2): + MedAster.append(MediumAsteroid(self.x,self.y,self.speed*1.5,random.uniform(0,2*math.pi),self.gameDisplay)) #makes two more medium asteroids in it's place with random directions + return MedAster + return [] +class MediumAsteroid(Asteroid): + """ + subclass of the asteroid, for the second asteroid + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 4,self.h // 4)) # half as big as large asteroid + self.w,self.h = self.image.get_size() + self.shrinkage = 25 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + def destroy(self): + """ + destroys the asteroid and returns the asteroids that should take it's place. + """ + if(not self.destroyed): + self.destroyed = True + SmallAster = [] + for i in range(2): + SmallAster.append(SmallAsteroid(self.x,self.y,self.speed*1.5,random.uniform(0,2*math.pi),self.gameDisplay)) #makes two more small asteroids in it's place with random directions + return SmallAster + return [] +class SmallAsteroid(Asteroid): + """ + subclass of the asteroid, for the last asteroid + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 8,self.h // 8)) # half as big as medium asteroid + self.w,self.h = self.image.get_size() + self.shrinkage = 12 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + def destroy(self): + """ + destroys the asteroid and returns nothing because it is the smallest asteroid + """ + self.destroyed = True + return [] +class CollectionOfAsteroids(): + """ + A collection of the Asteroids in the game + listOfAsteroids - a list of the asteroids in the game + gameDisplay - the display + """ + def __init__(self,gameDisplay): + self.listOfAsteroids = [] + self.gameDisplay = gameDisplay + self.speed = 1 + def spawnAsteroids(self,numberOfAsteroids): + """ + spawns a set number of asteroids in the sides of the game + """ + width, height = self.gameDisplay.get_size() + listOfAsteroids = [] # initializes a list of asteroids to update + listOfRects = [] # initializes a list of hitboxes + sampleAsteroid = LargeAsteroid(0,0,0,0,self.gameDisplay) # a sample asteroid to know where to spawn the asteroids in case we change the size later + smallArea = 100 # the area that asteroids are to spawn around the the edge + for i in range(numberOfAsteroids): + side = random.randint(1,4) + if(side == 1): # left side of the screen + x = random.randint(-sampleAsteroid.w // 2,smallArea - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.h // 2,height - sampleAsteroid.h // 2) + elif(side == 2): # top side of the screen + x = random.randint(-sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.w // 2,smallArea - sampleAsteroid.w // 2) + elif(side == 3): # right side of the screen + x = random.randint(width-smallArea - sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.w // 2,height - sampleAsteroid.w // 2) + elif(side == 4): # bottom of the screen + x = random.randint(-sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(height-smallArea - sampleAsteroid.w // 2,height - sampleAsteroid.w // 2) + direction = random.uniform(0,math.pi * 2) # initiate each asteroid with a random direction + listOfAsteroids.append(LargeAsteroid(x,y,self.speed,direction,self.gameDisplay)) + listOfRects.append(listOfAsteroids[i].rect) + self.listOfAsteroids = listOfAsteroids + self.listOfRects = listOfRects + def update(self): + """ + updates all the asteroids, deleting them from the list if they are destroyed. + """ + listOfRects = [] # asteroid + ListToDelete = [] # a list that incluedes the indicies of what to delete + for i in range(len(self.listOfAsteroids)): + if(self.listOfAsteroids[i].destroyed): + ListToDelete.append(i) # if the asteroid is destroyed, remember the number to remove it later + else: + self.listOfAsteroids[i].update() + listOfRects.append(self.listOfAsteroids[i].rect) + for j in reversed(ListToDelete): # reversed so that it doesn't delete one and shift mid for loop. + del self.listOfAsteroids[j] + self.listOfRects = listOfRects + def destroyAll(self): # function for testingasteroid, not for the real game + sizeOfAsteroids = range(len(self.listOfAsteroids)) + for i in sizeOfAsteroids: + newAsteroid = self.listOfAsteroids[i].destroy() + if(newAsteroid != None): + self.listOfAsteroids += newAsteroid # destroying all of the asteroids making them medium + for i in sizeOfAsteroids: + self.listOfAsteroids.pop(0) +class Projectile(): + """ + projectiles that fire and destroy asteroids, ufos and players. + x - position x + y - position y + w, h - size of the projectiles + speed - speed of the projectile + direction- direction given to the projectiles + rect - the hitbox of the projectile + gameDisplay - the display + destroyed - senses whether the projectile is destroyed or not + distanceTravelled - detects how far the projectile has travelled + """ + def __init__(self,x,y,direction,alliance,gameDisplay): + size = 3 + self.x = x + self.y = y + self.w = size + self.h = size + self.speed = 10 + self.direction = direction + self.rect = ((self.x,self.y),(size,size)) + self.image = pygame.Surface((size,size)) + self.image.fill((255,165,0)) + self.gameDisplay = gameDisplay + self.destroyed = False + self.distanceTravelled = 0 # asteroids + width, height = self.gameDisplay.get_size() + self.distanceWanted = 5/8 * # the distance that the projectile travels before it is destroyed + self.alliance = alliance + def update(self): + if(self.distanceTravelled < self.distanceWanted): # if the projectile has travelled farther than the wanted distance, it destroys itself + width, height = self.gameDisplay.get_size() # gets the display's width and length + self.x = self.x + (self.speed * math.cos(self.direction)) # Sets the speed to a small change in space + self.y = self.y + (self.speed * math.sin(self.direction)) + self.distanceTravelled += self.speed # updates the disnance travelled + if(self.x >= width): # If the projectile's coordinate goes outside of the window, set that coordinate to the other side of the map + self.x = 0 - self.w # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.x <= 0 - self.w): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.x = width + if(self.y >= height): + self.y = 0 - self.h + elif(self.y <= 0 - self.h): + self.y = height + self.rect = pygame.Rect((self.x,self.y),(self.w,self.h)) + self.gameDisplay.blit(self.image,(self.x,self.y)) # draws the pixel on the screen + #pygame.draw.rect(self.gameDisplay,(0,255,0),self.rect) # display's the projectile's hit box in green (for testing) + else: + self.destroy() # satisfying to right + def destroy(self): + self.destroyed = True +class CollectionOfProjectiles(): + def __init__(self,gameDisplay): + self.listOfProjectiles = [] #initializes the asteroidprojectiles + self.listOfRects = [] # initializes their hitboxes + self.gameDisplay = gameDisplay + def addProjectile(self,x,y,direction,alliance): + self.listOfProjectiles.append(Projectile(x,y,direction,alliance,self.gameDisplay)) # The spacebar command should call this + # with the x,y and directions of the ship (with an offset bc of the front of the ship and that the origin is top left) + def update(self): + ListToDelete = [] # initializes the indices of what to delete + for i in range(len(self.listOfProjectiles)): + if(self.listOfProjectiles[i].destroyed): + ListToDelete.append(i) # adding the index of destroyed particles to delete + else: + self.listOfProjectiles[i].update() + for j in reversed(ListToDelete): + del self.listOfProjectiles[j] + +class UFO(): + def __init__(self,y,FacingRight,gameDisplay,listOfProjectiles): + self.y = y + self.speed = 2 + self.destroyed = False + self.image = pygame.image.load('UFO.gif').convert() + self.image.set_colorkey((0,0,0)) + self.w, self.h = self.image.get_size() + self.straight = bool(random.getrandbits(1)) + self.FacingRight = FacingRight + self.gameDisplay = gameDisplay + width, height = gameDisplay.get_size() + self.counter = 0 + self.listOfProjectiles = listOfProjectiles + if(FacingRight): + self.x = -self.w + else: + self.x = width + def update(self): + if(self.counter % self.fireRate == 0): + self.shoot() + self.counter += 1 + width, height = self.gameDisplay.get_size() + if(self.straight == True): + if(self.FacingRight): + self.direction = 0 + else: + self.direction = math.pi + else: + if(self.FacingRight): + if((self.x + self.w / 2) < width * 1 / 4): + self.direction = 0 + elif(self.x + self.w / 2 < width / 2): + self.direction = math.pi / 4 + else: + self.direction = - math.pi / 4 + else: + if((self.x + self.w / 2) > width * 3 / 4): + self.direction = math.pi + elif(self.x + self.w / 2 > width / 2): + self.direction = 5 * math.pi / 4 + else: + self.direction = 3 * math.pi / 4 + self.x = self.x + (self.speed * math.cos(self.direction)) # Sets the speed to a small change in space + self.y = self.y + (self.speed * math.sin(self.direction)) + if(self.x >= width and self.FacingRight): # if the UFO goes out of the screen, destroy it + self.destroyed = True + elif(self.x <= 0 - self.w and not self.FacingRight): + self.destroyed = True + if(self.y >= height): # If the UFOs coordinate goes outside of the window, set that coordinate to the other side of the map + self.y = 0 - self.h # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.y <= 0 - self.h): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.y = height + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + #pygame.draw.rect(self.gameDisplay,(0,0,255),self.rect) # display's the asteroid's hit box in red (for testing) + self.gameDisplay.blit(self.image,(self.x,self.y)) + def destroy(self): + self.destroyed = True +class BigUFO(UFO): + def __init__(self,y,FacingRight,gameDisplay,listOfProjectiles): + super().__init__(y,FacingRight,gameDisplay,listOfProjectiles) + self.image = pygame.transform.scale(self.image,(self.w // 2,self.h // 2)) + self.w,self.h = self.image.get_size() + self.shrinkage = 30 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + self.fireRate = 60 + def shoot(self): + if(not self.destroyed): + self.listOfProjectiles.addProjectile(self.x + self.w / 2,self.y + self.h / 2,random.uniform(0,2*math.pi),"UFO") +class CollectionOfUFOs(): + def __init__(self,gameDisplay,listOfProjectiles): + self.listOfUFOs = [] #initializes the projectiles + self.listOfRects = [] # initializes their hitboxes + self.gameDisplay = gameDisplay + self.listOfProjectiles = listOfProjectiles + def spawnBigUFO(self): + width, height = self.gameDisplay.get_size() + sampleUFO = BigUFO(0,True,self.gameDisplay,self.listOfProjectiles) + y = random.randint(-sampleUFO.h // 2,height - sampleUFO.h // 2) + facingRight = bool(random.getrandbits(1)) + self.listOfUFOs.append(BigUFO(y,facingRight,self.gameDisplay,self.listOfProjectiles)) + def update(self): + listOfRects = [] + ListToDelete = [] # initializes the indices of what to delete + for i in range(len(self.listOfUFOs)): + if(self.listOfUFOs[i].destroyed): + ListToDelete.append(i) # adding the index of destroyed particles to delete + else: + self.listOfUFOs[i].update() + listOfRects.append(self.listOfUFOs[i].rect) + for j in reversed(ListToDelete): + del self.listOfUFOs[j] + self.listOfRects = listOfRects +class listOfObjects(): + def __init__(self,gameDisplay): + self.gameDisplay = gameDisplay + self.Asteroids = CollectionOfAsteroids(gameDisplay) + self.Projectiles = CollectionOfProjectiles(gameDisplay) # contains the CollectionOfAsteroids and CollectionOfProjectiles objects + self.UFOs = CollectionOfUFOs(gameDisplay,self.Projectiles) + def update(self): + self.Asteroids.update() + self.UFOs.update() + self.Projectiles.update() + for i in self.Projectiles.listOfProjectiles: # runs through each projectile + collisionsAster = i.rect.collidelist(self.Asteroids.listOfRects) # detects if any of the asteroids are in contact with the projectile + if (collisionsAster != -1): # if there is a collision + self.Asteroids.listOfAsteroids += self.Asteroids.listOfAsteroids[collisionsAster].destroy() #destroy both the asteroid and the projectile. + i.destroy() + collisionsUFO = i.rect.collidelist(self.UFOs.listOfRects) + if (collisionsUFO != -1 and i.alliance != "UFO"): # if there is a collision + self.UFOs.listOfUFOs[collisionsUFO].destroy() #destroy both the asteroid and the projectile. + i.destroy() + for i in self.UFOs.listOfUFOs: + collisionsAster = i.rect.collidelist(self.Asteroids.listOfRects) # detects if any of the asteroids are in contact with the projectile + if (collisionsAster != -1): # if there is a collision + self.Asteroids.listOfAsteroids += self.Asteroids.listOfAsteroids[collisionsAster].destroy() #destroy both the asteroid and the projectile. + i.destroy() + + +pygame.init() +gameDisplay = pygame.display.set_mode((800,600)) +clock = pygame.time.Clock() +numberOfAsteroids = 4 +Black = (0,0,0) # black screen is the background +gameDisplay.fill(Black) +AllThings = listOfObjects(gameDisplay) +AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) +AllThings.UFOs.spawnBigUFO() +AllThings.update() # testing the many asteroids spawn in the right spot +pygame.display.update() +running = True # for the exit of the game +randomCounter = 0 +while running: + randomCounter += 1 + randomInt = random.randint(1,1000) + if(randomInt == 1 and len(AllThings.UFOs.listOfUFOs) == 0): + AllThings.UFOs.spawnBigUFO() + if(randomCounter % 30 == 0): + AllThings.Projectiles.addProjectile(400,300,random.uniform(0,2*math.pi),"Ship") + gameDisplay.fill(Black) + AllThings.update()# update each asteroid + pygame.display.update() + if(len(AllThings.Asteroids.listOfAsteroids) == 0 and len(AllThings.UFOs.listOfUFOs) == 0): + numberOfAsteroids += 1 + AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) + clock.tick(60) + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + if event.type == pygame.KEYUP: + if event.key == pygame.K_q: + running = False +pygame.quit() diff --git a/FullGame.png b/FullGame.png new file mode 100644 index 00000000..2e193ebd Binary files /dev/null and b/FullGame.png differ diff --git a/ProjectProposal.md b/ProjectProposal.md new file mode 100644 index 00000000..90d3867d --- /dev/null +++ b/ProjectProposal.md @@ -0,0 +1,15 @@ +### Main Idea +The main idea is to create the game "Asteroids". We will explore classes and the pygame library and generate a playable game at the end. Our MVP is having a controllable rocket ship and asteroids that move around and can be shot. Our strectch goal is adding the UFOs. + +### Learning Goals +Corey: To learn how user-game interactions work as well as UI. +Nathan: To learn how the pygame library works, and explore its functions. + +### Libraries +We plan to use mostly pygame for our project, and also random and math. We don't think we will need any other libraries for this project. + +### Mid-Project Check-in +We will make an arena and a a player that shoots. + +### Biggest Risks +Underestimating the amount of time it will take to complete the game and other tasks. diff --git a/ProjectReflection.md b/ProjectReflection.md new file mode 100644 index 00000000..0256384e --- /dev/null +++ b/ProjectReflection.md @@ -0,0 +1,40 @@ +# Project Reflection +### Authors: Corey Cochran-Lepiz, Nathan Estill + +## Project Overview + +We created a Pygame version of the classic arcade game, Asteroids. + +## Results + +      We ended up with a game that does a good job resembling the original game. The player starts with three lives. They have a ship that moves with an acceleration and turning based system. The ship slows down when not accelerating and can "drift" when turning while going forward. Colliding with asteroids or UFOs, or getting hit by the UFOs projectile makes the player lose a life. The ship shoots a projectile that goes about 5/8 of the size of the screen. Hitting other objects with the ship's projectiles grants points depending on the object. No other interactions grant points. For every 10000 points the player receives, another life is granted. + +      The asteroids spawn in random places around the map and break up when hit with a non-asteroid object. They break up twice into two more smaller asteroids and disappear on the third hit. Large asteroids are worth 20 points, medium asteroids are worth 50 and small asteroids are worth 100. The UFOs spawn randomly throughout the game. They move in either a straight line or a in a up and down fashion. They shoot in random directions at a distance that is lower than that of the Ship's projectiles. UFOs can shoot asteroids and hit asteroids, but does not grant points if destroyed in this manner. The UFO is worth 500 points. +![A screen shot of the game working](FullGame.png) + +## Implementation [~2-3 paragraphs + UML diagram] +      Our implementation for the non-controllable objects consisted of making a class for each object. Each class had an update function that handled the movement of the object and a destroy function, for when the object was destroyed. The asteroid and UFO had subclasses that inherited them, one for each size of asteroid, and one for each type of asteroid (We had initially planned to add a second type of UFO, but ran out of time). Each of these classes had an overarching class that contained all of the instances of the lower class. This made collision detection much easier and allowed us to simply update the greater class, which in turn updated all of the instances. There was one class that contained all of the objects in the game, holding the overarching classes for each object. This class handled collision detection and updated all of the objects. + +      On the design choices. For the Ship class we decided to go with Pygame's rotation function which was tricky since each time it rotates an image it loses overall quality. Another route we could have gone is making a folder with every angle of the ship inside of it so that all it had to do was pull from the folder when we called for a rotation instead of modifying an image each time. Another design choice we made was to connect surfaces to rectangles that approximately represented their shape. We could have made many smaller rectangles to more accurately represent the shape of the object, but we decided that it wasn't worth the additional time spent. The estimated hit boxes are close enough, and the additional accuracy would have made an insignificant improvement. + +Class Diagram +* listOfObjects - contains and keeps track of all objects + * CollectionOfAsteroids - contains and keeps track of all asteroids + * Asteroid - Base Asteroid class + * Small Asteroid - Inherits from Asteroid + * Medium Asteroid - Inherits from Asteroid + * Large Asteroid - Inherits from Asteroid + * CollectionOfUFOs - contains and keeps track of all UFOs + * UFO - Base UFO class + * BigUFO - Inherits from UFO + * SmallUFO (not implemented) - Inherits from UFO + * CollectionOfProjectiles - contains and keeps track of all Projectiles + * Projectile - Base Projectile class + * Ship - kept all data for the player + * GUI - in charge of all user interface realated things + +## Reflection [~2 paragraphs] + +      Looking back at the project we both felt like it was a great success. We feel like the project was scoped well for where our skill levels are. We weren't completely comfortable going into it but at the same time we didn't spend hours on hours debugging code. At the beginning of the project we sat down one dinner with a white board to talk about the overarching design goals we had for the project, written [here](keepInMind.md), which was a great start as it meant we had laid out the project as well as all of the responsibilities. We decided to go with the 'divide and conquer' approach alongside meeting every few days to work next to each other for a few hours which turned out to be an effective strategy for us. We wish we had brainstormed ways to go above and beyond with the project early on so that we could have implemented them from the start. One of the ideas being a two-player Asteroids where one person drives and another aims and shoots a canon. + +Looking back, we could have made a lot more of the classes inherit from each other. We could have made a class that was a basic object that moved in space that all of the other objects could have inherited from. We also could have made a class that the could have been a base for all of the 'collection' classes that would have made that part go smoother. diff --git a/README.md b/README.md index 5f822327..4c60d462 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,11 @@ # InteractiveProgramming -This is the base repo for the interactive programming project for Software Design, Spring 2018 at Olin College. +run python3 interface.py +How to play: +Up key: Move the ship forward +Left/Right Key: rotates the ship +Space Bar: Shoot (can be held down) +Q: quits the game + +Cheat Codes: +R: Rapid Fire Mode (Toggle) +L: Add life diff --git a/UFO.gif b/UFO.gif new file mode 100644 index 00000000..fe6c0b87 Binary files /dev/null and b/UFO.gif differ diff --git a/classes.py b/classes.py new file mode 100644 index 00000000..9bc2893a --- /dev/null +++ b/classes.py @@ -0,0 +1,617 @@ +import pygame +from math import cos,sin,sqrt,radians,atan,pi +import random +pygame.init() + +class GUI(): + WHITE = (255,255,255) + font = pygame.font.SysFont("couriernew",32) + bx = 700 + by = 100 + + def __init__(self,gD): + self.gD = gD + + def update(self,ship): + box = pygame.surface.Surface((self.bx, self.by)) + txt_surf = self.font.render(str(ship.score), True, self.WHITE) # bottom line + txt_rect = txt_surf.get_rect(center=(75, 40)) + box.blit(txt_surf, txt_rect) + w,h = ship.oImage.get_size() + txt_surf = pygame.transform.scale(ship.oImage,(int(w*.5),int(h*.5))) + for x in range(ship.lives): + spacing = x*45 + txt_rect = txt_surf.get_rect(center=(75-45+spacing, 90)) + box.blit(txt_surf, txt_rect) + self.gD.blit(box,(0,0)) + + def gameOver(self,ship): + box = pygame.surface.Surface((300,500)) + gO = self.font.render("GAME OVER", True,self.WHITE) + gO_rect = gO.get_rect(center=(90,90)) + box.blit(gO,gO_rect) + txt_surf = self.font.render(str(ship.score), True, self.WHITE) # bottom line + txt_rect = txt_surf.get_rect(center=(90, 200)) + box.blit(txt_surf,txt_rect) + w,h = self.gD.get_size() + self.gD.blit(box,(int(w/2)-90,int(h/2)-90-40)) + + +def rot_center(image, angle): + """rotate a Surface, maintaining position.""" + #DOES NOT WORK + + loc = image.get_rect().center #rot_image is not defined + rot_sprite = pygame.transform.rotate(image, angle) + # rot_sprite.get_rect().center = loc + rotRect = rot_sprite.get_rect() + rotRect.center = loc + return rot_sprite,rotRect + +class Ship(): + """Ship class! + Holds data on ship: + angle (degress) + x_speed + y_speed + x (position) + y (position) + oImage (img) original + nImage (img) new + drift (boolean) + rect (pygame surface) + + """ + x_speed = 0 + y_speed = 0 + drift = False + def __init__(self,x,y,angle,img,gD): + """ + Initliazes with where the ship is facing as the angle + """ + self.startingAngle = angle + self.angle = angle + self.startingX = x + self.startingY = y + self.x = x + self.y = y + self.oImage = img + self.nImage = img + self.w,self.h = img.get_size() + self.gD = gD + self.rect = img.get_rect() + self.changex = self.rect.w / 2 + self.changey = self.rect.h / 2 + self.rect.inflate_ip(-self.changex,-self.changey) + self.rect.center = (self.x,self.y) + self.score = 0 + self.lives = 3 + self.destroyed = False + self.extraLives = 1 + def move(self): + """FORWARD!!! + Moves the ship forward in the direction it's heading (its angle) + """ + self.drift = False + self.x_speed += cos(radians(self.angle))*.4 + self.y_speed += sin(radians(self.angle))*.4 + # if sqrt(self.x_speed**2+self.y_speed**2) < 10: + + def rotate(self,posNeg): + """Rotates ship""" + self.nImage,self.rect = rot_center(self.oImage,posNeg*3+(270-self.angle)) + self.changex = self.rect.w / 2 + self.changey = self.rect.h / 2 + self.rect.inflate_ip(-self.changex,-self.changey) + self.rect.center = (self.x,self.y) + self.angle -= posNeg*3 + + def update(self): + """MAGIC + Does magic and makes the ship work. + Updates position + """ + if(self.score > 10000 * self.extraLives): + self.lives += 1 + self.extraLives += 1 + if(self.destroyed): + self.counter += 1 + if(self.counter == 120): + self.destroyed = False + width,height = self.gD.get_size() + speed = sqrt(self.x_speed**2+self.y_speed**2) + # print(speed) + if speed < .08 and self.drift: + self.drift = False + self.x_speed = 0 + self.y_speed = 0 + if speed > 10: + self.x_speed = cos(radians(self.angle))*10 + self.y_speed = sin(radians(self.angle))*10 + if self.drift: + #theta = atan(self.y_speed/self.x_speed) + self.x_speed *= .98 + self.y_speed *= .98 + + self.y += self.y_speed + self.x += self.x_speed + + if(self.x >= width): + self.x = 0 - self.w + elif(self.x <= 0 - self.w): + self.x = width + if(self.y >= height): + self.y = 0 - self.h + elif(self.y <= 0 - self.h): + self.y = height + self.rect.center = (self.x,self.y) + #self.nImage.center = (self.x,self.y) + #pygame.draw.rect(self.gD,(255,0,255),self.rect) # display's the ship's hit box in purple (for testing + self.gD.blit(self.nImage,(self.rect.x - self.changex / 2, self.rect.y - self.changey / 2)) + #pygame.draw.rect(self.gD,(255,0,255),self.rect) # display's the ship's hit box in purple (for testing) + def shoot(self,AllThings): + x = self.x + int(5 * cos(self.angle)) + y = self.y + int(5 * sin(self.angle)) + AllThings.Projectiles.addProjectile(x,y,radians(self.angle),"Ship") + def destroy(self): + self.destroyed = True + self.lives = self.lives - 1 # right now just a test, need to put something else here + self.x = self.startingX + self.y = self.startingY + self.angle = self.startingAngle + self.rotate(0) + self.x_speed = 0 + self.y_speed = 0 + self.counter = 0 +class Asteroid(): + """ + Asteroid Class: + x - position + y - position + speed - speed of asteroid + direction - direction of asteroid + image - surface containing picture of asteroid + gameDisplay - the display to put the asteroid on + w, h - the width and height of the surface + """ + def __init__(self,x,y,speed,direction,gameDisplay): + self.x = x + self.y = y + self.speed = speed + self.direction = direction + self.image = pygame.image.load('Asteroid.png').convert() + self.image.set_colorkey((0,0,0)) # Sets the Asteroids Blackness to be transparent + self.w, self.h = self.image.get_size() # Gets the Asteroid's width and height + self.destroyed = False + self.gameDisplay = gameDisplay + def update(self): + """ + updates the position of the asteroid and rectangle + """ + if(not self.destroyed): # once the asteroid is destroyed, it will stop redrawing the asteroid + width, height = self.gameDisplay.get_size() # gets the display's width and length + self.x = self.x + (self.speed * cos(self.direction)) # Sets the Asteroid's to a small change in space + self.y = self.y + (self.speed * sin(self.direction)) + if(self.x >= width): # If the asteroid's coordinate goes outside of the window, set that coordinate to the other side of the map + self.x = 0 - self.w # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.x <= 0 - self.w): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.x = width + if(self.y >= height): + self.y = 0 - self.h + elif(self.y <= 0 - self.h): + self.y = height + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) # The Rect is for the hitbox + self.gameDisplay.blit(self.image,(self.x,self.y)) # draws the asteroid on the screen + #pygame.draw.rect(self.gameDisplay,(0,255,0),self.rect) # display's the asteroid's hit box in red (for testing) +class LargeAsteroid(Asteroid): + + """ + subclass of the asteroid, for the starting asteroids + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 2,self.h // 2)) # scales the asteroid to size + self.w,self.h = self.image.get_size() + self.shrinkage = 50 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) # lessening the hitbox so the corners don't stick out + self.type = "Large" + def destroy(self): + """ + destroys the asteroid and returns the asteroids that should take it's place. + """ + if(not self.destroyed): + self.destroyed = True + MedAster = [] + for i in range(2): + MedAster.append(MediumAsteroid(self.x,self.y,self.speed*1.5,random.uniform(0,2*pi),self.gameDisplay)) #makes two more medium asteroids in it's place with random directions + return MedAster + return [] +class MediumAsteroid(Asteroid): + """ + subclass of the asteroid, for the second asteroid + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 4,self.h // 4)) # half as big as large asteroid + self.w,self.h = self.image.get_size() + self.shrinkage = 25 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + self.type = "Medium" + def destroy(self): + """ + destroys the asteroid and returns the asteroids that should take it's place. + """ + if(not self.destroyed): + self.destroyed = True + SmallAster = [] + for i in range(2): + SmallAster.append(SmallAsteroid(self.x,self.y,self.speed*1.5,random.uniform(0,2*pi),self.gameDisplay)) #makes two more small asteroids in it's place with random directions + return SmallAster + return [] +class SmallAsteroid(Asteroid): + """ + subclass of the asteroid, for the last asteroid + shrinkage - number that the rectangle hitbox shrinks by + rect - the hitbox rectangle + """ + def __init__(self,x,y,speed,direction,gameDisplay): + super().__init__(x,y,speed,direction,gameDisplay) + self.image = pygame.transform.scale(self.image,(self.w // 8,self.h // 8)) # half as big as medium asteroid + self.w,self.h = self.image.get_size() + self.shrinkage = 12 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + self.type = "Small" + def destroy(self): + """ + destroys the asteroid and returns nothing because it is the smallest asteroid + """ + self.destroyed = True + return [] +class CollectionOfAsteroids(): + """ + A collection of the Asteroids in the game + listOfAsteroids - a list of the asteroids in the game + listOfRects - a list of the hitboxes of the asteroids + gameDisplay - the display + """ + def __init__(self,gameDisplay): + self.listOfAsteroids = [] + self.gameDisplay = gameDisplay + self.speed = 1 + def spawnAsteroids(self,numberOfAsteroids): + """ + spawns a set number of asteroids in the sides of the game + """ + width, height = self.gameDisplay.get_size() + listOfAsteroids = [] # initializes a list of asteroids to update + listOfRects = [] # initializes a list of hitboxes + sampleAsteroid = LargeAsteroid(0,0,0,0,self.gameDisplay) # a sample asteroid to know where to spawn the asteroids in case we change the size later + smallArea = 100 # the area that asteroids are to spawn around the the edge + for i in range(numberOfAsteroids): + side = random.randint(1,4) + if(side == 1): # left side of the screen + x = random.randint(-sampleAsteroid.w // 2,smallArea - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.h // 2,height - sampleAsteroid.h // 2) + elif(side == 2): # top side of the screen + x = random.randint(-sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.w // 2,smallArea - sampleAsteroid.w // 2) + elif(side == 3): # right side of the screen + x = random.randint(width-smallArea - sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(-sampleAsteroid.w // 2,height - sampleAsteroid.w // 2) + elif(side == 4): # bottom of the screen + x = random.randint(-sampleAsteroid.w // 2,width - sampleAsteroid.w // 2) + y = random.randint(height-smallArea - sampleAsteroid.w // 2,height - sampleAsteroid.w // 2) + direction = random.uniform(0,pi * 2) # initiate each asteroid with a random direction + listOfAsteroids.append(LargeAsteroid(x,y,self.speed,direction,self.gameDisplay)) + listOfRects.append(listOfAsteroids[i].rect) + self.listOfAsteroids = listOfAsteroids + self.listOfRects = listOfRects + def update(self): + """ + updates all the asteroids, deleting them from the list if they are destroyed. + """ + listOfRects = [] # asteroid + ListToDelete = [] # a list that incluedes the indicies of what to delete + for i in range(len(self.listOfAsteroids)): + if(self.listOfAsteroids[i].destroyed): + ListToDelete.append(i) # if the asteroid is destroyed, remember the number to remove it later + else: + self.listOfAsteroids[i].update() + listOfRects.append(self.listOfAsteroids[i].rect) + for j in reversed(ListToDelete): # reversed so that it doesn't delete one and shift mid for loop. + del self.listOfAsteroids[j] + self.listOfRects = listOfRects + def destroyAll(self): + """ + function for testingasteroid, not for the real game + """ + sizeOfAsteroids = range(len(self.listOfAsteroids)) + for i in sizeOfAsteroids: + newAsteroid = self.listOfAsteroids[i].destroy() + if(newAsteroid != None): + self.listOfAsteroids += newAsteroid # destroying all of the asteroids making them medium + for i in sizeOfAsteroids: + self.listOfAsteroids.pop(0) +class Projectile(): + """ + projectiles that fire and destroy asteroids, ufos and players. + x - position x + y - position y + w, h - size of the projectiles + speed - speed of the projectile + direction- direction given to the projectiles + rect - the hitbox of the projectile + gameDisplay - the display + destroyed - senses whether the projectile is destroyed or not + distanceTravelled - detects how far the projectile has travelled + """ + def __init__(self,x,y,direction,alliance,gameDisplay): + size = 3 + self.x = x + self.y = y + self.w = size + self.h = size + self.speed = 10 + self.direction = direction + self.rect = ((self.x,self.y),(size,size)) + self.image = pygame.Surface((size,size)) + self.image.fill((255,255,255)) + self.gameDisplay = gameDisplay + width,height = self.gameDisplay.get_size() + self.destroyed = False + self.distanceTravelled = 0 # asteroids + if(alliance == "Ship"): + self.distanceWanted = 5/8 * height # the distance that the projectile travels before it is destroyed + else: + self.distanceWanted = 3/8 * height + self.alliance = alliance + def update(self): + """ + updates the position of the particle + """ + if(self.distanceTravelled < self.distanceWanted): # if the projectile has travelled farther than the wanted distance, it destroys itself + width, height = self.gameDisplay.get_size() # gets the display's width and length + self.x = self.x + (self.speed * cos(self.direction)) # Sets the speed to a small change in space + self.y = self.y + (self.speed * sin(self.direction)) + self.distanceTravelled += self.speed # updates the disnance travelled + if(self.x >= width): # If the projectile's coordinate goes outside of the window, set that coordinate to the other side of the map + self.x = 0 - self.w # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.x <= 0 - self.w): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.x = width + if(self.y >= height): + self.y = 0 - self.h + elif(self.y <= 0 - self.h): + self.y = height + self.rect = pygame.Rect((self.x,self.y),(self.w,self.h)) + self.gameDisplay.blit(self.image,(self.x,self.y)) # draws the pixel on the screen + #pygame.draw.rect(self.gameDisplay,(0,255,0),self.rect) # display's the projectile's hit box in green (for testing) + else: + self.destroy() # satisfying to right + def destroy(self): + self.destroyed = True +class CollectionOfProjectiles(): + """ + A collection of the Projectiles in the game + listOfProjectiles - a list of the asteroids in the game + listOfRects - a list of the hitboxes + gameDisplay - the display + """ + def __init__(self,gameDisplay): + self.listOfProjectiles = [] #initializes the asteroidprojectiles + self.listOfRects = [] # initializes their hitboxes + self.gameDisplay = gameDisplay + def addProjectile(self,x,y,direction,alliance): + """ + Adds a projectile to the game at the given x y and direction with an alliance of either "UFO" or "Ship" + """ + self.listOfProjectiles.append(Projectile(x,y,direction,alliance,self.gameDisplay)) # The spacebar command should call this + # with the x,y and directions of the ship (with an offset bc of the front of the ship and that the origin is top left) + def update(self): + """ + Updates all of the projectiles + """ + ListToDelete = [] # initializes the indices of what to delete + ListOfRects = [] + for i in range(len(self.listOfProjectiles)): + if(self.listOfProjectiles[i].destroyed): + ListToDelete.append(i) # adding the index of destroyed particles to delete + else: + self.listOfProjectiles[i].update() + ListOfRects.append(self.listOfProjectiles[i].rect) + for j in reversed(ListToDelete): + del self.listOfProjectiles[j] + self.listOfRects = ListOfRects +class UFO(): + """ + A class of the general UFO, that moves autonomously and shoots + (we didn't have enough time to implement a second UFO, so there is only one type of UFO) + x - x position + y - y position + speed - speed of the ufo, constant + destroyed - whether the UFO is destroyed or not + image - the UFO image + w,h - the height and width of the image + FacingRight - the direction the UFO is facing(UFO goes either right to left or left to right)(also determines x position) + counter - for recording the refractory period of the shooting + listOfProjectiles - to allow it to call the add projectile function + straight - whether the UFO goes straight across the screen or down then up + """ + def __init__(self,y,FacingRight,gameDisplay,listOfProjectiles): + self.y = y + self.speed = 2 + self.destroyed = False + self.image = pygame.image.load('UFO.gif').convert() + self.image.set_colorkey((0,0,0)) + self.w, self.h = self.image.get_size() + self.straight = bool(random.getrandbits(1)) + self.FacingRight = FacingRight + self.gameDisplay = gameDisplay + width, height = gameDisplay.get_size() + self.counter = 0 + self.listOfProjectiles = listOfProjectiles + if(FacingRight): + self.x = -self.w + else: + self.x = width + def update(self): + """ + updates the position of the UFO, as well as deciding when to shoot + """ + if(self.counter % self.fireRate == 0): + self.shoot() + self.counter += 1 + width, height = self.gameDisplay.get_size() + if(self.straight == True): # sometimes it goes straight accross, sometimes down then up + if(self.FacingRight): + self.direction = 0 + else: + self.direction = pi + else: + if(self.FacingRight): # algorithm for going down then up + if((self.x + self.w / 2) < width * 1 / 4): + self.direction = 0 + elif(self.x + self.w / 2 < width / 2): + self.direction = pi / 4 + else: + self.direction = - pi / 4 + else: + if((self.x + self.w / 2) > width * 3 / 4): + self.direction = pi + elif(self.x + self.w / 2 > width / 2): + self.direction = 5 * pi / 4 + else: + self.direction = 3 * pi / 4 + self.x = self.x + (self.speed * cos(self.direction)) # Sets the speed to a small change in space + self.y = self.y + (self.speed * sin(self.direction)) + if(self.x >= width and self.FacingRight): # if the UFO goes out of the screen, destroy it + self.destroyed = True + elif(self.x <= 0 - self.w and not self.FacingRight): + self.destroyed = True + if(self.y >= height): # If the UFOs coordinate goes outside of the window, set that coordinate to the other side of the map + self.y = 0 - self.h # adding the width of the image to make sure that the image doesn't appear suddenly (the image's position is the top right of the image) + elif(self.y <= 0 - self.h): # same as above (makes it so that the whole image has to leave the screen for it to go to the other side) + self.y = height + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + #pygame.draw.rect(self.gameDisplay,(0,0,255),self.rect) # display's the UFO's hit box in blue (for testing) + self.gameDisplay.blit(self.image,(self.x,self.y)) + def destroy(self): + """ + destroys the UFO + """ + self.destroyed = True +class BigUFO(UFO): + """ + A subclass of UFO that shoots in a random direction and moves either straight or down then up + shrinkage - how much to shrink the UFO image + fireRate - cooldown for how often the UFO fires + """ + def __init__(self,y,FacingRight,gameDisplay,listOfProjectiles): + super().__init__(y,FacingRight,gameDisplay,listOfProjectiles) + self.image = pygame.transform.scale(self.image,(self.w // 2,self.h // 2)) + self.w,self.h = self.image.get_size() + self.shrinkage = 30 + self.rect = pygame.Rect((self.x + self.shrinkage / 2,self.y + self.shrinkage / 2),(self.w - self.shrinkage,self.h - self.shrinkage)) + self.fireRate = 60 + def shoot(self): + """ + shoots a projectile + """ + if(not self.destroyed): + self.listOfProjectiles.addProjectile(self.x + self.w / 2,self.y + self.h / 2,random.uniform(0,2*pi),"UFO") +class CollectionOfUFOs(): + """ + A collection of the UFOs on the screen(there can only be one UFO, but this allows for an opportunity to add more if the game is too easy) + listOfUFOs - the list of the UFOs on screen + listOfRects - the list of the hitboxes of UFOs + gameDisplay - display + listOfProjectiles - the list of projectiles on screen so UFOs can shoot + """ + def __init__(self,gameDisplay,listOfProjectiles): + self.listOfUFOs = [] #initializes the projectiles + self.listOfRects = [] # initializes their hitboxes + self.gameDisplay = gameDisplay + self.listOfProjectiles = listOfProjectiles + def spawnBigUFO(self): + """ + Spawns a big UFO in the game (would have been complemented by a spawnSmallUFO if time alloted) + """ + width, height = self.gameDisplay.get_size() + sampleUFO = BigUFO(0,True,self.gameDisplay,self.listOfProjectiles) + y = random.randint(-sampleUFO.h // 2,height - sampleUFO.h // 2) + facingRight = bool(random.getrandbits(1)) + self.listOfUFOs.append(BigUFO(y,facingRight,self.gameDisplay,self.listOfProjectiles)) + def update(self): + """ + updates the list of UFOs + """ + listOfRects = [] + ListToDelete = [] # initializes the indices of what to delete + for i in range(len(self.listOfUFOs)): + if(self.listOfUFOs[i].destroyed): + ListToDelete.append(i) # adding the index of destroyed particles to delete + else: + self.listOfUFOs[i].update() + listOfRects.append(self.listOfUFOs[i].rect) + for j in reversed(ListToDelete): + del self.listOfUFOs[j] + self.listOfRects = listOfRects +class listOfObjects(): + """ + List of all objects in the game: + gameDisplay - display + Asteroids - the collection of Asteroids + Projectiles - the collection of Projectiles + UFOs - the collection of UFOs + ship - the ship in the game + """ + def __init__(self,gameDisplay, ship): + self.gameDisplay = gameDisplay + self.Asteroids = CollectionOfAsteroids(gameDisplay) + self.Projectiles = CollectionOfProjectiles(gameDisplay) # contains the CollectionOfAsteroids and CollectionOfProjectiles objects + self.UFOs = CollectionOfUFOs(gameDisplay,self.Projectiles) + self.ship = ship + def update(self): + """ + updates all objects and handles any collinion detection between objects + """ + self.Asteroids.update() + self.UFOs.update() + self.Projectiles.update() + collisionsAster = self.ship.rect.collidelist(self.Asteroids.listOfRects) # detects if any of the asteroids are in contact with the projectile + if (collisionsAster != -1 and self.ship.destroyed == False): # if there is a collision + self.Asteroids.listOfAsteroids += self.Asteroids.listOfAsteroids[collisionsAster].destroy() #destroy both the asteroid and the projectile. + self.ship.destroy() + collisionsUFO = self.ship.rect.collidelist(self.UFOs.listOfRects) + if (collisionsUFO != -1 and self.ship.destroyed == False): # if there is a collision + #self.UFOs.listOfUFOs[collisionsUFO].destroy() #destroy both the asteroid and the projectile. + self.ship.destroy() + collisionsProj = self.ship.rect.collidelist(self.Projectiles.listOfRects) + if (collisionsProj != -1 and self.Projectiles.listOfProjectiles[collisionsProj].alliance != "Ship" and self.ship.destroyed == False): + self.Projectiles.listOfProjectiles[collisionsProj].destroy() + self.ship.destroy() + for i in self.Projectiles.listOfProjectiles: # runs through each projectile + collisionsAster = i.rect.collidelist(self.Asteroids.listOfRects) # detects if any of the asteroids are in contact with the projectile + if (collisionsAster != -1): # if there is a collision + if(i.alliance == "Ship"): + if(self.Asteroids.listOfAsteroids[collisionsAster].type == "Large"): + self.ship.score += 20 + elif(self.Asteroids.listOfAsteroids[collisionsAster].type == "Medium"): + self.ship.score += 50 + elif(self.Asteroids.listOfAsteroids[collisionsAster].type == "Small"): + self.ship.score += 100 + self.Asteroids.listOfAsteroids += self.Asteroids.listOfAsteroids[collisionsAster].destroy() #destroy both the asteroid and the projectile. + i.destroy() + collisionsUFO = i.rect.collidelist(self.UFOs.listOfRects) + if (collisionsUFO != -1 and i.alliance != "UFO"): # if there is a collision + self.UFOs.listOfUFOs[collisionsUFO].destroy() #destroy both the asteroid and the projectile. + i.destroy() + self.ship.score += 500 + for i in self.UFOs.listOfUFOs: + collisionsAster = i.rect.collidelist(self.Asteroids.listOfRects) # detects if any of the asteroids are in contact with the projectile + if (collisionsAster != -1): # if there is a collision + self.Asteroids.listOfAsteroids += self.Asteroids.listOfAsteroids[collisionsAster].destroy() #destroy both the asteroid and the projectile. + i.destroy() diff --git a/interface.py b/interface.py new file mode 100644 index 00000000..f141f56b --- /dev/null +++ b/interface.py @@ -0,0 +1,117 @@ +""" +Asteroids via Pygame + +@authors coreyacl & nathanestill + +""" + +import pygame +from math import cos,sin,sqrt,radians,atan,pi +from classes import * +import time +import random + + +""" initiate pygame """ +pygame.init() + +""" initiate screen """ +display_width = 1600 +display_height = 1200 +imgScale = .13 +gameDisplay = pygame.display.set_mode((display_width,display_height)) +pygame.display.set_caption("Asteroids") +black = (0,0,0) +white = (255,255,255) + +""" some other important things """ +clock = pygame.time.Clock() +running = True + +""" initiate ship object """ +shipImg = pygame.image.load('spa1.png') +shipX = (display_width * .5) +shipY = (display_height * .5) +w,h = shipImg.get_size() +shipImg = pygame.transform.scale(shipImg,(int(w*imgScale),int(h*imgScale))) +ship = Ship(shipX,shipY,270,shipImg,gameDisplay) + +""" initiate asteroids and UFOs """ +numberOfAsteroids = 4 +AllThings = listOfObjects(gameDisplay,ship) +AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) +AllThings.UFOs.spawnBigUFO() +AllThings.update() # testing the many asteroids spawn in the right spot + +""" initiate GUI """ +gui = GUI(gameDisplay) + +""" initialize the ship to be able to shoot""" +canShoot = True +shoot_count = 20 + +while running: + + """ listens to events and does stuff """ + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + # print(event) + if event.type == pygame.KEYUP: + if event.key == pygame.K_q: + running = False + if event.key == pygame.K_t: + ship.lives += 1 + if event.key == pygame.K_r: + if shoot_count == 20: + shoot_count = 2 + elif shoot_count == 2: + shoot_count = 20 + """ player interface """ + keys_pressed = pygame.key.get_pressed() + if keys_pressed[pygame.K_UP]: + ship.move() + else: + ship.drift = True + if keys_pressed[pygame.K_LEFT]: + ship.rotate(1) + if keys_pressed[pygame.K_RIGHT]: + ship.rotate(-1) + if(canShoot): + counter = 0 + else: + counter += 1 + if(counter >= shoot_count): + canShoot = True + if keys_pressed[pygame.K_SPACE]: + if(canShoot): + ship.shoot(AllThings) + canShoot = False + counter = 0 + """ UFO spawning """ + randomInt = random.randint(1,1000) + if(randomInt == 1 and len(AllThings.UFOs.listOfUFOs) == 0): + AllThings.UFOs.spawnBigUFO() + + """ spawns new level """ + if(len(AllThings.Asteroids.listOfAsteroids) == 0 and len(AllThings.UFOs.listOfUFOs) == 0): + numberOfAsteroids += 1 + AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) + + + + gameDisplay.fill(black) + + gui.update(ship) + if ship.lives > 0: + ship.update() + AllThings.update() + + if ship.lives == 0: + gui.gameOver(ship) + + pygame.display.update() + clock.tick(60) + +pygame.quit() +quit() diff --git a/keepInMind.md b/keepInMind.md new file mode 100644 index 00000000..71e619f3 --- /dev/null +++ b/keepInMind.md @@ -0,0 +1,47 @@ +# Things to keep in mind when programming game +@author Corey Cochran-Lepiz & Nathan Estill + +### Objects +1. Player + * Needs to receive movement + * Needs to shoot projectile +2. Arena + * Doesn't actually end. Loops on itself (boundless but finite) +3. HUD + * High score and # of lives left +4. Projectile + * Limited to short burst +5. Asteroid + * Small, Medium, and Large +6. UFO + * Small, and Large + +### Notes +1. Asteroids spawn + * Asteroids kill all + * Except for themselves + * Float through each other +2. Player can shoot self + * Careful about spawning. Don't want to spawn into an asteroid +3. Asteroid speed + * Gets faster after breakup +4. Number of asteroids/level??? +5. Projectile + * Travels ~3/4 of map length +6. Collision! + * UFO wins against player + * Player and asteroid lose together + * UFO and asteroid lose together +7. BIG UFO + * Travels from one side to the other + * Down diagonally then up until out of play space +8. Smol UFO + * One side to other in straight line + +### Points! +Large asteroid = 20 +Medium asteroid = 50 +Smol asteroid = 100 +BIG UFO = 200 +smol UFO = 1,000 +Life +1 after 10,000 diff --git a/qbert.py b/qbert.py new file mode 100644 index 00000000..f141f56b --- /dev/null +++ b/qbert.py @@ -0,0 +1,117 @@ +""" +Asteroids via Pygame + +@authors coreyacl & nathanestill + +""" + +import pygame +from math import cos,sin,sqrt,radians,atan,pi +from classes import * +import time +import random + + +""" initiate pygame """ +pygame.init() + +""" initiate screen """ +display_width = 1600 +display_height = 1200 +imgScale = .13 +gameDisplay = pygame.display.set_mode((display_width,display_height)) +pygame.display.set_caption("Asteroids") +black = (0,0,0) +white = (255,255,255) + +""" some other important things """ +clock = pygame.time.Clock() +running = True + +""" initiate ship object """ +shipImg = pygame.image.load('spa1.png') +shipX = (display_width * .5) +shipY = (display_height * .5) +w,h = shipImg.get_size() +shipImg = pygame.transform.scale(shipImg,(int(w*imgScale),int(h*imgScale))) +ship = Ship(shipX,shipY,270,shipImg,gameDisplay) + +""" initiate asteroids and UFOs """ +numberOfAsteroids = 4 +AllThings = listOfObjects(gameDisplay,ship) +AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) +AllThings.UFOs.spawnBigUFO() +AllThings.update() # testing the many asteroids spawn in the right spot + +""" initiate GUI """ +gui = GUI(gameDisplay) + +""" initialize the ship to be able to shoot""" +canShoot = True +shoot_count = 20 + +while running: + + """ listens to events and does stuff """ + for event in pygame.event.get(): + if event.type == pygame.QUIT: + running = False + # print(event) + if event.type == pygame.KEYUP: + if event.key == pygame.K_q: + running = False + if event.key == pygame.K_t: + ship.lives += 1 + if event.key == pygame.K_r: + if shoot_count == 20: + shoot_count = 2 + elif shoot_count == 2: + shoot_count = 20 + """ player interface """ + keys_pressed = pygame.key.get_pressed() + if keys_pressed[pygame.K_UP]: + ship.move() + else: + ship.drift = True + if keys_pressed[pygame.K_LEFT]: + ship.rotate(1) + if keys_pressed[pygame.K_RIGHT]: + ship.rotate(-1) + if(canShoot): + counter = 0 + else: + counter += 1 + if(counter >= shoot_count): + canShoot = True + if keys_pressed[pygame.K_SPACE]: + if(canShoot): + ship.shoot(AllThings) + canShoot = False + counter = 0 + """ UFO spawning """ + randomInt = random.randint(1,1000) + if(randomInt == 1 and len(AllThings.UFOs.listOfUFOs) == 0): + AllThings.UFOs.spawnBigUFO() + + """ spawns new level """ + if(len(AllThings.Asteroids.listOfAsteroids) == 0 and len(AllThings.UFOs.listOfUFOs) == 0): + numberOfAsteroids += 1 + AllThings.Asteroids.spawnAsteroids(numberOfAsteroids) + + + + gameDisplay.fill(black) + + gui.update(ship) + if ship.lives > 0: + ship.update() + AllThings.update() + + if ship.lives == 0: + gui.gameOver(ship) + + pygame.display.update() + clock.tick(60) + +pygame.quit() +quit() diff --git a/spa.png b/spa.png new file mode 100644 index 00000000..ed418780 Binary files /dev/null and b/spa.png differ diff --git a/spa1.png b/spa1.png new file mode 100644 index 00000000..789fa768 Binary files /dev/null and b/spa1.png differ