Python Pygame changing two positions based on a angle [duplicate] - python
This question already has answers here:
How to set the pivot point (center of rotation) for pygame.transform.rotate()?
(5 answers)
How can you rotate an image around an off center pivot in Pygame
(1 answer)
Closed 4 months ago.
Basically im trying to make a line point out out of the muzzle of this gun.
Picture of player, gun and make shift cross hair for reference (not part of the game)
How i made the gun was by get_rect() the gun surface, and setting the rectangles pos values to the players right side. I also made it so the gun will rotate to always point towards the mouse, but when the cross hair goes past the players right side (the angle goes past 90 or -90) the gun will flip to the players left side. Sorry for very messy code, im still quite new.
gunxposmdl = Player.player_rect.right
gunyposmdl = Player.player_rect.top
gunyposmdl += 20
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - gunxposmdl, mouse_y - gunyposmdl
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
if angle > 90 or angle < -90:
if angle > 0:
angle =-abs(angle)
else:
angle = abs(angle)
rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle))
rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl))
player_surf_cpy = Player.player_surf.copy()
player_fliped = pygame.transform.flip(player_surf_cpy, False, True)
flipgunxposmdl = Player.player_rect.left
rshotgunmdlcpy = rshotgunmdl.copy()
rshotgunmdlfliped = pygame.transform.flip(rshotgunmdlcpy, False, True )
rshotgunrectfliped = rshotgunmdlfliped.get_rect(midright=(flipgunxposmdl, gunyposmdl))
screen.blit(player_fliped, Player.player_rect)
screen.blit(rshotgunmdlfliped, rshotgunrectfliped)
sg_muzzle_x, sg_muzzle_y = rshotgunrectfliped.midleft[0], rshotgunrectfliped.midleft[1]
else:
rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle))
rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl))
screen.blit(Player.player_surf, Player.player_rect)
screen.blit(rshotgunmdl, rshotgunrect)
sg_muzzle_x, sg_muzzle_y = rshotgunrect.midright[0], rshotgunrect.midright[1]
#Cast rays
pygame.draw.line(screen, ("Green"), (sg_muzzle_x, sg_muzzle_y), (sg_muzzle_x, sg_muzzle_y), 3)
The problem with this is that when the guns surface is rotated, the rectangle doesnt rotate with it. So when i rotate the gun with the mouse, the line still gets drawn where the rectangles right side is and not where the muzzle is. I need the line to stick out of the muzzle as a visual on where the gun is pointing. Im going to be using this to implement hit casting.
E.g.
Image example (actual in game cross hair this time)
Could somebody please help me make it so the line is coming out of the muzzle in both examples please?
full code:
#!/bin/env python3
import pygame
from sys import exit
import time
import math
pygame.init()
video_infos = pygame.display.Info()
width, height = video_infos.current_w, video_infos.current_h
screen = pygame.display.set_mode((width, height), pygame.FULLSCREEN,
pygame.RESIZABLE)
pygame.display.set_caption('2D Quake')
clock = pygame.time.Clock()
keys = pygame.key.get_pressed()
mousec = pygame.image.load("textures/UI/crossh.png").convert_alpha()
pygame.mouse.set_visible(False)
backround = pygame.Surface((width, height)).convert_alpha()
backround.fill('White')
sbar = pygame.image.load("textures/UI/HUD/SBAR.png").convert_alpha()
class Player:
player_surf = pygame.Surface((50,100))
player_surf.fill('Black')
player_rect = player_surf.get_rect(midbottom = (300, height))
player_gravity = 0
player = {"Inv": [1,1,0], "Hand": 1, "Health": 100}
class Weapons:
shotgun = pygame.image.load("textures/weapons/SHOTGUN.png").convert_alpha()
class Enemies:
esoldier_surf = pygame.Surface((50,100))
esoldier_surf.fill('Black')
esoldier_rect = esoldier_surf.get_rect(midbottom = (800, height))
esoldier = {"Health": 100}
print(width, height)
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
pygame.quit()
sys.exit()
if event.key == pygame.K_h:
Player.player["Health"] -= 1
#Backround
screen.blit(backround,(0,0))
#Enemies
screen.blit(Enemies.esoldier_surf, Enemies.esoldier_rect)
#Player
healthnum = Player.player["Health"]
healthnums = [int(x) for x in str(healthnum)]
Player.player_gravity += 1
Player.player_rect.y += Player.player_gravity
if Player.player_rect.bottom >= height: Player.player_rect.bottom = height
keys = pygame.key.get_pressed()
if keys[pygame.K_d] == 1:
if keys[pygame.K_LCTRL] == 1:
Player.player_rect.x += 9
else:
Player.player_rect.x += 5
if keys[pygame.K_a] == 1:
if keys[pygame.K_LCTRL] == 1:
Player.player_rect.x -= 9
else:
Player.player_rect.x -= 5
if keys[pygame.K_SPACE] == 1 and Player.player_rect.bottom >= height:
Player.player_gravity = -20
gunxposmdl = Player.player_rect.right
gunyposmdl = Player.player_rect.top
gunyposmdl += 20
mouse_x, mouse_y = pygame.mouse.get_pos()
rel_x, rel_y = mouse_x - gunxposmdl, mouse_y - gunyposmdl
angle = (180 / math.pi) * -math.atan2(rel_y, rel_x)
if angle > 90 or angle < -90:
if angle > 0:
angle =-abs(angle)
else:
angle = abs(angle)
rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle))
rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl))
player_surf_cpy = Player.player_surf.copy()
player_fliped = pygame.transform.flip(player_surf_cpy, False, True)
flipgunxposmdl = Player.player_rect.left
rshotgunmdlcpy = rshotgunmdl.copy()
rshotgunmdlfliped = pygame.transform.flip(rshotgunmdlcpy, False, True )
rshotgunrectfliped = rshotgunmdlfliped.get_rect(midright=(flipgunxposmdl,
gunyposmdl))
screen.blit(player_fliped, Player.player_rect)
screen.blit(rshotgunmdlfliped, rshotgunrectfliped)
sg_muzzle_x, sg_muzzle_y = rshotgunrectfliped.midleft[0],
rshotgunrectfliped.midleft[1]
else:
rshotgunmdl = pygame.transform.rotate(Weapons.shotgun, int(angle))
rshotgunrect = rshotgunmdl.get_rect(midleft=(gunxposmdl,gunyposmdl))
screen.blit(Player.player_surf, Player.player_rect)
screen.blit(rshotgunmdl, rshotgunrect)
sg_muzzle_x, sg_muzzle_y = rshotgunrect.midright[0], rshotgunrect.midright[1]
#Cast rays
pygame.draw.line(screen, ("Green"), (sg_muzzle_x, sg_muzzle_y), (sg_muzzle_x,
sg_muzzle_y), 3)
#UI
mpos = pygame.mouse.get_pos()
sbarxpos = width - 600
sbarxpos = sbarxpos / 2
sbarypos = height - 46
screen.blit(sbar,(sbarxpos,sbarypos))
if len(healthnums) == 3:
hnum1 = pygame.image.load("textures/UI/HUD/NUM_1.png").convert_alpha()
hnum2 = pygame.image.load("textures/UI/HUD/NUM_0.png").convert_alpha()
hnum3 = pygame.image.load("textures/UI/HUD/NUM_0.png").convert_alpha()
if width == 1366:
screen.blit(hnum1,(645,727))
screen.blit(hnum2,(685,727))
screen.blit(hnum3,(727,727))
elif width == 1920:
numypos = height - 44
#screen.blit(hnum1,(, numypos))
#screen.blit(hnum2,(, numypos))
#screen.blit(hnum3,(, numypos))
if len(healthnums) == 2:
numa = healthnums[0]
numb = healthnums[1]
hnum2 = pygame.image.load(f"textures/UI/HUD/NUM_{numa}.png").convert_alpha()
hnum3 = pygame.image.load(f"textures/UI/HUD/NUM_{numb}.png").convert_alpha()
if width == 1366:
screen.blit(hnum2,(685,727))
screen.blit(hnum3,(727,727))
elif width == 1920:
numypos = height - 44
if len(healthnums) == 1:
numb = healthnums[0]
hnum3 = pygame.image.load(f"textures/UI/HUD/NUM_{numb}.png").convert_alpha()
screen.blit(hnum3,(727,727))
screen.blit(mousec, (mpos))
pygame.display.update()
clock.tick(60)
Link to Source files: https://www.dropbox.com/s/4tuk7v675go1urv/2DQuake.zip?dl=0
Related
Python PyGame append object on the screen multiple times with different movement
I am trying to make an object with very random movement which is fine, that works. But if I want the same object to append to the screen 10 times then it does not work or i dont know how to do that. The object is called ant. I am sorry if its a bad, unreadable code im still a beginner in pygame. So the ant object(which is a small red square), need to be visible on the screen 10 times, each ant need to move randomly. I appreciate all your answers thanks. import pygame as pg import random as rd import math import sys from pygame.time import Clock pg.init() screen = pg.display.set_mode((500,500)) title = pg.display.set_caption("TEST") #REDRAWING SCREEN def redraw(): screen.fill((0,0,0)) pg.display.update() #FOOD def food(): food_x = 200 food_y = 200 FOOD_COLOR = (255,255,255,0) FWIDTH = 15 fooddraw = pg.draw.circle(screen, FOOD_COLOR, (food_x, food_y), FWIDTH) #ANT VARIABLES ant_x = 200 ant_y = 200 COLOR = (220,20,60,0) ANT_WIDTH = 5 #RANDOM MOVES USING VECTOR def move(): global ant_x,ant_y x_boundary = 1000 y_boundary = 1000 pos = pg.math.Vector2(ant_x, ant_y) ant_x = rd.randrange(0, x_boundary) ant_y = rd.randrange(0, y_boundary) speed = 1 maxdist = 1 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 2).rotate(rd.randrange(360)) pos += direction * speed dist -= speed ant_x, ant_y = round(pos.x), round(pos.y) if dist <= 0: dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_x >= screen.get_width(): #IF ANTS X IS OFF THE SCREEN WIDTH THEN RESET THE X POSITION ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_y >= screen.get_height(): #RESET POSITION IF ANTS Y IS OFF THE SCREEN HEIGHT ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass #TRIED TO DUPLICATE ANT 10 TIMES WITH DIFFERENT MOVES BUT DOESNT WORK, I STILL SEE ONLY 1 ANT ON THE SCREEN ant_spawn = [] for i in range(10): for j in range(2): ant_x = 250 ant_y = 250 ant_spawn.append(j) while True: FPS = pg.time.Clock() redraw() for event in pg.event.get(): if event.type == pg.QUIT: pg.quit() #APPEND TO SCREEN for ants in ant_spawn: antdraw = pg.draw.rect(screen, COLOR, (ant_x, ant_y,3,3)) #MOVE FUNCTION ACTIVATE move() pg.display.update() FPS.tick(60)
You have to create a list of coordinates instead a list of indices: ant_spawn = [] for i in range(10): ant_x = 250 ant_y = 250 ant_spawn.append([ant_x, ant_y]) Move and draw the ants in loops: for ants in ant_spawn: move(ants) for ants in ant_spawn: pg.draw.rect(screen, COLOR, (*ants, 3, 3)) The move function changes the coordinates of an ant: def move(ants): ant_x, ant_y = ants # change ant_x and ant_y # [...] ants[0] = ant_x ants[1] = ant_y Complete example (start with space bar) import pygame as pg import random as rd pg.init() screen = pg.display.set_mode((500,500)) title = pg.display.set_caption("TEST") ant_x = 200 ant_y = 200 COLOR = (220,20,60,0) ANT_WIDTH = 5 def move(ants): ant_x, ant_y = ants x_boundary = 1000 y_boundary = 1000 pos = pg.math.Vector2(ant_x, ant_y) c = rd.randrange(0, x_boundary) ant_y = rd.randrange(0, y_boundary) speed = 1 maxdist = 1 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 2).rotate(rd.randrange(360)) pos += direction * speed dist -= speed ant_x, ant_y = round(pos.x), round(pos.y) if dist <= 0: dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_x >= screen.get_width(): #IF ANTS X IS OFF THE SCREEN WIDTH THEN RESET THE X POSITION ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass if ant_y >= screen.get_height(): #RESET POSITION IF ANTS Y IS OFF THE SCREEN HEIGHT ant_x = 200 dist = rd.randrange(maxdist) direction = pg.math.Vector2(1, 0).rotate(rd.randrange(360)) else: pass ants[0] = ant_x ants[1] = ant_y ant_spawn = [] for i in range(10): ant_x = 250 ant_y = 250 ant_spawn.append([ant_x, ant_y]) move_ants = False run = True while run: FPS = pg.time.Clock() for event in pg.event.get(): if event.type == pg.QUIT: run = False if event.type == pg.KEYDOWN and event.key == pg.K_SPACE: move_ants = not move_ants if move_ants: for ants in ant_spawn: ants = move(ants) screen.fill((0,0,0)) for ants in ant_spawn: #pg.draw.rect(screen, COLOR, (*ants, 3, 3)) pg.draw.circle(screen, COLOR, ants, 5) pg.display.update() FPS.tick(60) pg.quit()
Pygame - Fix issue with pause menu? [duplicate]
I've developed a Python code and am looking for improvements and how to add a pause option. I repeat the exact same lines one after another although I don't know an easier way to do it. import math, pygame, random, sys, turtle from itertools import cycle from datetime import datetime from pygame import gfxdraw from pygame.locals import * def print_text(surface, font, text, surf_rect, x = 0, y = 0, center = False, color = (255,215,0)): if not center: textimage = font.render(text, True, color) surface.blit(textimage, (x, y)) else: textimage = font.render(text, True, color) text_rect = textimage.get_rect() x = (surf_rect.width // 2) - (text_rect.width // 2 ) surface.blit(textimage, (x, y)) def game_is_over(surface, font, ticks): timer = ticks surf_rect = surface.get_rect() surf_height = surf_rect.height surf_width = surf_rect.width print_text(screen, font, "Y O U G O T Y E E T E D (Game Over)", surf_rect, y = 260,\ center = True) pygame.display.update() while True: ticks = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if ticks > timer + 3000: break def next_level(level): level += 1 if level > 6: level = 6 return level #The Level Creator def load_level(level): invaders, colors = [], [] start_intx, end_intx, increment_intx = 85, 725, 40 start_inty, end_inty, increment_inty = 60, 60, 30 end_inty = end_inty + level * 30 # 30 being the number of rows / intruders color_val = 256 / end_inty #For Colour Repetition for x in range(start_intx, end_intx, increment_intx): for y in range(start_inty, end_inty, increment_inty): invaders.append(pygame.Rect(x, y, 30, 15)) colors.append(((x * 0.35) % 256, (y * color_val) % 256)) return invaders, colors, len(invaders) def draw_title_invader(): rect = Rect(285,247,230,115)#INVATOR rect2 = Rect(0,0,230,115) rect3 = Rect(340,120,120,128)#TOP OF HAT rect4 = Rect(300,200,200,48)#BOT OF HAT rect5 = Rect(340,182,120,18)#LINE IN HAT rect_width = 230 a = 175 b = 55 pygame.draw.rect(backbuffer, MGOLD,rect) #The Left Eye in Title Screen pygame.draw.circle(backbuffer,(0,255,255), (rect.x+46,rect.y+30), 23) #The Right Eye in Title Screen pygame.draw.circle(backbuffer,(0,255,255),(rect.x+rect_width-46,rect.y+30)\ ,23) #The Left Side Mouth in Title Screen pygame.draw.line(backbuffer, RED, (rect.x+46, rect.y+92),\ (rect.x + 115, rect.y + 61), 2) #The Right Side Mouth in Title Screen pygame.draw.line(backbuffer, RED, (rect.x+rect_width-46,\ rect.y+92), (rect.x+rect_width-115,\ rect.y+61), 2) #The Right Eye pygame.draw.circle(backbuffer,RED,(rect.x+rect_width-115,rect.y+65)\ ,23) #The Hat pygame.draw.rect(backbuffer, DIMGRAY,(340,120,120,128)) pygame.draw.rect(backbuffer, DIMGRAY,(300,200,200,48)) pygame.draw.rect(backbuffer, WHITE,(340,182,120,18)) def draw_bonus_invader(i, bonus_color, bx, bonus_x): x, y = bonus_invader.x, bonus_invader.y pygame.draw.circle(backbuffer, bonus_color, (x+bx, y+7), 2) if i == 0: pygame.draw.circle(backbuffer, bonus_color, (bonus_invader.x+bx,bonus_invader.y+7),2) if i == 1: pygame.draw.circle(backbuffer, bonus_color, (bonus_invader.x+bx,bonus_invader.y+7),2) if i == 2: pygame.draw.circle(backbuffer, bonus_color, (bonus_invader.x+bx,bonus_invader.y+7),2) if i == 3: pygame.draw.circle(backbuffer, bonus_color, (bonus_invader.x+bx,bonus_invader.y+7),2) if i == 4: pygame.draw.circle(backbuffer, bonus_color, (bonus_invader.x+bx,bonus_invader.y+7),2) if i == 5: bx = next(bonus_x) def draw_invader(backbuffer, rect, a, b, animate_invaders, ticks,\ animation_time): invader_width = 30 #THe Intruder In Game pygame.draw.rect(backbuffer, MGOLD, rect) #CONSOLE GRAY pygame.draw.rect(backbuffer, DIMGRAY,(0,510,800,110)) #Left Eye in game pygame.gfxdraw.filled_circle(backbuffer, rect.x + 6, rect.y + 4, 3, \ RED) #Right EYe in game pygame.gfxdraw.filled_circle(backbuffer, rect.x + invader_width - 7,\ rect.y + 4, 3, RED) #The draw animation (if needed) if animate_invaders: pygame.gfxdraw.filled_trigon(backbuffer, rect.x+6, rect.y + 12,\ rect.x + 14, rect.y + 4, rect.x +\ invader_width - 7, rect.y + 12, RED) else: #The Left Side of the mouth pygame.gfxdraw.line(backbuffer, rect.x + 6, rect.y + 12,\ rect.x + 15, rect.y + 8, RED) #The Right Side of the mouth pygame.gfxdraw.line(backbuffer, rect.x + invader_width - 7,\ rect.y + 12, rect.x + invader_width - 15,\ rect.y + 8, RED) if ticks > animation_time + 200: animate_invaders = False return animate_invaders pygame.init() pygame.mixer.init() # not always called by pygame.init() screen = pygame.display.set_mode((800, 600)) pygame.display.set_caption("Yeet The Intruders") fpsclock = pygame.time.Clock() #get screen metrics the_screen = screen.get_rect() screen_width = the_screen.width screen_height = the_screen.height backbuffer = pygame.Surface((the_screen.width, the_screen.height)) # fonts font1 = pygame.font.SysFont(None, 30) font2 = pygame.font.SysFont("Impact", 54) font3 = pygame.font.SysFont("Impact", 36) # User event frequencies RELOAD_SPEED = 400 MOVE_SIDEWAYS = 1000 MOVE_DOWN = 1000 BONUS_FREQ = 10000 INV_SHOOT_FREQ = 500 # create user events move_invaders_sideways = pygame.USEREVENT + 1 move_invaders_down = pygame.USEREVENT + 2 reload = pygame.USEREVENT + 3 invader_shoot = pygame.USEREVENT + 4 bonus = pygame.USEREVENT + 5 # event timers pygame.time.set_timer(move_invaders_down, 0) pygame.time.set_timer(move_invaders_sideways, MOVE_SIDEWAYS) pygame.time.set_timer(reload, RELOAD_SPEED) pygame.time.set_timer(invader_shoot, INV_SHOOT_FREQ) pygame.time.set_timer(bonus, BONUS_FREQ) #List of Colours used BLACK = (0,0,0) WHITE = (255,255,255) RED = (255,0,0) GREEN = (0,255,0) BLUE = (0,0,255) YELLOW = (255,255,0) DIMGRAY = (105,105,105) MGOLD = (212,175,55) shots, invader_shots, inv_shot_colors, bonus_invaders = [], [], [], [] #MY Space Ship player = Rect(380,578,42,20) player_gun = Rect(player.x + 18,player.y - 4, 6, 4) # make screen rect for purposes of text-centering etc the_screen = screen.get_rect() # invader animation variables animation_time = 0 animate_invaders = False invader_width = 30 invader_height = 15 # flashing text vars the_text = cycle(["Press Enter To Play, Yeet Master...", ""]) insert = next(the_text) flash_timer = 0 # flashing bonus item vars y1,y2,y3,y4,y5,y6 = (255,255,0), (225,225,0), (195,195,0), (165,165,0),\ (135,135,0), (105,105,0) bonus_colors = cycle([y1,y2,y3,y4,y5,y6]) bonus_color = next(bonus_colors) bonus_x = cycle([4,11,18,25,32,39]) # change draw x coord bonus_timer = 0 # used to control frequency of changes # vars for moving invaders down move_right, move_down, reloaded = True, True, True vert_steps = 0 side_steps = 0 moved_down = False invaders_paused = False invaders = 0 # prevents error until list is created initial_invaders = 0 # use to manage freq of inv shots as invaders removed shoot_level = 1 # manage freq of shots # various gameplay variables game_over = True score = 0 lives = 2 level = 0 playing = False # event loop while True: ticks = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYUP: if event.key == pygame.K_1 and not game_over: print("Next level") if event.type == invader_shoot and not game_over: i = random.randint(0, len(invaders)-1) shot_from = invaders[i] a, b = colors[i] invader_fired = True invader_shots.append(Rect(shot_from.x, shot_from.y, 5, 7)) inv_shot_colors.append(RED) if event.type == reload and not game_over: reloaded = True pygame.time.set_timer(reload, 0) if event.type == move_invaders_sideways and not game_over: if move_right: for invader in invaders: invader.move_ip(10,0) side_steps += 1 else: for invader in invaders: invader.move_ip(-10,0) side_steps -= 1 if side_steps == 6 or side_steps == -6: if vert_steps <= 31: # and not moved_down pygame.time.set_timer(move_invaders_sideways, 0) pygame.time.set_timer(move_invaders_down, MOVE_DOWN) # keep invaders moving horizontally after 31 down movements else: move_right = not move_right if event.type == move_invaders_down and not game_over: #for i in range(20): print("down event") move_right = not move_right animate_invaders = True animation_time = ticks # reset move_sideways timer pygame.time.set_timer(move_invaders_sideways, MOVE_SIDEWAYS) # cancel move_down timer pygame.time.set_timer(move_invaders_down, 0) for invader in invaders: invader.move_ip(0,10) vert_steps += 1 if event.type == bonus and not game_over: #a = Rect(769,20,45,15) bonus_invaders.append(Rect(797,20,45,15)) # keyboard polling pressed = pygame.key.get_pressed() if pressed[K_ESCAPE]: pygame.quit(), sys.exit() elif pressed[K_RETURN]: if game_over: game_over = False elif pressed[K_d] or pressed[K_RIGHT]:player.move_ip((8, 0)) #player_gun.move_ip((8,0)) elif pressed[K_a] or pressed[K_LEFT]: player.move_ip((-8, 0)) if pressed[K_SPACE]: if reloaded: reloaded = False # create timeout of RELOAD_SPEED pygame.time.set_timer(reload, RELOAD_SPEED) # shrink copy of player rect to imitate a missile missile = player.copy().inflate(-38, -10) # spawn missile higher to ensure appears missile fired from 'gun' # when the ship is moving horizontally missile.y -= 9 shots.append(missile) #missile_sound.play() backbuffer.fill(BLACK) if not game_over: playing = True if level == 0: level = next_level(level) invaders, colors, initial_invaders = load_level(level) move_right, move_down, reloaded = True, True, True vert_steps = 0 side_steps = 0 moved_down = False invaders_paused = False pygame.time.set_timer(invader_shoot, 500) shoot_level = 1 for shot in invader_shots: shot.move_ip((0,random.randint(5,11))) if not backbuffer.get_rect().contains(shot): i = invader_shots.index(shot) del invader_shots[i] del inv_shot_colors[i] if shot.colliderect(player) and shot.y < the_screen.height -10: lives -= 1 if lives < 0: lives = 0 game_over = True i = invader_shots.index(shot) del invader_shots[i] del inv_shot_colors[i] for shot in shots: shot.move_ip((0, -8)) for inv_shot in invader_shots: if inv_shot.colliderect(shot): shots.remove(shot) i = invader_shots.index(inv_shot) del invader_shots[i] del inv_shot_colors[i] for b_invader in bonus_invaders: if b_invader.colliderect(shot): shots.remove(shot) i = bonus_invaders.index(b_invader) del bonus_invaders[i] score += 1 if not backbuffer.get_rect().contains(shot): shots.remove(shot) else: hit = False for invader in invaders: if invader.colliderect(shot): score += 1 hit = True i = invaders.index(invader) del invaders[i] del colors[i] if hit: shots.remove(shot) # move bonus invader for bonus_invader in bonus_invaders: bonus_invader.move_ip((-4,0 )) ## if not screen.get_rect().contains(bonus_invader): ## bonus_invaders.remove(bonus_invader) if bonus_invader.x < -55: bonus_invaders.remove(bonus_invader) # check if all invaders killed, if so, move to next level if len(invaders) == 0: level = next_level(level) invaders, colors, initial_invaders = load_level(level) move_right, move_down, reloaded = True, True, True vert_steps = 0 side_steps = 0 moved_down = False invaders_paused = False pygame.time.set_timer(invader_shoot, 500) shoot_level = 1 # adjust shot freq when invader numbers decrease if len(invaders) < initial_invaders*.75 and shoot_level == 1: pygame.time.set_timer(invader_shoot, 750) shoot_level = 2 elif len(invaders) < initial_invaders*.5 and shoot_level == 2: pygame.time.set_timer(invader_shoot, 1000) shoot_level = 3 elif len(invaders) < initial_invaders*.25 and shoot_level == 3: pygame.time.set_timer(invader_shoot, 1500) shoot_level = 4 # draw invaders for rect, (a, b) in zip(invaders, colors): animate_invaders = draw_invader(backbuffer, rect, a, b,\ animate_invaders, ticks, \ animation_time) # draw bonus invaders if ticks > bonus_timer + 169: bonus_timer = ticks # change colors every 169ms approx for bonus_invader in bonus_invaders: pygame.draw.rect(backbuffer, (0,0,0,0), bonus_invader) pygame.draw.ellipse(backbuffer,MGOLD,bonus_invader) for i in range(6): bonus_color = next(bonus_colors) bx = next(bonus_x) draw_bonus_invader(i, bonus_color, bx, bonus_x) # draw space ship shots for shot in shots: pygame.draw.rect(backbuffer, GREEN, shot) # draw invader shots for shot, color in zip(invader_shots, inv_shot_colors): pygame.draw.rect(backbuffer, color, shot) #update 'gun' position and draw ship/gun #player_gun = Rect(player.x, player.y, 6, 4) player_gun.x = player.x+18 pygame.draw.rect(backbuffer, (204,0,255), player) pygame.draw.rect(backbuffer, (0,204,255), player_gun) player.clamp_ip(backbuffer.get_rect()) print_text(backbuffer, font1, "Intruders Rekt#: {}".format(score),\ the_screen, x=0, y=520) print_text(backbuffer, font1, "Hearts#: {}".format(lives), the_screen,\ x=0, y=550) print_text(backbuffer, font1, "Round#: {}".format(level), the_screen,\ x=0, y=580) if game_over: if playing: game_is_over(backbuffer, font2, ticks) playing = False level = 0 lives = 2 score = 0 shots, invader_shots, inv_shot_colors, bonus_invaders = [], [], [], [] print_text(backbuffer, font2, "_/¯Yeet The Intruders¯\_", the_screen, y=5,\ center=True) draw_title_invader() if ticks > flash_timer + 800: # "press to play" flashing text insert = next(the_text) flash_timer = ticks print_text(backbuffer, font3, insert, the_screen, y =\ the_screen.height-40, center=True) screen.blit(backbuffer, (0,0)) pygame.display.update() fpsclock.tick(30)
Your code is quite large, but to pause the game is very general task: Add a pause state. Toggle the pause state on an certain event, e.g. when the p key is pressed. Skip the game processing if pauseis stated. e,.g. pause = False # pause state while True: ticks = pygame.time.get_ticks() for event in pygame.event.get(): if event.type == QUIT: pygame.quit() sys.exit() if event.type == KEYUP: if event.key == pygame.K_1 and not game_over: print("Next level") # toggle pause if "p" is pressed if event.key == pygame.K_p: pause = not pause # [...] if pause: # [...] draw pause screen pass elif not game_over: # <--- elif is important playing = True # [...] screen.blit(backbuffer, (0,0)) pygame.display.update() fpsclock.tick(30)
How to fix 'apples' sometimes not appearing in my version of Snake? [duplicate]
I'm currently in the process of creating a Snake game and I want to create a food generator that generates an apple every 10 seconds based on my in-game timer. The timer counts down from 60 to 0(when the game ends) and I want an new apple to generate every 10 seconds, keeping the old one even if it hasn't been eaten. I don't know how to approach this and could use some help. Here is my full program. Edit: this is a beginner Computer Science school project so the more basic the better. import random import pygame pygame.init() #---------------------------------------# # # window properties # width = 640 # height = 480 game_window=pygame.display.set_mode((width,height)) black = ( 0, 0, 0) # #---------------------------------------# # snake's properties outline=0 body_size = 9 head_size = 10 apple_size = 8 speed_x = 8 speed_y = 8 dir_x = 0 dir_y = -speed_y segx = [int(width/2.)]*3 segy = [height, height + speed_y, height + 2*speed_y] segments = len(segx) apple_counter = 0 grid_step = 8 regular_font = pygame.font.SysFont("Andina",18) blue = [11, 90, 220] clock = pygame.time.Clock() time = 60 fps = 25 time = time + 1.0/fps text = regular_font.render("Time from start: "+str(int(time)), 1, blue) text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue) apple_x = random.randrange(0, 640, grid_step) apple_y = random.randrange(0, 480, grid_step) apple_colour = (255,0,0) def redraw_game_window(): game_window.fill(black) for i in range(segments): segment_colour = (random.randint(1,50),random.randint(100,150),random.randint(1,50)) head_colour = (random.randint(180,220),random.randint(180,220),random.randint(1,15)) apple_colour = (255,0,0) pygame.draw.circle(game_window, segment_colour, (segx[i], segy[i]), body_size, outline) pygame.draw.circle(game_window, head_colour, (segx[0], segy[0]), head_size, outline) game_window.blit(text, (530, 20)) game_window.blit(text2, (30, 20)) pygame.draw.circle(game_window, apple_colour, (apple_x, apple_y), apple_size, outline) pygame.display.update() exit_flag = False print "Use the arrows and the space bar." print "Hit ESC to end the program." ########################################################## TIMER/CONTROLS while exit_flag == False: redraw_game_window() clock.tick(fps) time = time - 1.00/fps text = regular_font.render("Time: "+str(int(time)), 1, blue) text2 = regular_font.render("Score: "+str(int(apple_counter)), 1, blue) if time < 0.1: print "Game Over" exit_flag = True pygame.event.get() keys = pygame.key.get_pressed() if time == if keys[pygame.K_ESCAPE]: exit_flag = True if keys[pygame.K_LEFT] and dir_x != speed_x: dir_x = -speed_x dir_y = 0 if keys[pygame.K_RIGHT] and dir_x != -speed_x: dir_x = speed_x dir_y = 0 if keys[pygame.K_UP] and dir_y != speed_x: dir_x = 0 dir_y = -speed_y if keys[pygame.K_DOWN] and dir_y != -speed_x: dir_x = 0 dir_y = speed_y ############################################################ SNAKE MOVEMENT for i in range(segments-1,0,-1): segx[i]=segx[i-1] segy[i]=segy[i-1] segx[0] = segx[0] + dir_x segy[0] = segy[0] + dir_y ############################################################ COLLISION for i in range(segments-1, 3, -1): if segments > 3: if segx[0] == segx[i] and segy[0] == segy[i]: print "You have collided into yourself, Game Over." exit_flag = True ############################################################# BORDERS if segx[0] > 640 or segx[0] < 0: print "Game Over, you left the borders." break if segy[0] > 480 or segy[0] < 0: print "Game Over, you left the borders." break ############################################################# APPLE DETECT for i in range (0 , 13): if segx[0] == apple_x + i and segy[0] == apple_y + i: segments = segments + 1 segx.append(segx[-1]) segy.append(segy[-1]) apple_counter = apple_counter + 1 if segx[0] == apple_x - i and segy[0] == apple_y - i: segments = segments + 1 segx.append(segx[-1]) segy.append(segy[-1]) apple_counter = apple_counter + 1 ############################################################# pygame.quit()
You either A) use pygame.time.set_timer to call a function every 10 seconds to spawn food, and every 60 seconds to end the round. or B) compare get_ticks() def new_round(): last_apple = pygame.time.get_ticks() + 10*1000 while true: now = pygame.time.get_ticks() if now - last_apple >= 1000: spawn_apple() last_apple = now if now - round_start >= 60*1000: round_end()
Rotating Mask collision in Pygame
So I'm trying to make a game where there are 2 objects and am trying to make it so that my program knows when the 2 objects collide. if players[0].mask.overlap(players[1].mask, offset): print("collided")` My objects both rotate clockwise and counterclockwise and have masks both for the head and the body. (I have 2 images which are both the same size but one contains only the head and another contains only the body, in order to use mask.from_surface().) def turn(self, event): self.surface = self.ojSurface if event == 0: self.deg -= self.turnSpd elif event == 1: self.deg += self.turnSpd self.surface = pygame.transform.rotate(self.surface, self.deg) self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2) self.mask = pygame.mask.from_surface(self.surface) However, what I find is when I try to check for collision when they are rotated the mask isn't in the same place as the image. It should update the mask every frame through the move function which is called every loop def move(self): self.pos[0] = pygame.mouse.get_pos()[0] self.pos[1] = pygame.mouse.get_pos()[1] self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2) self.mask = pygame.mask.from_surface(self.surface) Faulty Hit Detection If you want to peruse what I have so far here it is: import pygame class Player: def __init__(self, pos,surface,screen): self.pos = [pos[0],pos[1]] self.newPos = (pos[0] - surface.get_width()/2, pos[1] - surface.get_height()/2) self.deg = 0 self.surface = surface self.ojSurface = surface self.screen = screen self.mask = pygame.mask.from_surface(self.surface) def turn(self, event): self.surface = self.ojSurface #clockwise if event == 0: self.deg -= 0.1 #counter clockwise elif event == 1: self.deg += 0.1 self.surface = pygame.transform.rotate(self.surface, self.deg) #resetting pos and mask self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2) self.mask = pygame.mask.from_surface(self.surface) def move(self): self.pos[0] = pygame.mouse.get_pos()[0] self.pos[1] = pygame.mouse.get_pos()[1] self.newPos = (self.pos[0] - self.surface.get_width() / 2, self.pos[1] - self.surface.get_height() / 2) self.mask = pygame.mask.from_surface(self.surface) def draw(self): self.screen.blit(self.surface, self.newPos) screenRes = (640,480) screen = pygame.display.set_mode(screenRes) closed = False players = [Player((320,240),pygame.image.load("body.png"), screen),Player((480,240),pygame.image.load("body.png"), screen)] controls = [[pygame.K_a,pygame.K_s],[pygame.K_k,pygame.K_l]] while not closed: screen.fill((0, 0, 0)) keys = pygame.key.get_pressed() offset = (int(players[0].newPos[0] - players[1].newPos[0]), int(players[0].newPos[1] - players[1].newPos[1])) #collision if players[0].mask.overlap(players[1].mask, offset): print("collided") #controls for i in range(len(players)): if keys[controls[i][0]]: players[i].turn(0) if keys[controls[i][1]]: players[i].turn(1) players[i].draw() players[0].move() pygame.display.update() for event in pygame.event.get(): # standard quit if event.type == pygame.QUIT: pygame.display.quit() closed = True pygame.quit()
You're calculating the offset in the wrong order. Subtract players[0]'s position from players[1]'s position: offset = ( int(players[1].newPos[0] - players[0].newPos[0]), int(players[1].newPos[1] - players[0].newPos[1]), )
So I've reviewed your GitHub code, and while I'm not completely sure (I skimmed your code, looking for certain methods being called), I believe that you're not updating your masks every frame. Somewhere in your code, you'll have to update the masks every frame/loop, allowing for the collision to work properly. Try to update all the masks, the head, body and the one in the Player class. Hope this helps!
Scrolling in 2D game?
I'm trying to add a scrolling "camera" that follows the player when it moves but can't figure out how to do this. I know that you can just move the level in the opposite direction when you press one of the movement keys but I'd rather not do that as I plan on adding enemies later on and don't want have to keep update their coordinates as the player moves. I've added my code with a sample level below. Code: import pygame, sys, time, random, math from pygame.locals import * BACKGROUNDCOLOR = (255, 255, 255) WINDOWW = 800 WINDOWH = 600 PLAYERW = 66 PLAYERH = 22 FPS = 60 MOVESPEED = 3 YACCEL = 0.13 GRAVITY = 2 BLOCKSIZE = 30 pygame.init() screen = pygame.display.set_mode((WINDOWW, WINDOWH), 0, 32) mainClock = pygame.time.Clock() testLevel = [ (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,1,1,1,1,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,), (1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,)] def createblock(length, height, color): tmpblock = pygame.Surface((length, height)) tmpblock.fill(color) tmpblock.convert() return tmpblock def terminate(): # Used to shut down the software pygame.quit() sys.exit() def add_level(lvl, bSize): # Creates the level based on a map (lvl) and the size of blocks bList = [] # List of every block bListDisp = [] # List of every block to display bTypeList = [] # List with corresponding type of block(wall, air, etc.) for y in range(len(lvl)): for x in range(len(lvl[0])): if lvl[y][x] == 0: # If the block type on lvl[y][x] is '0', write "air" down in the type list bTypeList.append("air") elif lvl[y][x] == 1: # If the block type on lvl[y][x] is '1', write "wall" down in the type list bTypeList.append("solid") bList.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block that is registered bListDisp.append(pygame.Rect((bSize * x), (bSize * y), bSize, bSize)) #Append every block to display that is registered return bList, bListDisp, bTypeList player = pygame.Rect((WINDOWW/2), (WINDOWH - BLOCKSIZE*3), PLAYERW, PLAYERH) wallblock = createblock(BLOCKSIZE, BLOCKSIZE,(20,0,50)) lastTime = pygame.time.get_ticks() isGrounded = False vx = 0 vy = 0 allLevels = [testLevel] # A list containing all lvls(only one for now) maxLevel = len(allLevels) # Checks which level is the last currLevel = allLevels[0] # Current level(start with the first lvl) blockList, blockListDisp, blockTypeList = add_level(currLevel, BLOCKSIZE) # A list with every block and another list with the blocks types thrusters = True jumping = False falling = True while True: """COLLISION""" collision = False for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": if player.colliderect(blockList[i]): collision = True if vx > 0 and not falling: player.right = blockListDisp[i].left vx = 0 print('Collide Right') if vx < 0 and not falling: player.left = blockListDisp[i].right vx = 0 print('Collide Left') if vy > 0: player.bottom = blockListDisp[i].top isGrounded = True falling = False vy = 0 print('Collide Bottom') if vy < 0: player.top = blockListDisp[i].bottom vy = 0 print('Collide Top') else: player.bottom += 1 if player.colliderect(blockList[i]): collision = True #isGrounded = True #falling = False player.bottom -= 1 if not collision: falling = True isGrounded = False # Input pressedKeys = pygame.key.get_pressed() # Checks which keys are being pressed timeDiff = pygame.time.get_ticks() - lastTime # Calculates time difference lastTime += timeDiff # Last time checked reset to current time # Shut-down if the ESC-key is pressed or the window is "crossed down" for event in pygame.event.get(): if event.type == QUIT or event.type == KEYDOWN and event.key == K_ESCAPE: terminate() """X-axis control""" if pressedKeys[ord('a')]: vx = -MOVESPEED if pressedKeys[ord('d')]: vx = MOVESPEED if not pressedKeys[ord('d')] and not pressedKeys[ord('a')]: vx = 0 """Y-axis control""" # Controls for jumping if pressedKeys[ord('w')] and thrusters == True: vy -= YACCEL * timeDiff; # Accelerate along the y-xis when "jumping", but not above/below max speed if vy <= -4: vy = -4 isGrounded = False # You are airborne jumping = True # You are jumping if event.type == KEYUP: # If you let go of the "jump"-button, stop jumping if event.key == ord('w') and vy < 0 and not isGrounded: jumping = False falling = True player.x += vx player.y += vy # Gravity if not isGrounded or falling: vy += 0.3 if vy > 80: vy = 80 screen.fill(BACKGROUNDCOLOR) for i in range(len(blockTypeList)): if blockTypeList[i] == "solid": screen.blit(wallblock, (blockListDisp[i].x, blockListDisp[i].y)) #blit the wall-block graphics pygame.draw.rect(screen, (0, 0, 0), player) pygame.display.update() mainClock.tick(FPS)
The trick is to keep track of camera coordinates and use these as an offset in your rendering code. It looks like you're doing you're rendering right at the end of the code you've posted, drawing each block with coord x,y to pixel x,y on the screen. As you say, shifting the level around isn't great. Instead, have your key inputs (or other camera moving device) change cameraX and cameraY variables, and then add (or subtract, depending which direction you want to go) these values from the block x and y values to change which pixels map to which blocks. I.e. change your rendering to: screen.blit(wallblock, (blockListDisp[i].x + cameraX, blockListDisp[i].y + cameraY)) This means if your camera moves to (10, 20) then you map your block at (5, 5) to (15, 25) on the screen, shifting your whole level across while your underlying model of the level stays the same. Make sense? You can also take this slightly further; if your camera is only being moved to follow your character you can make swap cameraX and cameraY in the above for some function of the character position, and have the whole thing just managed directly there.