Moving sprites with key events in pygame [duplicate] - python

This question already has answers here:
How can I make a sprite move when key is held down
(6 answers)
Closed last month.
I just got a sprite moving around the window using "w,a,s,d", and i want to make this sprite "shoot" by pressing the space bar.
The problem i've got is that the sprite appears but doesn't move until i release the space bar, and i want the sprite shot to go forward until the end of the window, just by pressing the space bar, not by releasing it.
This is my main loop:
while pygame.event.poll().type != QUIT:
screen.blit(background, (0, 0))
#"player" is the sprite moving around the window
player.move(width,height)
screen.blit(player.image, player.rect)
key = pygame.key.get_pressed()
if key[K_SPACE]:
xpos = player.rect.right
ypos = player.rect.top
shot_on_screen = True
if shot_on_screen:
xpos += 1
#"kame" is the sprite i want to move forward
screen.blit(shot.kame, (xpos, ypos))
else:
shot.x = player.rect.right
shot.y = player.rect.top
shot_on_screen = False
pygame.display.update()
I'm new at this python-pygame world, i've checked a lot of manuals and documentation before asking though, hope you can help, thanks.

You are setting xpos to the player most right position on every loop when the K_SPACE is pressed. You shouldn't do that. Try removing that line.
if key[K_SPACE]:
ypos = player.rect.top
shot_on_screen = True
Also, I don't know if you are checking if the shot is on the screen somewhere else on the code, but the code you posted will not set it to false.
if shot_on_screen:
xpos += 1
#"kame" is the sprite i want to move forward
screen.blit(shot.kame, (xpos, ypos))
else:
shot.x = player.rect.right
shot.y = player.rect.top
shot_on_screen = False # won't work. It is already False.

Related

Pygame collision with rectangles in list not working right

I'm making python pygame labyrinth game.
Moving works with moving walls, but not player because player never escapes the screen. Currently I'm working on moving on X axis, but something goes wrong. When player collides with left wall, it normally doesn't let it go more to the left. But when going to the right it slightly ignores collision and goes more to the right.
Reproducible example:
import pygame
import math
pygame.init()
surface = pygame.display.set_mode((1000,600))
clock = pygame.time.Clock()
surfrect = surface.get_rect()
player=pygame.Rect((0,0), (35,35))
player.center=surfrect.w/2,surfrect.h/2
touched = False
levelR=[]
control=pygame.Rect((0,0), (50,50))
controld=[]
yspeed,xspeed=0,0
for i in range(-1,2):
oxr=surfrect.w/2-75*3/2
oyr=surfrect.h/2-75*3/2+75*3*i
yr,xr=oyr,oxr
gf=[]
for y in '#-#n#-#n#-#'.split('n'):
for x in y:
if x=='#':
gf.append(pygame.Rect((xr, yr), (75,75)))
xr+=75
yr+=75
xr=oxr
levelR.append([gf,pygame.Rect((oxr,oyr),(75*3,75*3))])
while True:
for ev in pygame.event.get():
if ev.type == pygame.QUIT:
pygame.quit()
elif ev.type == pygame.MOUSEBUTTONDOWN:
touched = True
elif ev.type == pygame.MOUSEBUTTONUP:
touched = False
surface.fill((0,0,0))
for sd in levelR:pygame.draw.rect(surface,(35,35,35),sd[1])
pygame.draw.circle(surface, (200,200,200), player.center, player.width/2-player.width/19, player.width)
for sd in levelR:
for rect in sd[0]:
scale=0.6
recti=pygame.Rect((rect.x,rect.y), (rect.width*scale,rect.height*scale))
recti.center=rect.center
pygame.draw.rect(surface,(255,255,255),rect)
pygame.draw.rect(surface,(0,0,0),recti)
if touched and not controld:
controld=pygame.mouse.get_pos()
control.center=controld
elif touched and controld:
radius = 150
x,y=pygame.mouse.get_pos()
distance=math.dist((x, y),controld)
if distance < radius:
pos=(x, y)
else:
dx=x-controld[0]
dy=y-controld[1]
ratio=radius/distance
pos=controld[0]+ratio*dx, controld[1]+ratio*dy
control.center=pos
try:xmd=(x-controld[0])/abs(x-controld[0])
except:xmd=0
try:ymd=(y-controld[1])/abs(y-controld[1])
except:ymd=0
sr=0.02*(150/radius)
xspeed=xmd*abs(control.centerx-controld[0])*sr
yspeed=ymd*abs(control.centery-controld[1])*sr
collisiont=5
for sd in levelR:
for block in sd[0]:
block.x-=xspeed
sd[1].x-=xspeed
for rect in [i for sd in levelR for i in sd[0]]:
if player.colliderect(rect):
for sd in levelR:
for block in sd[0]:
block.x+=xspeed
sd[1].x+=xspeed
break
pygame.draw.circle(surface, (255,255,255), control.center, control.width,control.width)
pygame.draw.circle(surface, (255,255,255), controld, radius, 5)
elif not touched and controld:controld=()
pygame.display.flip()
clock.tick(60)
I have tried debugging the collision and discovered that the moving to the right when collides with walls it stops the player from moving more to the right but player slightly moves to the right.
I expected walls not moving when player collides with walls.
It happened that the moving to the right when player collides with wall doesn't stop it directly.
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data.
The coordinates for Rect objects are all integers. [...]
The fractional part of the coordinates is lost when the position of the Rect object is changed. In your case this means that the movement to the left and right behaves differently and block.x += xspeed is not the inverse operation of block.x -= xspeed. Consider that, 5 - 0.1 is 4, but 4 + 0.1 is still 4.
You can solve the problem by testing the object for collisions before you actually move it, and not moving the object at all if a collision is detected:
collide = False
for rect in [i for sd in levelR for i in sd[0]]:
text_rect = rect.copy()
text_rect.x -= xspeed
if player.colliderect(text_rect):
collide = True
break
if not collide:
for sd in levelR:
for block in sd[0]:
block.x-=xspeed
sd[1].x-=xspeed

How to make a rectangular object to move slowly across the screen? [duplicate]

This question already has an answer here:
Pygame clock and event loops
(1 answer)
Closed 2 years ago.
i am new to python programming and now starting to write simple "Snake" game.
i am trying to make the head of the snake move around the screen in a way that
when an arrow key is pressed, the head should move in that direction non-stop, until a perpendicular
arrow is pressed.
i cant make the head of the snake ( a rectangle) to move non stop in single direction slowly.
i tried to use time.sleep() --> it made the whole code stuck.
i tried to use for loop --> it just made the rectangle to transport to the other direction fast.
this is my main function:
while not GameOver:
#create time delay of 10 milliseconds
pygame.time.delay(10)
# Get all of the events on the game's screen [all of the user's input]
# Loop on the user's activity and look for exit button pressing.
for event in pygame.event.get():
# If the user presses the X button:
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
# Define a shortcut name to "get_pressed" method in pygame:
keys = pygame.key.get_pressed()
# defining x location
x = head_position[0]
y = head_position[1]
# if he pressed left key
if keys[pygame.K_LEFT]:
# reducing location by constant
head_position[0] -= head_speed
elif keys[pygame.K_RIGHT]:
# increacing location by constant
head_position[0] += head_speed
elif keys[pygame.K_UP]:
# increacing location by constant
head_position[1] -= head_speed
elif keys[pygame.K_DOWN]:
# increacing location by constant
head_position[1] += head_speed
#If the head passes the screen Boundaries, it would pop in the other side.
if head_position[0] >= WIDTH:
head_position[0] = 0
elif head_position[0] < 0:
head_position[0] = WIDTH-head_size
elif head_position[1] >= HEIGHT:
head_position[1] = 0
elif head_position[1] < 0:
head_position[1] = HEIGHT-head_size
Use the pygame.time module to control the frames per second. Generate a pygame.time.Clock object. If you pass the optional framerate argument to pygame.time.Clock.tick, the function will delay to keep the game running slower than the given ticks per second:
clock = pygame.time.Clock()
while not GameOver:
clock.tick(10) # 10: the game runs with 10 frames per second
# [...]
if you want to make it move at a constant speed in one direction after one key press then make a
right = False varible
then make a
key[pygame.K_YOURCHOICE]:
right = True
if right:
head_pos[1 or 0] -= head_speed

Implementing a collision detect feature between a Rect object and a ball in pygame [duplicate]

This question already has an answer here:
Sometimes the ball doesn't bounce off the paddle in pong game
(1 answer)
Closed 2 years ago.
import pygame, random, time
# main function where we call all other functions, start the game loop, quit pygame and clean up the window. Inside we create a game object, display surface, and start the game loop by calling the play method on the game object. There is also a set caption with the title of the game.
def main():
pygame.init()
size =(500,400)
surface=pygame.display.set_mode(size)
pygame.display.set_caption('Pong v2')
game = Game(surface)
game.play()
pygame.quit()
# This is where we define the class game
class Game():
# an object in this class represents a complete game
# here we initialize a game. self is the game that is initialized surface is the display window surface object we also set default values for continuing the game and closing the window. we also define what fps we are running the game at, and define the velocity color position and radius of the ball
def __init__(self,surface):
# defining surface, fps, background color
self.surface=surface
self.FPS=120
self.bg_color=pygame.Color('black')
screen_width = surface.get_width()
screen_height = surface.get_height()
# defining ball attributes
ball_radius=10
ball_pos = [random.randint(ball_radius, screen_width-ball_radius),
random.randint(ball_radius, screen_height-ball_radius)]
ball_color=pygame.Color('white')
ball_velocity=[2,1]
self.ball=Ball(ball_pos,ball_radius,ball_color,ball_velocity,surface)
# defining paddle attributes
rect_left=[50,450]
rect_top=225
rect_height=60
rect_width=10
self.Paddle1=Rect(rect_left[0],rect_top,rect_width,rect_height,surface)
self.Paddle2=Rect(rect_left[1],rect_top,rect_width,rect_height,surface)
self.game_Clock=pygame.time.Clock()
self.close_clicked=False
self.continue_game=True
self.score1=0
self.score2=0
self.frame_counter=0
def play(self):
# game is played until player clicks close
while not self.close_clicked:
self.handle_events()
self.draw()
# if nothing sets this to false the game will continue to update
if self.continue_game:
self.update()
self.game_Clock.tick(self.FPS)
# score is drawn onto the screen (unimportant this is just playing with a feature for the next version), we define color font background etc of the score message and update score upon points being scored
def draw_score(self):
font_color = pygame.Color("white")
font_bg = pygame.Color("black")
font = pygame.font.SysFont("arial", 18)
text_img = font.render("Score for Player 1: " + str(self.score1) + ' Score for Player 2: ' + str(self.score2), True, font_color, font_bg)
text_pos = (0,0)
self.surface.blit(text_img, text_pos)
# ball, surface, score, and two paddles are drawn, pygame also updates this drawing once per frame
def draw(self):
self.surface.fill(self.bg_color)
self.draw_score()
#pygame.draw.rect(self.surface,pygame.Color('blue'),(50,225,10,50))
#pygame.draw.rect(self.surface,pygame.Color('red'),(450,225,10,50))
self.Paddle1.draw()
self.Paddle2.draw()
self.ball.draw()
pygame.display.update()
# score value set to default of 0 we tell ball to move and add 1 to frame counter upon each update. update game object for the next frame
def update(self):
self.ball.move()
self.score=0
self.frame_counter+=self.frame_counter+1
# here we setup an event loop and figure out if the action to end the game has been done
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
# user defined class ball
class Ball:
# self is the ball to intialize. color/center/radius are defined for the ball that is initialized
def __init__(self,center,radius,color,velocity,surface):
self.center=center
self.radius=radius
self.color=color
self.velocity=velocity
self.surface=surface
# screen size is determined and edge of ball is checked that it is not touching the edge. if it is touching the edge it bounces and reverses velocity
def move(self):
screen_width=self.surface.get_width()
screen_height=self.surface.get_height()
screen_size=(screen_width,screen_height)
for i in range(0,len(self.center)):
self.center[i]+=self.velocity[i]
if (self.center[i]<=0 + self.radius or self.center[i]>=screen_size[i] - self.radius):
self.velocity[i]=-self.velocity[i]
# ball is drawn
def draw(self):
pygame.draw.circle(self.surface,self.color,self.center,self.radius)
class Rect:
def __init__(self,left,top,width,height,surface):
#self.left=left
#self.top=top
#self.width=width
#self.height=height
self.surface=surface
self.rect=pygame.Rect(left,top,width,height)
def draw(self):
pygame.draw.rect(self.surface,pygame.Color('red'),self.rect)
def collide(self):
if pygame.Rect.collide(x,y) == True:
return True
else:
return False
main()
Above is my work so far basically its supposed to be the retro arcade game pong where the ball bounces off of the edges of the paddles and if it doesn't the opposite side scores a point upon it hitting the edge of the window. So specifically this part of the project requires me to make the ball bounce off of the front of the paddles and I'm confused as to how to do this. My idea originally was to use the collidepoint method inside of the class Rect that if it returns true would reverse the balls velocity. However, I don't have access to the centre coordinates of the ball inside of the class or inside of the method play in the class game where I intended to make this work on the specific instances of ball and paddle1,paddle2 so I don't know how to do this.
Evaluate if the ball hits the left paddle at the right, respectively the right paddle at the left in Game.update.
If the ball hits the paddle the the score can be incremented:
class Game():
# [...]
def update(self):
self.ball.move()
# evaluate if Ball hits the left paddle (Paddle1) at the right
if self.ball.velocity[0] < 0 and self.Paddle1.rect.top <= self.ball.center[1] <= self.Paddle1.rect.bottom:
if self.Paddle1.rect.right <= self.ball.center[0] <= self.Paddle1.rect.right + self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle1.rect.right + self.ball.radius
self.score1 += 1
# evaluate if Ball hits the right paddle (Paddle2) at the left
if self.ball.velocity[0] > 0 and self.Paddle2.rect.top <= self.ball.center[1] <= self.Paddle2.rect.bottom:
if self.Paddle2.rect.left >= self.ball.center[0] >= self.Paddle2.rect.left - self.ball.radius:
self.ball.velocity[0] = -self.ball.velocity[0]
self.ball.center[0] = self.Paddle2.rect.left - self.ball.radius
self.score2 += 1
Get the state of the keys by pygame.key.get_pressed() and change the position of the paddles is Game.handle_events.
e.g. Move the left paddle by w / s and the right paddle by UP / DOWN:
class Game():
# [...]
def handle_events(self):
events=pygame.event.get()
for event in events:
if event.type== pygame.QUIT:
self.close_clicked=True
keys = pygame.key.get_pressed()
if keys[pygame.K_w]:
self.Paddle1.rect.top = max(0, self.Paddle1.rect.top - 3)
if keys[pygame.K_s]:
self.Paddle1.rect.bottom = min(self.surface.get_height(), self.Paddle1.rect.bottom + 3)
if keys[pygame.K_UP]:
self.Paddle2.rect.top = max(0, self.Paddle2.rect.top - 3)
if keys[pygame.K_DOWN]:
self.Paddle2.rect.bottom = min(self.surface.get_height(), self.Paddle2.rect.bottom + 3)

Getting my character to move fluidly in Pygame

I am trying to create a pygame for my com sci final, but my teacher has not done an excellent job at explaining how pygame works. My trouble here is I need make my character, a custom sprite I created, move fluidly when any of the 4 arrow keys are held. So far, all I've been able to do is make it move with every individual key press, and I have no idea how to fix this.
I'm not sure if my code is the most efficient, but essentially depending on what key you press, not only do the X and Y coordinates change, but the png image changes to face the appropriate direction. This is all draw using blits.
import pygame
pygame.init()
def redraw_game_window(texture):
screen = pygame.display.set_mode(size)
background_image = pygame.image.load("lava.jpg").convert()
background_image = pygame.transform.scale(background_image, (size))
screen.blit(background_image, [0, 0])
texture= pygame.transform.scale(texture, (65,64))
texture=texture.convert_alpha()
screen.blit(texture,(EarlX,EarlY))
size=1200,600
EarlWidth=65
EarlLength=64
texture = pygame.image.load("UpFacingEarl.png")
EarlX=500
EarlY=150
speed=50
inPlay= True
while inPlay:
redraw_game_window(texture)
pygame.time.delay(20)
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]:
inPlay = False
if keys[pygame.K_LEFT]:
texture = pygame.image.load("LeftFacingEarl.png")
EarlX-=speed
if keys[pygame.K_RIGHT]:
texture = pygame.image.load("RightFacingEarl.png")
EarlX+=speed
if keys[pygame.K_UP]:
texture = pygame.image.load("UpFacingEarl.png")
EarlY-=speed
if keys[pygame.K_DOWN]:
texture = pygame.image.load("DownFacingEarl.png")
EarlY+=speed
pygame.display.flip()
Any help on how to improve my code would be greatly appreciated, I know im not including the png's but theres not much I can do there.
First, you don't need to double space your code. Now that we've gotten that out of the way:
screen = pygame.display.set_mode(size)
should only called at the beginning of the program. Not every time! This is the reason your program isn't working.
Here are other less important things to fix:
this should also only be called at the beginning:
background_image = pygame.image.load("lava.jpg").convert()
background_image = pygame.transform.scale(background_image, (size))
all in all, you redraw_game_window function should look like:
def redraw_game_window(texture, EarlX, EarlY, screen): #you should pass all the variables you are using to the function. Python doesn't work well with global variables. if you really want to use global variables like you did here, use the global keyword
screen.fill((0,0,0)) #clear the screen
screen.blit(background_image, [0, 0]) #draw new background
texture = pygame.transform.scale(texture, (65,64))
texture = texture.convert_alpha()
screen.blit(texture,(EarlX,EarlY)) #draw earl
This event loop is fine:
for event in pygame.event.get():
if event.type == pygame.QUIT: #you need this or else pressing the red "x" on the window won't close it
quit()
keys = pygame.key.get_pressed()
if keys[pygame.K_ESCAPE]: #press esc to exit program
quit()
if keys[pygame.K_UP]:
EarlY -= 1
if keys[pygame.K_DOWN]:
EarlY -= -1
if keys[pygame.K_LEFT]:
EarlX += -1
if keys[pygame.K_RIGHT]:
EarlX += 1

How to make turtle move continuously without multiple key presses?

I am making a game where the player gets to move on a line at the bottom of the screen.
At this time to move it the user repeatedly hits the arrow keys, but I want to make it move continuously when the key is pressed.
How do I fix this?
You will want to use the get_pressed() function of key, Link.
import pygame
pygame.init()
# Variables
black_background = (0, 0, 0)
screen = pygame.display.set_mode((1280, 960))
running = True
turtle_image = pygame.image.load('location/to/turtle/image.png')
turtle_rect = turtle_image.get_rect() # Using rect allows for collisions to be checked later
# Set spawn position
turtle_rect.x = 60
turtle_rect.y = 60
while running:
keys = pygame.key.get_pressed() #keys pressed
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
if keys[pygame.K_LEFT]:
turtle_rect.x -= 1
if keys[pygame.K_RIGHT]:
turtle_rect.x += 1
screen.fill(black_background)
screen.blit(turtle_image, turtle_rect)
Remember, this is just a basic layout and you'll have to read a little more into how you want to accomplish collisions and/or movement with other keys. Intro Here.
Below is my turtle-based solution. It uses a timer to keep the turtle moving at the bottom of the window but lets the timer expire when nothing's happening (turtle has stopped at an edge) and restarts the timer as needed:
from turtle import Turtle, Screen
CURSOR_SIZE = 20
def move(new_direction=False):
global direction
momentum = direction is not None # we're moving automatically
if new_direction:
direction = new_direction
if direction == 'right' and turtle.xcor() < width / 2 - CURSOR_SIZE:
turtle.forward(1)
elif direction == 'left' and turtle.xcor() > CURSOR_SIZE - width / 2:
turtle.backward(1)
else:
direction = None
if ((not new_direction) and direction) or (new_direction and not momentum):
screen.ontimer(move, 10)
screen = Screen()
width, height = screen.window_width(), screen.window_height()
turtle = Turtle('turtle', visible=False)
turtle.speed('fastest')
turtle.penup()
turtle.sety(CURSOR_SIZE - height / 2)
turtle.tilt(90)
turtle.showturtle()
direction = None
screen.onkeypress(lambda: move('left'), "Left")
screen.onkeypress(lambda: move('right'), "Right")
screen.listen()
screen.mainloop()
Use the left and right arrows to control the turtle's direction.
use the function: onkeypress.
here is an example were onkeypress is a higher order function:
def move_forward():
t.forward(10)
screen.onkeypress(key='w', fun= move_forward)
You can use onkeypress(fun, key=None)
instead of using onkey() or onkeyrelease(). These two only register the input when the key is released.
onkeypress(fun, key=None) registers it while the key is pressed down.
Here's a link to it in the Turtle documentation.

Categories

Resources