How to make my cannon fire continuously in pygame - python

In my game I can place a cannon on user player's location. I'm able to make the cannon fire once but can't make it repeat the firing process multiple times. I want the bullet to be reset at its original value after it travels a certain distance and repeat the firing process. I'm attaching my whole code below along with the part that places the cannon. Thank you for your help.
import pygame
import random
import math
pygame.font.init()
width = 900
height = 600
screen = pygame.display.set_mode([width, height])
walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20, 20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40, 40))
pics = [bomb_pic, bomb_explosion]
shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))
boss = pygame.image.load("enemyboss.png")
player = [walkLeft, walkRight, char]
enemy_Left = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'),
pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'),
pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png')]
enemy_pic = pygame.image.load('L1E.png')
boss = pygame.image.load('pixel_monster.png')
cannon = pygame.image.load('tank_cannon.png')
bullet = pygame.image.load('bullet.png')
position = [60, 60]
x = 50 # same as position
y = 50 # same as position
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0
run_once = False
enemy_list = []
clock = pygame.time.Clock()
FPS = 60
font = pygame.font.Font('freesansbold.ttf', 32)
font_large = pygame.font.Font('freesansbold.ttf', 45)
items_font = pygame.font.Font('freesansbold.ttf', 16)
font_small = pygame.font.Font('freesansbold.ttf', 18)
font_tiny = pygame.font.Font('freesansbold.ttf', 13)
font_verytiny =pygame.font.Font('freesansbold.ttf', 9)
bombs = []
explosions = []
bag = {'bomb': 0, 'heal': 0, 'cannon': 0}
health = 100
base_health = 150
normal_enemies = []
kills = 0
cannon_list = []
bullet_list = []
class Button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 20)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def shop_run():
bright_green = (0, 255, 0)
green = (0, 200, 0)
shop_bomb = Button((0, 200, 0), 820, 150, 70, 20, text="Bomb_b")
shop_bomb.draw(screen)
shop_heal = Button((0, 200, 0), 820, 120, 70, 20, text="Heal_h")
shop_heal.draw(screen)
shop_cannon = Button((0, 200, 0), 820, 180, 70, 20, text="Cannon_c")
shop_cannon.draw(screen)
def walk():
global walkCount
global walkcount
if walkCount + 1 >= 27:
walkCount = 0
if left:
screen.blit(player[0][walkCount // 3], (x, y))
walkCount += 1
elif right:
screen.blit(player[1][walkCount // 3], (x, y))
walkCount += 1
elif down:
screen.blit(player[2], (x, y))
walkcount = 0
elif up:
screen.blit(player[2], (x, y))
walkcount = 0
else:
screen.blit(player[2], (x, y))
walkCount = 0
def enemy_spawn(number_of_enemies):
global normal_enemies
global health
global base_health
global kills
# for random_velocity in range(number_of_enemies):
player_rect = pygame.Rect(x+20, y+20, 20, 20)
for ne in range(number_of_enemies):
random_velocity = random.uniform(0.3, 1.3)
random_enemy_location_y = random.randrange(170, 470)
random_enemy_location_x = random.randrange(800, 1000)
normal_enemies.append([random_enemy_location_x, random_enemy_location_y, random_velocity])
# print(normal_enemies[ne][0], normal_enemies[ne][1], normal_enemies[ne][2])
for e in range(number_of_enemies):
ex, ey, evel = normal_enemies[e]
screen.blit(enemy_pic, (ex, ey))
if ex > 75:
normal_enemies[e][0] -= evel
else:
base_health -= 0.02
normal_enemy_rect = pygame.Rect(ex, ey, 50, 50)
if player_rect.colliderect(normal_enemy_rect):
health -= 0.2
for j in reversed(range(len(explosions))):
pos, end_time_2, hurt = explosions[j]
explosion_rect = pygame.Rect(pos[0], pos[1], 20, 20)
if explosion_rect.colliderect(normal_enemy_rect):
normal_enemies.pop(e)
kills += 1
def redrawGameWindow():
global walkCount
global font
global font_small
global font_tiny
global font_verytiny
global bag
global items_font
global enemy_list
global pics
global position
global health
global base_health
global run_once
global explosions
global bullet_list
current_time = pygame.time.get_ticks()
dx = []
dy = []
dist = []
screen.fill([166, 166, 166])
pygame.draw.rect(screen, (220, 0, 0), (700, 500, 100, 100))
# for five_enemies in range(5):
# random_enemy_location_y = random.randrange(170, 470)
# random_enemy_location_x = random.randrange(700, 840)
# enemy_list.append([random_enemy_location_x, random_enemy_location_y])
# for enemies in range(5):
# screen.blit(enemy_Left[enemies], enemy_list[enemies])
# dx.append(position[0] - enemy_list[enemies][0])
# dy.append(position[1] - enemy_list[enemies][1])
# dist.append(math.hypot(dx[enemies], dy[enemies]))
# dx[enemies], dy[enemies] = dx[enemies] / dist[enemies], dy[enemies] / dist[enemies]
# enemy_list[enemies][0] += dx[enemies] * 2
# enemy_list[enemies][1] += dy[enemies] * 2
pygame.draw.rect(screen, (70, 0, 220), (0, 120, 100, 400)) # main base
pygame.draw.rect(screen, (220, 0, 0), (50, 470, 5, -300))
pygame.draw.rect(screen, (0, 220, 0), (50, 470, 5, -base_health*2))
screen.blit(font.render("B", True, (0, 0, 0)), (10, 200 + 40))
screen.blit(font.render("A", True, (0, 0, 0)), (10, 235 + 40))
screen.blit(font.render("S", True, (0, 0, 0)), (10, 270 + 40))
screen.blit(font.render("E", True, (0, 0, 0)), (10, 305 + 40))
enemy_spawn(5)
# cannon_balls()
pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
if x + char.get_width() < 60 and y + char.get_height() < 60:
shop_run()
screen.blit(shop, (0, 0))
screen.blit(font_small.render("Shop", True, (0, 0, 0)), (5, 5))
pygame.draw.rect(screen, (220, 0, 0), (position[0] - 3, position[1], 50, 5))
pygame.draw.rect(screen, (0, 220, 0), (position[0] - 3, position[1], health/2, 5))
screen.blit(font.render("Menu", True, (255, 255, 255)), (805, 10))
screen.blit(items_font.render("Bombs: " + str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
screen.blit(items_font.render("Heal: " + str(bag["heal"]), True, (255, 255, 255)), (805, 570))
screen.blit(items_font.render("Cannon: " + str(bag["cannon"]), True, (255, 255, 255)), (805, 530))
# screen.blit(bullet, (450, 300))
# screen.blit(bomb_explosion, (450, 300))
# screen.blit(boss, (450, 300))
walk()
for i in reversed(range(len(bombs))):
pos, end_time = bombs[i]
if current_time > end_time:
end_time_2 = end_time + 5000
pos2 = (pos[0] - 10, pos[1] - 20)
explosions.append((pos2, end_time_2, False))
bombs.pop(i)
else:
screen.blit(pics[0], pos)
for j in reversed(range(len(explosions))):
pos, end_time_2, hurt = explosions[j]
if current_time > end_time_2:
explosions.pop(j)
else:
screen.blit(pics[1], pos)
if not hurt:
explosion_rect = pygame.Rect(pos[0], pos[1], 20, 20)
player_rect = pygame.Rect(x+20, y+20, 20, 20)
if player_rect.colliderect(explosion_rect):
explosions[j] = (pos, end_time_2, True)
health -= 5
# print(health)
for i in cannon_list:
screen.blit(cannon, i)
for j in bullet_list:
screen.blit(bullet, j)
j[0] += 3
screen.blit(font_tiny.render("Health: " + str("{:.2f}".format(health)), True, (255, 255, 255)), (805, 60))
screen.blit(font_verytiny.render("Base Health: " + str("{:.2f}".format(base_health)), True, (255, 255, 255)), (805, 90))
screen.blit(font_tiny.render("Kills: " + str(kills), True, (255, 255, 255)), (805, 110))
pygame.display.update()
def main():
run = True
pygame.display.set_caption("bomb-mania")
global x
global y
global width
global height
global vel
global left
global right
global down
global up
global walkCount
global bomb_pic
global font
global bombs
global explosions
global position
global health
global kills
global cannon_list
global bullet_list
while run:
current_time = pygame.time.get_ticks()
redrawGameWindow()
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
run = False
shop_rect = pygame.Rect(0, 0, 40, 40)
player_rect = pygame.Rect(x+20, y+20, 20, 20)
if player_rect.colliderect(shop_rect):
buy = pygame.key.get_pressed()
if buy[pygame.K_b]:
bag["bomb"] += 1
# print(bag["bomb"])
if buy[pygame.K_h]:
bag["heal"] += 1
if buy[pygame.K_c] and kills > 3:
kills -= 3
bag["cannon"] += 1
# print(bag["cannon"])
if event.type == pygame.KEYDOWN and not player_rect.colliderect(shop_rect):
if (event.key == pygame.K_SPACE or event.key == pygame.K_b) and bag["bomb"] >= 1:
current_time_2 = pygame.time.get_ticks()
pos = x + char.get_width() / 2, y + char.get_height() - 20
pos2 = ((x + char.get_width() / 2) - 10), (y + char.get_height() - 30)
end_time = current_time + 3000 # 3000 milliseconds = 3 seconds
bombs.append((pos, end_time))
bag["bomb"] -= 1
if event.key == pygame.K_h and not player_rect.colliderect(shop_rect) and health < 90 and bag["heal"] >= 1:
health += 10
bag["heal"] -= 1
if event.key == pygame.K_c and not player_rect.colliderect(shop_rect):
print("reached")
cannon_list.append([x,y])
bullet_list.append([x,(y-20)])
if health <= 0 or base_health <= 0:
main_menu()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel - 15:
x -= vel
position[0] -= vel
left = True
right = False
down = False
up = False
# print(position)
elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
x += vel
position[0] += vel
left = False
right = True
down = False
up = False
# print(position)
elif keys[pygame.K_DOWN] and y < 600 - height:
y += vel
position[1] += vel
left = False
right = False
down = True
up = False
# print(position)
elif keys[pygame.K_UP] and y > vel - 15:
y -= vel
position[1] -= vel
left = False
right = False
down = False
up = True
# print(position)
else:
left = False
right = False
down = False
up = False
walkCount = 0
clock.tick(FPS)
pygame.display.flip()
def main_menu():
global width
global height
global health
global base_health
global bag
global position
global x
global y
global left
global right
global down
global up
global walkCount
global normal_enemies
global explosions
global bombs
global enemy_list
global kills
global cannon_list
cannon_list =[]
kills = 0
enemy_list = []
normal_enemies = []
bombs = []
explosions = []
position = [60, 60]
x = 50 # same as position
y = 50 # same as position
left = False
right = False
down = False
up = False
walkCount = 0
enemy_vel = 2
enemy_list = []
bag["bomb"] = 0
bag["heal"] =0
health = 100
base_health = 150
pygame.display.set_caption("Main Menu")
run = True
bright_green = (0, 255, 0)
green = (0, 200, 0)
screen.fill((163, 163, 194))
while run:
mouse = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
run = False
if 400 + 100 > mouse[0] > 400 and 275 + 50 > mouse[1] > 275:
pygame.draw.rect(screen, bright_green, (400, 275, 100, 50))
if event.type == pygame.MOUSEBUTTONDOWN:
main()
else:
pygame.draw.rect(screen, green, (400, 275, 100, 50))
screen.blit(font_large.render("Bomb-Mania", True, (255, 255, 255)), (325, 50))
screen.blit(font.render("Play", True, (0, 0, 0)), (417, 285))
pygame.display.flip()
clock.tick(FPS)
main_menu()
The code below is the part that places the cannon.
This is part of main()
if event.key == pygame.K_c and not player_rect.colliderect(shop_rect):
print("reached")
cannon_list.append([x,y])
bullet_list.append([x,(y-20)])
This is part of redrawGameWindow()
for i in cannon_list:
screen.blit(cannon, i)
for j in bullet_list:
screen.blit(bullet, j)
j[0] += 3
Edits
bullet_list.append([x,(y+25),0, 0])
The changes i"ve made to keep track of the distance traveled are given below
for i in cannon_list:
screen.blit(cannon, i)
for j in bullet_list:
screen.blit(bullet, (j[0], j[1]))
j[3] = j[0]
if j[0] == j[3]:
j[0] += 3
j[2] += 3
if j[2] >= 100:
j[0] = j[3]
Edits2
I'm implementing OOP, Please help me debug.
class Cannon():
global cannon_list
global bullet_list
def __init__(self, x, y, track, old_x):
self.x = x
self.y = y
self.track = track
self.old_x = old_x
def spawnBullet(self):
for j in bullet_list:
self.old_x = j[3]
self.track = j[2]
screen.blit(bullet, (j[0], j[1]))
def moveBullet(self):
if self.x <= self.track:
self.x += 3
self.track += 3
def resetBullet(self):
if self.x >= self.track:
self.x = self.old_x
def spawnCannon(self):
for i in cannon_list:
screen.blit(cannon, i)
Using class Cannon
for j in bullet_list:
cannonsAndBullets = Cannon(j[0], j[1], j[2], j[0])
cannonsAndBullets.spawnCannon()
cannonsAndBullets.spawnBullet()
cannonsAndBullets.moveBullet()
cannonsAndBullets.resetBullet()

You say that you want the canons bullet to travel a fixed distance and then refire. What have you done to try to achieve that?
This appears to be the code that causes the bullet to move:
for j in bullet_list:
screen.blit(bullet, j)
j[0] += 3
There is nothing in here that stops it after it has travelled some specific distance or triggers a re-firing.
Your code would significantly benefit from object oriented restructuring and use of classes, in particular I would recommend that you make player, enemy, canon. bullet into classes. It not only cleans up the code but makes it simpler to keep track of the objects and all the respective state information that you need for each object.
For example for you question of re-firing after a certain distance has been traveled by the bullet. Right now the only information that you are keeping on the bullet is its position. To make it stop after a certain distance, you also need to know either its initial position or how far it has traveled since being fired. To re-fire you again need to know the canon it was fired from or possibly just its initial position if you just want to restart from the same place (assuming the canon is stationary). What about if the bullet hits something? Can the canon immediately re-fire, or does it have to wait for the same amount of time to elapse as if it had not hit anything and had to travel the full distance? If later you want your canon to be rate limited or something rather than just only one existing bullet at a time you will need state information in the canon about firing rate and the last time it fired.
Structuring your elements as objects allows you to cleanly keep all of that specific state information together for each instance of each object. It makes it simpler for you to modify behaviour when you want to since the logic related to the objects can be contained and you know where it all is. That is very important as the code gets larger and more complicated, but is always good practice regardless.
It also generally makes it simpler for other to look at, understand your code when you are asking for assistance, or if you were to pass a project on to someone else.
Edit after OP modified question based on comments:
You edited your code and added this:
for j in bullet_list:
screen.blit(bullet, (j[0], j[1]))
j[3] = j[0]
if j[0] == j[3]:
j[0] += 3
j[2] += 3
if j[2] >= 100:
j[0] = j[3]
Which does not make sense. The line if j[0] == j[3]: will always be true since in the line immediately before it you set j[3] = j[0].
I think what you are trying to do is have this state information for initial position and distance traveled in the list along with the x,y position, and that j[3] is supposed to be the initial position and j[2] is the distance? This is not how I would do this at all, but ... You might try this instead:
bullet_list.append([x,(y+25),0, x])
and
for j in bullet_list:
screen.blit(bullet, (j[0], j[1]))
j[0] += 3
j[2] += 3
if j[2] >= 100:
j[0] = j[3]
Again, you really should be using a class for this, not trying to keep the state as parts of a list.

Related

How to rotate an image to the cursor? [duplicate]

This question already has answers here:
How to rotate an image(player) to the mouse direction?
(2 answers)
Still Having Problems With Rotating Cannon's Properly Towards The Player Pygame
(1 answer)
How do I make image rotate with mouse python [duplicate]
(1 answer)
How do I make my player rotate towards mouse position?
(1 answer)
Closed 4 months ago.
I have a shotgun attached to my player. I want this shotgun to point at the cursor. Ive tried searching this up on google, but each time i tried the answer my shotgun kinda moved and rotated. How can i make it so it only rotates?
My code:
import pygame
import numpy
import math
import random
screen = pygame.display.set_mode((1280, 720))
class Player:
def __init__(self, pos: tuple, gravity: tuple, sprite: pygame.Surface, maxVel, minVel, maxVelY):
self.pos = pos
self.velocity = (0,0)
self.gravity = gravity
self.sprite = sprite
self.maxVel = maxVel
self.minVel = minVel
self.maxVelY = maxVelY
def addForce(self, force: tuple):
self.velocity = numpy.add(self.velocity, force)
def resetVelocity(self):
self.velocity = (0,0)
def update(self):
global scene, highscore, score, playerrect
self.velocity = numpy.add(self.velocity, (0, self.gravity/10))
if self.velocity[0] > self.maxVel:
self.velocity[0] = self.maxVel
elif self.velocity[0] < self.minVel:
self.velocity[0] = self.minVel
if self.velocity[1] > self.maxVelY:
self.velocity[1] = self.maxVelY
self.pos = numpy.subtract(self.pos, self.velocity)
if self.pos[1] < -50:
self.pos[1] += 770
elif self.pos[1] > 770:
scene = menu
self.pos = (self.sprite.get_rect(center=(1280/2, 720/2))[0], self.sprite.get_rect(center=(1280/2, 720/2))[1])
if highscore < score:
highscore = score
if self.pos[0] < 0:
self.pos[0] += 1330
if self.pos[0] > 1280:
self.pos[0] -= 1330
def draw(self): # Here is the problem
global shotgun
screen.blit(self.sprite, self.pos)
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - self.pos[0], mouse_y - self.pos[1]
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
rotshotgun = pygame.transform.rotate(shotgun, int(angle)) # Im thinking this doesnt work correctly
screen.blit(rotshotgun, (self.pos[0]+50, self.pos[1]+50))
class Particle:
def __init__(self, x, y, color):
self.x = x
self.y = y
self.color = color
def draw(self):
pygame.draw.rect(screen, self.color, (self.x, self.y, 10, 10))
player = pygame.image.load("other/player.png").convert_alpha()
player = pygame.transform.scale(player, (100, 100))
player = Player((590, 310), -9.81, player, 10, -10, 20)
clock = pygame.time.Clock()
new_velocity = (0,0)
game = 0
menu = 1
scene = menu
shotgun = pygame.image.load("other/shotgun.png").convert_alpha()
shotgun = pygame.transform.scale(shotgun, (83, 66))
pygame.font.init()
font = pygame.font.Font("font/AlfaSlabOne-Regular.ttf", 150)
font2 = pygame.font.Font("font/PoorStory-Regular.ttf", 50)
font3 = pygame.font.SysFont("Arial", 350)
font4 = pygame.font.Font("font/Nunito-Black.ttf", 75)
titel = font.render("ShootShot", True, (0, 255, 0))
clicktext = font2.render("Click to start!", True, (0,0,0))
scoretext = font3.render("0", True, (0,0,0))
with open("other/highscore.txt", "r") as f:
highscore = f.read()
highscore.replace("\n", "")
highscore = int(highscore)
highscoretext = font4.render(f"Highscore: {highscore}", True, (0,0,0))
coins = []
for x in range(6):
coins.append(pygame.transform.scale(pygame.image.load(f"coin/coin{x+1}.png"), (50, 50)))
number = 0
particles = []
colors = [(255, 0, 0),
(0, 0, 255),
(255, 255, 0),
(255, 0, 255),
(0, 255, 255),
(255, 125, 0),
(125, 255, 0),
(0, 255, 125),
(0, 125, 255),
(125, 0, 255)]
r = True
scoretext.set_alpha(75)
scoretext = scoretext.convert_alpha()
coinLocation = (random.randint(0, 1280), random.randint(0, 720))
titlerect = titel.get_rect(center=(1280/2, 150))
scorerect = scoretext.get_rect(center=(1280/2, 720/2))
highscorerect = highscoretext.get_rect(center=(1280/2, 50))
number2 = 0
currentCoin = 0
coin = pygame.Rect(coinLocation[0], coinLocation[1], 50, 50)
score = 0
while r:
clock.tick(45)
screen.fill((255,255,255))
mousePos = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
r = False
if event.type == pygame.MOUSEBUTTONDOWN:
if scene == game:
new_velocity = numpy.subtract(player.pos, mousePos)*-1
player.resetVelocity()
player.addForce(new_velocity/10)
else:
scene = game
score = 0
new_velocity = numpy.subtract(player.pos, mousePos) * -1
player.resetVelocity()
player.addForce(new_velocity / 10)
screen.blit(scoretext, scorerect)
player.draw()
if scene == game:
player.update()
screen.blit(coins[currentCoin], coinLocation)
number2 += 0.15
currentCoin = math.ceil(number2)
if currentCoin > 5:
currentCoin = 0
number2 = -1
playerrect = pygame.Rect(player.pos[0], player.pos[1], 50, 50)
if coin.colliderect(playerrect):
score += 1
scoretext = font3.render(str(score), True, (0, 0, 0))
scoretext.set_alpha(75)
coinLocation = (random.randint(0, 1280), random.randint(0, 720))
coin = pygame.Rect(coinLocation[0], coinLocation[1], 50, 50)
elif scene == menu:
highscoretext = font4.render(f"Highscore: {highscore}", True, (0, 0, 0))
scoretext = font3.render("0", True, (0, 0, 0))
scoretext = scoretext.convert_alpha()
scoretext.set_alpha(75)
score = 0
screen.blit(titel, titlerect)
number += 0.1
clickTextAddY = math.cos(number)*30
screen.blit(clicktext, (525, 600-clickTextAddY))
particles.append(Particle(random.randint(250, 1075), random.randint(100, 200), random.choice(colors)))
for particle in particles:
particle.draw()
if len(particles) > 70:
particles.pop(random.randint(0, len(particles)-1))
screen.blit(highscoretext, highscorerect)
pygame.display.flip()
with open("other/highscore.txt", "w") as f:
f.write(str(highscore))
pygame.quit()
I hope anyone can help me with this.
As i already said, i have tried searcing it up. The shotgun pointed to my mouse, but it also moved.

how do i move obstacles faster for dino game

I'm trying my best to make a dino game replica and I'm stuck at increasing the speed of the obstacles that comes towards the character. The speed of 5 just seems to be the magic number but when I change it to six the rock doesn't get detected and ultimately doesn't spawn the next rock.
I don't know why.
there are no errors except for when the rock doesn't get spawned and I get an index out of range:
Traceback (most recent call last):
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 109, in <module>
main()
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 102, in main
if game.collide():
File "C:\Users\austi\OneDrive\Desktop\scroller game\main.py", line 71, in collide
olh = self.obstacles[0].hitBox[0]
IndexError: list index out of range
here is what I got:
main.py
import pygame
import math
import random
from player import Player
from objects import Obstacle
WIDTH, HEIGHT = 700, 400
class Game:
def __init__(self, playerSprite=None, obSprite= None):
self.playerSprite = playerSprite
self.obSprite = obSprite
self.player = Player(50, 50, 50, 50, 8, (0, 0, 0), None)
self.obstacle = Obstacle(1, (800, 350, 50, 50), 5, None)
self.obstacles = [self.obstacle]
self.spawnGap = -100
self.speed = 5
def spawn(self):
for obstacle in self.obstacles:
#if its at the specific spot than spawn a rock
if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:
#get shape of rock
type = round(random.randint(0, 3))
rect = (700, 350, 50, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (50, 50))
if type == 1:
rect = (700, 350, 25, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (25, 50))
if type == 2 :
rect = (700, 375, 50, 25)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (50, 25))
if type == 3:
rect = (700, 350, 75, 50)
if self.obSprite != None:
self.obSprite = pygame.transform.scale(self.obSprite, (75, 50))
#increase the place in which the rock spawns
self.spawnGap += 10
#create a new obstacle and append it to the obstacle array
self.obstacle = Obstacle(type, rect, 5, None)
self.obstacles.append(self.obstacle)
#delete obstacle when its at the end
if obstacle.pos.x < -obstacle.w:
index = self.obstacles.index(obstacle)
del self.obstacles[index]
def draw(self, win):
self.spawn()
self.hitBoxes()
self.player.draw(window)
for obstacle in self.obstacles:
obstacle.draw(window)
def collide(self):
plh = self.player.hitBox[0]
prh = self.player.hitBox[0] + self.player.hitBox[2]
pth = self.player.hitBox[1]
pbh = self.player.hitBox[1] + self.player.hitBox[3]
olh = self.obstacles[0].hitBox[0]
orh = self.obstacles[0].hitBox[0] + self.obstacles[0].hitBox[2]
oth = self.obstacles[0].hitBox[1]
obh = self.obstacles[0].hitBox[1] + self.obstacles[0].hitBox[3]
if prh >= olh and plh <= orh:
if pbh >= oth:
return True
else:
return False
def hitBoxes(self):
key = pygame.key.get_pressed()
if key[pygame.K_a]:
self.player.showHitBoxes = True
for obstacle in self.obstacles:
obstacle.showHitBoxes = True
if key[pygame.K_s]:
self.player.showHitBoxes = False
for obstacle in self.obstacles:
obstacle.showHitBoxes = False
def main():
done = False
pressed = False
game = Game()
time = pygame.time.Clock()
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
window.fill((255, 255, 255))
game.draw(window)
if game.collide():
done = True
pygame.draw.line(window, (255, 0, 0), (WIDTH/2, 0), (WIDTH/2, HEIGHT))
pygame.draw.line(window, (0, 0, 255), (WIDTH/2 + game.spawnGap, 0), (WIDTH/2 + game.spawnGap, HEIGHT))
pygame.display.update()
time.tick(60)
pygame.quit()
main()
objects.py
import pygame
from pygame.math import Vector2
class Obstacle:
def __init__(self, type, rect, speed, rockSprite=None):
self.obSprite = rockSprite
self.xSpawn = rect[0]
self.ySpawn = rect[1]
self.pos = Vector2(self.xSpawn, self.ySpawn)
self.w = rect[2]
self.h = rect[3]
self.type = type
self.speed = speed
self.hitBox = ()
self.showHitBoxes = False
def draw(self, win):
self.hitBox = (self.pos.x, self.pos.y, self.w, self.h)
if self.showHitBoxes:
pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
if self.obSprite != None:
win.blit(self.obSprite, self.pos)
else:
pygame.draw.rect(win, (0, 0, 0), (self.pos.x, self.pos.y, self.w, self.h))
self.update()
def update(self):
self.pos.x -= self.speed
player.py
import pygame
from pygame.math import Vector2
WIDTH, HEIGHT = 700, 400
class Player:
def __init__(self, x, y, w, h, jumpVel, color, sprite=None):
self.playerSprite = sprite
self.x = x
self.y = y
self.w = w
self.h = h
self.pos = Vector2(self.x, self.y)
self.color = color
self.gravity = 0.3
self.vel = self.gravity
self.jVel = jumpVel
self.touching_surface = False
self.canJump = True
self.jumping = False
self.hitBox = ()
self.showHitBoxes = False
def draw(self, win):
self.hitBox = (self.pos.x + 0, self.pos.y + 10, 50, 30)
if self.showHitBoxes:
pygame.draw.rect(win, (255, 0, 0), self.hitBox, 2)
if self.playerSprite != None:
win.blit(self.playerSprite, self.pos)
else:
pygame.draw.rect(win, (255, 0, 0), (self.pos.x, self.pos.y, 50, 50))
self.update()
def update(self):
mouse = pygame.mouse.get_pressed()
if self.pos.y + self.h >= HEIGHT:
self.touching_surface = True
self.vel = 0
self.pos.y = HEIGHT - self.h
else:
self.touching_surface = False
self.vel += self.gravity
if self.pos.x <= 0:
self.pos.x = 0
elif self.pos.x + self.w >= WIDTH:
self.pos.x = WIDTH - self.w
if self.touching_surface:
self.canJump = True
else:
self.canJump = False
if mouse[0] == 1 and self.canJump and not self.jumping:
self.jumping = True
self.canJump = False
self.vel = -self.jVel
if mouse[0] == 0:
self.jumping = False
self.pos.y += self.vel
and I suspect the issue is in the spawn function in the Game class in main.py
I've been trying to work on this for a couple days now and I still cannot solve my issue.
The problem is the condition
if obstacle.pos.x + obstacle.w == WIDTH/2 + self.spawnGap:
# [...]
This condition is only True when the obstacle is exactly at a certain position. If the speed changes, the obstacle does not exactly hit the position.
Test on a small range instead of a position. e.g.:
right = obstacle.pos.x + obstacle.w
target = WIDTH/2 + self.spawnGap
if traget <= right < traget + speed:
# [...]

how can I make the enemy move towards the player and predict its path in pygame

I am making a pygame game and I want my enemies follow the player and predict its path. I don't just want to reduce the distance between the player and the enemy. The number of enemies will be according to the level, every 3 levels will add a new enemy. I'm attaching my whole code along with a screenshot showing that my enemies are currently just moving in a straight line.
import pygame
import random
pygame.font.init()
width = 900
height = 600
screen = pygame.display.set_mode([width, height])
walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20,20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40,40))
pics = [bomb_pic, bomb_explosion]
# char_rect = char.get_rect()
enemy_Left = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'),
pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'),
pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png')]
x = 50
y = 50
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0
enemy_vel = 2
enemy_list = []
shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))
clock = pygame.time.Clock()
FPS = 60
font = pygame.font.Font('freesansbold.ttf', 32)
items_font = pygame.font.Font('freesansbold.ttf', 16)
bombs =[]
explosions = []
bag = {'bomb': 0}
print(bag["bomb"])
class Button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 20)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def shop_run():
shop_bomb = Button((0, 200, 0), 820, 150, 60, 20, text="Bomb_b")
bright_green = (0, 255, 0)
green = (0, 200, 0)
shop_bomb.draw(screen)
def redrawGameWindow():
global walkCount
global font
global bag
global items_font
global enemy_list
global pics
current_time = pygame.time.get_ticks()
screen.fill([166, 166, 166])
for five_enemies in range(6):
random_enemy_location_y = random.randrange(100, 400)
random_enemy_location_x = random.randrange(800, 840)
enemy_list.append([random_enemy_location_x, random_enemy_location_y])
for enemies in range(6):
screen.blit(enemy_Left[enemies], enemy_list[enemies])
enemy_list[enemies][0] -= 0.3
pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
if x + char.get_width() < 60 and y + char.get_height() < 60:
shop_run()
screen.blit(shop, (0, 0))
screen.blit(font.render("Menu", True, (255,255,255)),(805, 10))
screen.blit(items_font.render("Bombs: "+ str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
# screen.blit(bomb_explosion, (450, 300))
if walkCount + 1 >= 27:
walkCount = 0
if left:
screen.blit(walkLeft[walkCount // 3], (x, y))
walkCount += 1
elif right:
screen.blit(walkRight[walkCount // 3], (x, y))
walkCount += 1
elif down:
screen.blit(char, (x, y))
walkcount = 0
elif up:
screen.blit(char, (x, y))
walkcount = 0
else:
screen.blit(char, (x, y))
walkCount = 0
for i in reversed(range(len(bombs))):
pos, end_time = bombs[i]
if current_time > end_time:
bombs.pop(i)
# current_time_2 = pygame.time.get_ticks()
# for j in reversed(range(len(explosions))):
# pos2, end_time_2 = explosions[j]
# if current_time_2 > end_time_2:
# explosions.pop(j)
# else:
# screen.blit(bomb_explosion, pos2)
else:
screen.blit(pics[0], pos)
for j in reversed(range(len(explosions))):
pos, end_time_2 = explosions[j]
if current_time > end_time_2:
explosions.pop(j)
elif current_time > (end_time_2 - 2000):
screen.blit(pics[1], pos)
else:
continue
pygame.display.update()
def main():
run = True
# shopper()
pygame.display.set_caption("bomb-mania")
global x
global y
global width
global height
global vel
global isJump
global jumpCount
global left
global right
global down
global up
global walkCount
global bomb_pic
global font
global bombs
global explosions
while run:
current_time = pygame.time.get_ticks()
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x + char.get_width() < 60 and y + char.get_height() < 60:
buy = pygame.key.get_pressed()
if buy[pygame.K_b]:
bag["bomb"] += 1
print(bag["bomb"])
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and bag["bomb"] >= 1:
current_time_2 = pygame.time.get_ticks()
pos = x + char.get_width()/2, y + char.get_height() - 20
pos2 = ((x + char.get_width()/2)-10), y + char.get_height() - 30
end_time = current_time + 3000 # 3000 milliseconds = 3 seconds
end_time_2 = current_time_2 + 5000
explosions.append((pos2, end_time_2))
bombs.append((pos, end_time))
bag["bomb"] -= 1
redrawGameWindow()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel - 15:
x -= vel
left = True
right = False
down = False
up = False
elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
x += vel
left = False
right = True
down = False
up = False
elif keys[pygame.K_DOWN] and y < 600 - height:
y += vel
left = False
right = False
down = True
up = False
elif keys[pygame.K_UP] and y > vel - 15:
y -= vel
left = False
right = False
down = False
up = True
else:
left = False
right = False
down = False
up = False
walkCount = 0
clock.tick(FPS)
pygame.display.flip()
main()
You'll need some vector math for this, so I recommend to restructure your code and learn how to use Sprites; you can find an example here.
To find an answer to your question ("predict the path"), you could google for intercept vector or pursuit vector. That should yield some results, such as How to calculate the vector of an interception? or Calculating Intercepting Vector.
For example, I translated the last answer of the second question and copy/pasted it into one of my answers, since a) I'm too lazy to write everything again and b) there's a single point of code I have to change to implement the intercept logic (the EnemyController class).
import pygame
import random
import math
from pygame import Vector2
SPRITE_SHEET = None
GREEN_SHIP = pygame.Rect(0, 292, 32, 32)
RED_SHIP = pygame.Rect(0, 324, 32, 32)
BLUE_SHIP = pygame.Rect(0, 356, 32, 32)
YELLOW_SHIP = pygame.Rect(0, 388, 32, 32)
class EnemyController:
def __init__(self, target):
self.direction = Vector2(1, 0)
self.target = target
def update(self, sprite, events, dt):
k = self.target.vel.magnitude() / sprite.speed;
distance_to_target = (sprite.pos - self.target.pos).magnitude()
b_hat = self.target.vel
c_hat = sprite.pos - self.target.pos
CAB = b_hat.angle_to(c_hat)
ABC = math.asin(math.sin(CAB) * k)
ACB = math.pi - (CAB + ABC)
j = distance_to_target / math.sin(ACB)
a = j * math.sin(CAB)
b = j * math.sin(ABC)
time_to_collision = b / self.target.vel.magnitude() if self.target.vel.magnitude() > 0 else 1
collision_pos = self.target.pos + (self.target.vel * time_to_collision)
v = sprite.pos - collision_pos
if v.length() > 0:
sprite.direction = -v.normalize()
if v.length() <= 10:
sprite.pos = pygame.Vector2(400, 100)
class PlayerController:
movement = {
pygame.K_UP: Vector2( 0, -1),
pygame.K_DOWN: Vector2( 0, 1),
pygame.K_LEFT: Vector2(-1, 0),
pygame.K_RIGHT: Vector2( 1, 0)
}
def update(self, sprite, events, dt):
pressed = pygame.key.get_pressed()
v = Vector2(0, 0)
for key in PlayerController.movement:
if pressed[key]:
v += PlayerController.movement[key]
sprite.direction = v
for e in events:
if e.type == pygame.KEYDOWN:
if e.key == pygame.K_SPACE:
sprite.groups()[0].add(Explosion(sprite.pos))
class Animation:
def __init__(self, frames, speed, sprite):
self.sprite = sprite
self.speed = speed
self.ticks = 0
self.frames = frames
self.running = 0
self.start()
def cycle_func(self, iterable):
saved = []
for element in iterable:
yield element
saved.append(element)
if hasattr(self.sprite, 'on_animation_end'):
self.sprite.on_animation_end()
while saved:
for element in saved:
yield element
if hasattr(self.sprite, 'on_animation_end'):
self.sprite.on_animation_end()
def stop(self):
self.running = 0
if self.idle_image:
self.sprite.image = self.idle_image
def start(self):
if not self.running:
self.running = 1
self.cycle = self.cycle_func(self.frames)
self.sprite.image = next(self.cycle)
def update(self, dt):
self.ticks += dt
if self.ticks >= self.speed:
self.ticks = self.ticks % self.speed
if self.running:
self.sprite.image = next(self.cycle)
class AnimatedSprite(pygame.sprite.Sprite):
def __init__(self, pos, frames, speed):
super().__init__()
self.animation = Animation(frames, speed, self)
self.rect = self.image.get_rect(center=pos)
self.pos = Vector2(pos)
self.animation.start()
def update(self, events, dt):
self.animation.update(dt)
class Explosion(AnimatedSprite):
frames = None
def __init__(self, pos):
if not Explosion.frames:
Explosion.frames = parse_sprite_sheet(SPRITE_SHEET, pygame.Rect(0, 890, 64, 64), 6, 4)
super().__init__(pos, Explosion.frames, 50)
def on_animation_end(self):
self.kill()
class DirectionalImageSprite(pygame.sprite.Sprite):
directions = [(1,0),(1,-1),(0,-1),(-1,-1),(-1,0),(-1,1),(0,1),(1,1),(0,0)]
def __init__(self, pos, directional_images_rect):
super().__init__()
images = parse_sprite_sheet(SPRITE_SHEET, directional_images_rect, 9, 1)
self.images = { x: img for (x, img) in zip(DirectionalImageSprite.directions, images) }
self.direction = Vector2(0, 0)
self.image = self.images[(self.direction.x, self.direction.y)]
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
class SpaceShip(DirectionalImageSprite):
def __init__(self, pos, controller, directional_images_rect):
super().__init__(pos, directional_images_rect)
self.controller = controller
self.speed = 2
self.vel = pygame.Vector2(0, 0)
def update(self, events, dt):
super().update(events, dt)
if self.controller:
self.controller.update(self, events, dt)
self.vel = Vector2(0, 0)
if (self.direction.x, self.direction.y) in self.images:
self.image = self.images[(self.direction.x, self.direction.y)]
if self.direction.length():
self.vel = self.direction.normalize() * self.speed
self.pos += self.vel
self.rect.center = int(self.pos[0]), int(self.pos[1])
def parse_sprite_sheet(sheet, start_rect, frames_in_row, lines):
frames = []
rect = start_rect.copy()
for _ in range(lines):
for _ in range(frames_in_row):
frame = sheet.subsurface(rect)
frames.append(frame)
rect.move_ip(rect.width, 0)
rect.move_ip(0, rect.height)
rect.x = start_rect.x
return frames
def main():
screen = pygame.display.set_mode((800, 600))
global SPRITE_SHEET
SPRITE_SHEET = pygame.image.load("ipLRR.png").convert_alpha()
clock = pygame.time.Clock()
dt = 0
player = SpaceShip((400, 300), PlayerController(), YELLOW_SHIP)
enemy = SpaceShip((400, 100), EnemyController(player), GREEN_SHIP)
enemy.speed = 4
all_sprites = pygame.sprite.Group(
player,
enemy
)
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
all_sprites.update(events, dt)
screen.fill((0, 0, 0))
all_sprites.draw(screen)
pygame.display.flip()
dt = clock.tick(120)
main()

how to replace picture and check for collision with new image and and a moving image in pygame [duplicate]

This question already has answers here:
Animated sprite from few images
(4 answers)
how to make image/images disappear in pygame?
(1 answer)
Closed 2 years ago.
I'm making a pygame game where a player is moving around on a screen and can buy items from a shop(bombs). I'm able to drop the bombs, now I need to replace the bomb image with an explosion image after 3 secs and check for collision with the enemy. I'm attaching my whole code along with some screenshots for reference. Any help is appreciated, thank you
import pygame
import random
pygame.font.init()
width = 900
height = 600
screen = pygame.display.set_mode([width, height])
walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20,20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40,40))
# char_rect = char.get_rect()
enemy_Left = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'),
pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'),
pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png')]
x = 50
y = 50
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0
enemy_vel = 2
enemy_list = []
shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))
clock = pygame.time.Clock()
FPS = 60
font = pygame.font.Font('freesansbold.ttf', 32)
items_font = pygame.font.Font('freesansbold.ttf', 16)
bombs =[]
bag = {'bomb': 0}
print(bag["bomb"])
class Button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 20)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def shop_run():
shop_bomb = Button((0, 200, 0), 820, 150, 60, 20, text="Bomb_b")
bright_green = (0, 255, 0)
green = (0, 200, 0)
shop_bomb.draw(screen)
def redrawGameWindow():
global walkCount
global font
global bag
global items_font
global enemy_list
screen.fill([166, 166, 166])
for five_enemies in range(6):
random_enemy_location_y = random.randrange(100, 400)
random_enemy_location_x = random.randrange(800, 840)
enemy_list.append([random_enemy_location_x, random_enemy_location_y])
for enemies in range(6):
screen.blit(enemy_Left[enemies], enemy_list[enemies])
enemy_list[enemies][0] -= 0.3
pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
if x + char.get_width() < 60 and y + char.get_height() < 60:
shop_run()
screen.blit(shop, (0, 0))
screen.blit(font.render("Menu", True, (255,255,255)),(805, 10))
screen.blit(items_font.render("Bombs: "+ str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
# screen.blit(bomb_explosion, (450, 300))
if walkCount + 1 >= 27:
walkCount = 0
if left:
screen.blit(walkLeft[walkCount // 3], (x, y))
walkCount += 1
elif right:
screen.blit(walkRight[walkCount // 3], (x, y))
walkCount += 1
elif down:
screen.blit(char, (x, y))
walkcount = 0
elif up:
screen.blit(char, (x, y))
walkcount = 0
else:
screen.blit(char, (x, y))
walkCount = 0
for pos in bombs:
screen.blit(bomb_pic, pos)
pygame.display.update()
def main():
run = True
# shopper()
pygame.display.set_caption("bomb-mania")
global x
global y
global width
global height
global vel
global isJump
global jumpCount
global left
global right
global down
global up
global walkCount
global bomb_pic
global font
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x + char.get_width() < 60 and y + char.get_height() < 60:
buy = pygame.key.get_pressed()
if buy[pygame.K_b]:
bag["bomb"] += 1
print(bag["bomb"])
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and bag["bomb"] >= 1:
bombs.append(((x + (char.get_width()/2)),( y + (char.get_height() - 20))))
bag["bomb"] -= 1
redrawGameWindow()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel - 15:
x -= vel
left = True
right = False
down = False
up = False
elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
x += vel
left = False
right = True
down = False
up = False
elif keys[pygame.K_DOWN] and y < 600 - height:
y += vel
left = False
right = False
down = True
up = False
elif keys[pygame.K_UP] and y > vel - 15:
y -= vel
left = False
right = False
down = False
up = True
else:
left = False
right = False
down = False
up = False
walkCount = 0
clock.tick(FPS)
pygame.display.flip()
main()
enemies are moving towards the left
able to drop bombs
able to buy bombs from the shop
I'm adding a code below please help me troubleshoot and debug this
import pygame
import random
pygame.font.init()
width = 900
height = 600
screen = pygame.display.set_mode([width, height])
walkRight = [pygame.image.load('R1.png'), pygame.image.load('R2.png'), pygame.image.load('R3.png'),
pygame.image.load('R4.png'), pygame.image.load('R5.png'), pygame.image.load('R6.png'),
pygame.image.load('R7.png'), pygame.image.load('R8.png'), pygame.image.load('R9.png')]
walkLeft = [pygame.image.load('L1.png'), pygame.image.load('L2.png'), pygame.image.load('L3.png'),
pygame.image.load('L4.png'), pygame.image.load('L5.png'), pygame.image.load('L6.png'),
pygame.image.load('L7.png'), pygame.image.load('L8.png'), pygame.image.load('L9.png')]
char = pygame.image.load('standing.png')
bomb_pic = pygame.transform.scale(pygame.image.load('bomb.png'), (20,20))
bomb_explosion = pygame.transform.scale(pygame.image.load('explosion1.png'), (40,40))
# char_rect = char.get_rect()
enemy_Left = [pygame.image.load('L1E.png'), pygame.image.load('L2E.png'), pygame.image.load('L3E.png'),
pygame.image.load('L4E.png'), pygame.image.load('L5E.png'), pygame.image.load('L6E.png'),
pygame.image.load('L7E.png'), pygame.image.load('L8E.png'), pygame.image.load('L9E.png')]
x = 50
y = 50
width = 40
height = 60
vel = 5
isJump = False
jumpCount = 10
left = False
right = False
down = False
up = False
walkCount = 0
enemy_vel = 2
enemy_list = []
shop = pygame.transform.scale(pygame.image.load("shop.png"), (60, 60))
clock = pygame.time.Clock()
FPS = 60
font = pygame.font.Font('freesansbold.ttf', 32)
items_font = pygame.font.Font('freesansbold.ttf', 16)
bombs =[]
bag = {'bomb': 0}
bomb_timer = {"bomb0":0}
bomb_placed = False
bombCount = 1
bombs_dict = {"bomb0": False}
bombTimers = []
explosion_dict = {"explosion0": False}
print(bag["bomb"])
class Button():
def __init__(self, color, x, y, width, height, text=''):
self.color = color
self.x = x
self.y = y
self.width = width
self.height = height
self.text = text
def draw(self, win, outline=None):
# Call this method to draw the button on the screen
if outline:
pygame.draw.rect(win, outline, (self.x - 2, self.y - 2, self.width + 4, self.height + 4), 0)
pygame.draw.rect(win, self.color, (self.x, self.y, self.width, self.height), 0)
if self.text != '':
font = pygame.font.SysFont('comicsans', 20)
text = font.render(self.text, 1, (0, 0, 0))
win.blit(text, (
self.x + (self.width / 2 - text.get_width() / 2), self.y + (self.height / 2 - text.get_height() / 2)))
def shop_run():
shop_bomb = Button((0, 200, 0), 820, 150, 60, 20, text="Bomb_b")
bright_green = (0, 255, 0)
green = (0, 200, 0)
shop_bomb.draw(screen)
def redrawGameWindow():
global walkCount
global font
global bag
global items_font
global enemy_list
global bomb_timer
global bomb_placed
global explosion_dict
screen.fill([166, 166, 166])
for five_enemies in range(6):
random_enemy_location_y = random.randrange(100, 400)
random_enemy_location_x = random.randrange(800, 840)
enemy_list.append([random_enemy_location_x, random_enemy_location_y])
for enemies in range(6):
screen.blit(enemy_Left[enemies], enemy_list[enemies])
enemy_list[enemies][0] -= 0.3
pygame.draw.rect(screen, (0, 0, 0), (800, 0, 100, 600))
if x + char.get_width() < 60 and y + char.get_height() < 60:
shop_run()
screen.blit(shop, (0, 0))
screen.blit(font.render("Menu", True, (255,255,255)),(805, 10))
screen.blit(items_font.render("Bombs: "+ str(bag["bomb"]), True, (255, 255, 255)), (805, 550))
# screen.blit(bomb_explosion, (450, 300))
if walkCount + 1 >= 27:
walkCount = 0
if left:
screen.blit(walkLeft[walkCount // 3], (x, y))
walkCount += 1
elif right:
screen.blit(walkRight[walkCount // 3], (x, y))
walkCount += 1
elif down:
screen.blit(char, (x, y))
walkcount = 0
elif up:
screen.blit(char, (x, y))
walkcount = 0
else:
screen.blit(char, (x, y))
walkCount = 0
for pos in bombs:
for i in bombs_dict:
if bombs_dict["bomb"+str(bag["bomb"])]:
screen.blit(bomb_pic, pos)
bomb_timer["bomb"+str(bag["bomb"])] += clock.get_time()
if bomb_timer["bomb"+str(bag["bomb"])] >= 3000:
bombs_dict["bomb"+str(bag["bomb"])] = False
explosion_dict["explosion" + str(bag["bomb"])] = True
del bombs_dict["bomb"+str(bag["bomb"])]
else:
if explosion_dict["explosion" + str(bag["bomb"])]:
screen.blit(bomb_explosion, (pos))
if bomb_timer["bomb"+str(bag["bomb"])] >= 5000:
explosion_dict["explosion" + str(bag["bomb"])] = False
del explosion_dict["explosion" + str(bag["bomb"])]
pygame.display.update()
def main():
run = True
pygame.display.set_caption("bomb-mania")
global x
global y
global width
global height
global vel
global isJump
global jumpCount
global left
global right
global down
global up
global walkCount
global bomb_pic
global font
global bombs_dict
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if x + char.get_width() < 60 and y + char.get_height() < 60:
buy = pygame.key.get_pressed()
if buy[pygame.K_b]:
bag["bomb"] += 1
bombs_dict["bomb"+str(bag["bomb"])] = False
print(bag["bomb"])
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE and bag["bomb"] >= 1:
bombs.append(((x + (char.get_width()/2)),( y + (char.get_height() - 20))))
bombs_dict["bomb"+str(bag["bomb"])] = True
bag["bomb"] -= 1
redrawGameWindow()
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT] and x > vel - 15:
x -= vel
left = True
right = False
down = False
up = False
elif keys[pygame.K_RIGHT] and x < 800 - vel - width:
x += vel
left = False
right = True
down = False
up = False
elif keys[pygame.K_DOWN] and y < 600 - height:
y += vel
left = False
right = False
down = True
up = False
elif keys[pygame.K_UP] and y > vel - 15:
y -= vel
left = False
right = False
down = False
up = True
else:
left = False
right = False
down = False
up = False
walkCount = 0
clock.tick(FPS)
pygame.display.flip()
main()
Look at pygame.time.Clock(). If you set an FPS rate (say 20), All you have to do is:
myClock = pg.time.Clock()
FPS = 20
bomb_placed = True
bomb_timer = 0
main_loop():
myClock.tick(FPS) # This will keep your FPS at 20 or below so you can estimate time
if bomb_placed:
bomb_timer += myClock.get_time()
if bomb_timer >= 3000:
# Draw the bomb explosion to the screen
if bomb_timer >= 5000:
bomb_timer = 0
bomb_placed = False # Stops displaying the bomb explosion
Hope that helped. Feel free to check out the pygame docs on the subject:
https://www.pygame.org/docs/ref/time.html#pygame.time.Clock.get_time
Example for multiple bombs:
myClock = pg.time.Clock()
FPS = 20
bombA, bombB, bombC = False, False, False
bombA_timer, bombB_timer, bombC_timer = 0, 0, 0
bombs = [bombA, bombB, bombC]
bomb_timers = [bombA_timer, bombB_timer, bombC_timer]
main_loop():
myClock.tick(FPS)
for i in len(bombs):
if bombs[i]:
bomb_timers[i] += myClock.get_time()
if bomb_timers[i] >= 3000:
draw_explosion()
if bomb_timer >= 5000:
bomb_timers[i] = 0
bomb_placed[i] = False
This example simply loops through each of the bomb variables that represent the possible bombs on the screen- in this case a max of 3.
For lots of bombs, first create two dictionaries to hold the values of the bombs (active or not active) and their respective timers. Then loop through each of the bombs and, if the bomb is active, update the timer accordingly:
bombs = {"bomb0": False} # Create dictionaries for the bombs and their timers, with 0-1 entries. (I did one to show you what it looks like).
bombTimers = {bombTimer0: 0}
main_loop():
myClock.tick(FPS)
for i in len(bombs): # For each bomb you have created
bombName = "bomb" + str(i) # get the name of the bomb and corresponding timer
timerName = "bombTimer" + str(i)
if bombs[bombName]: # If the bomg has a value of True:
bombTimers[timerName] += myClock.get_time() # Update the timer
if bombTimers[timerName] >= 3000: # Check if you need to draw the explosion
draw_explosion()
if bomb_timer >= 5000: # Check if the bomb animation is over, and reset the bomb so that it can be used again in the future
bombTimers[timerName] = 0
bombs[bombName] = False
This code works exactly the same as the previous example, but it is much easier to work with. You can easily add more bombs while not starting with an unnecessary amount, like so:
bombCount = 1
bombs = {"bomb0": False}
bombTimers = {"bombTimer0": 0}
main_loop():
if player_placed_bomb(): # When the player drops a bomb in your game:
placed = False # Make a variable saying that you have not activaded the bomb and timer yet
for key, val in bombs: # For each bomb in the bombs dictionary:
if not val: # If the value of that bomb is False (Not being used):
val = True # Activates the bomb that was already created
placed = True # Updates the placed variable indicating that you set a bomb to active
break
if not placed: # After looping through the dictionary, if there were no inactive bomb variables:
bombs["bomb" + str(bombCounter)] = True # Create a new bomb in your dictionary, with a unique name.
bombTimers["bombTimer" + str(bombCounter)] = 0 # Created a new corresponding timer
bombCounter += 1 # Add 1 to the bombCounter, which is used to create the unique name of the next bomb you create.
# Rest of main_loop()
This code ensures that you are working with the smallest dictionaries possible to reduce unneeded computation power. When a bomb explodes, it will become false and it's matching timer reset. The first if loop in the code above ensures that all existing bombs are active, and if not reuses the old bomb and timer. If all bombs are being used, It creates new entries in the dictionaries to allow for more bombs.
I added an unnecesarry amount of comments, but I hope it makes it more understandable for you. Let me know if you have more questions on how this works. If you are unfamiliar with python dict() objects, there are lots of resources on the internet that explain them thoughroughly.
Disclaimer: I haven't actually ran the code, but it should function how it is supposed to. Let me know if it doesn't.
Here are some thoughts on the reformed code:
You use "str(bag["bombs"])" quite a bit in your code- consider defining a variable: b = str(bag["bombs"]), and using that. It would make it much simpler.
I don't know why you delete the "bombs_dict" and "explosions_dict" entries after you are finished with them. The way your loop is set up, I will result in an error. I would suggest rather than deleting the entries, you keep them and attempt to reuse them, as shown in the code snippet above.
Or, if you like deleting them, you need to figure out a way to rename certain keys in your dictionaries so that the names are in order. (if you delete bomb2 from bombs_dict, and you have bomb1 and bomb3, you need to change bomb3 to bomb2 or the loop wont work as intended). You will also need to alter your bombCount variable accordingly.

Pygame surface suddenly inverting variables [duplicate]

This question already has an answer here:
Why is my collision test always returning 'true' and why is the position of the rectangle of the image always wrong (0, 0)?
(1 answer)
Closed 2 years ago.
I was trying to make a game where you chop down some trees and sell it, but in doing so I managed to find a mind-breaking bug - the first tree would invert all the other trees! I know it doesn't make sense, but what i mean is that I have sorted the trees into a class and I have created a variable instance called self.tree_property which determines if the tree has been cut down or not. When you chop down the first tree all the other tree_property get set to True again. When you chop down any other tree, the first tree's tree_property gets set to True again Can anyone tell me how to fix it, and why it is happening?
The code is below:
import pygame
import time
pygame.init()
print('Loading game...')
icon = pygame.image.load('C:\Program Files\Foraging Simulator\icon.png')
market = pygame.image.load('C:\Program Files\Foraging Simulator\market.png')
house = pygame.image.load('C:\Program Files\Foraging Simulator\house.png')
pygame.display.set_icon(icon)
market = pygame.transform.scale(market, (100, 100))
house = pygame.transform.scale(house, (100, 100))
root = pygame.display.set_mode((603, 573))
pygame.display.set_caption("Foraging Simulator")
window_is_open = True
white = (255, 255, 255)
black = (0, 0, 0)
width = 10
leaves_width = 30
height = 20
leaves_height = 10
x = 0
tree_trunk_x = 10
y = 0
tree_trunk_y = 10
vel = 5
brown = (150, 75, 0)
green = (58, 95, 11)
grass = (124, 252, 0)
score = 0
chop_wood = 'C:\Program Files\Foraging Simulator\chopping.mp3'
clock = pygame.time.Clock()
time = 0
happiness = 10
def test(string):
print(string)
def reset_trees():
for tree in trees:
tree.tree_property = True
def open_house():
reset_trees()
score = 0
def open_market():
happiness = score / 2
class Tree: # The class for the trees
def __init__(self, tree_x, tree_y):
self.tree_x = tree_x
self.tree_y = tree_y
self.tree_property = True # Creating instance tree_property
self.trunk = None
self.leaves = None
def destroy(self):
self.tree_property = False
def create_tree(self):
if self.tree_property:
trunk_x = self.tree_x + 10
trunk_y = self.tree_y + 10
self.trunk = pygame.draw.rect(root, brown, (trunk_x, trunk_y, width, height))
self.leaves = pygame.draw.rect(root, green, (self.tree_x, self.tree_y, leaves_width, leaves_height))
def redraw(self):
self.create_tree()
trees = []
for x in range(5):
for y in range (5):
trees.append(Tree(x*50, y*50))
root.fill(grass)
destroy_tree = None
countdown = 3
clock.tick(60)
say = True
print('Loading and attributes finsihed! Using mainloop...')
while window_is_open:
if say:
print('Mainloop loaded! Ready to go.')
say = False
time = pygame.time.delay(100)
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
if market.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
mouse_x, mouse_y = event.pos
if house.get_rect().collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees: # Clicking detection
mouse_x, mouse_y = pygame.mouse.get_pos()
if tree.trunk.collidepoint(mouse_x, mouse_y):
dx = mouse_x - x
dy = mouse_y - y
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]:
x += vel
if keys[pygame.K_LEFT]:
x -= vel
if keys[pygame.K_UP]:
y -= vel
if keys[pygame.K_DOWN]:
y += vel
if destroy_tree != None:
if countdown == 3:
pygame.time.delay(950)
countdown = countdown - 1
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
elif countdown > 0:
pygame.mixer.music.load(chop_wood)
pygame.mixer.music.play()
while pygame.mixer.music.get_busy():
pygame.time.Clock().tick(1)
pygame.time.delay(950)
countdown = countdown - 1
else:
destroy_tree.destroy()
destroy_tree = None
countdown = 3
score = score + 1
font = pygame.font.SysFont('Tahoma', 18, True, False)
count = font.render(str(countdown), True, (0, 0, 0))
screen_score = font.render("Score: " + str(score), True, (0, 0, 0))
rendered_happiness = font.render("Happines: " + str(happiness), True, (0, 0, 0))
root.blit(rendered_happiness, (410, 40))
root.blit(count, (410, 0))
root.blit(screen_score, (410, 20))
rectangle = pygame.draw.rect(root, (0, 0, 0), (x, y, width, 10))
for tree in trees:
tree.redraw()
root.blit(market, (400, 300))
seconds = clock.tick()
pre = time + seconds / 1000
time = int(pre)
root.blit(house, (400, 100))
pygame.display.update()
root.fill(grass)
pygame.quit()
Thanks!
A pygame.Surface object has no location. The position of the rectangle which is returned by get_rect() is always (0, 0).
Note, you specify a location when the surface is blit:
root.blit(house, (400, 100))
You have to set the same location, when you retrieve the rectangle for the collision. You can pass keyword argument values to this function, which are set to the pygame.Rect object:
house.get_rect(topleft = (400, 100))
For instance:
while window_is_open:
# [...]
for event in pygame.event.get():
if event.type == pygame.QUIT:
window_is_open = False
if event.type == pygame.MOUSEBUTTONDOWN:
mouse_x, mouse_y = event.pos
dx = mouse_x - x
dy = mouse_y - y
if market.get_rect(topleft = (400, 300)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_market()
if house.get_rect(topleft = (400, 100)).collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
open_house()
for tree in trees:
if tree.trunk.collidepoint(mouse_x, mouse_y):
if abs(dx) <= 50 and abs(dy) <= 50:
countdown = 3
destroy_tree = tree
# [...]
Furthermore in you have to use the global statement to treat happiness as a variable in global namespace in the function open_market:
def open_market():
global happiness
happiness = score / 2

Categories

Resources