Typewriter Effect Pygame - python

This question is really difficult to ask, but I know you guys here at Stack Overflow are the brightest minds.
I'm totally blinded by why this issue happens (I'm fairly at Python and Pygame, so any suggestions on how to improve the code will be received with the love of improving my skills).
What I'm creating:
It's really a gimmick project, I have a little 2.5" screen (PiTFT) attached to a Raspberry Pi and the code is creating a typewriter effect with a moving cursor in front of the text as it's being written.
Challenge 1 was that every time you move a sprite in pygame, you must redraw everything, otherwise you will see a trail, and since the cursor is moving in front of the text, the result would look like this:
I managed to solve this issue by blackening / clearing the screen. But then I lost all the previously written letters.
So I created a list (entireword), which I'm populing with all the previously written characters. I use this list every time I cycle through the loop to redraw all the previous written text.
So now:
As you can see, the text looks funny.
It's supposed to read:
[i] Initializing ...
[i] Entering ghost mode ... []
I've been spending hours and hours getting to this point - and the code ALMOST works perfectly! The magic happens in the function print_screen(), but WHAT in my code is causing the text to include a letter from the other line in the end? :>
Help is GREATLY appreciated <3
Here's the entire code:
import pygame
import time
import os
import sys
from time import sleep
from pygame.locals import *
positionx = 10
positiony = 10
entireword = []
entireword_pos = 10
counter = 0
entire_newline = False
#Sets the width and height of the screen
WIDTH = 320
HEIGHT = 240
speed = 0.05
#Importing the external screen
os.putenv('SDL_FBDEV', '/dev/fb1')
os.putenv('SDL_MOUSEDRV', 'TSLIB')
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')
#Initializes the screen - Careful: all pygame commands must come after the init
pygame.init()
#Sets mouse cursor visibility
pygame.mouse.set_visible(False)
#Sets the screen note: must be after pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
# initialize font; must be called after 'pygame.init()' to avoid 'Font not Initialized' error
myfont = pygame.font.SysFont("monospace", 18)
#Class
class cursors(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill((0,255,0))
self.rect = self.image.get_rect()
self.rect.center = (positionx + 10, positiony + 10)
def update(self):
self.rect.x = positionx + 10
self.rect.y = positiony
#Functions
#Prints to the screen
def print_screen(words, speed):
rel_speed = speed
for char in words:
#speed of writing
if char == ".":
sleep(0.3)
else:
sleep(rel_speed)
#re-renders previous written letters
global entireword
# Old Typewriter functionality - Changes position of cursor and text a newline
#Makes sure the previous letters are rendered and not lost
#xx is a delimter so the program can see when to make a newline and ofcourse ignore writing the delimiter
entireword.append(char)
if counter > 0:
loopcount = 1
linecount = 0 # This is to which line we are on
for prev in entireword:
if prev == 'xx':
global linecount
global positiony
global loopcount
linecount = linecount + 1
positiony = 17 * linecount
loopcount = 1
if prev != 'xx': #ignore writing the delimiter
pchar = myfont.render(prev, 1, (255,255,0))
screen.blit(pchar, (loopcount * 10, positiony))
loopcount = loopcount + 1
if char != 'xx':
# render text
letter = myfont.render(char, 1, (255,255,0))
#blits the latest letter to the screen
screen.blit(letter, (positionx, positiony))
# Appends xx as a delimiter to indicate a new line
if entire_newline == True:
entireword.append('xx')
global entire_newline
entire_newline = False
global positionx
positionx = positionx + 10
all_sprites.update()
all_sprites.draw(screen)
pygame.display.flip()
screen.fill((0,0,0)) # blackens / clears the screen
global counter
counter = counter + 1
#Positions cursor at new line
def newline():
global positionx
global positiony
positionx = 10
positiony = positiony + 17
all_sprites = pygame.sprite.Group()
cursor = cursors()
all_sprites.add(cursor)
#Main loop
running = True
while running:
global speed
global entire_newline
words = "[i] Initializing ..."
entire_newline = True
newline()
print_screen(words,speed)
words = "[i] Entering ghost mode ..."
entire_newline = True
newline()
print_screen(words,speed)
#Stops the endless loop if False
running = False
sleep(10)

Sorry if I don't answer your question directly, because your code is too confusing for me now, so I took the liberty to rewrite your code to get done what you want.
The idea is to have two sprites:
the cursor, which is a) displayed on the screen and b) keeps track of what text to write and where
the board, which is basically just a surface that the text is rendered on
Note how all the writing logic is on the Cursor class, and we have a nice, simple and dumb main loop.
import pygame
import os
#Sets the width and height of the screen
WIDTH = 320
HEIGHT = 240
#Importing the external screen
os.putenv('SDL_FBDEV', '/dev/fb1')
os.putenv('SDL_MOUSEDRV', 'TSLIB')
os.putenv('SDL_MOUSEDEV', '/dev/input/touchscreen')
#Initializes the screen - Careful: all pygame commands must come after the init
pygame.init()
clock = pygame.time.Clock()
#Sets mouse cursor visibility
pygame.mouse.set_visible(False)
#Sets the screen note: must be after pygame.init()
screen = pygame.display.set_mode((WIDTH, HEIGHT))
class Board(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((WIDTH, HEIGHT))
self.image.fill((13,13,13))
self.image.set_colorkey((13,13,13))
self.rect = self.image.get_rect()
self.font = pygame.font.SysFont("monospace", 18)
def add(self, letter, pos):
s = self.font.render(letter, 1, (255, 255, 0))
self.image.blit(s, pos)
class Cursor(pygame.sprite.Sprite):
def __init__(self, board):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((10, 20))
self.image.fill((0,255,0))
self.text_height = 17
self.text_width = 10
self.rect = self.image.get_rect(topleft=(self.text_width, self.text_height))
self.board = board
self.text = ''
self.cooldown = 0
self.cooldowns = {'.': 12,
'[': 18,
']': 18,
' ': 5,
'\n': 30}
def write(self, text):
self.text = list(text)
def update(self):
if not self.cooldown and self.text:
letter = self.text.pop(0)
if letter == '\n':
self.rect.move_ip((0, self.text_height))
self.rect.x = self.text_width
else:
self.board.add(letter, self.rect.topleft)
self.rect.move_ip((self.text_width, 0))
self.cooldown = self.cooldowns.get(letter, 8)
if self.cooldown:
self.cooldown -= 1
all_sprites = pygame.sprite.Group()
board = Board()
cursor = Cursor(board)
all_sprites.add(cursor, board)
text = """[i] Initializing ...
[i] Entering ghost mode ...
done ...
"""
cursor.write(text)
#Main loop
running = True
while running:
for e in pygame.event.get():
if e.type == pygame.QUIT:
running = False
all_sprites.update()
screen.fill((0, 0, 0))
all_sprites.draw(screen)
pygame.display.flip()
clock.tick(60)

Use the pygame.event module. Use pygame.time.set_timer() to repeatedly create a USEREVENT in the event queue. The time has to be set in milliseconds. e.g.:
typewriter_event = pygame.USEREVENT+1
pygame.time.set_timer(typewriter_event, 100)
Add a new letter to the text, when the timer event occurs:
while run:
for event in pygame.event.get():
# [...]
if event.type == typewriter_event:
text_len += 1
See also Typewriter
Minimal example:
repl.it/#Rabbid76/PyGame-Typewriter
import pygame
pygame.init()
window = pygame.display.set_mode((500, 150))
clock = pygame.time.Clock()
font = pygame.font.SysFont(None, 100)
background = pygame.Surface(window.get_size())
ts, w, h, c1, c2 = 50, *window.get_size(), (32, 32, 32), (64, 64, 64)
tiles = [((x*ts, y*ts, ts, ts), c1 if (x+y) % 2 == 0 else c2) for x in range((w+ts-1)//ts) for y in range((h+ts-1)//ts)]
for rect, color in tiles:
pygame.draw.rect(background, color, rect)
text = 'Hello World'
text_len = 0
typewriter_event = pygame.USEREVENT+1
pygame.time.set_timer(typewriter_event, 100)
text_surf = None
run = True
while run:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == typewriter_event:
text_len += 1
if text_len > len(text):
text_len = 0
text_surf = None if text_len == 0 else font.render(text[:text_len], True, (255, 255, 128))
window.blit(background, (0, 0))
if text_surf:
window.blit(text_surf, text_surf.get_rect(midleft = window.get_rect().midleft).move(40, 0))
pygame.display.flip()
pygame.quit()
exit()

Related

Sprite in not updated

This code is supposed to animate a sprite on a background, but it is just showing the sprite without any movement. I spend a day trying to figure out the issue, traced the code (I am novice so I might overlooked something), compared the code with the original author code line by line with no result, btw the original code runs smoothly meaning that it is not a problem in my PC.
Could you help me please
import os
import random
import math
import pygame
from os import listdir
from os.path import isfile, join
pygame.init()
pygame.display.set_caption("Platformer") #set the caption at the top of the window
BG_COLOR = (255,255,255) #White background, dont need it anymore
WIDTH, HEIGHT = 1000, 640 #screen dimensions
FPS = 60
PLAYER_VEL = 5 # the speed of the player
window = pygame.display.set_mode((WIDTH, HEIGHT)) #set the window with sizes
def flip(sprites):
return [pygame.transform.flip(sprite, True, False) for sprite in sprites]
def load_sprite_sheets(dir1, dir2, width, height, direction=False):
path = join("assets", dir1, dir2)
images = [f for f in listdir(path) if isfile(join(path, f))] #if f is a file put in the images list
all_sprites = {}
for image in images:
sprite_sheet = pygame.image.load(join(path, image)).convert_alpha() #loaded transparent bg image
sprites = []
for i in range(sprite_sheet.get_width() // width):
surface = pygame.Surface((width, height), pygame.SRCALPHA, 32)
rect = pygame.Rect(i * width, 0, width, height)
surface.blit(sprite_sheet, (0, 0), rect)
sprites.append(pygame.transform.scale2x(surface))
if direction:
all_sprites[image.replace(".png", "") + "_right"] = sprites
all_sprites[image.replace(".png", "") + "_left"] = flip(sprites)
else:
all_sprites[image.replace(".png", "")] = sprites
return all_sprites
class Player(pygame.sprite.Sprite): #sprite is useful for perfect pixel collision
COLOR = (0,0,255)
GRAVITY = 1
SPRITES = load_sprite_sheets("MainCharacters" , "MaskDude", 32 , 32 , True)
ANIMATION_DELAY = 3
def __init__(self, x,y, width , height):
self.rect = pygame.Rect(x,y,width,height)
self.x_vel = 0
self.y_vel = 0
self.mask = None
self.direction = "left" # to record which animation to show
self.animation_count = 0 #
self.fall_count = 0
def move(self,dx,dy):
self.rect.x += dx
self.rect.y += dy #here we draw only the motion calculated in the next functions
def move_left(self, vel):
self.x_vel = -vel
if self.direction != "left":
self.direction = "left"
self.animation_count = 0
def move_right(self, vel):
self.x_vel = vel
if self.direction != "right":
self.direction = "right"
self.animation_count = 0
def loop(self , fps):
# self.y_vel += min(1 , (self.fall_count/fps) * self.GRAVITY)
self.move(self.x_vel,self.y_vel)
self.fall_count += 1
self.update_sprite()
def update_sprite(self): #is about changing the sprite shape to look walking
sprite_sheet = "idle"
if self.x_vel !=0:
sprite_sheet = "run"
sprite_sheet_name = sprite_sheet + "_" + self.direction
sprites = self.SPRITES[sprite_sheet_name]
sprite_index = (self.animation_count //
self.ANIMATION_DELAY) % len(sprites) #set new index every ANIMATION_DELAY = 5
self.sprite = sprites[sprite_index]
self.animation_count += 1
def draw(self, win):
#print(self.SPRITES)
self.sprite = self.SPRITES["idle_"+self.direction][0]
win.blit(self.sprite , (self.rect.x , self.rect.y))
def get_background(name): #name is bg image, this create the bg image position list
image = pygame.image.load(join("assets", "Background", name))
_, _, width, height = image.get_rect()
tiles = [] #list of tles I need to fil my window bg
for i in range(WIDTH//width + 1):
for j in range (HEIGHT // height+1):
pos = tuple([i * width , j*height]) # convert list into tuple
tiles.append(pos)
return tiles, image # now I now the list of positions to fill the bg and the exact file to use
def draw(window, background, bg_image, player):
for tile in background:
window.blit(bg_image, tile) # drawing the image at every position
player.draw(window)
pygame.display.update()
def handle_move(player): #check keys and collision
keys = pygame.key.get_pressed()
player.x_vel = 0; #as moveleft change the velocity we have to change it to zero so w
if keys[pygame.K_LEFT]:
player.move_left(PLAYER_VEL)
if keys[pygame.K_RIGHT]:
player.move_right(PLAYER_VEL)
def main(window):
clock = pygame.time.Clock()
background, bg_image = get_background("Blue.png")
player = Player(100,100,50,50)
run = True
while(run):
clock.tick(FPS) #fix the refresh rate to this otherwise it will be dpending on the computer power
for event in pygame.event.get():
if event.type == pygame.QUIT: #if teh program detect an event of the user Quiting the game
run = False
break
player.loop(FPS)
handle_move(player)
draw(window, background, bg_image, player)
pygame.quit()
quit() #quiting the python itself
if __name__ == "__main__":
main(window) #when we run the file go to the main function with this arg
comparing the code with the original author code, change my code
I think that the problem lies in the following line of the Player.draw function:
self.sprite = self.SPRITES["idle_"+self.direction][0]
The sprite attribute was already set in the update_sprite function to the correct sprite taking the animation count and state into an account. However, this line resets it to the first image ([0]) from the idle spritesheet ("idle_"), without taking the animation count or state into an account. This does make that only this sprite and not the correct one is being drawn.
Removing the line should resolve the problem.

Why does this bug happen when I click on two sprites at the same time?

I'm making a simple game using pygame where you keep clicking on tiles as fast as you can until you miss a tile. this is the progress I've made so far. sometimes when I click on a tile (usually when 2 tiles are next to each other and you click between them) one of them does what they're supposed to while the other just disappears from the screen.
import pygame
import random
import sys
#Setting up all possible Tile positions
grid = [[0,0], [0,150], [0,300], [0,450], [0,600],
[150,0],[150,150],[150,300],[150,450],[150,600],
[300,0],[300,150],[300,300],[300,450],[300,600],
[450,0],[450,150],[450,300],[450,450],[450,600],
[600,0],[600,150],[600,300],[600,450],[600,600]]
taken = []
#Classes
class Cursor(pygame.sprite.Sprite):
def __init__(self, pic):
super().__init__()
self.image = pygame.image.load(pic).convert_alpha()
self.image = pygame.transform.scale(self.image, (50,50))
self.rect = self.image.get_rect()
def destroyTile(self):
pygame.sprite.spritecollide(cursor, tileGroup, True)
def update(self):
self.rect.topleft = pygame.mouse.get_pos()
class Tiles(pygame.sprite.Sprite):
def __init__(self, tileSize, color, x, y):
super().__init__()
self.image = pygame.Surface(([tileSize, tileSize]))
self.image.fill(color)
self.rect = self.image.get_rect()
self.rect.topleft = [x, y]
def drawTiles():
takenLen = len(taken)
while takenLen != 3:
m = random.randint(0,24)
x, y = grid[m]
if grid[m] not in taken:
blackTile = Tiles(150, black, x, y)
blackTile.add(tileGroup)
taken.append(grid[m])
takenLen += 1
def handleTiles():
mx, my = pygame.mouse.get_pos()
modx = mx % 150
mody = my % 150
x = mx - modx
y = my - mody
taken.remove([x, y])
drawTiles()
def drawRedTile():
mx, my = pygame.mouse.get_pos()
modx = mx % 150
mody = my % 150
x = mx - modx
y = my - mody
redTile = Tiles(150, red, x, y)
redTile.add(tileGroup)
#Colours
white = (255, 255, 255)
black = (0, 0, 0)
red = (255, 0, 0)
blue = (0, 0, 255)
grey = (46, 46, 46)
#Initializing Pygame
pygame.init()
clock = pygame.time.Clock()
#Screen
screenWidth = 750
screenHeight = 900
screen = pygame.display.set_mode((screenWidth, screenHeight))
pygame.display.set_caption("Tiles Game")
whiteSurface = pygame.Surface((750, 750))
whiteSurface.fill(white)
pygame.mouse.set_visible(False)
#Blue line
line = pygame.Surface((750, 10))
line.fill(blue)
#Groups
tileGroup = pygame.sprite.Group()
cursor = Cursor("cursor.png")
cursorGroup = pygame.sprite.Group()
cursorGroup.add(cursor)
score = 0
drawTiles()
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.MOUSEBUTTONDOWN:
score += 1
print(score)
print(taken)
print(tileGroup)
cursor.destroyTile()
handleTiles()
#Background
screen.fill(grey)
screen.blit(whiteSurface, (0,0))
screen.blit(line, (0,750))
tileGroup.draw(screen)
cursorGroup.draw(screen)
cursorGroup.update()
pygame.display.update()
In the code I tried using print statements to see if the tile that seems to have disappeared is still there. When this happens, I assume that the tile is not in its group anymore since the number of sprites in the tile group went from 3 to 2. But the list showing all the taken positions still shows that there are 3 positions that are taken. I can still click on the tile if I just click on the space where there should be a tile and the tile comes back. I thought the game should exit when a tile isn't clicked on but it doesn't if there is an "invisible" tile in that position.
How do I make it so that this bug doesn't happen and every new tile made is visible?
The problem is that the cursor has an area and can hit more than one block at a time. So in destroyTile more than 1 block can be removed at once:
def destroyTile(self):
pygame.sprite.spritecollide(cursor, tileGroup, True)
However, the function handleTiles cannot handle this, because it can only remove one block position from the taken list. I suggest to simplify the code and recreate the taken list completely from tileGroup when blocks are removed:
def handleTiles():
taken.clear()
for tile in tileGroup:
x, y = tile.rect.topleft
taken.append([x, y])
drawTiles()

Pygame. How to make a rect change direction on collision (boundary check)

Part of an assignment I'm working on is making a ball bounce around the screen, I can make it move, but my boundary test doesn't seem to be working: the ball simply moves in direction instead of changing direction. So to clarify, what I want to ball to do is change direction as it hits the screen edge.
import sys
import pygame
SCREEN_SIZE = 750, 550
BALL_DIAMETER = 16
BALL_RADIUS = BALL_DIAMETER // 2
MAX_BALL_X = SCREEN_SIZE[0] - BALL_DIAMETER
MAX_BALL_Y = SCREEN_SIZE[1] - BALL_DIAMETER
BLACK = (0, 0, 0)
WHITE = (255, 255, 255)
LEFT = 11
RIGHT = 12
pygame.init()
clock = pygame.time.Clock()
pygame.display.init()
font = pygame.font.SysFont("impact", 20)
pygame.display.set_caption("Breakout")
screen = pygame.display.set_mode(SCREEN_SIZE)
class Ball:
def __init__(self):
''' '''
self.ball = pygame.Rect(300, 730 -
BALL_DIAMETER,
BALL_DIAMETER, BALL_DIAMETER)
# Draw ball
def draw_ball(self):
pygame.draw.circle(screen,
WHITE, (self.ball.left
+ BALL_RADIUS, self.ball.top +
BALL_RADIUS), BALL_RADIUS)
# Updates the coordinates by adding the speed components
def move_ball(self, x, y):
self.xspeed = x
self.yspeed = y
self.ball = self.ball.move(self.xspeed, self.yspeed)
# bounds check
if self.ball.left <= 0:
self.ball.left = 0
self.xspeed = -self.xspeed
elif self.ball.left >= MAX_BALL_X:
self.ball.left = MAX_BALL_X
self.xspeed = -self.xspeed
if self.ball.top < 0:
self.ball.top = 0
self.yspeed = -self.yspeed
elif self.ball.top >= MAX_BALL_Y:
self.ball.top = MAX_BALL_Y
self.yspeed = -self.yspeed
# shows a message on screen, for testing purposes
class Text:
def show_message(self, message):
self.font = pygame.font.SysFont("impact", 20)
font = self.font.render(message,False, WHITE)
screen.blit(font, (200, 400))
class Game:
def __init__(self):
''' '''
def run(self):
b = Ball()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
keys = pygame.key.get_pressed()
# fps lock, screen fill and method call for input
clock.tick(60)
screen.fill(BLACK)
b.draw_ball()
b.move_ball(5, -5)
# used to keep track of various elements
# Text().show_message("P: " + str(p))
pygame.display.flip()
# Creates instance of the game class, and runs it
if __name__ == "__main__":
Game().run()
Your only call to move_ball uses a constant vector.
Since you never change the call parameters, the ball moves only that way.
b.move_ball(5, -5)
Yes, you change the vector components within move_ball when you hit a wall. However, on the next call, you change them back to the original values and move the ball in the original direction.
You have to initialize the vector outside move_ball, and then let the routine access the existing vector when it's called.

Pygame health / maxhealth = 0.0 and not 0.66 [duplicate]

This question already has answers here:
How can I force division to be floating point? Division keeps rounding down to 0?
(11 answers)
Closed 5 years ago.
I am trying to make a healthbar in pygame and it is supposed to show in percent. The formula that I'm using is (lives/mlives)*100, mlives stands for max lives.
When the game begins, player has 3 lives to start with. Each time the shielding gets down to 0 or less, -1 is removed from lives. So on the first run everything works perfectly fine. But when -1 if removed from lives the health bar shows 0 and i have tried to print the formula on terminal without multiplying it with 100 to see what it would show when lives / mlives.
now when player has 3 lives the formula gives 1.0 and that is 100%
when the player has 2 lives the formula gives 0.0 instead of 0.66 which is 66%
player has 3 lives
player has 2 lives
game.py -- funciton name is draw_health_bar
#!/usr/bin/python
import os, sys, player, enemy, config, random
from os import path
try:
import pygame
from pygame.locals import *
except ImportError, err:
print 'Could not load module %s' % (err)
sys.exit(2)
# main variables
FPS, BGIMG = 30, 'FlappyTrollbg.jpg'
# initialize game
pygame.init()
screen = pygame.display.set_mode((config.WIDTH,config.HEIGHT))
pygame.display.set_caption("FlappyTroll - Python2.7")
# background
background = pygame.Surface(screen.get_size())
background = background.convert()
background.fill((255,255,255))
img_dir = path.join(path.dirname(__file__), 'img')
class Background(pygame.sprite.Sprite):
def __init__(self, image_file, location):
pygame.sprite.Sprite.__init__(self)
self.width = config.WIDTH
self.height = config.HEIGHT
self.image = pygame.image.load(path.join(img_dir,image_file)).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
#self.rect.left, self.rect.top = location
self.rect.x, self.rect.y = location
self.speedx = 5
def update(self):
self.rect.x -= self.speedx
if self.rect.x <= -config.WIDTH:
self.rect.x = config.WIDTH
def draw_shield_bar(surf, x, y, percent):
if percent <= 0:
percent = 0
bar_length = 100
bar_height = 10
fill = (percent / 100) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.green, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_health_bar(surf, x, y, percent):
bar_length = 100
bar_height = 10
fill = float(percent) * bar_length
bar_outline = pygame.Rect(x,y,bar_length,bar_height)
bar_filled = pygame.Rect(x,y, fill, bar_height)
pygame.draw.rect(surf, config.red, bar_filled)
pygame.draw.rect(surf, config.white, bar_outline, 2)
def draw_text(surf, text, size, x, y):
font_ = pygame.font.SysFont("Arial", size)
show_kills = font_.render(text, True, config.white)
surf.blit(show_kills, (x, y))
# blitting
screen.blit(background,(0,0))
pygame.display.flip()
# clock for FPS settings
clock = pygame.time.Clock()
def newSprite(group, obj):
group.add(obj)
def main():
all_sprites = pygame.sprite.Group()
bgs = pygame.sprite.Group()
creature = pygame.sprite.Group()
attack = pygame.sprite.Group()
eattack = pygame.sprite.Group()
bgs.add(Background(BGIMG, [0,0]))
bgs.add(Background(BGIMG, [config.WIDTH,0]))
troll = player.FlappyTroll()
creature.add(troll)
for i in range(0,4):
newSprite(all_sprites, enemy.TrollEnemy())
# variable for main loop
running = True
# init umbrella
# event loop
while running:
clock.tick(FPS)
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
for e in all_sprites:
if (pygame.time.get_ticks() - e.starttime) >= e.delay:
newEAtk = enemy.EnemyAttack(e.rect.x, e.rect.y)
eattack.add(newEAtk)
e.starttime = pygame.time.get_ticks()
keys = pygame.key.get_pressed()
if (keys[pygame.K_SPACE]) and (pygame.time.get_ticks() - troll.starttime) >= troll.delay:
newAtk = player.FlappyAttack(troll.rect.x, troll.rect.y)
attack.add(newAtk)
troll.starttime = pygame.time.get_ticks()
b_gets_hit = pygame.sprite.groupcollide(eattack, attack, True, True)
p_gets_hit_eatk = pygame.sprite.groupcollide(eattack, creature, True, False)
gets_hit = pygame.sprite.groupcollide(all_sprites, attack, True, True)
p_gets_hit = pygame.sprite.groupcollide(all_sprites, creature, True, False)
if gets_hit or p_gets_hit:
newEnemy = enemy.TrollEnemy()
newSprite(all_sprites, newEnemy)
for p in creature:
if p_gets_hit or p_gets_hit_eatk:
troll.shield -= random.randint(1,5)*1.5
if troll.shield <= 0:
troll.lives -= 1
troll.shield = 100
if troll.lives == 0:
print "#--- GAME OVER ---#"
break
screen.fill([255, 255, 255])
# update
bgs.update()
all_sprites.update()
creature.update()
attack.update()
eattack.update()
# draw
bgs.draw(screen)
all_sprites.draw(screen)
creature.draw(screen)
attack.draw(screen)
eattack.draw(screen)
draw_shield_bar(screen, 5, 5, troll.shield)
draw_health_bar(screen, 5, 20, (troll.lives/troll.mlives))
draw_text(screen, ("Lives: "+str(troll.lives)), 20, (config.WIDTH / 2), 0)
print float(troll.lives/troll.mlives)
# flip the table
pygame.display.flip()
pygame.quit()
if __name__ == '__main__':
main()
player.py (object name is Troll in game.py)
import pygame, config
from pygame.locals import *
from os import path
from random import randint
img_dir = path.join(path.dirname(__file__), 'img')
class FlappyTroll(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.width = 64
self.height = 64
self.image = pygame.image.load(path.join(img_dir,"flappytroll.png")).convert_alpha()
self.image = pygame.transform.scale(self.image,(self.width,self.height))
self.rect = self.image.get_rect()
self.rect.x = self.width*2
self.rect.y = config.HEIGHT/2-self.height
self.speedy = 5
self.starttime = pygame.time.get_ticks()
self.delay = 500
self.shield = 100
self.lives = 3
self.mlives = 3
def update(self):
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
self.rect.y -= self.speedy*2
elif self.rect.y < config.HEIGHT-self.height*1.5:
self.rect.y += self.speedy
Try dividing by 3.0 rather than just 3. I'm pretty sure this will work as it is what Python 2's integer division is coded as. Hope this helps :-) P.S. If you do any more additions, subtractions, divisions or multiplications, be sure to put .0 at the end unless you want another D.P. e.g. 2 * 7.0.

Pygame update bug

I am creating my game on python with pygame but I have got a bug while updating screen :
my older character position are displayed
I am using a pattern to fill my screen and I refresh it each time and this before my character so it shouldn't be laggy, this is my code :
import pygame, sys
from pygame.locals import *
pygame.init()
#Open Pygame window
taille_fenetre = [960, 640]
fenetre = pygame.display.set_mode(taille_fenetre)
pygame.key.set_repeat(400, 30)
clock = pygame.time.Clock()
#Chargement et collage du fond
fond = pygame.image.load("brock.png").convert()
for a in range(taille_fenetre[0] // fond.get_width() + 1):
for i in range(taille_fenetre[1] // fond.get_height() + 1):
fenetre.blit(fond, (a*fond.get_width(),i*fond.get_height()))
room_size = (taille_fenetre[0]-64*2,taille_fenetre[1]-64*2)
room = pygame.Surface(room_size)
room.fill((255,0,0))
terre = pygame.image.load("terre.png").convert()
for a in range(room.get_width() // terre.get_width() + 1):
for i in range(room.get_height() // terre.get_height() + 1):
room.blit(terre, (a*terre.get_width(),i*terre.get_height()))
shadow_room = pygame.image.load('shadow_room.png')
def blit_alpha(target, source, location, opacity):
x = location[0]
y = location[1]
temp = pygame.Surface((source.get_width(), source.get_height())).convert()
temp.blit(target, (-x, -y))
temp.blit(source, (0, 0))
temp.set_alpha(opacity)
target.blit(temp, location)
class Personnage:
def __init__(self):
self.imagesrc = "perso.png"
self.image = pygame.image.load(self.imagesrc).convert_alpha()
self.size = self.image.get_size()
self.position = [0,0]
self.vitesse = 5
self.sante = 5
def bouger(self,facteur):
self.position = [self.position[0] - self.vitesse * facteur[0],self.position[1] - self.vitesse * facteur[1]]
def afficher(self,fenetre):
print(self.position)
fenetre.blit(self.image,self.position)
#INFINITE LOOP
continuer = 1
perso = Personnage()
while continuer:
relachex = True
relachey = True
#Re-collage
fenetre.blit(fond, (0,0))
fenetre.blit(room,(64,64))
blit_alpha(room,shadow_room,(0,0),128)
for event in pygame.event.get(): #Attente des événements
if event.type == QUIT:
continuer = 0
pygame.quit()
sys.exit()
keys = pygame.key.get_pressed()
if keys[K_LEFT]:
perso.bouger([1,0])
if keys[K_RIGHT]:
perso.bouger([-1,0])
if keys[K_DOWN]:
perso.bouger([0,-1])
if keys[K_UP]:
perso.bouger([0,1])
#REFRESH
perso.afficher(fenetre)
pygame.display.flip()
clock.tick(60)
you are drawing over the previous images again and again without clearing out the old stuff. You can't tell with the other images because they are static, but it shows with the character. Add:
fenetre.fill((0, 0, 0))
to the top of your game loop to refresh the screen to draw the new locations.

Categories