I would like my player to move diagonally at the same speed as it does when only moving horizontally or vertically. This does work when moving in a line with a negative gradient however, when moving along a positive gradient, the player doesn't move at a perfect 45 degree angle.
This is my code for moving the player:
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.rect.x += self.direction.x * speed
self.rect.y += self.direction.y * speed
As I said earlier, all I want is for the player to move diagonally at the same speed as it does in only the x or y direction.
Here is the full player class in case it is needed:
class Player:
def __init__(self, x, y):
self.image = pygame.Surface((30, 30))
self.image.fill((255, 255, 255))
self.rect = self.image.get_rect(center = (x, y))
self.direction = pygame.math.Vector2()
self.speed = 5
def input(self):
keys = pygame.key.get_pressed()
if keys[K_w]:
self.direction.y = -1
elif keys[K_s]:
self.direction.y = 1
else:
self.direction.y = 0
if keys[K_a]:
self.direction.x = -1
elif keys[K_d]:
self.direction.x = 1
else:
self.direction.x = 0
def move(self, speed):
if self.direction.magnitude() != 0:
self.direction = self.direction.normalize()
self.rect.x += self.direction.x * speed
self.rect.y += self.direction.y * speed
def update(self):
self.input()
self.move(self.speed)
def draw(self, screen):
screen.blit(self.image, self.rect.center)
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 fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If you want to move the object with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location of the rectangle:
class Player:
def __init__(self, x, y):
self.image = pygame.Surface((30, 30))
self.image.fill((255, 255, 255))
self.rect = self.image.get_rect(center = (x, y))
self.direction = pygame.math.Vector2()
self.speed = 5
self.position = pygame.math.Vector2(x, y)
def input(self):
keys = pygame.key.get_pressed()
dx = keys[K_d] - keys[K_a]
dy = keys[K_s] - keys[K_w]
self.direction = pygame.math.Vector2(dx, dy)
if dx != 0 and dy != 0:
self.direction /= 1.41421
def move(self, speed):
self.position += self.direction * speed
self.rect.x = round(self.position.x)
self.rect.y = round(self.position.y)
def update(self):
self.input()
self.move(self.speed)
def draw(self, screen):
screen.blit(self.image, self.rect.center)
See also Pygame doesn't let me use float for rect.move, but I need it and moving with a normalized vector in pygame inconsistent?.
Note, that pygame.math.Vector2.magnitude and math.Vector2.normalize are expensive for performance. Try to avoid this operations. Normalization of the vector is only required when moving along both axes. Since the absolute value of the vector is √2 in this case, the normalization can be replaced by dividing by √2.
dx = keys[K_d] - keys[K_a] # dx is -1, 0 or 1
dy = keys[K_s] - keys[K_w] # dy is -1, 0 or 1
self.direction = pygame.math.Vector2(dx, dy)
if dx != 0 and dy != 0:
self.direction /= 1.41421 # sqrt(2) is ~1.41421
Related
I'm making a simple topdown shooter game. The problem is with bullet starting point. I have player image where rifle barrel is between topmid and topright of rectangle. How to make bullet start always from the rifle barrel regardless image rotation?
# key input
def get_input(self, dt):
keys = pygame.key.get_pressed()
if keys[pygame.K_LEFT]:
self.direction.rotate_ip(dt * -360)
if keys[pygame.K_RIGHT]:
self.direction.rotate_ip(dt * 360)
self.angle = self.direction.angle_to((0, -1))
self.image = pygame.transform.rotate(self.image, self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
if keys[pygame.K_UP]:
self.movement = 1
self.status = 'move'
else:
self.movement = 0
self.status = 'idle'
if keys[pygame.K_DOWN]:
self.movement = -1
self.status = 'move'
if keys[pygame.K_SPACE] and not self.bullet.sprites():
self.create_bullet(dt)
shoot = pygame.mixer.Sound('audio/rumble.flac')
shoot.play()
# movement
def move(self, speed, dt):
movement_v = self.direction * self.movement
if movement_v.length() > 0:
movement_v.normalize_ip()
self.pos += movement_v * dt * speed
self.rect = self.image.get_rect(center=self.pos)
# boundary
if self.rect.x <= 0:
self.rect.x = 0
if self.rect.x >= 1216:
self.rect.x = 1216
if self.rect.y <= 0:
self.rect.y = 0
if self.rect.y >= 704:
self.rect.y = 704
# create bullet instance
def create_bullet(self, dt):
self.bullet.add(Bullet(self.rect.center, self.direction.normalize(), self.bullet_speed, self.angle))
You have rotate the offset of the bullet starting point with pygame.math.Vector2.rotate. e.g.:
offset = pygame.math.Vecotr2(15, 10) # (15, 10) is just an example
rotated_offset = offset.rotate(-self.angle)
pos = pygame.math.Vector2(self.rect.center) + rotated_offset
bullet = Bullet((pos.x, pos.y), ...)
i have made a simple car game in pygame, w for acceleration, a and d for steering. However, Ive noticed that after some rotational motion, my sprite is going sideways while the image displays as if its going straight.
By printing velocity vector I have noticed that after some rotation and translation, value is no longer 0, but e-13/14. Could this be the problem? And if yes, how do I round it up, so the image actually follows the vector?
My code so far:
import pygame
import os
import sys
pygame.init()
screen_height = 750
screen_width = 1500
screen = pygame.display.set_mode((screen_width,screen_height))
robot = pygame.Surface((100,81), pygame.SRCALPHA)
robot.fill((255, 0, 0))
class Robot(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.robot_image = robot
self.image = self.robot_image
self.rect = self.image.get_rect(center=(100,355))
self.velocity = pygame.math.Vector2(1,0)
self.angle = 0
self.rotation = 5
self.state = 0
self.direction = 0
def update(self):
self.drive()
self.rotate()
def drive(self):
if self.state == 1:
self.rect.center += self.velocity * 6
if self.state == -1:
self.rect.center -= self.velocity * 6
def rotate(self):
if self.direction == 1:
self.angle -= self.rotation
self.velocity.rotate_ip(self.rotation)
if self.direction == -1:
self.angle += self.rotation
self.velocity.rotate_ip(-self.rotation)
self.rect = self.image.get_rect(center=self.rect.center)
self.image = pygame.transform.rotozoom(self.robot_image,self.angle,1)
rob = pygame.sprite.GroupSingle(Robot())
def main():
run = True
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
input = pygame.key.get_pressed()
if sum(pygame.key.get_pressed()) <= 1:
rob.sprite.state = 0
rob.sprite.direction = 0
if input[pygame.K_w]:
rob.sprite.state = 1
if input[pygame.K_s]:
rob.sprite.state = -1
if input[pygame.K_d]:
rob.sprite.direction = 1
if input[pygame.K_a]:
rob.sprite.direction = -1
screen.fill(0)
rob.draw(screen)
rob.update()
pygame.display.update()
main()
As you accumulate the rotation of the vector, a floating point precision error accumulates over time. I suggest calculating the velocity vector from the angle.
self.velocity = pygame.math.Vector2(1,0)
self.velocity.rotate_ip(-self.angle)
Since pygame.Rect is supposed to represent an area on the screen, a pygame.Rect object can only store integral data. The fraction part of the coordinates gets lost when the velocity vector is added to the position of the rectangle. If you want to store object positions with floating point accuracy, you have to store the location of the object in separate attribute and to synchronize the pygame.Rect object.
if self.state == 1:
self.position += self.velocity * 6
if self.state == -1:
self.position -= self.velocity * 6
self.rect.center = self.position
Complete class Robot:
class Robot(pygame.sprite.Sprite):
def __init__(self):
super().__init__()
self.robot_image = robot
self.image = self.robot_image
self.rect = self.image.get_rect(center=(100,355))
self.position = self.rect.center
self.velocity = pygame.math.Vector2(1,0)
self.angle = 0
self.rotation = 5
self.state = 0
self.direction = 0
def update(self):
self.drive()
self.rotate()
def drive(self):
if self.state == 1:
self.position += self.velocity * 6
if self.state == -1:
self.position -= self.velocity * 6
self.rect.center = self.position
def rotate(self):
if self.direction == 1:
self.angle -= self.rotation
if self.direction == -1:
self.angle += self.rotation
self.velocity = pygame.math.Vector2(1,0)
self.velocity.rotate_ip(-self.angle)
self.rect = self.image.get_rect(center=self.rect.center)
self.image = pygame.transform.rotozoom(self.robot_image,self.angle,1)
I don't know if there is a better way to implement ramps.
First i calculate the points that belong to the hipotenuse and use collidepoint to see if there is a collision between the rectangle and any point that belongs to the hipotenuse, then i update the rectangle based on the point where there was a collision.
Being careful when the rectangle is at the top of the ramp.
The rectangle ascends the ramp perfectly, but when the rectangle descends the ramp, the rectangle shakes.
import sys
import pygame
from pygame.locals import *
pygame.init()
fps = 60
fpsClock = pygame.time.Clock()
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
def draw_grid():
for y in range(0,height,32):
pygame.draw.line(screen,'red',(0,y),(width,y))
for x in range(0,width,32):
pygame.draw.line(screen,'red',(x,0),(x,height))
class Ramp(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height), pygame.SRCALPHA)
#self.image.fill('green')
pygame.draw.polygon(self.image, color,
points=[(0, 0), (0, height), (width, height)])
self.rect = self.image.get_rect(topleft=(x, y))
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill('blue')
self.rect = self.image.get_rect(topleft=(x,y))
self.speed = 5
self.direction = pygame.math.Vector2(0,0)
self.gravity = 0.9
self.initial_jump = -20
self.on_ground = True
def apply_gravity(self):
self.direction.y += self.gravity
self.rect.y += self.direction.y
def move(self):
keys=pygame.key.get_pressed()
if keys[K_LEFT]:
self.direction.x = -self.speed
elif keys[K_RIGHT]:
self.direction.x = self.speed
else:
self.direction.x = 0
if keys[K_UP] and self.on_ground:
self.direction.y = self.initial_jump
self.on_ground = False
self.rect.x += self.direction.x
def check_borders(self):
if self.rect.x <= 0:
self.rect.x = 0
if self.rect.right >= width:
self.rect.right = width
if self.rect.bottom >= height:
self.rect.bottom = height
self.direction.y = 0
self.on_ground = True
if self.rect.colliderect(ramp_rect):
if self.direction.x > 0 and abs(self.rect.right-ramp_rect.left) <= 5:
self.rect.right = ramp_rect.left
# ramp stuff
for p in hypotenuse_points:
if self.rect.collidepoint(p):
if self.rect.left >= ramp_rect.left:
self.rect.bottomleft = p
else:
self.rect.bottom = ramp_rect.top
self.on_ground = True
self.direction.y = 0
def update(self):
self.move()
self.apply_gravity()
self.check_borders()
player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect
m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft
hypotenuse_points = []
for x in range(ramp_rect.left,ramp_rect.right):
hypotenuse_points.append((x,m*(x-x1)+y1)) # Point-slope equation
while True:
screen.fill('white')
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
ramp.draw(screen)
player.update()
player.draw(screen)
#draw_grid()
pygame.draw.lines(screen,'black',False,hypotenuse_points,3)
pygame.display.update()
fpsClock.tick(fps)
There is no problem with your code. Only gravity is too weak. The movement is so fast that gravity is acting too late. Note that instead of moving down the slope, you move to the right and then fall.
Of course there is one problem with your code. 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 fraction part of the coordinates gets lost when the new position of the object is assigned to the Rect object. If this is done every frame, the position error will accumulate over time.
If you want to store object positions with floating point accuracy, you have to store the location of the object in separate variables respectively attributes and to synchronize the pygame.Rect object. round the coordinates and assign it to the location of the rectangle.
Instead of the list of points I suggest to compute the height of the ramp under the palyer:
if self.rect.colliderect(ramp_rect):
ratio = ramp_rect.height / ramp_rect.width
self.rect.bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
self.y = self.rect.y
Complete example:
import sys
import pygame
from pygame.locals import *
pygame.init()
fps = 60
fpsClock = pygame.time.Clock()
width, height = 640, 480
screen = pygame.display.set_mode((width, height))
def draw_grid():
for y in range(0,height,32):
pygame.draw.line(screen,'red',(0,y),(width,y))
for x in range(0,width,32):
pygame.draw.line(screen,'red',(x,0),(x,height))
class Ramp(pygame.sprite.Sprite):
def __init__(self, x, y, width, height, color):
super().__init__()
self.image = pygame.Surface((width, height), pygame.SRCALPHA)
#self.image.fill('green')
pygame.draw.polygon(self.image, color,
points=[(0, 0), (0, height), (width, height)])
self.rect = self.image.get_rect(topleft=(x, y))
class Player(pygame.sprite.Sprite):
def __init__(self,x,y):
super().__init__()
self.image = pygame.Surface((32, 32))
self.image.fill('blue')
self.rect = self.image.get_rect(topleft=(x,y))
self.x, self.y = self.rect.topleft
self.speed = 5
self.direction = pygame.math.Vector2(0,0)
self.gravity = 0.9
self.initial_jump = -20
self.on_ground = True
def apply_gravity(self):
self.direction.y += self.gravity
self.y += self.direction.y
self.rect.y = round(self.y)
def move(self):
keys = pygame.key.get_pressed()
self.direction.x = (keys[K_RIGHT] - keys[K_LEFT]) * self.speed
if keys[K_UP] and self.on_ground:
self.direction.y = self.initial_jump
self.on_ground = False
self.x += self.direction.x
self.rect.x = round(self.x)
def check_borders(self):
if self.rect.x <= 0:
self.rect.x = 0
self.x = self.rect.x
if self.rect.right >= width:
self.rect.right = width
self.x = self.rect.x
if self.rect.bottom >= height:
self.rect.bottom = height
self.direction.y = 0
self.on_ground = True
self.y = self.rect.y
if self.rect.colliderect(ramp_rect):
if self.old_rect.right-1 <= ramp_rect.left:
self.rect.right = ramp_rect.left
self.x = self.rect.x
else:
ratio = ramp_rect.height / ramp_rect.width
bottom = ramp_rect.bottom - (ramp_rect.right - max(self.rect.left, ramp_rect.left)) * ratio
if self.on_ground or self.rect.bottom > bottom:
self.rect.bottom = bottom
self.y = self.rect.y
self.direction.y = 0
self.on_ground = True
def update(self):
self.old_rect = self.rect.copy()
self.move()
self.apply_gravity()
self.check_borders()
player = pygame.sprite.GroupSingle(Player(12*32,10*32))
ramp = pygame.sprite.GroupSingle(Ramp(5*32,10*32,7*32,5*32,'red'))
ramp_rect = ramp.sprite.rect
m = (ramp_rect.height)/( ramp_rect.width)
x1,y1 = ramp_rect.topleft
while True:
screen.fill('white')
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
ramp.draw(screen)
player.update()
player.draw(screen)
pygame.display.update()
fpsClock.tick(fps)
I am teaching myself pygame and am looking at making my character able to rotate and then move in the direction they are facing.
I can do the rotation but cannot get the character to move in the direction the image is then facing.
The code is on Trinket HERE
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
super().__init__()
self.image = pygame.Surface([width , height])
self.image.fill(BLACK)
self.image.set_colorkey(BLACK)
#Loading the image for the character
self.img = pygame.image.load("char.jfif")
#creating a copy of the image
self.img_orig = self.img.copy()
#defining the starting angle of the character image
self.angle = 0
self.velocity = 5
self.rect = self.img_orig.get_rect()
def moveLeft(self):
self.angle += 1
self.img = pygame.transform.rotate(self.img_orig, self.angle)
def moveRight(self):
self.rect.x += self.velocity
if self.rect.x > 485:
self.rect.x = 485
while run:
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False
keys = pygame.key.get_pressed()
if keys[pygame.K_UP]:
pSprite.moveForward()
if keys[pygame.K_DOWN]:
pSprite.moveDown()
if keys[pygame.K_LEFT]:
pSprite.moveLeft()
if keys[pygame.K_RIGHT]:
pSprite.moveRight()
#---- Game Logic Here
#--- Drawing Code Here
#Reset the screen to blank
screen.fill(BLUE)
#Draw Play Area
#Draw Sprites
screen.blit(pSprite.img,(pSprite.rect.x, pSprite.rect.y))
You can use pygame's Vector2 class instead of calculating the position of your sprite yourself.
I also suggest to let the sprite itself handle its movement instead of doing so in the main loop and using a clock for constant framerates and easy control of the speed of your sprites.
You also probably want to use an image format with alpha channel (like PNG).
Here's a simple example:
import pygame
class Actor(pygame.sprite.Sprite):
def __init__(self, pos, *grps):
super().__init__(*grps)
self.image = pygame.image.load('char.png').convert_alpha()
self.image_org = self.image.copy()
self.rect = self.image.get_rect(center=pos)
self.pos = pygame.Vector2(pos)
self.direction = pygame.Vector2((0, -1))
def update(self, events, dt):
pressed = pygame.key.get_pressed()
# if a is pressed, rotate left with 360 degress per second
if pressed[pygame.K_a]: self.direction.rotate_ip(dt * -360)
# if d is pressed, rotate right with 360 degress per second
if pressed[pygame.K_d]: self.direction.rotate_ip(dt * 360)
# check if should move forward or backward
movement = 0
if pressed[pygame.K_w]: movement = 1
if pressed[pygame.K_s]: movement = -1
movement_v = self.direction * movement
if movement_v.length() > 0:
movement_v.normalize_ip()
# move 100px per second in the direction we're facing
self.pos += movement_v * dt * 100
# rotate the image
self.image = pygame.transform.rotate(self.image_org, self.direction.angle_to((0, -1)))
self.rect = self.image.get_rect(center=self.pos)
def main():
pygame.init()
screen = pygame.display.set_mode((600, 480))
sprites = pygame.sprite.Group()
Actor((100, 100), sprites)
clock, dt = pygame.time.Clock(), 0
while True:
events = pygame.event.get()
for e in events:
if e.type == pygame.QUIT:
return
screen.fill('grey')
sprites.draw(screen)
sprites.update(events, dt)
pygame.display.flip()
dt = clock.tick(60) / 1000
main()
char.png
Rotate the player around its center (see How do I rotate an image around its center using PyGame?):
self.angle += 1
self.img = pygame.transform.rotate(self.img_orig, self.angle)
self.rect = self.img.get_rect(center = self.rect.center)
Use an attribute x and y to store the position of the player with floating point accuracy.
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
# [...]
self.x, self.y = self.rect.center
Compute the direction of the player dependent on the angle with the trgonometric function math.sin and math.cos. Change the position attributes and update the rect attribute:
self.x += self.velocity * math.cos(math.radians(self.angle + 90))
self.y -= self.velocity * math.sin(math.radians(self.angle + 90))
self.rect.center = round(self.x), round(self.y)
The y-axis needs to be reversed (-dy) as the y-axis is generally pointing up, but in the PyGame coordinate system the y-axis is pointing down. In addition, a correction angle must be deducted (+ 90). Since the "angle" is 0 ° when the sprite is looking up, you need to add 90 ° to the angle for the calculation of the direction vector.
See also te in pygame while moving with the keys](How to turn the sprite in pygame while moving with the keys.
Class Bob:
import pygame
import math
BLACK = (0,0,0)
class Bob(pygame.sprite.Sprite):
def __init__(self, color , height , width):
super().__init__()
self.image = pygame.Surface([width , height])
self.image.fill(BLACK)
self.image.set_colorkey(BLACK)
#Loading the image for the character
self.img = pygame.image.load("char.jfif")
#creating a copy of the image
self.img_orig = self.img.copy()
#defining the starting angle of the character image
self.angle = 0
self.velocity = 5
self.rect = self.img_orig.get_rect()
self.x, self.y = self.rect.center
def rotate(self, change_angle):
self.angle += change_angle
self.img = pygame.transform.rotate(self.img_orig, self.angle)
self.rect = self.img.get_rect(center = self.rect.center)
def move(self, distance):
self.x += distance * math.cos(math.radians(self.angle + 90))
self.y -= distance * math.sin(math.radians(self.angle + 90))
self.rect.center = round(self.x), round(self.y)
def moveLeft(self):
self.rotate(1)
def moveRight(self):
self.rotate(-1)
def moveForward(self):
self.move(self.velocity)
def moveDown(self):
self.move(-self.velocity)
When setting the starting position of the player, you need to set the x, y and rect attribute:
pSprite = Bob(WHITE , 25,25)
pSprite.rect.x = 50
pSprite.rect.y = 50
pSprite.x, pSprite.y = pSprite.rect.center
So basically I want to try to make the Sprite bounce off the boundaries of my pygame screen (1920, 1020) but it doesn't work along with the player movement. For me, either have the player movement or the bounce. The code below includes the player movement but not the bounce. But I want bottth... Any ideas? Thanks! Credits to Rabbid76 and Ann Zen.
Code:
import pygame
import os
import random
import math
import winsound
# winsound.PlaySound("explosion.wav", winsound.SND_ALIAS)
os.environ['SDL_VIDEO_WINDOW_POS'] = "%d,%d" % (0, 30)
wn = pygame.display.set_mode((1920, 1020))
clock = pygame.time.Clock()
icon = pygame.image.load('Icon.png')
pygame.image.load('Sprite0.png')
pygame.image.load('Sprite0.png')
pygame.display.set_icon(icon)
pygame.display.set_caption('DeMass.io')
class Player(pygame.sprite.Sprite):
def __init__(self, x, y):
pygame.sprite.Sprite.__init__(self)
z = random.randint(1, 2)
if z == 2:
self.original_image = pygame.image.load('Sprite0.png')
else:
self.original_image = pygame.image.load('Sprite3.png')
self.image = self.original_image
self.rect = self.image.get_rect(center=(x, y))
self.direction = pygame.math.Vector2((0, -1))
self.velocity = 5
self.position = pygame.math.Vector2(x, y)
def point_at(self, x, y):
self.direction = pygame.math.Vector2(x, y) - self.rect.center
if self.direction.length() > 0:
self.direction = self.direction.normalize()
angle = self.direction.angle_to((0, -1))
self.image = pygame.transform.rotate(self.original_image, angle)
self.rect = self.image.get_rect(center=self.rect.center)
def move(self, x, y):
self.position -= self.direction * y * self.velocity
self.position += pygame.math.Vector2(-self.direction.y, self.direction.x) * x * self.velocity
self.rect.center = round(self.position.x), round(self.position.y)
def reflect(self, NV):
self.direction = self.direction.reflect(pygame.math.Vector2(NV))
def update(self):
self.position += self.direction * self.velocity
self.rect.center = round(self.position.x), round(self.position.y)
def hit(self, player):
distance = math.sqrt(math.pow(self.xcor() - player.xcor(), 2) + math.pow(self.ycor() - player.ycor(), 2))
if distance < 20:
return True
else:
return False
player = Player(200, 200)
while True:
clock.tick(60)
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
elif event.type == pygame.MOUSEMOTION:
player.point_at(*event.pos)
keys = pygame.key.get_pressed()
if keys[pygame.K_w] or keys[pygame.K_UP]:
player.move(0, -1)
wn.fill((255, 255, 255))
all_sprites.draw(wn)
pygame.display.update()
If you want to restrict the player to a rectangular area, you must restrict the player's position and rect attributes. Add an additional argument clamp_rect to the method move. If the player's position exceeds the boundaries of the rectangle, move the player inside the rectangle:
class Player(pygame.sprite.Sprite):
# [...]
def move(self, x, y, clamp_rect):
self.position -= self.direction * y * self.velocity
self.position += pygame.math.Vector2(-self.direction.y, self.direction.x) * x * self.velocity
self.rect.center = round(self.position.x), round(self.position.y)
if self.rect.left < clamp_rect.left:
self.rect.left = clamp_rect.left
self.position.x = self.rect.centerx
if self.rect.right > clamp_rect.right:
self.rect.right = clamp_rect.right
self.position.x = self.rect.centerx
if self.rect.top < clamp_rect.top:
self.rect.top = clamp_rect.top
self.position.y = self.rect.centery
if self.rect.bottom > clamp_rect.bottom:
self.rect.bottom = clamp_rect.bottom
self.position.y = self.rect.centery
Pass the window rectangle (wn.get_rect()) to move:
while True:
# [...]
keys = pygame.key.get_pressed()
if keys[pygame.K_w] or keys[pygame.K_UP]:
player.move(0, -1, wn.get_rect())
# [...]
make a bounce animation. define your boundaries and when you hit a wall make the animation run. (for this example you have to predefine hit_wall and have an if statement to check if it is on your boundaries)
Example:
if hit_wall:
animate(direction)
def animate(direction):
if direction == (0, 1):
/code to make it bounce the opposite direction
elif direction == (0, -1):
/code to make it bounce the opposite direction
elif direction == (1, 0):
/code to make it bounce the opposite direction
elif direction == (-1, 0):
/code to make it bounce the opposite direction