Related
So I've been testing out this code, I found a tutorial on how I can add spritesheets in pygame and decided to try this one:
https://www.spriters-resource.com/3ds/dragonballzextremebutoden/sheet/67257/
I did as the video said and counted the columns and rows, this is my code:
pygame.init()
CLOCK = pygame.time.Clock()
DS = pygame.display.set_mode((W, H))
FPS = 60
class spritesheet:
def __init__(self, filename, cols, rows):
self.sheet = pygame.image.load(filename).convert_alpha()
self.cols = cols
self.rows = rows
self.totalCellCount = cols * rows
self.rect = self.sheet.get_rect()
w = self.cellWidth = self.rect.width / cols
h = self.cellHeight = self.rect.height / rows
hw, hh = self.cellCenter = (w / 2, h / 2)
self.cells = list([(index % cols * w, index / cols * h, w, h) for index in range(self.totalCellCount)])
self.handle = list([
(0,0), (-hw, 0), (-w, 0),
(0, -hh), (-hw, -hh), (-w, -hh),
(0, -h), (-hw, -h), (-w, -h),])
def draw(self, surface, cellIndex, x, y, handle = 0):
surface.blit(self.sheet,
(x + self.handle[handle][0], y + self.handle[handle][1],
self.cells[cellIndex][2], self.cells[cellIndex][3]))
s = spritesheet('Number18.png', 58, 6)
CENTER_HANDLE = 6
Index = 0
#mainloop
run = True
while run:
s.draw(DS, Index % s.totalCellCount, HW, HH, CENTER_HANDLE)
Index +=1
#pygame.draw.circle(DS, WHITE, (HW, HW), 20, 10)
DS.blit(bg,(0,0))
pygame.display.update()
CLOCK.tick(FPS)
DS.fill(BLACK)
The line s = spritesheet("Number18.png", 58, 6) has the numbers 58, 6 which are basically the number of rows and columns I counted on this spritesheet block, but I'm getting problems such as the pygame window on "not responding", the image does not load up and I can't move the pygame screen.
I'm getting problems such as the pygame window on "not responding", [...]
The fist thing what you've to do is to add en event loop to the main loop of the application.
pygame.event removes a pending event message from the queue and returns it.
At least you should handle the QUIT event. Set the value of control variable for the main loop False:
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
The tiles in the sprite sheet don't have equal size. Limit the cells list to some parts of the sheet which have equal size.
Try the following:
class spritesheet:
def __init__(self, filename, py, tw, th, tiles):
self.sheet = pygame.image.load(filename).convert_alpha()
self.py = py
self.tw = tw
self.th = th
self.totalCellCount = tiles
self.rect = self.sheet.get_rect()
w, h = tw, th
hw, hh = self.cellCenter = (w / 2, h / 2)
self.cells = [(1+i*tw, self.py, tw-1, th-1) for i in range(tiles)]
self.handle = list([
(0,0), (-hw, 0), (-w, 0),
(0, -hh), (-hw, -hh), (-w, -hh),
(0, -h), (-hw, -h), (-w, -h),])
s = spritesheet('Number18.png', 1085, 80, 134, 8)
[...] the image does not load up [...]
Make sure that the image is located int the working directory of the application.
If you want to draw a sub-image of a spritsheet then you've to set the area parameter (3rd parameter) of pygame.Surface.blit to the rectangle area of the sub-image:
def draw(self, surface, cellIndex, x, y, handle = 0):
hdl = self.handle[handle]
surface.blit(self.sheet, (x + hdl[0], y + hdl[1]), area=self.cells[cellIndex])
[...] I can't move [...]
You have to change the position of the sprite. Handle the KEYDOWN event. Store the position of the sprite (px, py). Change the position when the K_UP, K_DOWN, K_LEFT or K_RIGHT key is pressed:
run = True
px, py, speed = HW, HH, 10
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
py -= speed
elif event.key == pygame.K_DOWN:
py += speed
elif event.key == pygame.K_LEFT:
px -= speed
elif event.key == pygame.K_RIGHT:
px += speed
Minimal Sprite Sheet example:
import os
import pygame
class SpriteSheet:
def __init__(self, filename, px, py, tw, th, m, tiles, color_key = None):
self.sheet = pygame.image.load(filename)
if color_key:
self.sheet = self.sheet.convert()
self.sheet.set_colorkey(color_key)
else:
self.sheet = self.sheet.convert_alpha()
self.cells = [(px + tw * i, py, tw-m, th) for i in range(tiles)]
self.index = 0
def update(self):
self.tile_rect = self.cells[self.index % len(self.cells)]
self.index += 1
def draw(self, surface, x, y):
rect = pygame.Rect(self.tile_rect)
rect.center = (x, y)
surface.blit(self.sheet, rect, self.tile_rect)
pygame.init()
window = pygame.display.set_mode((400, 300))
clock = pygame.time.Clock()
sprite_sheet = SpriteSheet('awesomepossum sheet.bmp', 18, 580, 64, 66, 0, 6, (0, 128, 0))
run = True
while run:
clock.tick(10)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
sprite_sheet.update()
window.fill(0)
sprite_sheet.draw(window, *window.get_rect().center)
pygame.display.update()
pygame.quit()
exit()
Sprite sheet form OpenGameArt.org:
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.
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:
# [...]
I've been struggling with this problem for a couple days now. I want to eventually convert my snake game script to an exe, for my friends to play. I want the snake to move at the same speed, no matter the size of the window.
For Example: My window size right now is (400, 400). If I increase the size to (800, 800), the snake will move slower. However, the snake velocity is constant at 20 pixels. It almost seems like my main game loop is looping at a slower pace as the window size increases.
I know that the snake has more pixels to traverse with a bigger window size, but how does that affect the snake velocity at all? I'm thinking the solution lies somewhere in the speed at which I'm drawing the snake to the screen, but can't be sure.
import pygame
import sys
import random
import math
import time
pygame.display.set_caption('Snake')
pygame.font.init()
game_running = True
width = 400
height = 400
size = (width, height)
window = pygame.display.set_mode(size) # our surface type
pygame.display.set_caption("Snake Game by Nick Rinaldi")
class Food:
def __init__(self, block_size, surface, x_loc, y_loc): # pass in color and random_x/random_y. block size is a constant
self.block_size = block_size
self.surface = surface # green
self.x_loc = x_loc
self.y_loc = y_loc
self.mask = pygame.mask.from_surface(self.surface)
def draw(self, window):
window.blit(self.surface, (self.x_loc, self.y_loc))
class Snake:
def __init__(self, block_size, surface, x_loc, y_loc):
self.block_size = block_size
self.surface = surface # red
self.x_loc = x_loc
self.y_loc = y_loc
self.body = []
self.direction = None
self.velocity = 20
self.mask = pygame.mask.from_surface(self.surface)
def draw(self, color, window, block_size):
self.seg = []
self.head = pygame.Rect(self.x_loc, self.y_loc, block_size, block_size)
pygame.draw.rect(window, color, self.head)
if len(self.body) > 0:
for unit in self.body:
segment = pygame.Rect(unit[0], unit[1], block_size, block_size)
pygame.draw.rect(window, color, segment)
self.seg.append(segment)
def add_unit(self):
if len(self.body) != 0:
index = len(self.body) - 1
x = self.body[index][0]
y = self.body[index][1]
self.body.append([x, y])
else:
self.body.append([1000, 1000])
def move(self, step):
for index in range(len(self.body) -1, 0, -1):
x = self.body[index-1][0]
y = self.body[index-1][1]
self.body[index] = [x, y]
if len(self.body) > 0:
self.body[0] = [self.x_loc, self.y_loc]
if self.direction == "right": # if specific constant, keep moving in direction
self.x_loc += self.velocity * step
if self.direction == "left":
self.x_loc -= self.velocity * step
if self.direction == "down":
self.y_loc += self.velocity * step
if self.direction == "up":
self.y_loc -= self.velocity * step
def collision(self, obj):
return collide(food)
def gameOver(snake):
white = pygame.Color(255, 255, 255)
display = True
while display:
window.fill(white)
score_font = pygame.font.SysFont("Courier New", 16)
score_label = score_font.render("Your score was: " + str(len(snake.body) + 1), 1, (0, 0, 0))
replay_label = score_font.render("To replay, click the mouse button", 1, (0, 0, 0))
window.blit(score_label, (50, 100))
window.blit(replay_label, (50, 130))
pygame.display.update()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
sys.exit()
clock = pygame.time.Clock()
def main():
game_over = False
x = 20 # x position
y = 20 # y position
block_snakes = []
pygame.init()
clock = pygame.time.Clock()
red = pygame.Color(255, 0, 0)
green = pygame.Color(0, 255, 0)
white = pygame.Color(255, 255, 255)
black = pygame.Color(0, 0, 0)
block_size = 20
randx_green = random.randrange(0, width, 20)
randy_green = random.randrange(0, height, 20)
randx_red = random.randrange(0, width, 20)
randy_red = random.randrange(0, height, 20)
red_square = pygame.Surface((block_size, block_size))
red_square.fill(red)
green_square = pygame.Surface((block_size, block_size))
green_square.fill(green)
snake = Snake(block_size, red_square, 20, 20) # create snake instance
food = Food(block_size, green_square, randx_green, randy_green) # create food instance
def redraw_window():
draw_grid(window, height, width, white)
while game_running:
dt = clock.tick(30) # time passed between each call
step = dt/1000
print(step)
FPS = 60
window.fill(black)
food.draw(window)
snake.draw(red, window, block_size)
redraw_window()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
keys = pygame.key.get_pressed()
if keys[pygame.K_RIGHT]: # sets direction attribute as a constant
snake.direction = "right"
if keys[pygame.K_LEFT]:
snake.direction = "left"
if keys[pygame.K_DOWN]:
snake.direction = "down"
if keys[pygame.K_UP]:
snake.direction = "up"
snake.move(step)
collision = collide_food(snake.x_loc, snake.y_loc, food.x_loc, food.y_loc)
if collision:
ac_rand_x = random.randrange(0, width, 20) # after collision, random x
ac_rand_y = random.randrange(0, height, 20) # after collision, random y
# check snake.direction.
food = Food(block_size, green_square, ac_rand_x, ac_rand_y)
food.draw(window)
snake.add_unit()
wall_collide = collide_wall(snake.x_loc, snake.y_loc)
if wall_collide:
gameOver(snake)
# break
for block in snake.body:
if snake.x_loc == block[0] and snake.y_loc == block[1]:
gameOver(snake)
pygame.display.update()
# clock.tick(FPS)
def collide_food(snake_x, snake_y, obj_x, obj_y):
distance = math.sqrt((math.pow(snake_x - obj_x, 2)) + (math.pow(snake_y - obj_y, 2)))
if distance < 20:
return True
else:
return False
def collide_wall(snake_x, snake_y):
if snake_x > width:
game_over = True
return game_over
if snake_y > height:
game_over = True
return game_over
if snake_x < 0:
game_over = True
return game_over
if snake_y < 0:
game_over = True
return game_over
def collide_self(snake_x, snake_y, body_x, body_y):
if (snake_x and snake_y) == (body_x and body_y):
return True
else:
return False
def draw_grid(window, height, width, color):
x = 0
y = 0
grid_blocks = 20
for i in range(height):
x += 20
pygame.draw.line(window, color, (x, 0), (x, height), 1)
for j in range(width):
y += 20
pygame.draw.line(window, color, (0, y), (height, y), 1)
# pygame.display.update()
def display_score():
score_font = pygame.font.SysFont()
def main_menu(width, height):
clock = pygame.time.Clock()
FPS = 60
width = width
height = height
run = True
title_font = pygame.font.SysFont("Courier New", 16)
title_font.set_bold(True)
white = pygame.Color(255, 255, 255)
while run:
window.fill(white)
title_label = title_font.render("Snake Game by Nick Rinaldi ", 1, (0, 0, 0))
sponser_label = title_font.render("Sponsored by #goodproblemsnyc", 1, (0, 0, 0))
window.blit(title_label, ((width/4, height/4)))
window.blit(sponser_label, ((width/4, height/4 + 30)))
pygame.display.update()
for event in pygame.event.get(): # if we hit "x" to close out the game, close out the game.
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.MOUSEBUTTONDOWN:
main()
pygame.quit()
main_menu(width, height)```
The bottleneck in your game is the function draw_grid, which draws far too many lines out of the window.
def draw_grid(window, height, width, color):
x = 0
y = 0
grid_blocks = 20
for i in range(height):
x += 20
pygame.draw.line(window, color, (x, 0), (x, height), 1)
for j in range(width):
y += 20
pygame.draw.line(window, color, (0, y), (height, y), 1)
If you draw a line outside the window, the statement does not draw anything, nevertheless the nested for loops still run.
Furthermore, you don't need a nested loops. You don't want to draw 19 horizontal lines for each vertical line. You want to draw 19 vertical and 19 horizontal lines. Hence 1 for-loop is enough.
Use the step argument of range to define the list of positions for the vertical and horizontal lines
def draw_grid(window, height, width, color):
tile_size = 20
for p in range(tile_size, height, tile_size):
pygame.draw.line(window, color, (p, 0), (p, height), 1)
pygame.draw.line(window, color, (0, p), (height, p), 1)
if the size is 400 * 400 pixels, the total pixels 160000 so moving at a 20 pixel rate, because there are less pixels the an 800 * 800 board (320000 pixels) it looks like you are going faster because there are less pixels. Find an equation to calculate the correct speed on differently sized boards.
Sincerely,
Zac
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.