After hours of searching, I still can't figure out why only the most recently spawned circle is affected by the collision detection. I commented out the code in question. I experimented with sprites and that may be the answer but I still got the same results.
import pygame,random
pygame.init()
width,height,radius = 1280,720,20
class Ball():
def __init__(self):
self.x = 0
self.y = 0
self.vx = 0
self.vy = 0
def make_ball():
ball = Ball()
ball.x = random.randrange(radius, width - radius)
ball.y = random.randrange(radius, 100)
ball.vx = random.randint(1,2)
ball.vy = 0
return ball
def main():
rect_x = 60
display = pygame.display.set_mode((width,height))
pygame.display.set_caption("BOUNCE")
running = True
ball_list = []
ball = make_ball()
ball_list.append(ball)
while running:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
ball = make_ball()
ball_list.append(ball)
for ball in ball_list:
ball.x += ball.vx
ball.vy += 0.02
ball.y += ball.vy
if ball.y >= height - radius:
ball.vy *= -1
if ball.x >= width - radius or ball.x <= radius:
ball.vx *= -1
display.fill((0,0,0))
for ball in ball_list:
random_color = (random.randint(1,255),random.randint(1,255),random.randint(1,255))
circle = pygame.draw.circle(display,random_color,(int(ball.x), int(ball.y)),radius)
rectangle = pygame.draw.rect(display,(255,255,255),(int(rect_x),660,60,60))
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and rect_x > 0:
rect_x -= 2
if event.key == pygame.K_RIGHT and rect_x < width - 60:
rect_x += 2
'''if pygame.Rect(circle).colliderect(rectangle) == True: ###THIS IS THE BAD CODE!
print('Your Score:',pygame.time.get_ticks())
running = False'''
text = pygame.font.Font(None,120).render(str(pygame.time.get_ticks()),True,(255,255,255))
display.blit(text,(50,50))
pygame.display.flip()
pygame.quit()
if __name__ == "__main__":
main()
Indentation and code organization is the key to this. the offending section is (comments removed):
for ball in ball_list:
random_color = (random.randint(1,255),random.randint(1,255),random.randint(1,255))
circle = pygame.draw.circle(display,random_color,(int(ball.x), int(ball.y)),radius)
rectangle = pygame.draw.rect(display,(255,255,255),(int(rect_x),660,60,60))
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT and rect_x > 0:
rect_x -= 2
if event.key == pygame.K_RIGHT and rect_x < width - 60:
rect_x += 2
if pygame.Rect(circle).colliderect(rectangle) == True:
print('Your Score:',pygame.time.get_ticks())
running = False
You had all the correct pieces, but the order in which you are doing them is off as well as the indentation:
for ball in ball_list:
random_color = (random.randint(1,255),random.randint(1,255),random.randint(1,255))
circle = pygame.draw.circle(display,random_color,(int(ball.x), int(ball.y)),radius)
rectangle = pygame.draw.rect(display,(255,255,255),(int(rect_x),660,60,60))
if pygame.Rect(circle).colliderect(rectangle):
print('Your Score:',pygame.time.get_ticks())
running = False
This will now run through every ball in the list and check each one for collision. notice the colliderect if statement is indented into the for loop. Also notice i removed the KEYDOWN check from in the middle of it all
Speaking of that I would recommend using:
pressed = pygame.key.get_pressed()
if pressed[pygame.K_LEFT] and rect_x > 0:
rect_x -= 2
if pressed[pygame.K_RIGHT] and rect_x < width - 60:
rect_x += 2
for ball in ball_list:
# for loop from above
instead of what you had. This works best for when you want to allow for holding a key down. pygame.key.get_pressed() gets the state of all the keys all the time, not just when an event happens
Related
I'm quite new with python and coding. Trying to code a simple game using pygame I have found an issue that I can't solve in my movement function.
I have a function that uses the pygame method get_pressed() in order to set the direction of the player velocity vector (see below). With this code if in the game I hold down a and then press d the player surface changes direction even if i'm still holding down the a key, while if I do the opposite the player continues moving to the right as if I don't press the a key.
I understand that this is due to the order of the if statements in the function and my question is: is there a way to make the player change direction in both cases?
I really hope that the question is clear and thanks in advance if someone will help me.
The function that sets the direction of the velocity:
def get_keys(self):
self.vel = vec(0, 0)
keys = pg.key.get_pressed()
if keys[pg.K_a]:
self.vel.x = -PLAYER_SPEED
if keys[pg.K_d]:
self.vel.x = PLAYER_SPEED
if keys[pg.K_w]:
self.vel.y = -PLAYER_SPEED
if keys[pg.K_s]:
self.vel.y = PLAYER_SPEED
if self.vel.x != 0 and self.vel.y != 0:
self.vel *= 0.7071
The update function:
def update(self)
self.get_keys()
self.pos += self.vel * self.game.dt
self.rect.centerx = self.pos.x
self.rect.centery = self.pos.y
For what you want to do you have to use the keyboard events. Note that you want to change direction as soon as a key is pressed. This is and event. e.g.:
import pygame
pygame.init()
window = pygame.display.set_mode((400, 400))
clock = pygame.time.Clock()
pos = pygame.Vector2(window.get_rect().center)
dx, dy = 0, 0
speed = 5
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_a:
dx = -1
if event.key == pygame.K_d:
dx = 1
if event.key == pygame.K_w:
dy = -1
if event.key == pygame.K_s:
dy = 1
if event.type == pygame.KEYUP:
if (event.key == pygame.K_a and dx < 0) or (event.key == pygame.K_d and dx > 0):
dx = 0
if (event.key == pygame.K_w and dy < 0) or (event.key == pygame.K_s and dy > 0):
dy = 0
vel = pygame.Vector2(dx, dy)
if vel.x != 0 or vel.y != 0:
vel.scale_to_length(speed)
pos += vel
pos.x %= window.get_width()
pos.y %= window.get_height()
window.fill(0)
pygame.draw.circle(window, (255, 0, 0), pos, 20)
pygame.display.flip()
clock.tick(60)
pygame.quit()
exit()
I am making a mini-game that involves movement. I created an object that moves according to the controls, but how do i make it not move if it collides with a wall?
#Imports
import pygame, sys
#Functions
#General Set Up
pygame.init()
clock = pygame.time.Clock()
#Main Window
swid = 1280
shgt = 700
screen = pygame.display.set_mode((swid, shgt))
pygame.display.set_caption("Raid: The Game || Movement Test")
#Game Rectangles
player = pygame.Rect(30, 30, 30, 30)
wall = pygame.Rect(140, 80, 1000, 20)
window = pygame.Rect(300, 400, 220, 20)
door = pygame.Rect(500, 500, 120, 18)
bgcol = (0,0,0)
playercol = (200,0,0)
wallcol = pygame.Color("grey12")
doorcol = pygame.Color("blue")
windowcol = (100,100,100)
xwalkspeed = 0
ywalkspeed = 0
while True:
#Handling Input
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_DOWN:
ywalkspeed += 10
if event.key == pygame.K_UP:
ywalkspeed -= 10
if event.key == pygame.K_LEFT:
xwalkspeed -= 10
if event.key == pygame.K_RIGHT:
xwalkspeed += 10
if event.type == pygame.KEYUP:
if event.key == pygame.K_DOWN:
ywalkspeed -= 10
if event.key == pygame.K_UP:
ywalkspeed += 10
if event.key == pygame.K_LEFT:
xwalkspeed += 10
if event.key == pygame.K_RIGHT:
xwalkspeed -= 10
player.x += xwalkspeed
player.y += ywalkspeed
distancebetween = 0
if player.colliderect(wall):
ywalkspeed = 0
xwalkspeed= 0
if player.top <= 0:
player.top = 0
if player.bottom >= shgt:
player.bottom = shgt
if player.left <= 0:
player.left = 0
if player.right >= swid:
player.right = swid
#Visuals
screen.fill(bgcol)
pygame.draw.ellipse(screen, playercol, player)
pygame.draw.rect(screen, wallcol, wall)
pygame.draw.rect(screen, doorcol, door)
pygame.draw.rect(screen, windowcol, window)
#Updating the Window
pygame.display.flip()
clock.tick(60)
Whenever my Object collides upon a wall, it starts moving the opposite way until it is no longer seen in the screen.
I Found the Reason why it kept moving the opposite way when it collides the wall. It is because if I move towards the wall, my ywalkspeed = 10 and when I collided with the wall, it becomes 0, then if I started to let go of the movement key, the ywalkspeed becomes ywalkspeed = -10. I currently don't know a way how to fix this as I am just a noob.
Make a copy of the original player rectangle with copy(). Move the player. If a collision is detected, restore the player rectangle:
copy_of_player_rect = player.copy()
# move player
# [...]
if player.colliderect(wall):
player = copy_of_player_rect
You can watch for the distance between the wall and the player and when the distance according to axis positions reaches zero, just do
xwalkspeed = 0
ywalkspeed = 0
To measure the distance and smooth realistic movement, you can also use equations of motion from Physics.
I have been creating a game where an image moves according to player input with Keydown and Keyup methods. I want to add boundaries so that the user cannot move the image/character out of the display (I dont want a game over kind of thing if boundary is hit, just that the image/character wont be able to move past that boundary)
import pygame
pygame.init()#initiate pygame
black = (0,0,0)
white = (255,255,255)
red = (255,0,0)
display_width = 1200
display_height = 800
display = pygame.display.set_mode((display_width,display_height))
characterimg_left = pygame.image.load(r'/Users/ye57324/Desktop/Make/coding/python/characterimg_left.png')
characterimg_right = pygame.image.load(r'/Users/ye57324/Desktop/Make/coding/python/characterimg_right.png')
characterimg = characterimg_left
def soldier(x,y):
display.blit(characterimg, (x,y))
x = (display_width * 0.30)
y = (display_height * 0.2)
pygame.display.set_caption('No U')
clock = pygame.time.Clock()#game clock
flip_right = False
x_change = 0
y_change = 0
bg_x = 0
start = True
bg = pygame.image.load(r'/Users/ye57324/Desktop/Make/coding/python/bg.png').convert()
class player:
def __init__(self, x, y):
self.jumping = False
p = player(x, y)
while start:
for event in pygame.event.get():
if event.type == pygame.QUIT or (event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE):
start = False
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change += -4
if flip_right == True:
characterimg = characterimg_left
flip_right = False
x += -150
elif event.key == pygame.K_RIGHT:
x_change += 4
if flip_right == False:
characterimg = characterimg_right
flip_right = True
x += 150
elif event.key == pygame.K_UP:
y_change += -4
elif event.key == pygame.K_DOWN:
y_change += 4
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT:
x_change += 4
elif event.key == pygame.K_RIGHT:
x_change += -4
elif event.key == pygame.K_UP:
y_change += 4
elif event.key == pygame.K_DOWN:
y_change += -4
x += x_change
y += y_change
display.fill(white)
soldier(x,y)
pygame.display.update()
clock.tick(120)#fps
pygame.quit()
I have tried several times including switching to the key pressed method but they all failed. Help please, thank you.
Basically you want to limit the player's movement.
So everytime you want to "move" the player (I'm guessing this is "x_change" / "y_change") you need to check whether they would still be inside your boundaries after the move.
Example: Your display x boundary is 0 pixels on the left of your screen and 500 to the right. I only allow the actual movement if the result of the movement is within my boundaries.
boundary_x_lower = 0
boundary_x_upper = 500
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
if boundary_x_lower < (x_change - 4):
# I allow the movement if I'm still above the lower boundary after the move.
x_change -= 4
elif event.key == pygame.K_RIGHT:
if boundary_x_upper > (x_change +4):
# I allow the movement if I'm still below the upper boundary after the move.
x_change += 4
PS: I am confused by your code as you subtract when you move to the right... I am used to 2D games where you increment the player's position if you move to the right... and subtract if you go to the left.
Feel free to adapt the code to fit your project. The basic principle applies also to the y-axis movement: with boundary_y_lower & _y_upper. if you have further questions, just ask!
Just clamp the x and y values between 0 and the display width and height.
# In the main while loop after the movement.
if x < 0:
x = 0
elif x + image_width > display_width:
x = display_width - image_width
if y < 0:
y = 0
elif y + image_height > display_height:
y = display_height - image_height
I also recommend checking out how pygame.Rects work. You could define a rect with the size of the display,
display_rect = display.get_rect()
and a rect for the character which will be used as the blit position:
rect = characterimg_left.get_rect(center=(x, y))
Then move and clamp the rect in this way:
rect.move_ip((x_change, y_change))
rect.clamp_ip(display_rect)
display.fill(white)
# Blit the image at the `rect.topleft` coordinates.
display.blit(characterimg, rect)
For my code, I would like the chickens (enemies) to move 4 different ways:
vertical, horizontal, diagonal and following the player (pig).
Each chicken should have its own movement and move independently. Only two chickens can go in diagonal.
I have only programmed their vertical and horizontal movements but there is a problem with them; sometimes, they all move horizontally, sometimes, vertically. Sometimes they don't move at all.
Here is my code:
def game_loop():
x_change = 0
y_change = 0
foodCounter = 0
Score = 0
list = ["Vertical","Vertical","Horizontal","Horizontal","Follow","Diagonal1","Diagonal2"]
baddies = []
item = 0
x = (display_width * 0.45)
y = (display_height * 0.8)
foodx = random.randrange(48, display_width - 48)
foody = random.randrange(54, display_height - 54)
gameExit = False
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit
quit()
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_LEFT:
x_change = -8
if event.key == pygame.K_RIGHT:
x_change = 8
if event.key == pygame.K_UP:
y_change = -8
if event.key == pygame.K_DOWN:
y_change = 8
if event.type == pygame.KEYUP:
if event.key == pygame.K_LEFT or event.key == pygame.K_RIGHT:
x_change = 0
if event.key == pygame.K_UP or event.key == pygame.K_DOWN:
y_change = 0
if x > 705:
x_change = 0
x = 705
if x < -10:
x_change = 0
x = -10
if y < -15:
y_change = 0
y = -15
if y > 505:
y_change = 0
y = 505
x += x_change
y += y_change
gameDisplay.fill(white)
gameDisplay.blit(background,(-50,-50))
food = pygame.Rect(foodx, foody,48 , 54)
if foodCounter == 0:
gameDisplay.blit(foodImage, food)
player = pygame.Rect(x, y,108,105)
if player.colliderect(food):
foodCounter += -1
Score += 1
foodx = random.randrange(48, display_width - 48)
foody = random.randrange(54, display_height - 54)
foodCounter += 1
item = random.randint(1, len(list))
print(item)
if item == 1 or item == 2:
newchicken = {'rect':pygame.Rect(random.randint(0,display_width-45),0,45,63),
'surface':pygame.transform.scale(enemyImage,(45,63)),
'vertical': "vertical",
'down': "down"
}
item = 0
baddies.append(newchicken)
if item == 3 or item == 4:
newchicken = {'rect':pygame.Rect(0,random.randint(0,display_height-45),45,63),
'surface': pygame.transform.scale(enemyImage, (45,63)),
'horizontal': "horizontal",
'right': "right"
}
item = 0
baddies.append(newchicken)
if item == 6:
newchicken = {'rect':pygame.Rect(200,0,45,63),
'surface':pygame.transform.scale(enemyImage,(45,63)),
'diagonal1': "diagonal1"
}
if "Diagonal1" in list:
list.remove("Diagonal1")
item = 0
baddies.append(newchicken)
if len(list) == 7:
if item == 7:
newchicken = {'rect':pygame.Rect(100,600,45,63),
'surface':pygame.transform.scale(enemyImage,(45,63)),
'diagonal2': "diagonal2"
}
if "Diagonal2" in list:
list.remove("Diagonal2")
item = 0
baddies.append(newchicken)
if len(list) == 6:
if item == 6:
newchicken = {'rect':pygame.Rect(100,600,45,63),
'surface':pygame.transform.scale(enemyImage,(45,63)),
'diagonal2': "diagonal2"
}
if "Diagonal2" in list:
list.remove("Diagonal2")
item = 0
baddies.append(newchicken)
gameDisplay.blit(pigImage, player)
for b in baddies:
gameDisplay.blit(b['surface'],b['rect'])
for b in baddies:
if "vertical" in newchicken:
if "down" in newchicken:
if not b['rect'].bottom >= 600:
b['rect'].move_ip(0, 2)
if b['rect'].bottom >= 600 :
del newchicken['down']
newchicken["up"] = "up"
if "up" in newchicken:
if not b['rect'].top <= 0:
b['rect'].move_ip(0,-2)
if b['rect'].top <= 0:
del newchicken ['up']
newchicken["down"] = "down"
if "horizontal" in newchicken:
print ("horizontal")
if "right" in newchicken:
if not b['rect'].right >= 800:
b['rect'].move_ip(2,0)
if b['rect'].right >= 800:
del newchicken['right']
newchicken['left'] = "left"
if "left" in newchicken:
if not b['rect'].left <= 0:
b['rect'].move_ip(-2,0)
if b['rect'].left <= 0:
del newchicken ['left']
newchicken['right'] = "right"
pygame.display.update()
clock.tick(60)
game_loop()
Your code does not work because you always check for keys in newchicken when you iterate over baddies, while I guess you should check b instead.
But what you should do instead is to change your code in a way that seperates the different logical concerns of your game. Also, just use a class instead of a dict to group data and behaviour that belongs together.
Let's think a bit:
We have some kind of entity that basically consists of three things: an image, a position, and a behavior. You already know this, because you created these dicts:
newchicken = {'rect':pygame.Rect(100,600,45,63),
'surface':pygame.transform.scale(enemyImage,(45,63)),
'diagonal2': "diagonal2"
}
Let's tweak this a bit and use a Sprite (really, read the docs), which already has these things (more or less). Our class class could look like this (and don't tell me you don't want to use classes. That's stupid IMHO since you already use dozens of classes):
class Animal(pg.sprite.Sprite):
def __init__(self, color, pos, logic, *groups):
super().__init__(*groups)
self.image = pg.surface.Surface((40, 40))
self.image.fill(color)
self.rect = self.image.get_rect(topleft=pos)
self.logic = logic
self.direction = pg.Vector2((0, 0))
def update(self):
self.logic(self)
So, we have an image (which is just a plain rect so we have a working game at the end that everybody can copy/paste and run), a rect that stores the position, a mysterious logic argument that's a function (the "AI" of our animal) which will be called every tick, and a direction, which defines in which direction out animal will walk.
So, let's create the actual logic that will move out animals. We create three functions, one for vertical movement, one for horizontal movement, and one that will always walk towards the mouse cursor:
def move_vertical(animal):
if animal.direction.length() == 0:
animal.direction = pg.Vector2(5, 0)
animal.rect.move_ip(*animal.direction)
if not screen_rect.contains(animal.rect):
animal.direction *= -1
animal.rect.move_ip(animal.direction)
def move_horizontal(animal):
if animal.direction.length() == 0:
animal.direction = pg.Vector2(0, 5)
animal.rect.move_ip(*animal.direction)
if not screen_rect.contains(animal.rect):
animal.direction *= -1
animal.rect.move_ip(animal.direction)
def move_to_mouse(animal):
pos = pg.mouse.get_pos()
v = pg.Vector2(pos) - pg.Vector2(animal.rect.center)
if v.length() > 0:
v.normalize_ip()
v *= 5
animal.rect.move_ip(*v)
If we now want to create an Animal, we simply pass one of those functions to the constructor. No need for massive if/else blocks in your main loop (note how clean and simple it is in the end).
So, here's our final example. Note how we create four animals: two that move vertically, one that moves horizontally, and one that follows the mouse.
import pygame as pg
pg.init()
clock = pg.time.Clock()
running = True
screen = pg.display.set_mode((640, 480))
screen.fill((255, 255, 255))
screen_rect = screen.get_rect()
def move_vertical(animal):
if animal.direction.length() == 0:
animal.direction = pg.Vector2(5, 0)
animal.rect.move_ip(*animal.direction)
if not screen_rect.contains(animal.rect):
animal.direction *= -1
animal.rect.move_ip(animal.direction)
def move_horizontal(animal):
if animal.direction.length() == 0:
animal.direction = pg.Vector2(0, 5)
animal.rect.move_ip(*animal.direction)
if not screen_rect.contains(animal.rect):
animal.direction *= -1
animal.rect.move_ip(animal.direction)
def move_to_mouse(animal):
pos = pg.mouse.get_pos()
v = pg.Vector2(pos) - pg.Vector2(animal.rect.center)
if v.length() > 0:
v.normalize_ip()
v *= 5
animal.rect.move_ip(*v)
class Animal(pg.sprite.Sprite):
def __init__(self, color, pos, logic, *groups):
super().__init__(*groups)
self.image = pg.surface.Surface((40, 40))
self.image.fill(color)
self.rect = self.image.get_rect(topleft=pos)
self.logic = logic
self.direction = pg.Vector2((0, 0))
def update(self):
self.logic(self)
sprites = pg.sprite.Group()
Animal(pg.color.Color('yellow'), ( 10, 10), move_vertical, sprites)
Animal(pg.color.Color('red'), (200, 400), move_vertical, sprites)
Animal(pg.color.Color('orange'), (500, 100), move_horizontal, sprites)
Animal(pg.color.Color('brown'), (100, 200), move_to_mouse, sprites)
while running:
for e in pg.event.get():
if e.type == pg.QUIT:
running = False
sprites.update()
screen.fill((255, 255, 255))
sprites.draw(screen)
pg.display.update()
clock.tick(60)
making a simple game in pygame. have imported an image for my charactater and can move him around freely. I was wondering how to make the character slowly stop when the;
event.type == KEYUP:
as in, When I release the key the character will take say, 50 pixels from when the key has been released to the character slowly stopping.
I have tried a few different things but nothing seems to work. below is my code. any help would be great =]
bg="bg.jpg"
staticCharacter="toon.png"
import pygame, sys, time
from pygame.locals import *
pygame.init()
screen = pygame.display.set_mode((640,360),0,32)
background=pygame.image.load(bg).convert()
staticToon = pygame.image.load(characterMid).convert_alpha()
x = 0
y = 0
movex = 0
movey = 0
while True:
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
if event.type == KEYDOWN:
if event.key == K_LEFT:
movex = -0.20
elif event.key == K_RIGHT:
movex = +0.20
elif event.key == K_UP:
movey = -0.20
elif event.key == K_DOWN:
movey = +0.20
if event.type == KEYUP:
if event.key == K_LEFT:
movex = 0
elif event.key == K_RIGHT:
movex = 0
elif event.key == K_UP:
movey = 0
elif event.key == K_DOWN:
movey = 0
x += movex
y += movey
if x > 560:
x = 560
if y > 280:
y = 280
if x < 0:
x = 0
if y < 0:
y = 0
screen.blit(background,(0,0))
screen.blit(staticToon,(x,y))
pygame.display.update()
distance = speed*time
Instead of augmenting the position (through movex or movey), you want to augment the speed:
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
...
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
Now, to slow down the object (a ball in my example below), add some friction:
ball.speed[0] *= 0.95
ball.speed[1] *= 0.95
This diminishes the speed by a little bit each time through the event loop.
Move the object a distance equal to speed * time. To make things simple, let's just take change in time to be 1.
self.rect = self.rect.move(self.speed)
Here is a runnable example: (just add a correct path for staticCharacter.)
import sys
import pygame
import os
'''
Based on http://www.pygame.org/docs/tut/intro/intro.html
Draws a red ball bouncing around in the window.
Pressing the arrow keys moves the ball
'''
staticCharacter = "toon.png"
delta = {
pygame.K_LEFT: (-20, 0),
pygame.K_RIGHT: (+20, 0),
pygame.K_UP: (0, -20),
pygame.K_DOWN: (0, +20),
}
class Ball(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.image.load(staticCharacter)
self.rect = self.image.get_rect()
self.speed = [2, 2]
self.area = pygame.display.get_surface().get_rect()
def update(self):
self.rect = self.rect.move(self.speed)
if self.rect.left < 0 or self.rect.right > self.area.width:
self.speed[0] = -self.speed[0]
if self.rect.top < 0 or self.rect.bottom > self.area.height:
self.speed[1] = -self.speed[1]
class Main(object):
def __init__(self):
self.setup()
def setup(self):
pygame.init()
size = (self.width, self.height) = (640,360)
self.black = (0, 0, 0)
self.screen = pygame.display.set_mode(size, 0, 32)
self.ball = Ball()
self.setup_background()
def setup_background(self):
self.background = pygame.Surface(self.screen.get_size())
self.background = self.background.convert()
self.background.fill(self.black)
self.screen.blit(self.background, (0, 0))
pygame.display.flip()
def draw(self):
self.screen.blit(self.background, (0, 0))
self.screen.blit(self.ball.image, self.ball.rect)
pygame.display.flip()
def event_loop(self):
ball = self.ball
while True:
for event in pygame.event.get():
if ((event.type == pygame.QUIT) or
(event.type == pygame.KEYDOWN and
event.key == pygame.K_ESCAPE)):
sys.exit()
elif event.type == pygame.KEYDOWN:
deltax, deltay = delta.get(event.key, (0, 0))
ball.speed[0] += deltax
ball.speed[1] += deltay
ball.speed = [0.99*s for s in ball.speed]
ball.update()
self.draw()
pygame.time.delay(10)
if __name__ == '__main__':
app = Main()
app.event_loop()
Normally, character movement is made to be independent from the machine speed. In your example, you do not know how many times per second the While loop goes, it is dependent on the computer speed. So firstly, you should use the pygame Clock to calculate the time difference between each call to move().
myclock = pygame.Clock()
delta = myclock.tick()
when you have the time difference, you can use a distance equation such as this:
position += speed * direction * delta
delta is in ms, so you need to set your speed accordingly. The direction would be -1 or +1 depending on the current key pressed. A keyup would set direction to 0.
Now back to the question. You would have to change the above a bit differently.
A keyup would subtract a small value from direction.
Then check if direction isn't 1 or -1 and then subtract a value from your direction. You would do this every loop. This way it will get smaller and smaller, and it will appear that your character is slowing down. It is good to also multiply the value that you subtract by delta so it will be deacceleration will constant on every computer.
My advice:
Go to the following website: http://karpathy.ca/phyces/tutorial1.php
Download pygamehelper and startercode (I promise it will make your life easier).
FPS is built-in so you don't need to make the adjustments within your movement logic.
Review some of the other tutorials if you want further clarification and use the vector library if you want some extra snaz.