Most simple algorithm to reverse direction on collision - pygame

I teach Computer Programming. For first course students I'm looking for most easy algorithm to change direction on collide (with window frame). This is my actual code:
import sys, pygame
pygame.init()
speed_x = 1
speed_y = 1
black = (0, 0, 0)
width, height = 320, 240
size = ( width, height )
screen = pygame.display.set_mode(size)
display_surface = pygame.display.get_surface()
display_rectangle = display_surface.get_rect()
ball_img = pygame.image.load("data/ball.gif")
ball = ball_img.convert_alpha()
ballrect = ball.get_rect()
while 1:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
if not display_rectangle.contains( ballrect.move([speed_x, 0]) ):
speed_x *= -1
else:
ballrect = ballrect.move([speed_x, 0])
if not display_rectangle.contains( ballrect.move([0, speed_y]) ):
speed_y *= -1
else:
ballrect = ballrect.move([0, speed_y])
screen.fill(black)
screen.blit(ball, ballrect)
pygame.display.flip()
pygame.time.delay(30)
This code works fine, my question is if someone know about a easy algorithm or clear algorithm to reverse direction using contains or any other collide pygame test.
Some times I think that an approach is most easy for students and a new clear approach appears.
All suggestions are welcome
Some suggestions? Thanks!

I think your approach is fine. Your code models the velocity of the ball using the 2-vector [speed_x, speed_y]. Bouncing off a (horizontal or vertical) wall involves negating the appropriate component of the vector.
As long as your students have a background in introductory physics (velocity, momentum, collisions) then your code should be understandable.

Your code probably works ok (I haven't tried, but it seems fine), but I have mixed feelings about it. You check the x-movement then the y-movement, which is ok, but the Rect.contains() tests both x and y so it seems a bit redundant to me.
And depending on your students background, it kind of hides what you are doing.
I think I'd like testing everything manually :
if display_rectangle.left<=ballrect.left+speed_x or display_rectangle.right<=ballrect.right+speed_x
speed_x*=-1
if display_rectangle.top<=ballrect.top+speed_y or display_rectangle.bottom<=ballrect.bottom+speed_y
speed_y*=-1
ballrect.move([speed_x, speed_y])
BTW : why is it ballrect and not ball_rect ?

Related

How to set the position of an image in pygame?

I created an object called "alien" in my game. Uploaded the image for "alien" and used "get_rect()" to set its rect attributes.
Now I want to change the x-coordinate value of "alien". Which of the following two ways is correct?
alien.x = ...... or alien.rect.x = ......
I saw in a textbook that the following codes are used:
alien.x = alien_width + 2 * alien_width * alien_number
alien.rect.x = alien.x
Why didn't the author directly use alien.rect.x to change the x-coordinate value of "alien"? Like:
alien.rect.x = alien_width + 2 * alien_width * alien_number
Why does there have to be alien.x?
Unfortunately the answer is "it depends". Some people's code maintains the position of the object at an internal x and y, using a rect to handle collisions. Other code just maintains the rect, using the rect.x and rect.y if a single position is (ever) needed.
It's up to you really, but my advice is to keep it all inside a PyGame Rect, as this has benefits of easy collision detection, should you wish to use that down the track.
class Alien:
def __init__( self, x, y, image ):
self.image = image
self.rect = image.get_rect() # copy the image dimensions
self.rect.x = x
self.rect.y = y # move to location
def draw( self, window ):
window.blit( self.image, self.rect ) # paint it
When it's time to move the Alien, you can just as easy adjust the rectangle as an x and y
class Alien:
...
def moveBy( self, by_x, by_y ):
self.rect.move_ip( by_x, by_y )
def moveTo( self, x, y ):
self.rect.x = x
self.rect.y = y
Perhaps the author thought that having a separate x and y made the code easier to understand. This is a paramount reason that effects programming style. Program code is read many times more often than it is written, so often extra variables are included to illustrate the program flow better.
For example, like checking a mouse-click-event:
for event in pygame.event.get( ):
if event.type == pygame.QUIT:
running = False
elif event.type == pygame.MOUSEBUTTONUP:
handleGameClick( mouse2Board( event.pos ) ) # <-- HERE
Adding extra variables documents what's going on:
elif event.type == pygame.MOUSEBUTTONUP:
mouse_click_coord = event.pos
game_board_coord = mouse2Board( mouse_click_coord )
handleGameClick( game_board_coord )
Here it tells the reader that event.pos is a co-ordinate (so probably a pair of values), and is from the mouse. Then it reenforces that the co-ordinate is then converted into a game-board space before being passed out to handleGameClick() for processing.
The two pieces of code have exactly the same outcome (and probably execution speed), but the second is much easier to follow.
IMHO one should ideally write code such that someone unfamiliar with the language (but still a programmer) can understand it without too much trouble. This is why in my answers you wont see much "pythonic" loop-in-list creation like:
[[col +1 for col in row] for row in a] # Taken from 10 vote answer
Because unless your very familiar with python syntax, it's unreadable.

Moving Sprites Right and Downwards

I am working on this project to move a sprite, but I can't seem to figure out how to move a sprite to the right as well as move it downwards. Any thoughts?
Here is my program:
import pygame
import time
import sys
pygame.init()
# Set up window
screen = pygame.display.set_mode((320, 240))
# Load an image
rocket = pygame.image.load("rocket.png")
rocketrect = rocket.get_rect()
x_coord = 0
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT: sys.exit()
screen.fill((0,0,0))
screen.blit(rocket, rocketrect)
pygame.display.flip()
x_coord += 1
rocketrect.centerx = x_coord
In your method of mving the sprite, you change the coordinatses (x) and then assign it to the centerx of the images rectangle. If you want to keep this kind of method (changing and assigning), and also move the image down, you will need to give a y value. For example
# Define y variable
y_coord = 0
# Your code
…
y_coords += 1
rocketrect.centery = y_coord
This works similarly to how you moved your x_coords, but in total, the program is a bit basic and is not how programmers (or at least me) usually code. Another person might used a tuple for the location, along with making a rocket class. There are also some other ways to move the sprite, such as the .move() or .move_ip() that I would suggest. But it's up to you.

Need help creating game close to "Pong" (Pygame)

As you read from the description (or not), I need help creating a game close to Pong.
I am really new in programming, and I am learning all by myself. The game you help me create will be my first game ever.
My version of game, explained:
Picture (Can't post a picture here since I am new):
http://www.upload.ee/image/3307299/test.png (THIS LINK IS SAFE)
So, number 1 stands for walls (black ones)
Number 2 marks the area, where time stops (game over)
3 is your time survived.
Number 4 is the ball that bounces (like they do in Pong).
Code:
import pygame
import random
pygame.init()
screen = pygame.display.set_mode([640, 480])
paddle = pygame.image.load("pulgake.png")
pygame.display.set_caption("PONG!")
back = pygame.image.load("taust.png")
screen.blit(back, (0, 0))
screen.blit(paddle, (600, 240))
pygame.display.flip()
xpaddle = 600
ypaddle = 240
delay = 10
interval = 10
pygame.key.set_repeat(delay, interval)
while True:
screen.blit(back, (0,0))
screen.blit(paddle, (xpaddle, ypaddle))
pygame.display.flip()
pygame.time.delay(20)
for i in pygame.event.get():
if i.type == pygame.QUIT:
sys.exit()
elif i.type == pygame.KEYDOWN:
if i.key == pygame.K_UP:
ypaddle = ypaddle - 10
if ypaddle < 10:
ypaddle = 10
elif i.key == pygame.K_DOWN:
ypaddle = ypaddle + 10
if ypaddle > 410:
ypaddle = 410
I would like to have a bouncing ball, but i don't have the knowledge to create it.
It doesn't have to be really difficult(maybe using pygame.draw.circle?)
Actually it has to be simple, because sprites are maybe too much for me.
My idea was to change coordinates every time ball gets to specific coordinates.
I am not just asking somebody to make a game I like, it's for educational purposes.
I would love to see some comments with the code you provide.
As I told, i just started learning it.
My english isn't best. Sorry about that.
Thanks in advance!
(I know that my post is a bit confusing and unspecific)
If needed, I will upload the background and paddle picture too.
You could create a Ball class, that will have 2 methods:
update() - that will move the ball according to speed_x and speed_y and
check if any collisions are detected
draw() - that will blit the surface/ or draw a circle at ball position.
another thing you have to think about, is collisions.
You can find if a point is in a rectangle like this:
We have a rectangle with points : p1,p2,p3,p4, p0 is our testing point.
p0 is in the rectangle if dist(p0,p1) + disp(p0,p2) + ... + disp(p0,p4) == WIDTH+HEIGHT
you can try to figure out the equation for a circle. Hint: radius is what you need.
EDIT: The class example:
class Ball:
def __init__(self):
self.pos = [0,0]
self.velocity = [1,0]
def move():
self.pos[0] += self.velocity[0]
self.pos[1] += self.velocity[1]
def draw(screen):
pygame.draw.circle(screen,WHITE,self.pos,5)
To extend on #Bartlomiej Lewandowski, To make the ball you need to
Every loop ball.update() will do:
ball.x += ball.xvel
ball.y += ball.yvel`
Then
if you collide with left/right walls ball.x *= -1
if you collide with top/bot walls ball.y *= -1
Using pygame.Rect will simplfy logic
# bounce on right side
ball.rect.right >= screen.rect.right:
ball.yvel *= -1
# bounce top
ball.rect.top <= screen.rect.top:
# bot
ball.rect.bottom >= screen.rect.bot:
# etc...
edit: Bart added different names but it's the same thing.

Pygame - Limiting instances of sprites

I'm using an example regarding sprite generation for a space scroller shootup that I'm developing. By slowly trying to understand how it works, I've managed to get multiple sprites to transverse across the screen. However, there are many sprites that are generated.
So what I'm having trouble with is limiting the initial number of sprites instead of the multitude that the code produces. I thought of using if sprites.__len__ < 10: sprites.add(drone) but when I tried that, it didn't work.
My thinking was that each time it looped, it would check the number of sprites in the group and if it was less then 10, add a sprite to the group until it hit 10. That way if it went off screen or is destroyed, then it would keep doing the check and keeping it constant.
This is the player class:
class Player(pygame.sprite.Sprite):
def __init__(self, *groups):
super(Player, self).__init__(*groups)
self.image = pygame.image.load('player.png')
self.rect = pygame.rect.Rect((screen_width, (random.randrange(0,screen_height))), self.image.get_size())
self.dx = -10
self.pos = random.randrange(0,screen_height)
def update(self):
self.rect.centerx += self.dx
if self.rect.right < 0:
self.kill()
and this is the section regarding the adding of the sprite.
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
It's probably obvious, but I'm still learning so guidance would be great.
Second question - More of a confirmation of thought. If I don't want the sprite to be half drawn on the bottom of the screen. Do I basically say that if self.rect.bottom > screen_height, then position the sprite # screen_height
Full source: http://pastebin.com/PLRVHtxz
EDIT - I think I've solved it, just need to make the sprites run smoother.
while 1:
clock.tick(40)
numberAlien = 5
for event in pygame.event.get():
if event.type == pygame.QUIT:
return
if event.type == pygame.KEYDOWN and event.key == pygame.K_ESCAPE:
return
sprites.update()
screen.fill((200, 200, 200))
sprites.draw(screen)
drone = Player()
if sprites.__len__() < numberAlien:
self.y = random.randrange(0,screen_height)
sprites.add(drone)
pygame.display.flip()
You could subclass SpriteGroup, add a new field of the total number of sprites, and check in the add method to see if it can be added.
You shouldn't test check any variables with __.
As for the movement, i believe, you do not see a smooth movement because of clock.tick(40).
It waits for 40ms until it resumes running. You could reduce tick to 10, and tune the dx you change for the sprites, or try a more universal approach.
A call to clock.tick() returns amount of ms since the last call. This will be your time delta. You can then have a global SPEED. The amount of pixels to be moved would be calculated from SPEED * delta.

Pygame where Sprite Skates

I'm having difficulty making my sprite skate.
The thing is my sprite should be sliding until it hits something like the border of the screen or a block. Also, this should be done when the sprite is on ice and if the sprite isn't on ice then the sprite should walk. So if the person presses up once, then the sprite will skate until it hits something that will stop its movement.
Thanks!
I don't know how you currently have your code, but a general template would be:
# main loop
while True:
for each object:
update(framerate)
render()
# skater code
class skater:
moving = True
speed = [0,0]
x = 0
y = 0
def update(framerate):
# check for collisions
if collision:
self.moving = False
# move the skater
if self.moving:
self.x += self.speed[0]*framerate
self.y += self.speed[1]*framerate
moving will be set to true then an arrow key is pressed.
where speed is decided by what arrow keys have been pressed (- for left and up, + for right and down)
From looking at your code at http://pastebin.com/cEpp44NS you're doing things like:
self.speedX1 *= (self.ice * self.normal_friction)
Which is going to very rapidly reduce self.speedX1 (especially since self.ice is set to 0.01)
From what you've said, it sounds like you don't want the speed to be decreasing at all whilst sliding, so try just removing this code.