I am reading this book for Python called "More Python Programming for the Absolute Beginner" and in chapter 4, you create this bomb catching game. I am trying to modify it so you shoot at the bomb or enemy.
I want to be able to draw the bullets using
pygame.draw.circle()
and so it shoots when you click on the right mouse button, then it hits the enemy and it adds on your score.
By the way - I already know how to add the score on I just need help with shooting the shape :)
if you want to have a look at the original game source file, go here -
http://www.delmarlearning.com/companions/content/1435459806/relatedfiles/index.asp?isbn=1435459806
and it's in chapter 4, "BombCatcher"
original source code-
# Bomb Catcher Game
# Chapter 4
import sys, random, time, pygame
from pygame.locals import *
def print_text(font, x, y, text, color=(255,255,255)):
imgText = font.render(text, True, color)
screen.blit(imgText, (x,y))
#main program begins
pygame.init()
screen = pygame.display.set_mode((600,500))
pygame.display.set_caption("Bomb Catching Game")
font1 = pygame.font.Font(None, 24)
pygame.mouse.set_visible(False)
white = 255,255,255
red = 220, 50, 50
yellow = 230,230,50
black = 0,0,0
lives = 3
score = 0
clock_start = 0
game_over = True
mouse_x = mouse_y = 0
pos_x = 300
pos_y = 460
bomb_x = random.randint(0,500)
bomb_y = -50
vel_y = 0.7
#repeating loop
while True:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
elif event.type == MOUSEMOTION:
mouse_x,mouse_y = event.pos
move_x,move_y = event.rel
elif event.type == MOUSEBUTTONUP:
if game_over:
game_over = False
lives = 3
score = 0
keys = pygame.key.get_pressed()
if keys[K_ESCAPE]:
sys.exit()
screen.fill((0,0,100))
if game_over:
print_text(font1, 100, 200, "<CLICK TO PLAY>")
else:
#move the bomb
bomb_y += vel_y
#has the player missed the bomb?
if bomb_y > 500:
bomb_x = random.randint(0, 500)
bomb_y = -50
lives -= 1
if lives == 0:
game_over = True
#see if player has caught the bomb
elif bomb_y > pos_y:
if bomb_x > pos_x and bomb_x < pos_x + 120:
score += 10
bomb_x = random.randint(0, 500)
bomb_y = -50
#draw the bomb
pygame.draw.circle(screen, black, (bomb_x-4,int(bomb_y)-4), 30, 0)
pygame.draw.circle(screen, yellow, (bomb_x,int(bomb_y)), 30, 0)
#set basket position
pos_x = mouse_x
if pos_x < 0:
pos_x = 0
elif pos_x > 500:
pos_x = 500
#draw basket
pygame.draw.rect(screen, black, (pos_x-4,pos_y-4,120,40), 0)
pygame.draw.rect(screen, red, (pos_x,pos_y,120,40), 0)
#print # of lives
print_text(font1, 0, 0, "LIVES: " + str(lives))
#print score
print_text(font1, 500, 0, "SCORE: " + str(score))
pygame.display.update()
To check if a button was pressed, you should check for an event.type : MOUSEBUTTONDOWN,and then check for the RMB.
In order to shoot, you need some sort of a list, so that there will be many bullets possible.
You can create a simple Bullet class, that will have a draw and move functions.
An example:
class Bullet:
def __init__(self,position):
self.x,self.y = position
def move(self):
self.position += 5
def draw(self,screen):
pygame.draw.circle(screen, black, x, y, 30, 0)
This covers the drawing, and movement of your bullets. Apart from this, you should check if bullets do not go off screen, and if they do, remove them from the list.
Collision can be done in many ways. Pygame has a built in method called collide_rect, which checks if 2 rectangles are overlapping. But since you have 2 circles, the method is a bit different.
Since you have 2 circles, you have a distance between them. It's given by this equation:
D = sqrt((xa-xb)^2+(ya-yb)^2))
If the distance is smaller than R1 + R2, then the circles are overlapping. You can then proceed to remove the bullet as well as the bomb.
We can simplify this to:
(R1+R2)^2 > (xa-xb)^2+(ya-yb)^2)
Related
I've tried to add gravity using the quadratic formula but didn't seem to work and I tried to turn it into a function and that still didn't seem to work. I tried using a class but I don't quite understand how they work so can someone help me add gravity to my platformer. I've tried many videos but didn't seem to help so any help would be much appreciated
import pygame
from pygame.locals import *
from pygame import mixer
from typing import Tuple
import pickle
from os import path
import time
import sys
import os
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
screen.fill((white))
pygame.display.set_caption("Darsh's Game")
pygame.display.update()
pygame.draw.rect(screen,white,(200,150,100,50))
running = 1
speed = 1
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
pygame.display.flip()
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_rect()
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
screen.blit(mainright_img, (300, 400))
run = True
while run:
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > speed - 16:
x -= speed
left = True
right = False
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
draw_text("George is dumb", font, green, 100, 400)
pygame.display.update()
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
if keys[pygame.K_SPACE]: # jumping code
isJump = True
else:
if jumpCount >= -10:
time.sleep(0.02)
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
direction(left, right)
draw_text("George is dumb", font, green, 100, 400)
draw_rect()
pygame.display.update()
You don't need the quadratic formula to get a simple gravity going; a simple gravity variable increasing every frame should do the trick.
...
gravity = 0
while run:
...
...
if keys[pygame.K_SPACE]:
gravity += 1
y -= gravity
...
Note that I don't speak your language; I mainly code in JavaScript, but I did try python once and I got the syntax down pretty quick. Hopefully this suits your needs, with a bit of tweaking of course.
Add a variable fall = 0. Increment the variable if the player does not jump (fall += 0.2). Change the y coordinate of the player through the variable fall in each frame. Get the bounding rectangle of the player with pygame.Surface.get_rect(). If the bottom of the rectangle reaches the floor then stop falling, and restricting the bottom of the rectangle on the floor. Set fall = 0 if the player starts to jump:
while run:
# [...]
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
# [...]
Multiple calls to pygame.display.update() or pygame.display.flip() cause flickering. Remove all calls to pygame.display.update() from your code, but call it once at the end of the application loop.
In addition, draw each object only once.
Do not delay, wait or sleep. Use pygame.time.Clock to control the frames per second and thus the game speed.
The method tick() of a pygame.time.Clock object, delays the game in that way, that every iteration of the loop consumes the same period of time. See pygame.time.Clock.tick():
This method should be called once per frame.
That means that the loop:
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
runs 60 times per second.
See pygame.time.Clock.tick():
This method should be called once per frame. It will compute how many milliseconds have passed since the previous call.
Complete example:
import pygame
from pygame.locals import *
pygame.init()
white = 255,255,255
black = 0,0,0
green = 0, 255, 0
blue = 0,0,255
screen_width = 650
screen_height = 800
screen = pygame.display.set_mode([screen_width,screen_height])
pygame.display.set_caption("Darsh's Game")
font = pygame.font.SysFont('Comic Sans MS', 50, 70)
running = 6
speed = 2
fall = 0
x = 25
y = 669
isJump = False
jumpCount = 10
left = False
right = True
def draw_text(text, font, text_col, x, y):
img = font.render(text, True, text_col)
screen.blit(img, (x, y))
def draw_rect():
rect1 = pygame.Rect(0, 0, 25, 800)
pygame.draw.rect(screen, black, rect1)
rect2 = pygame.Rect(625, 0, 25, 800)
pygame.draw.rect(screen, black, rect2)
rect3 = pygame.Rect(0, 775, 650, 25)
pygame.draw.rect(screen, green, rect3)
def direction(left, right):
if left == True:
screen.blit(mainleft_img, (x, y))
elif right == True:
screen.blit(mainright_img, (x, y))
draw_text("George is dumb", font, green, 100, 400)
mainright_img = pygame.image.load('newgameimg/mainright.png')
mainleft_img = pygame.image.load('newgameimg/mainleft.png')
clock = pygame.time.Clock()
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and x > speed - 16:
x -= speed
left = True
right = False
if keys[pygame.K_d] and x < screen_width - speed - 89:
x += speed
left = False
right = True
if keys[pygame.K_ESCAPE]:
pygame.quit()
if not (isJump):
fall += 0.2
y += fall
player_rect = mainleft_img.get_rect(topleft = (x, y))
if player_rect.bottom > 775:
player_rect.bottom = 775
y = player_rect.top
fall = 0
if keys[pygame.K_SPACE]: # jumping code
isJump = True
fall = 0
else:
if jumpCount >= -10:
y -= (jumpCount * abs(jumpCount)) * 0.5
jumpCount -= 1
else:
jumpCount = 10
isJump = False # jumping code
screen.fill(white)
draw_rect()
direction(left, right)
pygame.display.update()
I am working on a pong game. When either of the scores hit 10, it is supposed to put up some text on the screen and say the right player has won or left player has won. However, in my program, it isn't working. When it has to show the text that the right or left player has won, it doesn't show it. But it works for everything else. Here is the code:
# Importing libraries
import pygame
import random
import time
# Initializing PyGame
pygame.init()
# Setting a window name
pygame.display.set_caption("Ping Pong")
# Creating a font
pygame.font.init()
font = pygame.font.SysFont(None, 30)
pong_font = pygame.font.SysFont("comicsansms", 75)
# Set the height and width of the screen
window_width = 700
window_height = 500
size = [window_width, window_height]
game_win = pygame.display.set_mode(size)
game_win2 = pygame.display.set_mode(size)
# Creating a messaging system
def message(sentence, color, x, y, font_type, display):
sentence = font_type.render(sentence, True, color)
display.blit(sentence, [x, y])
# Creating colors
white = (225, 225, 225)
black = (0, 0, 0)
gray = (100, 100, 100)
# Setting up ball
ball_size = 25
class Ball:
"""
Class to keep track of a ball's location and vector.
"""
def __init__(self):
self.x = 0
self.y = 0
self.change_x = 0
self.change_y = 0
def make_ball():
ball = Ball()
# Starting position of the ball.
ball.x = 350
ball.y = 250
# Speed and direction of rectangle
ball.change_x = 5
ball.change_y = 5
return ball
def main():
# Scores
left_score = 0
right_score = 0
pygame.init()
# Loop until the user clicks the close button.
done = False
ball_list = []
ball = make_ball()
ball_list.append(ball)
# Right paddle coordinates
y = 200
y_change = 0
x = 50
# Left paddle coordinates
y1 = 200
y1_change = 0
x1 = 650
while not done:
# --- Event Processing
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
y_change = -7
elif event.key == pygame.K_s:
y_change = 7
elif event.key == pygame.K_UP:
y1_change = -7
elif event.key == pygame.K_DOWN:
y1_change = 7
elif event.type == pygame.KEYUP:
y_change = 0
y1_change = 0
y += y_change
y1 += y1_change
# Preventing from letting the paddle go off screen
if y > window_height - 100:
y -= 10
if y < 50:
y += 10
if y1 > window_height - 100:
y1 -= 10
if y1 < 50:
y1 += 10
# Logic
for ball in ball_list:
# Move the ball's center
ball.x += ball.change_x
ball.y += ball.change_y
# Bounce the ball if needed
if ball.y > 500 - ball_size or ball.y < ball_size:
ball.change_y *= -1
if ball.x > window_width - ball_size:
ball.change_x *= -1
left_score += 1
if ball.x < ball_size:
ball.change_x *= -1
right_score += 1
ball_rect = pygame.Rect(ball.x - ball_size, ball.y - ball_size, ball_size * 2, ball_size * 2)
left_paddle_rect = pygame.Rect(x, y, 25, 75)
if ball.change_x < 0 and ball_rect.colliderect(left_paddle_rect):
ball.change_x = abs(ball.change_x)
right_paddle_rect = pygame.Rect(x1, y1, 25, 75)
if ball.change_x > 0 and ball_rect.colliderect(right_paddle_rect):
ball.change_x = -abs(ball.change_x)
# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else
if right_score == 10:
message("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
elif left_score == 10:
message("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
time.sleep(5)
pygame.quit()
quit()
# Drawing
# Set the screen background
game_win.fill(black)
# Draw the balls
for ball in ball_list:
pygame.draw.circle(game_win, white, [ball.x, ball.y], ball_size)
# Creating Scoreboard
message("Left player score: " + str(left_score), white, 10, 10, font, game_win)
message("Right player score: " + str(right_score), white, 490, 10, font, game_win)
# Drawing a left paddle
pygame.draw.rect(game_win, white, [x, y, 25, 100])
# Drawing a right paddle
pygame.draw.rect(game_win, white, [x1, y1, 25, 100])
# Setting FPS
FPS = pygame.time.Clock()
FPS.tick(60)
# Updating so actions take place
pygame.display.flip()
while True:
game_win2.fill(black)
pygame.event.get()
mouse = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
message("Pong", white, 280, 100, pong_font, game_win2)
if 150 + 100 > mouse[0] > 150 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [150, 350, 100, 50])
if click[0] == 1:
break
else:
pygame.draw.rect(game_win, white, [150, 350, 100, 50])
if 450 + 100 > mouse[0] > 450 and 350 + 50 > mouse[1] > 350:
pygame.draw.rect(game_win, gray, [450, 350, 100, 50])
if click[0] == 1:
pygame.quit()
quit()
else:
pygame.draw.rect(game_win, white, [450, 350, 100, 50])
message("Start", black, 175, 367, font, game_win2)
message("Quit", black, 475, 367, font, game_win2)
# Go ahead and update the screen with what we've drawn.
pygame.display.flip()
# Wrap-up
# Limit to 60 frames per second
clock = pygame.time.Clock()
clock.tick(60)
if __name__ == "__main__":
main()
I have added a little comment, it is: "# Here is the where the messaging system doesn't work, I don't know why! It works fine for everything else". Now when someone scores 10 points, Nothing happens. Its= wait for a couple of seconds. That is so you can read the "Left player has won" or "Right player has won" before the program closes. But it simply doesn't show up! I don't know why! Can someone help with this?
The display is updated only if either pygame.display.update() or pygame.display.flip()
is called. See pygame.display.flip():
This will update the contents of the entire display.
Further you've to handles the events with pygame.event.pump(), before the update of the display becomes visible in the window.
See pygame.event.pump():
For each frame of your game, you will need to make some sort of call to the event queue. This ensures your program can internally interact with the rest of the operating system.
If you want to display a text and delay the game, then you've to update the display and handle the events.
Write a function which delays the game and updates the display. I recommend to use the pygame.time module to implement the delay (e.g. pygame.time.delay())
def update_and_wait(delay):
pygame.display.flip()
pygame.event.pump()
pygame.time.delay(delay * 1000) # 1 second == 1000 milliseconds
Or even implement a function which its own event loop to keep the application responding. Measure the time by pygame.time.get_ticks():
def update_and_wait(delay):
start_time = pygame.time.get_ticks()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
print("auit")
pygame.quit()
return False
if pygame.time.get_ticks() >= start_time + delay * 1000:
break
return True
Use the function in the application:
def main():
# [...]
while not done:
# [...]
for ball in ball_list:
# [...]
if right_score == 0:
message_wait("RIGHT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()
elif left_score == 0:
message_wait("LEFT PLAYER HAS WON!!", white, 300, 200, font, game_win)
update_and_wait(5)
quit()
for some reason when I collide with the enemy it randomly spawns elsewhere which is all fine but when I un-collide the enemy goes back to its original position
here is the full code down below. If you run this code see what happens. Im looking for the red block to spawn in a random other location when the blue box collides with it.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
# draw player
def draw():
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy = pygame.draw.rect(screen, enemy_color, (new_x, new_y, 25, 25))
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
# pygame loop (always include)
running = True
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_x -= 10
if event.key == pygame.K_RIGHT:
player_x += 10
if event.key == pygame.K_UP:
player_y -= 10
if event.key == pygame.K_DOWN:
player_y += 10
draw()
pygame.display.update()
screen.fill(background_color)
clock.tick(fps)
The draw method explicitly states to draw enemy rect either in the initial point or in some ONE random point. The random point is selected only once - during the start of an application. You should set the random point every time there is a collision in a draw method, e.g.
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
You have to create a new random enemy position when the player collides with the enemy
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
Use the global statement, to interpreted the variables enemy_x and enemy_y as global. With the statement global it is possible to write to variables in the global namespace within a function:
global enemy_x, enemy_y
Function draw:
def draw():
global enemy_x, enemy_y
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
if player_rect.colliderect(enemy_rect):
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
As others have said you either need to add more "locations" to the list or better, use random.randint(), and also enemy's coordinates does not "get random"(or updated in any way) after every collision, but only once when you initialize them.
I added new function get_new_coord() which will return the random number(I use these limits so the enemy won't go off the screen), which gets called once for every coordinate after each collision.
Also I moved player_rect and enemy_rect outside of the loop, and now draw() returns enemy_rect so it can stay updated if collision occurs.
This is a quick fix to your code, you should consider using Classes. And of course if you want to do so, I could help you with transforming the code.
import pygame
import random
# places where enemies can spawn (2 to make it simple at first)
enemy_locations = [100, 200]
pygame.init()
# clock
clock = pygame.time.Clock()
# frames per second
fps = 30
# colors
background_color = (255, 255, 255)
player_color = (0, 0, 255)
enemy_color = (255, 0, 0)
# width and height of screen
width = 1000
height = 800
# screen
screen = pygame.display.set_mode((width, height))
# x, y coordinates player
player_x = 300
player_y = 300
# ememy x, y coordinates
enemy_x = random.choice(enemy_locations)
enemy_y = random.choice(enemy_locations)
# new x, y coordinates for enemy player
new_x = random.choice(enemy_locations)
new_y = random.choice(enemy_locations)
def get_new_coord():
return random.randint(0, 800-25)
# draw player
def draw(player_rect, enemy_rect):
screen.fill(background_color)
if player_rect.colliderect(enemy_rect):
enemy_rect = pygame.Rect(get_new_coord(), get_new_coord(), 25, 25)
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
else:
enemy = pygame.draw.rect(screen, enemy_color, enemy_rect)
player = pygame.draw.rect(screen, player_color, player_rect)
pygame.display.update()
return enemy_rect
# pygame loop (always include)
running = True
enemy_rect = pygame.Rect(enemy_x, enemy_y, 25, 25)
player_rect = pygame.Rect(player_x, player_y, 25, 25)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
# controls for player object
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player_rect.x -= 10
if event.key == pygame.K_RIGHT:
player_rect.x += 10
if event.key == pygame.K_UP:
player_rect.y -= 10
if event.key == pygame.K_DOWN:
player_rect.y += 10
enemy_rect = draw(player_rect, enemy_rect)
clock.tick(fps)
I want to know if my enemy is within 200 pixels of a defense tower so that I can start taking lives of the enemy. The enemy is moving and the defense is still FYI. if anyone can give me advice on how to do this that would be amazing. If I put my code up it will just confuse everyone because my code is very messy so just give me advice on how to do it thanks. Nick. I have added my code because I know I have done something wrong if anyone has the time to read through it and tell me what I am doing wrong which is probably everything that would be much appreciated.
import pygame
import math
from pygame.locals import *
def text():
font = pygame.font.SysFont("monospace", 14)
text = font.render("Start Round", True, black)
textpos = text.get_rect()
textpos.center = (790,675)
Background.blit(text, textpos)
def newRound():
pos = pygame.mouse.get_pos()
click = pygame.mouse.get_pressed()
if 730 < pos[0] < 850 and 650 < pos[1] < 800:
pygame.draw.rect(Background, (150,150,150), (730,650,120,50))
if click[0] == 1:
startGame()
else:
pygame.draw.rect(Background, (100,100,100), (730,650,120,50))
def startGame():
global startRound, endRound, intro
intro = 0
createRound()
intro = 1
startRound = True
endRound = False
def lifeText(lifes):
font = pygame.font.SysFont("monospace", 20)
text = font.render("Lives %s" % (lifes) , True, black)
textpos = text.get_rect()
textpos.center = (60,30)
Background.blit(text, textpos)
def life(self):
global hit, endRound, startRound, noEnemies, lifes
if noEnemies == 0 and lifes > 0:
startRound = False
endRound = True
if self.rect.x == 960:
hit = hit + 1
lifes = lifes - 1
if lifes == 0:
print("You have 0 lives Game Over")
pygame.quit()
if hit == 4:
startRound = False
endRound = True
hit = 0
noEnemies = noEnemies + 1
def createRound():
global enemies, noEnemies
enemies = []
x = -40
y = 210
for e in range(noEnemies):
x = x - 80
enemies.append(yellowEnemy(x, y, Background))
noEnemies = len(enemies)
def displayTower():
for tower in towers:
Background.blit(redtower, (tower))
class yellowEnemy(object):
image1 = pygame.image.load("enemySpriteFullHealth.jpg")
image2 = pygame.image.load("enemySpriteHalfHealth.jpg")
image3 = pygame.image.load("enemySpriteDead.jpg")
def __init__(self, x, y, Background):
self.Background = Background
self.Background_rect = Background.get_rect()
self.rect = self.image1.get_rect()
self.rect = self.image2.get_rect()
self.rect = self.image3.get_rect()
self.rect.x = x
self.rect.y = y
self.health = 20
self.dist_x = 2
self.dist_y = 0
def update(self):
self.rect.x += self.dist_x
self.rect.y += self.dist_y
def draw(self, Background):
timeDead = 0
if self.health > 9 and self.health < 21:
Background.blit(self.image1, self.rect)
elif self.health < 10 and self.health > 1:
Background.blit(self.image2, self.rect)
elif self.health < 1:
Background.blit(self.image3, self.rect)
self.dist_x = 0
life(self)
pygame.init()
width = 960
height = 720
black = (0,0,0)
lifes = 10
hit = 0
intro = 1
FPS = 200
noEnemies = 4
bx = 1000
by = 1000
towers = []
endRound = True
startRound = False
clicked = False
mx, my = pygame.mouse.get_pos()
clock = pygame.time.Clock()
test= False
mapImg = pygame.image.load("mapimage.jpg")
redtower = pygame.image.load("redTower.jpg")
Background = pygame.display.set_mode((width, height))
Background_rect = Background.get_rect()
while intro == 1:
mousePos = pygame.mouse.get_pos()
mousePressed = pygame.mouse.get_pressed()
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
if 530 < mousePos[0] < 590 and 650 < mousePos[1] < 710:
if mousePressed[0] == 1:
clicked = True
if clicked == True:
mx, my = pygame.mouse.get_pos()
pygame.display.update()
bx = 30
by = 30
if mousePressed[0] == 0:
clicked = False
tx = mx - bx
ty = my - by
towerCords = tx, ty
towers.append(towerCords)
if endRound == True:
Background.blit(mapImg, (0,0))
newRound()
text()
if startRound == True:
for enemy in enemies:
enemy.update()
Background.blit(mapImg, (0,0))
for enemy in enemies:
enemy.draw(Background)
Background.blit(redtower, (mx-bx, my-by))
if clicked == True:
pygame.draw.circle(Background, (220, 0, 0), (mx, my), 200, 4)
displayTower()
lifeText(lifes)
Background.blit(redtower, (530,650))
pygame.display.update()
clock.tick(FPS)
To find the distance between 2 points, you can use this code:
def get_dist(pos1, pos2):
return math.hypot(pos1[0] - pos2[0], pos1[1] - pos2[1])
This also requires you to import math at the beginning of your program.
If they are sprites, you can simply do:
import math
defense_rect = defense.get_rect()
if math.abs(enemy.rect.center - defense_rect.rect.center) <= 200:
# *do something*
The logic is to see if the enemy's center is 200 pixels from the defense's center from any position (hence the usage of math.abs() which is absolute value). When it is, you replace the comment with your code. Why does this work?
Check here.
Pygame has pygame.Rect() to keep object position and size.
Tower 200x200 with top left corner in point (0,0)
tower_rect = pygame.Rect(0,0, 300, 300)
or you can move it to have (0,0) in center
tower_rect = pygame.Rect(0,0, 300, 300)
tower_rect.center = (0, 0)
To check if other Rect() is fully inside tower
enemy_rect = pygame.Rect(10, 10, 50, 50)
if tower_rect.contains(enemy_rect):
or if it fully or only partially in tower (it coollides with tower)
if tower_rect.colliderect(enemy_rect):
You can event test with list of enemies
if tower_rect.collidelistall(list_of_enemies_rect):
or check one enemy with all towers
if enemy_rect.collidelistall(list_of_towers_rect):
I am trying to add in random drops from enemy deaths to a game I am making in Python and wondering how to implement it. the drops I am wanting to add currently are shield and health, with shield having a lower drop chance. The main code for Drops are here:
import pygame
class HealthDrop(pygame.sprite.Sprite):
def __init__(self, x, y):
self.image = pygame.image.load('images/Sprites/health.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.on_ground = False
self.gravity = 0.5
def update(self):
def render(self, surface):
surface.blit(self.image, self.rect)
class ShieldDrop(pygame.sprite.Sprite):
def __init__(self, x, y):
self.image = pygame.image.load('images/Sprites/shield.png')
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.on_ground = False
self.gravity = 0.5
def update(self):
def render(self, surface):
surface.blit(self.image, self.rect)
Then the code for the main file is here:
import pygame, sys
import random
import pygame.mixer
import Funk
from time import sleep
from player import *
from zombie import *
from level import *
from bullet import *
from constants import *
from Drops import *
import menu as dm
class Game():
def __init__(self):
pygame.init()
pygame.mixer.init()
#pygame.mixer.music.load('sounds/menugame.ogg')
#pygame.mixer.music.play(-1)
# A few variables
self.gravity = .50
self.ground = pygame.Rect(0, 640, 1280, 80)
self.red = (255, 0, 0)
self.darkred = (200, 0, 0)
self.darkblue = (0, 0, 200)
self.darkgreen = (0, 200, 0)
self.gameover = pygame.image.load('images/gameover.png')
self.victory = pygame.image.load('images/victory.png')
# Bullets
self.bullets = []
# Screen
size = (1280, 720)
self.screen = pygame.display.set_mode(size)
pygame.display.set_caption('Moon Survival!')
# Moon / Background
self.moon = Background()
self.text1 = pygame.image.load('images/TextSlides/Text1.jpg')
self.text2 = pygame.image.load('images/TextSlides/Text2.jpg')
# Zombies
self.zombies = []
for i in range(15):
self.zombies.append( Zombie(random.randint(0,1280), random.randint(0,720)) )
self.zombieskilled = 0
# Player
self.player = Player(25, 320, self.gravity)
# Font for text
self.font = pygame.font.SysFont(None, 72)
# game over
self.gameover_text = self.font.render("The Aliens Are Too Good", -1, (255, 0, 0))
self.gameover_rect = self.gameover_text.get_rect(center=self.screen.get_rect().center)
# game state
self.game_state = STATE_INGAME
def run(self):
clock = pygame.time.Clock()
# "state machine"
RUNNING = True
PAUSED = False
GAME_OVER = False
# Game loop
while RUNNING:
# (all) Events
if self.game_state == STATE_INGAME:
for event in pygame.event.get():
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_s:
self.bullets.append(Bullet(self.player.rect.x + 30, self.player.rect.y + 30, self.player.direction))
if event.key == pygame.K_ESCAPE:
RUNNING = False
elif event.key == pygame.K_p:
# set state to paused
self.game_state = STATE_PAUSED
# Player/Zomies events
self.player.handle_events(event)
# (all) Movements / Updates
self.player_move()
self.player.update()
for z in self.zombies:
self.zombie_move(z)
z.update(self.screen.get_rect())
for b in self.bullets:
b.update()
for tile in self.moon.get_surrounding_blocks(b):
if tile is not None:
if pygame.sprite.collide_rect(b, tile):
# Destroy block
x = tile.rect.x / tile.rect.width
y = tile.rect.y / tile.rect.height
self.moon.levelStructure[x][y] = None
self.bullets.remove(b)
# (all) Display updating
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
for b in self.bullets:
b.render(self.screen)
self.player.render(self.screen)
Funk.text_to_screen(self.screen, 'Level 1', 5, 675)
Funk.text_to_screen(self.screen, 'Health: {0}'.format(self.player.health), 5, 0)
Funk.text_to_screen(self.screen, 'Score: {0}'.format(self.player.score), 400, 0)
Funk.text_to_screen(self.screen, 'Time: {0}'.format(self.player.alivetime), 750, 0)
Funk.text_to_screen(self.screen, 'Kills: {0}'.format(self.zombieskilled), 5, 50)
Funk.text_to_screen(self.screen, 'Lives: {0}'.format(self.player.lives), 300, 50)
elif self.game_state == STATE_PAUSED:
# (all) Display updating
if self.game_state == STATE_INGAME:
if event.type == pygame.QUIT:
RUNNING = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
RUNNING = False
choose = dm.dumbmenu(self.screen, [
'Resume Game',
'Menu',
'Quit Game'], 200, 200,'orecrusherexpanded',100,0.75,self.darkred,self.red)
if choose == 0:
print "You choose 'Start Game'."
# set state to ingame
self.game_state = STATE_INGAME
elif choose == 1:
print "You choose 'Controls'."
if choose == 2:
print "You choose 'Quit Game'."
pygame.quit()
sys.exit()
#for event in pygame.event.get():
self.moon.render(self.screen)
for z in self.zombies:
z.render(self.screen)
for b in self.bullets:
b.render(self.screen)
self.player.render(self.screen)
Funk.text_to_screen(self.screen, 'Level 1', 5, 675)
Funk.text_to_screen(self.screen, 'Health: {0}'.format(self.player.health), 5, 0)
Funk.text_to_screen(self.screen, 'Score: {0}'.format(self.player.score), 400, 0)
Funk.text_to_screen(self.screen, 'Time: {0}'.format(self.player.alivetime), 750, 0)
Funk.text_to_screen(self.screen, 'Kills: {0}'.format(self.zombieskilled), 850, 0)
elif self.game_state == STATE_GAMEOVER:
self.screen.blit(self.gameover, (0, 0))
pygame.display.update()
choose = dm.dumbmenu(self.screen, [
'New Game',
' Menu ',
'Quit Game'], 200, 300,'orecrusherexpanded',100,0.75,self.darkred,self.red)
if choose == 0:
print "You choose 'Start Game'."
# set state to ingame
self.game_state = STATE_INGAME
execfile('MoonSurvival.py')
if choose == 1:
print "You choose 'Start Game'."
execfile('run_game.py')
if choose == 2:
print "You choose 'Start Game'."
pygame.quit()
sys.exit()
pygame.display.update()
# FTP
clock.tick(100)
# --- the end ---
pygame.quit()
def player_move(self):
# add gravity
self.player.do_jump()
# simulate gravity
self.player.on_ground = False
if not self.player.on_ground and not self.player.jumping:
self.player.velY = 4
# Health
for zombie in self.zombies:
if pygame.sprite.collide_rect(self.player, zombie):
self.player.health -= 5
# check if we die
if self.player.health <= 0:
self.player.lives -= 1
self.player.rect.x = 320
self.player.rect.y = 320
self.player.health += 200
if self.player.lives <= 0:
sleep(2)
self.game_state = STATE_GAMEOVER
# move player and check for collision at the same time
self.player.rect.x += self.player.velX
self.check_collision(self.player, self.player.velX, 0)
self.player.rect.y += self.player.velY
self.check_collision(self.player, 0, self.player.velY)
def zombie_move(self, zombie_sprite):
# add gravity
zombie_sprite.do_jump()
# simualte gravity
zombie_sprite.on_ground = False
if not zombie_sprite.on_ground and not zombie_sprite.jumping:
zombie_sprite.velY = 4
# Zombie damage
for zombie in self.zombies:
for b in self.bullets:
if pygame.sprite.collide_rect(b, zombie):
#The same bullet cannot be used to kill
#multiple zombies and as the bullet was
#no longer in Bullet.List error was raised
zombie.health -= 10
self.bullets.remove(b)
if zombie.health <= 0:
self.zombieskilled += 1
self.player.score += 20
self.zombies.remove(zombie)
break
# move zombie and check for collision
zombie_sprite.rect.x += zombie_sprite.velX
self.check_collision(zombie_sprite, zombie_sprite.velX, 0)
zombie_sprite.rect.y += zombie_sprite.velY
self.check_collision(zombie_sprite, 0, zombie_sprite.velY)
def check_collision(self, sprite, x_vel, y_vel):
# for every tile in Background.levelStructure, check for collision
for block in self.moon.get_surrounding_blocks(sprite):
if block is not None:
if pygame.sprite.collide_rect(sprite, block):
# we've collided! now we must move the collided sprite a step back
if x_vel < 0:
sprite.rect.x = block.rect.x + block.rect.w
if type(sprite) is Zombie:
# the sprite is a zombie, let's make it jump
if not sprite.jumping:
sprite.jumping = True
sprite.on_ground = False
if x_vel > 0:
sprite.rect.x = block.rect.x - sprite.rect.w
if type(sprite) is Zombie:
# the sprite is a zombie, let's make it jump
if not sprite.jumping:
sprite.jumping = True
sprite.on_ground = False
if y_vel < 0:
sprite.rect.y = block.rect.y + block.rect.h
if y_vel > 0 and not sprite.on_ground:
sprite.on_ground = True
sprite.rect.y = block.rect.y - sprite.rect.h
#---------------------------------------------------------------------
Game().run()
you need to edit this:
def zombie_move(self, zombie_sprite):
for zombie in self.zombies:
for b in self.bullets:
if pygame.sprite.collide_rect(b, zombie):
zombie.health -= 10
self.bullets.remove(b)
if zombie.health <= 0:
self.zombieskilled += 1
self.player.score += 20
#You need some code here (before removing the zombie)
self.zombies.remove(zombie)
break
I'm sorry I forgot how to do it in Python, but the logic is like that: In place of the comment inside the code add something like HealthDrop.append(x, y) or ShieldDrop.append(x, y) where x and y should be zombie's values (that's why you should do it before removing the zombie).
If you want random chance just add import random then do it like that:
percentage = random.randint(1, 100)
if (percentage >= 1) and (percentage < 10)
healthDrop.append(zombie.x, zombie.y)
else
if (percentage >= 10) and (percentage < 20)
shieldDrop.append(zombie.x, zombie.y)
In this example I set 10% for each "item" to drop (they can't both drop), it randomizes a number from 1 to 100 , if it's a number from 1 to 9 its healthDrop, if it's a number from 10 to 19 its shieldDrop, feel free to experiment with what percentages makes your game balanced
Also don't forget to add collision (I see you already have some code for collision so I guess you know how to do it). The rest of the code should be easy for you to do (like increasing health on pick up etc etc.
I'm sorry I don't remember python really well, but you can use them similar to the bullet class, I hope you have the idea, if there's anything troubling you please tell me and I'll help more :)