Collision system in my pygame platformer is almost perfect - python

I'm trying to find a way to make perfect collision in my platformer game for school. Most of it works but there is a slight problem. When I stand on a platform above ground level and move left or right off of it, the player continues to float in midair at the same height of the platform that I got off of. This can be fixed by jumping and the collision reverts to normal. I am using a state system to track if the player is standing or not by having a folder of possible player states and switching between them. This is set to "Falling" by default because the player starts in midair when the game runs. The code for the game is divided into three separate files below (main.py, obj.py and settings.py). Please tell me how I can fix this glitch.
Main.py
import pygame
import random
from settings import *
from obj import *
pygame.init()
pygame.mixer.init()
pygame.font.init()
pygame.display.set_caption(TITLE)
screen = pygame.display.set_mode([WIDTH,HEIGHT])
clock = pygame.time.Clock()
me = Player()
all_sprites.add(me)
platforms = []
pf = Wall(20,40,500,480, 0)
pf2 = Wall(WIDTH,40, 400,500, 0)
platforms.append(pf)
platforms.append(pf2)
for i in platforms:
wall_sprites.add(i)
running = True
while running:
clock.tick(FPS)
all_sprites.update()
wall_sprites.update()
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
screen.fill(GREY)
all_sprites.draw(screen)
wall_sprites.draw(screen)
pygame.display.update()
pygame.quit()
obj.py
import pygame
import math
from settings import *
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((40,40))
self.image.fill(BLACK)
self.rect = self.image.get_rect()
self.rect.x = WIDTH / 2
self.rect.y = 70
self.vx = 0
self.vy = 0
self.SW = False # Can you screen wrap?
self.player_states = ["Standing","Falling"]
self.state = self.player_states[1]
def update(self):
self.vx = 0 # X speed set to 0 if no input is received
if self.state == self.player_states[1]:
self.vy += GRAVITY # Gravity only added while falling
else:
self.vy = 0
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.vx = -SPEED
if keys[pygame.K_RIGHT]:
self.vx = SPEED
if keys[pygame.K_SPACE] and self.state == self.player_states[0]:
self.vy -= JUMP_SPEED
self.state = self.player_states[1]
self.rect.left += self.vx # X and Y positions are updated
self.collide(self.vx, 0, wall_sprites) # Collision is checked. Second param is 0 b/c we aren't checking for vertical collision here
self.rect.top += self.vy
self.collide(0, self.vy, wall_sprites)
if self.SW:
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
def collide(self, xDif, yDif, platform_list):
for i in platform_list: # Shuffle through list of platforms
if pygame.sprite.collide_rect(self, i): # If there is a collision between the player and a platform...
if xDif > 0: # And our x (horizontal) speed is greater than 0...
self.rect.right = i.rect.left # That means that we are moving right,
if xDif < 0: # So our right bounding box becomes equal to the left bounding box of all platforms and we don't collide
self.rect.left = i.rect.right
if yDif > 0:
self.rect.bottom = i.rect.top
self.state = self.player_states[0]
if yDif < 0:
self.rect.top = i.rect.bottom
class Wall(pygame.sprite.Sprite): # Collision is added for platforms just in case that they are moving. If they move to you, they push you
def __init__(self, width, height, xpos, ypos, speed):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.Surface((width,height))
self.image.fill(BLUE)
self.rect = self.image.get_rect()
self.rect.centerx = xpos
self.rect.centery = ypos
self.speed = speed
def update(self):
self.rect.left += self.speed
self.collide(self.speed, all_sprites) # Collision only for platforms moving left and right. Not up and down yet
def collide(self, xDif, player_list):
for i in player_list:
if pygame.sprite.collide_rect(self, i):
if xDif > 0: # If the platform is moving right... (has positive speed)
i.rect.left += self.speed # Platform pushes player
self.rect.right = i.rect.left # Player sticks to the wall and is pushed
if xDif < 0:
i.rect.right -= self.speed
self.rect.left = i.rect.right
settings.py
import pygame
FPS = 60
WIDTH = 800
HEIGHT = 600
TITLE = "Perfect collision"
GREY = (150,150,150)
BLACK = (0,0,0)
BLUE = (0,0,255)
SPEED = 5
JUMP_SPEED = 9
GRAVITY = 0.3
all_sprites = pygame.sprite.Group()
wall_sprites = pygame.sprite.Group()

Just add the GRAVITY to self.vy in every frame and set self.vy to 0 when the sprite touches the ground:
def update(self):
self.vx = 0
self.vy += GRAVITY
def collide(self, xDif, yDif, platform_list):
for i in platform_list:
if pygame.sprite.collide_rect(self, i):
# Code omitted.
if yDif > 0:
self.rect.bottom = i.rect.top
self.state = self.player_states[0]
self.vy = 0 # Set vy to 0 if the sprite touches the ground.

Related

How can I successfully double jump in pygame? [duplicate]

This question already has answers here:
How can I do a double jump in pygame?
(1 answer)
Pygame: Can someone help me implement double jumping?
(1 answer)
Closed 2 months ago.
When trying to create a double I either can jump infinitely or jump once, struggling to find a solution to only jump once.
I was able to create a double jump using two different keys however I want it to where I only have to use the 'W' key.
here's my code:
import pygame
pygame.init()
class Fighter:
def __init__(self, x, y):
self.x = x
self.y = y
self.rect = pygame.Rect((self.x, self.y, 70, 200))
self.x_vel = 0
self.y_vel = 0
self.jump = False
def draw_player(self, surface):
pygame.draw.rect(surface, "Red", self.rect)
def detect_collisions(self, platforms):
collisions = []
for platform in platforms:
if self.rect.colliderect(platform.rect):
collisions.append(platform)
return collisions
def movement(self, player, platforms, screen_height, screen_width):
self.x = 0
self.y = 0
self.y_vel = 0
pixel_move = 30
gravity = 5
# get key presses
key = pygame.key.get_pressed()
# player one inputs
if player == 1:
if key[pygame.K_a]:
self.x = -pixel_move
if key[pygame.K_d]:
self.x = pixel_move
if key[pygame.K_w] and self.jump == False:
self.y_vel = -pixel_move
self.jump = True
if key[pygame.K_s]:
self.y_vel = pixel_move
# player two inputs
elif player == 2:
if key[pygame.K_LEFT]:
self.x = -pixel_move
if key[pygame.K_RIGHT]:
self.x = pixel_move
# apply gravity
self.y_vel += gravity
# makes sure the player actually moves
self.rect.x += self.x
self.rect.y += self.y_vel
# make sure player stays onto the screen
self.platform_interaction(platforms)
# make sure the player doesn't fall off the screen
if self.rect.bottom + self.y_vel > screen_height:
self.y_vel = 0
self.rect.bottom = screen_height
self.jump = False
# make sure the player doesn't go above the screen
if self.rect.top < 0:
self.rect.top = 0
# make sure the player doesn't go across the right side of the screen
if self.rect.right > screen_width:
self.rect.right = screen_width
# makes sure the player doesn't go across the left side of the screen
if self.rect.left <= 0:
self.rect.left = 0
def platform_interaction(self, platforms):
collisions = self.detect_collisions(platforms)
for platform in collisions:
# player lands on top off platform
if abs(self.rect.bottom - platform.rect.top) < 10:
self.rect.bottom = platform.rect.top
self.jump = False
# player collides with the sides of the platform
elif abs(self.rect.right - platform.rect.left) < 20:
self.rect.right = platform.rect.left
elif abs(self.rect.left - platform.rect.right) < 20:
self.rect.left = platform.rect.right
elif platform.state:
# player hits themselves on bottom of the platform
if abs(self.rect.top + self.y_vel) < platform.rect.bottom:
self.rect.top = platform.rect.bottom
# make sure player doesn't fall through solid platforms
elif abs(self.rect.bottom + self.y_vel) > platform.rect.top:
self.rect.bottom = platform.rect.top

Rectangle Collision with each other [duplicate]

This question already has an answer here:
Sometimes the ball doesn't bounce off the paddle in pong game
(1 answer)
Closed 1 year ago.
I am currently trying to create a pong game but for some reason, the ball would not bounce off of the rectangles and I am not sure where I have made a mistake even though I followed a video about rectangle collision online. Please let me know where I made a mistake and how I can improve my code. Thanks!
import pygame, sys, random, time
from pygame.time import Clock
pygame.init()
#colours
black = ( 0, 0, 0)
white = ( 255, 255, 255)
green = ( 0, 255, 0)
red = ( 255, 0, 0)
# Display
screen_width = 1000
screen_height = 600
screen = pygame.display.set_mode((screen_width, screen_height))
pygame.display.set_caption("2-player Pong")
# Rectangles
class Rectangle():
def __init__(self, screen, x):
self.screen = screen
self.screen_rect= screen.get_rect()
self.x = x
self.y = 250
self.width = 30
self.height = 100
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
self.colour = black
self.velocity = 5
def draw_rectangle(self):
pygame.draw.rect(self.screen, self.colour, self.rect)
rect1 = Rectangle(screen, 50)
rect2 = Rectangle(screen, 920)
class Ball():
def __init__(self, colour):
self.screen = screen
self.colour = colour
self.width = 20
self.height = 20
self.x = screen_width//2 - 25
self.y = screen_height//2 - 25
self.rect = pygame.Rect(self.x, self.y, self.width, self.height)
self.possible_velocities_x = [-4, 4]
self.possible_velocities_y = [-2, 2]
self.velocity = [random.choice(self.possible_velocities_x), random.choice(self.possible_velocities_y)]
def draw_ball(self):
pygame.draw.rect(self.screen, self.colour, self.rect)
def move_ball(self):
global rect1, rect2
self.rect.x += self.velocity[0]
self.rect.y += self.velocity[1]
# Collision with Screen
if self.rect.top <= 10 or self.rect.bottom >= screen_height - 10:
self.velocity[1] *= -1
# Collision with Rectangles
if self.rect.colliderect(rect1) or self.rect.colliderect(rect2):
if self.rect.left - rect1.rect.right == 0:
self.possible_velocities_x *= -1
if self.rect.right - rect2.rect.left == 0:
self.possible_velocities_x *= -1
clock = pygame.time.Clock()
ball = Ball(white)
pong = True
while pong:
pygame.time.delay(10)
clock.tick(100)
# Watch for keyboard and mouse events.
for event in pygame.event.get():
if event.type == pygame.QUIT:
pong = False
keys = pygame.key.get_pressed()
if keys[pygame.K_UP] and rect2.rect.y >= 0:
rect2.rect.y -= rect2.velocity
if keys[pygame.K_DOWN] and rect2.rect.y <= 600 - rect2.rect.height:
rect2.rect.y += rect2.velocity
if keys[pygame.K_w] and rect1.rect.y >= 0:
rect1.rect.y -= rect1.velocity
if keys[pygame.K_s] and rect1.rect.y <= 600 - rect1.rect.height:
rect1.rect.y += rect1.velocity
screen.fill(green)
rect1.draw_rectangle()
rect2.draw_rectangle()
ball.draw_ball()
ball.move_ball()
# pygame.draw.rect(screen, black, (rect_x2, rect_y2, rect_width2, rect_height2))
pygame.display.update() # Make the most recently drawn screen visible.
pygame.quit()
You must change self.velocity[0] when the ball touches the paddle. Since the movement of the ball is more than 1 pixel per frame, the ball does not exactly touch the paddle. This mans the condition self.rect.left - rect1.rect.right == 0 and self.rect.right - rect2.rect.left == 0 will not be fulfilled. If the movement in x direction is negative, the new movement needs to be positive (abs(self.velocity[0])). If the movement in x direction is positive, the new movement needs to be negative (-abs(self.velocity[0])):
class Ball():
# [...]
def move_ball(self):
self.rect.x += self.velocity[0]
self.rect.y += self.velocity[1]
# Collision with Screen
if self.rect.top <= 10 or self.rect.bottom >= screen_height - 10:
self.velocity[1] *= -1
# Collision with Rectangles
if self.rect.colliderect(rect1) or self.rect.colliderect(rect2):
if self.velocity[0] < 0:
self.velocity[0] = abs(self.velocity[0])
else:
self.velocity[0] = -abs(self.velocity[0])
See also Sometimes the ball doesn't bounce off the paddle in pong game

Pygame Player teleports on top of rectangle

I am currently trying to get accustomed with pygame and therefore building a platformer game. I have searched for this problem, but I couldn't find anything related. The problem is that my player teleports on top of my platform rectangle when they collide on either the left or right. I've tried to implement multiple fixes, such as alternating the players y-position, which creates other problems, as well as only triggering the collision effect if the player hits either side, not the top, but nothing seems to forego the teleportation of the player. This leads to the collision not triggering or the player colliding with an invisible wall on either the top left or the top right of the platform. Here is the code of the relevant file (pg = pygame):
from pygame.locals import *
from settings import *
vec = pg.math.Vector2
platforms = pg.sprite.Group()
grounds = pg.sprite.Group()
all_except_player = pg.sprite.Group()
class Player(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((20, 20))
self.image.fill(blue)
self.rect = self.image.get_rect()
self.pos = vec((10, 780))
self.vel = vec(0, 0)
self.acc = vec(0, 0)
def update(self):
platforms.add(plat1)
grounds.add(ground)
all_except_player.add(plat1, ground)
self.acc = vec(0, 0.5)
hits = pg.sprite.spritecollide(p, all_except_player, False)
if p.vel.y > 0:
if hits:
self.pos.y = hits[0].rect.top + 1
self.vel.y = 0
# this is where I assume the problem lies
wall_hits = pg.sprite.spritecollide(p, platforms, False)
if p.vel.x > 0:
if wall_hits:
self.pos.x = wall_hits[0].rect.left - 10
self.vel.x = 0
if p.vel.x < 0:
if wall_hits:
self.pos.x = wall_hits[0].rect.right + 10
self.vel.x = 0
keys = pg.key.get_pressed()
if keys[pg.K_a]:
self.acc.x -= acc
if keys[pg.K_d]:
self.acc.x += acc
if keys[pg.K_SPACE]:
self.jump()
self.acc.x += self.vel.x * fric
self.vel += self.acc
self.pos += self.vel + 0.5 * self.acc
if self.pos.x > width:
self.pos.x = 0
if self.pos.x < 0:
self.pos.x = width
self.rect.midbottom = self.pos
def jump(self):
hits = pg.sprite.spritecollide(self, all_except_player, False)
if hits:
self.vel.y -= 15
class Ground(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((width, 20))
self.image.fill(green)
self.rect = self.image.get_rect(center=(width/2, height - 10))
class Platform(pg.sprite.Sprite):
def __init__(self):
pg.sprite.Sprite.__init__(self)
self.image = pg.Surface((100, 300))
self.image.fill(gray)
self.rect = self.image.get_rect(center=(width/2, height - 10))
plat1 = Platform()
p = Player()
ground = Ground()
I would greatly appreciate if someone could help me and point out my mistake.
Split it in 2 separate problems. First handle the collision with the sides of the platform.
The player collides with the left side of the platform when moving to the right, colliding but the left side of the player does not collide with the platform.
It collides with the right side of the platform when moving to the right, colliding but the right side does not collide with the platform.
If the player collides sideways, correct his position so that he does not collide further.
After that, do a new collision test and handle the collision with the top of the platform:
# handle sideways collision
self.rect.midbottom = self.pos
hits = pg.sprite.spritecollide(p, all_except_player, False)
if hits:
if p.vel.x > 0 and self.rect.left < hits[0].rect.left:
self.rect.right = hits[0].rect.left
self.pos = self.rect.midbottom
self.vel.y = 0
elif p.vel.x < 0 and self.rect.right > hits[0].rect.right:
self.rect.left = hits[0].rect.right
self.pos = self.rect.midbottom
self.vel.y = 0
# handel vertical collision
hits = pg.sprite.spritecollide(p, all_except_player, False)
if hits and p.vel.y > 0:
self.rect.bottom = hits[0].rect.top + 1
self.rect.midbottom = self.pos
self.vel.y = 0

How do I rotate the player movement? [duplicate]

This question already has answers here:
Pygame doesn't let me use float for rect.move, but I need it
(2 answers)
How to draw a moving circle in Pygame with a small angle at a low speed and blinking?
(1 answer)
Closed 2 years ago.
I am making an Astroid clone where you only move with the keyboard. The player is able to rotate and needs to move forward to were it is facing.
For example:
When I rotate the player by 30 degrees and push W to go forward I want the player to go forward in the direction the player is facing. I can't just use self.rect.x += self.vel
I also tried to use this function:
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
In theory, it should work because I am taking the sin and cos of this
Triangle.
But when I added that to my code the movement of the player wasn't as smooth as I hoped it to be. And sometimes the player moved indirectly forward.
I also noticed that the player moves faster when facing to the top left than to the bottom right.
Due to that the player doesn't move in a circle when turning always right. Instead the player moves elliptical to the top left.
Here is the code:
import pygame
import time
import random
import math
from os import path
#Colors:
WHITE = (255, 255, 255)
BLACK = (0, 0, 0)
RED = (255, 0, 0)
GREEN = (0, 255, 0)
BLUE = (0, 0, 255)
YELLOW = (255, 255, 0)
#Settings:
pygame.init()
pygame.mixer.init() #for sound
WIDTH = 700
HEIGHT = 500
screen = pygame.display.set_mode((WIDTH, HEIGHT))
pygame.display.set_caption("Astroids")
clock = pygame.time.Clock()
FPS = 60
#Images/Sounds:
game_folder = path.dirname(__file__)
img_folder = path.join(game_folder, "img")
player = pygame.image.load(path.join(img_folder, 'Player.png'))
stone = pygame.image.load(path.join(img_folder, 'Stein.png'))
#Game Classes
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.rect.centerx += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.rect.centery += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.rect.centery += -self.vel * math.sin(math.radians(self.degrees - 90))
self.rect.centerx += self.vel * math.cos(math.radians(self.degrees - 90))
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y
class Astroid(pygame.sprite.Sprite):
def __init__(self, life):
pygame.sprite.Sprite.__init__(self)
self.original_image = stone
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed = 2
self.life = life
self.speed_x = random.randrange(-3, 3)
self.speed_y = random.randrange(-3, 3)
self.rect.x = random.randrange(0, WIDTH - self.rect.width)
self.rect.y = random.randrange(-3, 3)
self.last_rotation = pygame.time.get_ticks() # keeps track of time in milliseconds
self.rotation_deegre = random.randint(0, 360)
self.rotation_speed = 5
def rotate(self):
current_time = pygame.time.get_ticks()
if current_time - self.last_rotation > 50:
self.last_rotation = current_time
self.rotation_deegre += self.rotation_speed
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.rotation_deegre)
self.rect = self.image.get_rect()
self.rect.center = old_center
'''def new_astroids(self):
for i in range(2):
m = Astroid()
all_sprites.add(m)
all_astroids.add(m)''' #to do
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
if self.rect.bottom < 0:
self.rect.top = HEIGHT
if self.rect.right < 0:
self.rect.left = WIDTH
if self.rect.top > HEIGHT:
self.rect.bottom = 0
def movement(self):
self.rect.x += self.speed_x
self.rect.y += self.speed_y
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#Game Funktions
def calculate_new_xy(speed, degrees):
add_x = (speed*(math.sin((degrees)*math.pi/180)))
add_y = (speed*(math.cos((degrees)*math.pi/180)))
return add_x, add_y
#Game Sprites
all_astroids = pygame.sprite.Group()
all_sprites = pygame.sprite.Group()
player = Player()
all_sprites.add(player)
for i in range (1):
m = Astroid(3)
all_astroids.add(m)
all_sprites.add(m)
#Main Game Loop
running = True
while running:
#Keep the game runnung at 60 FPS
clock.tick(FPS)
#Check for events:
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
#Update for Sprites
all_sprites.update()
#Check to see if any Astroids hit the player
astroid_colision = pygame.sprite.spritecollide(player, all_astroids, False)
if astroid_colision:
running = False
#Draw/Render
screen.fill(BLACK)
all_sprites.draw(screen)
#Update the display
pygame.display.update()
pygame.quit()
Any help is welcomed.
But when I added that to my code the movement of the player wasn't as smove as i hoped
The issue is caused by the fact, that the attributes of a pygame.Rect are integral values. Every time the position of the rectangle is changed, the fraction of the floating point motion vector is lost.
You have to compute the position with floating point accuracy and to synchronize the integral rectangle location by rounding the floating point position (round).
Add the attributes self.x and self.y:
class Player(pygame.sprite.Sprite):
def __init__(self):
# [...]
self.x, self.y = self.rect.center
Update self.x and self.y when the rectangle goes out of bounds:
class Player(pygame.sprite.Sprite):
# [...]
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
Change the the attributes self.x and self.y when the player moves and update self.rect.center by self.x and self.y:
class Player(pygame.sprite.Sprite):
# [...]
def movement(self):
# [...]
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
Class Player:
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.original_image = player
self.image = self.original_image.copy()
self.rect = self.image.get_rect()
self.rect.center = (WIDTH/2, HEIGHT/2)
self.speed_x = 0
self.speed_y = 0
self.vel = 3
self.degrees = 0
self.x, self.y = self.rect.center
def boundary(self):
if self.rect.left > WIDTH:
self.rect.right = 0
self.x = self.rect.centerx
if self.rect.bottom < 0:
self.rect.top = HEIGHT
self.y = self.rect.centery
if self.rect.right < 0:
self.rect.left = WIDTH
self.x = self.rect.centerx
if self.rect.top > HEIGHT:
self.rect.bottom = 0
self.y = self.rect.centery
def movement(self):
'''Player Movement'''
keystate = pygame.key.get_pressed()
if keystate[pygame.K_q]:
self.degrees += 3
if keystate[pygame.K_e]:
self.degrees -= 3
if keystate[pygame.K_w]:
self.x += (self.vel*(math.sin((self.degrees+180)*math.pi/180)))
self.y += (self.vel*(math.cos((self.degrees+180)*math.pi/180)))
if keystate[pygame.K_s]:
self.y += -self.vel * math.sin(math.radians(self.degrees - 90))
self.x += self.vel * math.cos(math.radians(self.degrees - 90))
self.rect.center = round(self.x), round(self.y)
def rotate(self):
old_center = self.rect.center
self.image = pygame.transform.rotate(self.original_image, self.degrees)
self.rect = self.image.get_rect()
self.rect.center = old_center
def update(self):
'''Picture is "printed"'''
self.movement()
self.boundary()
self.rotate()
#self.rect.y += self.speed_y

How do I make my player(ship) rotate when I press the right or left keys?

I am trying to make an asteroid game and was wondering how to rotate the player clock wise or counter clock wise when the right or left keys have been pressed, and then when the up key is pressed the player should move forward.
class Player(pygame.sprite.Sprite):
def __init__(self):
pygame.sprite.Sprite.__init__(self)
self.image = pygame.transform.scale(player_img, (50, 38))
self.image.set_colorkey(BLACK)
self.rect = self.image.get_rect()
self.radius = 20
# pygame.draw.circle(self.image, RED, self.rect.center, self.radius)
self.rect.centerx = WIDTH / 2
self.rect.bottom = HEIGHT - 10
self.speedx = 0
self.speedy = 0
self.shield = 100
self.shoot_delay = 250
self.last_shot = pygame.time.get_ticks()
self.lives = 3
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
if keystate[pygame.K_LEFT]:
self.speedx = -8
if keystate[pygame.K_RIGHT]:
self.speedx = 8
if keystate[pygame.K_DOWN]:
self.speedy = 8
if keystate[pygame.K_UP]:
self.speedy = -8
if keystate[pygame.K_SPACE]:
self.shoot()
self.rect.x += self.speedx
self.rect.y += self.speedy
if self.rect.right > WIDTH:
self.rect.right = WIDTH
if self.rect.left < 0:
self.rect.left = 0
def shoot(self):
now = pygame.time.get_ticks()
if now - self.last_shot > self.shoot_delay:
self.last_shot = now
bullet = Bullet(self.rect.centerx, self.rect.top)
all_sprites.add(bullet)
bullets.add(bullet)
def hide(self):
# hide player temporarily
self.hidden = True
self.hide_timer = pygame.time.get_ticks()
self.rect.center = (WIDTH / 2, HEIGHT + 200)````
You can find below a working example (just rename the loaded image), I've kept only the essential code for movement and rotation.
import sys
import math
import pygame
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
self.player_img = pygame.image.load("yourimage.png").convert()
self.image = self.player_img
self.rect = self.image.get_rect()
self.rect.move_ip(x, y)
self.current_direction = 0 #0 degree == up
self.speed = 10
def update(self):
self.speedx = 0
keystate = pygame.key.get_pressed()
prev_center = self.rect.center
if keystate[pygame.K_LEFT]:
self.current_direction += 10
if keystate[pygame.K_RIGHT]:
self.current_direction -= 10
if keystate[pygame.K_DOWN]:
self.rect.x += self.speed * math.sin(math.radians(self.current_direction))
self.rect.y += self.speed * math.cos(math.radians(self.current_direction))
if keystate[pygame.K_UP]:
self.rect.x -= self.speed * math.sin(math.radians(self.current_direction))
self.rect.y -= self.speed * math.cos(math.radians(self.current_direction))
if keystate[pygame.K_LEFT] or keystate[pygame.K_RIGHT]:
self.image = pygame.transform.rotate(self.player_img, self.current_direction)
self.rect = self.image.get_rect()
self.rect.center = prev_center
pygame.init()
screen = pygame.display.set_mode((500, 500))
player = Player(200, 200)
clock = pygame.time.Clock()
while True:
screen.fill((0, 0, 0), player.rect)
for event in pygame.event.get():
if event.type == pygame.QUIT:
sys.exit()
player.update()
screen.blit(player.image, player.rect)
pygame.display.update()
clock.tick(50)
Since you want rotation, you do not need to assing speed directly to x and y but you need to calculate it according to the direction the player is facing.
The basic idea is to use transform.rotate. Keep track of the rotation angle (I called it current_direction), add / subcract a fixed amount (the rotation speed, if you wish) to this angle when LEFT or RIGTH keys are pressed, and then rotate the original image. Since rotation scales the image, I also keep track of the center of the rect, save the new rect from the iamge and and reassing the previous center to the rect.center after rotation, so that the image remains centered on rotation.
When UP or DOWN keys are pressed, you need to decompose the velocity on x and y axes using trigonometry and move the rect attribute coordinates.

Categories

Resources