Related
I am currently trying to make a game animation based off the game plant versus zombies. When the zombie moves across the screen and contacts the plant, the plant sprite gets saved in a list called plants_eaten_list. The zombie also stops moving to the left. After a 1 second delay (when the plants_eaten_event is triggered), the plant loses 10 health points. When the plant's health reaches 0, the plant sprite dies through the plant.kill() command.
The code for setting up the timer (pygame.time.set_timer(plants_eaten_event, 1000) needs to be inside the main while loop in order to constantly check for a plant within the plants_eaten_list. However, my problem is that for every refresh of the while loop, the timer for the plants_eaten_event gets reset back to 1 second, so the plants_eaten_event never occurs.
Is there any way I can structure the logic of this program to alleviate this issue?
I do not own copyright to any of these images, and the images are intended only for private use.
The zombie image is downloaded from http://plantsvszombies.wikia.com/wiki/Zombie/Gallery?file=Regular_Zombie.png
The plant image is downloaded from http://plantsvszombies.wikia.com/wiki/File:Peashooter.png
Here is my code:
import pygame
import random
BLACK = (0,0,0)
WHITE = (255,255,255)
RED = (255,0,0)
BLUE = (0,0,255)
GREEN = (0,255,0)
pygame.init()
borders_sound = pygame.mixer.Sound("bump.wav")
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Regular_Zombie.png").convert()
self.image.set_colorkey(WHITE)
# Make our top-left corner the passed-in location.
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
# -- Attributes
# Set speed vector
self.change_x = -1
self.change_y = 0
def changespeed(self, x, y):
""" Change the speed of the player"""
self.change_x += x
self.change_y += y
def move(self):
""" Find a new position for the player"""
#self.change_x = -1
self.rect.x += self.change_x
#print(self.rect.x)
#times = 0
if self.rect.x < 0:
self.rect.x = 0
def eatplant(self,plant):
pygame.time.set_timer(plants_eaten_event,6000)
self.change_x = 0
plant.health = plant.health - 10
class Plant(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load("Peashooter.png").convert()
self.image.set_colorkey(BLUE)
self.rect = self.image.get_rect()
self.rect.x = x
self.rect.y = y
self.alive = True
self.health = 60
screen_width = 700
screen_height = 400
screen = pygame.display.set_mode([screen_width, screen_height])
all_sprites_list = pygame.sprite.Group()
plants_list = pygame.sprite.Group()
zombies_list = pygame.sprite.Group()
zombie1 = Zombie(690, 15)
plant1 = Plant(300,15)
all_sprites_list.add(zombie1)
all_sprites_list.add(plant1)
plants_list.add(plant1)
zombies_list.add(zombie1)
done = False
clock = pygame.time.Clock()
plants_eaten_event = pygame.USEREVENT + 1
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# If plant.health = 0 at the end of the timer, the plant is eaten.
# If plant.health != 0 at the end of the timer, the entire event
# must happen again in the while loop until the health = 0
elif event.type == plants_eaten_event:
print("Zombie 1 ate plant 1")
zombie1.eatplant(plant)
# Set timer equal to 0 until another plant gets eaten or zombie continues eating current plant
pygame.time.set_timer(plants_eaten_event, 0)
if plant.health == 0:
plant.alive = False
plant.kill()
zombie1.change_x = -1
zombie1.move()
elif event.type == zombie_eaten_plant_event:
zombie1.change_x = -1
zombie1.move()
print("Zombie 1 is eating plant")
pygame.time.set_timer(zombie_eaten_plant_event, 0)
screen.fill(WHITE)
zombie1.move()
if times == 0 and zombie1.rect.x <= 0:
times = times + 1
borders_sound.play()
all_sprites_list.draw(screen)
all_sprites_list.update()
# I set the do kill command in spritecollide command to False because I
# needed to delay the plants that were eaten before they were removed. They
# will be removed after six seconds(symbolize zombies eating plants). Aftera
# a period of time, then I will create a loop that will loop through all plants
# in the plants_eaten_list and use .kill() . I will need to create a separate
# collision function that will stall the zombies until the plants are killed.
plants_eaten_list = pygame.sprite.spritecollide(zombie1,plants_list,False)
# I changed the delay to 6 seconds, and plant 1 still died instantly. The zombie
# stayed frozen in place for 6 seconds before teh 2nd plant was instantly destroyed
for plant in plants_eaten_list:
# If I remove this, plant1 is not killed, even though
# there is the same code within the plants_eaten_event function.
if plant1.health == 0:
plant1.kill()
# The while loop loops so fast that the timer doesnot have a chance to
# finish its countdown before it restarts again. Will I need some kind of a threading
# module to prevent the timers from restarting until they end?
pygame.time.set_timer(plants_eaten_event, 1000)
for plant_eaten in plants_eaten_list:
borders_sound.play()
clock.tick(60)
pygame.display.flip()
pygame.quit()
my problem is that for every refresh of the while loop, the timer for the plants_eaten_event gets reset back to 1 second, so the plants_eaten_event never occurs.
That happens because the rect of the zombie still collides with the rect of the plant in the next frame, spritecollide returns the list with the colliding plant and the for plant in plants_eaten_list: loop where you call set_timer gets executed again each frame.
The first thing you need to do is to set the zombie.rect.left position to plant.rect.right after the first collision, so that the sprites don't collide anymore in the next frame:
for plant in plants_eaten_list:
zombie1.change_x = 0 # Stop the zombie.
# Move the rect so that it doesn't touch the plant's rect anymore.
zombie1.rect.left = plant.rect.right
if plant1.health <= 0:
plant1.kill()
pygame.time.set_timer(plants_eaten_event, 1000)
borders_sound.play()
However, once you start adding more zombies, the code will break because you use the zombie1 and the plant variable (which depends on the for plant in plants_eaten_list: loop) in the event loop, so it will work only for this zombie and the last collided plant. Unfortunately, it's not possible to send more information than the event type with pygame.time.set_timer, otherwise you could attach the specific zombie and plant to the event in order to handle them in the event loop.
An alternative solution would be to give each zombie an own timer attribute (just a number) which you can update in their update methods.
So when the zombie touches a plant, I start the timer by setting it to 1 (second) and then decrement it by the delta time (the time needed for the last frame) each frame. I also stop the zombie self.change_x = 0, move the rect back self.rect.left = plant.rect.right and store a reference to the plant because we need it to reduce its health when the timer is finished.
Once the timer is <= 0 the zombie starts to move again, takes a bite of the plant, the sprites touch again and the timer is started anew.
import pygame
WHITE = (255,255,255)
BLUE = (0,0,255)
GREEN = (0,255,0)
class Zombie(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 50))
self.image.fill(BLUE)
self.rect = self.image.get_rect(topleft=(x, y))
self.change_x = -1
self.timer = 0 # Timer for the attacks.
self.plant = None # Currently attacked target.
def update(self, dt):
"""Update the zombie."""
if self.timer != 0:
self.timer -= dt
if self.timer <= 0: # Move if the timer is inactive.
self.change_x = -1
# Check if a plant is touching and eat it.
if self.plant is not None:
self.plant.health -= 10
print('Health', self.plant.health)
if self.plant.health <= 0:
print('Eaten')
self.plant.kill()
self.plant = None
self.timer = 0
self.rect.x += self.change_x
if self.rect.x < 0:
self.rect.x = 0
def eatplant(self, plants):
print('Play sound')
self.change_x = 0 # Stop the zombie.
self.timer = 1 # Start the timer.
for plant in plants:
self.plant = plant # Store a reference to the plant.
# Move the zombie back, so that the sprites
# don't collide anymore.
self.rect.left = plant.rect.right
class Plant(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((30, 50))
self.image.fill(GREEN)
self.rect = self.image.get_rect(topleft=(x, y))
self.health = 30
pygame.init()
screen = pygame.display.set_mode([700, 400])
# No need to assign the sprites to variables.
plants = pygame.sprite.Group(Plant(500, 15), Plant(350, 115))
zombies = pygame.sprite.Group(Zombie(690, 15), Zombie(690, 115))
all_sprites = pygame.sprite.Group(plants, zombies)
clock = pygame.time.Clock()
dt = 0
done = False
while not done:
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
# Pass the delta time to the update methods of the sprites.
all_sprites.update(dt)
# groupcollide returns a dictionary of the collided zombies and plants.
plants_eaten = pygame.sprite.groupcollide(zombies, plants, False, False)
for zombie, collided_plants in plants_eaten.items():
zombie.eatplant(collided_plants)
screen.fill(WHITE)
all_sprites.draw(screen)
dt = clock.tick(60) / 1000 # Time in seconds since last tick.
pygame.display.flip()
pygame.quit()
i'm on ideal gas theory simulation project and i want to ask you how exactly adding all particle's speed when i press arrow up. Because in my code, it didn't work.
I tried adding speed at initial process and loop but nothing work.
You can see right away what I'm trying to do.
edited for more understanding
sorry if i didn't put english
import pygame, sys, random, math
from pygame.locals import *
pygame.init()
#colour
white = [255,255,255]
black = [0,0,0]
red = [255,0,0]
green = [0,255,0]
blue = [0,0,255]
#setup window
(width,height)=(800,600)
window=pygame.display.set_mode((width,height))
pygame.display.set_caption('ideal gas')
#setup fps window
FPS=30
clock=pygame.time.Clock()
def bounceparticle(p1,p2):
dx=p1.x-p2.x
dy=p1.y-p2.y
distance=math.hypot(dx,dy)
if distance<p1.size+p2.size:
tangent=math.atan2(dy,dx)
angle=0.5*math.pi+tangent
angle1=2*tangent-p1.angle
angle2=2*tangent-p2.angle
speed1=p2.speed+p2.fire #so that every bounce increases speed
speed2=p1.speed+p1.fire #so that every bounce increases speed
(p1.angle, p1.speed) = (angle1, speed1)
(p2.angle, p2.speed) = (angle2, speed2)
p1.x += math.sin(angle)
p1.y -= math.cos(angle)
p2.x -= math.sin(angle)
p2.y += math.cos(angle)
#partikel
class Partikel:
def __init__(self,(x,y)):
self.x = x
self.y = y
self.size = 4
self.speed = 0
self.angle = 0
self.colour = blue #it need a recursion function to define changing particle's colour
self.fire = 0
def partikel_on_windows(self):
pygame.draw.circle(window,self.colour,[int(self.x),int(self.y)],self.size)
def move(self):
self.x += math.sin(self.angle) * self.speed
self.y -= math.cos(self.angle) * self.speed
#self.speed += self.fire #it can increase the speed but it takes 100 times pressed arrow up keyboard
def bounce(self):
if self.x>=width-self.size:
self.x=2*(width-self.size)-self.x
self.angle=-self.angle
self.speed += self.fire
elif self.x<=self.size:
self.x=2*self.size-self.x
self.angle=-self.angle
self.speed += self.fire
if self.y>=height-50-self.size:
self.y=2*(height-50-self.size)-self.y
self.angle=math.pi-self.angle
self.speed += self.fire
elif self.y<=self.size:
self.y=2*self.size-self.y
self.angle=math.pi-self.angle
self.speed += self.fire
number_of_particles = 200
myparticle = []
for n in range(number_of_particles):
centralpoint = random.randint(10,50)
x = random.randint(centralpoint,width-centralpoint)
y = random.randint(centralpoint,height-centralpoint)
partikel=Partikel((x,y))
partikel.angle=random.uniform(0,math.pi*2)
partikel.speed = 2
#partikel.fire = 0 #it can increase the speed but it takes 100 times pressed arrow up keyboard
#partikel.colour= [0,0,[255-partikel.speed]] #can't change the colour
myparticle.append(partikel)
# main game loop
while True:
for event in pygame.event.get():
pygame.key.set_repeat(1,50)
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == K_UP:
partikel.fire += 5
if event.key == K_DOWN:
partikel.fire -= 5
"""if event.type == pygame.KEYUP:
if event.key == K_UP:
partikel.fire = 0
if event.key == K_DOWN:
partikel.fire = 0"""
window.fill(black)
#partikel.fire = 1 #still can't
for i in range(len(myparticle)):
partikel = myparticle[i]
partikel.move()
partikel.bounce()
partikel.partikel_on_windows()
#partikel.fire = 0 #can't controling from keyboard
#partikel.colour= [0,0,[255-partikel.speed]]
for partikel2 in myparticle[i+1:]:
bounceparticle(partikel,partikel2)
pygame.display.update()
clock.tick(FPS)
You have to add to every particle in list.
I use variable speed which I change using key and later I add it to every particle in loop:
for i in range(len(partikel_ku)):
partikel.cepat += speed
Every click increases/decreases speed.
Full code:
import pygame
import random
import math
import sys
# --- constants ---- (UPPER_CASE_NAMES)
PUTIH = (255, 255, 255)
HITAM = (0, 0, 0)
MERAH = (255, 0, 0)
HIJAU = (0, 255, 0)
BIRU = (0, 0, 255)
LEBAR = 800
TINGGI = 600
FPS = 30
# --- classes --- (CamelCaseNames)
class Partikel:
def __init__(self, pos):
x, y = pos
self.x = x
self.y = y
self.size = 4
self.cepat = 0
self.sudut = 0
self.warna = BIRU
self.api = 0
def partikel_di_layar(self):
pygame.draw.circle(jendela, self.warna, [int(self.x), int(self.y)], self.size)
def gerak(self):
self.x += math.sin(self.sudut) * self.cepat
self.y -= math.cos(self.sudut) * self.cepat
def mantul(self):
if self.x >= LEBAR-self.size:
self.x = 2 * (LEBAR-self.size) - self.x
self.sudut = -self.sudut
elif self.x <= self.size:
self.x = 2 * self.size - self.x
self.sudut = -self.sudut
if self.y >= TINGGI - 50 - self.size:
self.y = 2 * (TINGGI - 50 - self.size) - self.y
self.sudut = math.pi - self.sudut
elif self.y <= self.size:
self.y = 2 * self.size - self.y
self.sudut = math.pi - self.sudut
# --- functions --- (lower_case_names)
def pantulan(p1, p2):
dx = p1.x - p2.x
dy = p1.y - p2.y
jarak = math.hypot(dx,dy)
if jarak < p1.size + p2.size:
tangen = math.atan2(dy, dx)
sudut = 0.5 * math.pi + tangen
sudut1 = 2 * tangen - p1.sudut
sudut2 = 2 * tangen - p2.sudut
cepat1 = p2.cepat + p2.api
cepat2 = p1.cepat + p1.api
p1.sudut = sudut1
p1.cepat = cepat1
p2.sudut = sudut2
p2.cepat = cepat2
p1.x += math.sin(sudut)
p1.y -= math.cos(sudut)
p2.x -= math.sin(sudut)
p2.y += math.cos(sudut)
# --- main ---
bnyk_partikel = 100
partikel_ku = []
# - init -
pygame.init()
jendela = pygame.display.set_mode((LEBAR, TINGGI))
pygame.display.set_caption('Animasi Bola')
pygame.key.set_repeat(1, 50)
# - objects -
for n in range(bnyk_partikel):
cp = random.randint(10, 50)
x = random.randint(cp, LEBAR - cp)
y = random.randint(cp, TINGGI - cp)
partikel = Partikel((x,y))
partikel.sudut = random.uniform(0, math.pi*2)
partikel.cepat = 2
partikel_ku.append(partikel)
speed = 0
# - mainloop -
clock=pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_UP:
speed += 1
elif event.key == pygame.K_DOWN:
speed -= 1
elif event.type == pygame.KEYUP:
if event.key == pygame.K_UP:
speed = 0
elif event.key == pygame.K_DOWN:
speed = 0
jendela.fill(HITAM)
#partikel.api = 1 #masih g bisa
for i in range(len(partikel_ku)):
partikel = partikel_ku[i]
partikel.cepat += speed
partikel.gerak()
partikel.mantul()
partikel.partikel_di_layar()
partikel.api = 1 #tak bisa dipanggil dari kontrol keyboard
#partikel.warna= [0,0,[255-partikel.cepat]]
for partikel2 in partikel_ku[i+1:]:
pantulan(partikel,partikel2)
pygame.display.update()
clock.tick(FPS)
BTW: read PEP 8 -- Style Guide for Python Code
I have created two stimuli(red and green rectangles) as stimuli in Psychopy. Also, I have enabled mouse movements to four directions. Using the function[mouse.getPressed()] in Psychopy for selecting the stimuli, I am facing some issues.
Basically, I want the mouse to move in four directions, and when the mouse reaches red/green rectangle stimuli, I need to select that stimuli and change its color to blue.
Can anyone look into the issue and help me to resolve the same?
Here is my code:
from psychopy import visual, core, event
import numpy as np
# Create a window.
# For configuring and debugging the code turn off full screen.
fullscr = False
win = visual.Window(
[1200,1000],
monitor="testMonitor",
units="deg",
fullscr=fullscr
)
#cursor = visual.Circle(win, radius=0.2)
#cursor = visual.CustomMouse(win,
# leftLimit=-10, topLimit=10, rightLimit=10, bottomLimit=-10,
# showLimitBox=True, clickOnUp=True)
pos_zero = (0, 0)
cursor = visual.Rect(
win=win,
size=400,
pos=pos_zero,
opacity=0
)
mouse = event.Mouse(visible=True)
# Sinusoidal control version.
freq_one = 0.5
freq_two = 1.5
# Colors of the rectangles.
#color_zero='black'
color_one = 'red'
color_two = 'green'
# Positions of the rectanges.
pos_one = (-10, 0)
pos_two = (10, 0)
start = core.getTime()
cnt = 0
cursor.pos = mouse.getPos()
print cursor.pos
while cnt<600:
second = core.getTime() - start
sin_val_one = 0.5+0.5*np.sin(2 * np.pi * second * float(freq_one))
sin_val_two = 0.5+0.5*np.sin(2 * np.pi * second * float(freq_two))
#while not mouse.getPressed()[0]:
# Do something if mouse moved
for key in event.getKeys():
if key == 'escape':
core.quit()
elif key == "right":
cursor.pos = cursor.pos + (2,0)
elif key =="left":
cursor.pos = cursor.pos - (2,0)
elif key =="up":
cursor.pos = cursor.pos + (0,2)
elif key =="down":
cursor.pos = cursor.pos - (0,2)
#if cursor.pos == pos_one:
# mouse.getpressed(rect_one)
#elif cursor.pos == pos_two:
# mouse.getpressed(rect_two)
mouse.setPos(cursor.pos)
mouse.lastPos = cursor.pos
rect_one = visual.Rect(
win=win,
fillColor=color_one,
lineColor=color_one,
size=15,
pos=pos_one,
opacity=sin_val_one
)
rect_two = visual.Rect(
win=win,
fillColor=color_two,
lineColor=color_two,
size=15,
pos=pos_two,
opacity=sin_val_two
)
#images = [rect_one, rect_two]
#for image in images:
# if mouse.isPressedIn(image):
# pressed_shape = shape
#
# pressed_image.fillColor = 'blue'
# pressed_image.draw()
# print pressed_image.name
rect_one.draw()
rect_two.draw()
cursor.draw()
win.flip()
cnt += 1
win.close()
Any help is greatly appreciated. Thanks!
I made several changes and post the updated code below. It's significantly shorter and simpler.
Removed everything using mouse. Since you just want something visual to move on key presses, there's no need to invoke the mouse - especially when you actually don't want subjects to use the mouse...
Create the colored shapes once, then updating the color during runtime. This is significantly faster, i.e. avoids dropping frames.
Changed the cursor size. It was 400 degrees!
Removed the line around the rectangles so that only fillColor needs to be changed on overlap.
A few other simplifications.
Here is the code:
from psychopy import visual, core, event
import numpy as np
# Settings
fullscr = False
directions = {'left': (-2,0), 'right': (2,0), 'up': (0, 2), 'down': (0, -2)}
freq_one = 0.5 # # Sinusoidal control
freq_two = 1.5
# Create a window.
win = visual.Window([1200,1000], monitor="testMonitor", units="deg", fullscr=fullscr)
# The two rectangles
rect_one = visual.Rect(win=win, fillColor='red', lineColor=None, size=15, pos=(-10, 0))
rect_two = visual.Rect(win=win, fillColor='green', lineColor=None, size=15, pos=(10, 0))
# Cursor
cursor = visual.Circle(win, fillColor='white', radius=0.2)
# Run five trials
for trial in range(5):
# Set to initial condition
rect_one.fillColor = 'red'
rect_two.fillColor = 'green'
cursor.pos = (0, 0)
# Start task
start = core.getTime()
for frame in range(600):
# Set rectangle opacities
second = core.getTime() - start
rect_one.opacity = 0.5+0.5*np.sin(2 * np.pi * second * float(freq_one))
rect_two.opacity = 0.5+0.5*np.sin(2 * np.pi * second * float(freq_two))
# Show it
rect_one.draw()
rect_two.draw()
cursor.draw()
win.flip()
# Get keyboard responses and change color if there is overlap
for key in event.getKeys():
if key == 'escape':
core.quit()
elif key in directions.keys(): # if this is a key corresponding to a direction
cursor.pos += directions[key] # Lookup the pos change in "directions" and add it to current position
if cursor.overlaps(rect_one):
rect_one.fillColor = 'blue'
if cursor.overlaps(rect_two):
rect_two.fillColor = 'blue'
Thanks for looking at my question and helping me out. I have a user controlled sprite that should cause the screen to shift around the sprite's position, kinda like an old Gameboy Pokemon game, yet that does not happen. Instead my sprite is stuck inside a box of coordinates that I used to help define parameters for background shift. This is the code I believe is causing this:
# Shift world left (-x)
if player.rect.right >= 510:
diff = player.rect.right - 510
player.rect.right = 510
current_level.shift_world_x(-diff)
# Shift world right (+x)
if player.rect.left <= 130:
diff = 130 - player.rect.left
player.rect.left = 130
current_level.shift_world_x(diff)
# Shift world up (-y)
if player.rect.top <= 100:
diff = 100 - player.rect.top
player.rect.top = 100
current_level.shift_world_y(diff)
# Shift world down (y)
if player.rect.bottom >= 380:
diff = player.rect.bottom - 380
player.rect.bottom = 380
current_level.shift_world_y(-diff)
If I put a # next to player.rect.right = 510, or any of the other three varients, my sprite can move freely, yet the screen will not shift.
Zip file with all assets is here:
https://www.dropbox.com/s/3g2w0mv1fuupetl/DoneGeon.zip?dl=0
The entirety of my code is as follows:
import pygame
"""---Global Constants---"""
# Colors
BLACK = (0, 0, 0)
# Screen Variables
screen_x = 720
screen_y = 480
# Background variables
back_x = 0
back_y = -243
back_x_change = 0
back_y_change = 0
class Player(pygame.sprite.Sprite):
"""---User Controled Character---"""
# Methods
def __init__(self):
"""---Contructor Function---"""
# Call parent's constructor
pygame.sprite.Sprite.__init__(self)
# Load player image
self.image = pygame.image.load("player.png").convert()
# Set referance to image "hit box"
self.rect = self.image.get_rect()
# Set speed of player
self.change_x = 0
self.change_y = 0
def update(self):
"""---Move the Player---"""
# Move left/right
self.rect.x += self.change_x
# Move up/down
self.rect.y += self.change_y
# User controlled movements
def go_left(self):
self.change_x = -3
def go_right(self):
self.change_x = 3
def go_up(self):
self.change_y = -3
def go_down(self):
self.change_y = 3
def stop_x(self):
self.change_x = 0
def stop_y(self):
self.change_y = 0
class Barrier(pygame.sprite.Sprite):
"""---Barriers that prevent player from passing---"""
def __init__(self,width,height):
""" Barrier constructor """
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface([width,height])
self.image.fill(BLACK)
self.rect = self.image.get_rect()
class Level():
"""---All levels will spawn from this generic class---"""
def __init__(self,player):
""" Needed for when sprites collide with player """
self.barrier_list = pygame.sprite.Group()
self.enemy_list = pygame.sprite.Group()
self.player = player
# Distance world has been shifted left/right
self.world_shift_x = 0
# Background image
background_base = None
background_layera = None
# Distance world has been shifted up/down
self.world_shift_y = 0
def update(self):
""" Update everything on level """
self.barrier_list.update()
self.enemy_list.update()
def draw(self,screen):
""" Draw everything on level """
# Draw barriers
self.barrier_list.draw(screen)
# Draw the base layer
screen.blit(self.background_base,(back_x,back_y))
# Draw all sprites
self.enemy_list.draw(screen)
# Draw Layer A
screen.blit(self.background_layera,(back_x,back_y))
def shift_world_x(self,shift_x):
""" When character moves left/right, everything must follow """
# Track shift amount
self.world_shift_x += shift_x
# Go through all list and shift
for barrier in self.barrier_list:
barrier.rect.x += shift_x
for enemy in self.enemy_list:
enemy.rect.x += shift_x
def shift_world_y(self,shift_y):
""" When character moves up/down, everything must follow """
# Track shift amount
self.world_shift_y += shift_y
# Go through all list and shift
for barrier in self.barrier_list:
barrier.rect.y += shift_y
for enemy in self.enemy_list:
enemy.rect.y += shift_y
class Level_1(Level):
"""---Forest 1---"""
def __init__(self,player):
""" Create the level """
# Call parent constructor
Level.__init__(self,player)
self.level_limit_x = -1912
self.level_limit_y = 1080
# Make background
self.background_base = pygame.image.load("base1.png").convert()
self.background_layera = pygame.image.load("layera1.png").convert()
self.background_layera.set_colorkey(BLACK)
# List with w, h, x = 32, y = 32 of barriers
# Remove comment to activate-> level = []
# Go through the list above and add barriers
# Remove comment to activate-> for barriers in level:
def main():
"""---Main Program---"""
pygame.init()
# Set screen size and caption
size = (screen_x,screen_y)
screen = pygame.display.set_mode(size)
pygame.display.set_caption("DoneGeon")
# Create Player
player = Player()
# Create all levels
level_list= []
level_list.append(Level_1(player))
# Set current level
current_level_no = 0
current_level = level_list[current_level_no]
# Add all sprites
active_sprite_list = pygame.sprite.Group()
# Add player
player.level = current_level
player.rect.x = 360
player.rect.y = 240
active_sprite_list.add(player)
# Loop until closed
done = False
# Screen update rate
clock = pygame.time.Clock()
# -------- Main Program Loop -----------
while not done:
"""---Main event loop---"""
for event in pygame.event.get():
if event.type == pygame.QUIT:
done = True
"""---User Controls---"""
# Key Pressed
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
player.go_left()
if event.key == pygame.K_RIGHT:
player.go_right()
if event.key == pygame.K_UP:
player.go_up()
if event.key == pygame.K_DOWN:
player.go_down()
# Key Released
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
player.stop_x()
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
player.stop_y()
"""---Game Logic---"""
# Update Player
active_sprite_list.update()
# Update items in level
current_level.update()
# Shift world left (-x)
if player.rect.right >= 510:
diff = player.rect.right - 510
player.rect.right = 510
current_level.shift_world_x(-diff)
# Shift world right (+x)
if player.rect.left <= 130:
diff = 130 - player.rect.left
player.rect.left = 130
current_level.shift_world_x(diff)
# Shift world up (-y)
if player.rect.top <= 100:
diff = 100 - player.rect.top
player.rect.top = 100
current_level.shift_world_y(diff)
# Shift world down (y)
if player.rect.bottom >= 380:
diff = player.rect.bottom - 380
player.rect.bottom = 380
current_level.shift_world_y(-diff)
"""---Draw below here---"""
current_level.draw(screen)
active_sprite_list.draw(screen)
pygame.display.flip()
clock.tick(60)
pygame.quit()
if __name__ == "__main__":
main()
Thank you for any advice!
I think you'll find this easier to track down if you log the changes in player x and world x. Often with games small changes aren't visible so logging is the only way to monitor what's happening in the game loop.
I haven't tested this but my bet is that you've got a negative in the wrong place. This:
def shift_world_x(self,shift_x):
""" When character moves left/right, everything must follow """
suggests that if the player right edge moves past the world right edge the world right edge should extend to match the player but this code does the reverse:
if player.rect.right >= 510:
diff = player.rect.right - 510
player.rect.right = 510
current_level.shift_world_x(-diff)
If the player.right moves to 513 diff will be 3 so shift_world_x will be called with -3, moving the world box left to 507. Player right will then be set to 510 so the player right will still be outside the box the next loop. This suggests that the world image will drift to the left with the player sprite stuck at the right edge but it sounds like it's trapped against the right edge (I'm focussing on one edge because the logic is the same for all).
If you log player.rect.right and world.rect.right in the if shown above you should see the values as they change and get a better feel for what the game logic is doing.
I'm relatively new to python and pygame, so this is most certainly just my own inexperience. However, in my game loop, every time an image or shape is blitted to the screen, it doesn't disappear until another image or shape is blitzed over it.
game_loop function
def game_loop():
road
carImg
pygame.init()
pygame.mixer.init()
pygame.mixer.music.load('Rally-X.mp3')
pygame.mixer.music.play()
x = (display_width * 0.45)
y = (display_height * 0.8)
x_change = 0
road_starty = -23
road_speed = 4
thing_starty = -600
thing_speed = 4
thing_width = 100
thing_height = 100
thing_startx = random.randrange(0, display_width)
carImgCount = 1
dodged = 0
gameExit = False
pygame.display.update()
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
quit()
pygame.display.update()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == pygame.K_a:
x_change = -5
if event.key == pygame.K_RIGHT or event.key == pygame.K_d:
x_change = 5
if event.key == pygame.K_p:
paused()
pygame.display.update()
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT or event.key == pygame.K_d or event.key == pygame.K_a:
x_change = 0
pygame.display.update()
x += x_change
gameDisplay.blit(road,(0,road_starty))
car(x,y)
#things(thingx, thingy, thingw, thingh, color)
things(thing_startx, thing_starty, thing_width, thing_height)
road_starty += road_speed
thing_starty += thing_speed
things_dodged(dodged)
if x > display_width - car_width or x < 0:
crash()
if thing_starty > display_height:
thing_starty = 0 - thing_height
thing_startx = random.randrange(0,display_width)
dodged += 1
if thing_speed <= 10:
thing_speed += 1
pygame.display.update()
if y < thing_starty+thing_height:
#print('y crossover')
if x > thing_startx and x < thing_startx + thing_width or x+car_width > thing_startx and x + car_width < thing_startx+thing_width:
#print('x crossover')
crash()
pygame.display.update()
if road_starty == 0:
road_starty = -23
if dodged > 9:
things(thing_startx, thing_starty, thing_width, thing_height)
pygame.display.update()
clock.tick(60)
I started just calling the pygame.display.update() function just about everywhere in the game_loop function to try and fix it, but it still doesn't seem to help.
Here is a picture of the code acting up in action:
Those red lines are supposed to be squares, and the car... isn't supposed to look like that. If someone could tell me what I'm doing wrong, that'd be awesome.
You have to clear screen before you draw new frame.
gameDisplay.fill((0,0,0))
And after clearing screen you have to draw all elements again.
You will have to reorganize you code to fill, draw and update in one place
You can organize mainloop this way (simple example)
# --- mainloop ---
clock = pygame.time.Clock()
is_running = True
while is_running:
# --- events ---
for event in pygame.event.get():
# --- global events ---
if event.type == pygame.QUIT:
is_running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_ESCAPE:
is_running = False
# --- objects events (without draws)---
'''
player.handle_event(event)
'''
# --- updates (without draws)---
'''
player.update()
'''
# --- draws (without updates) ---
screen.fill(BLACK) # only once in loop
'''
player.draw(screen)
'''
pygame.display.update() # only once in loop
# --- FPS ---
clock.tick(25)
full example: PyGame simple template and other templates
there is an easy, cheap way of getting the image off the screen...
say the screen is 400 x 400 just change the coordinates of the picture to where the x is like 500 and the y is like 600 so that it is placed outside of the pygame canvas