Make moving more realistic - python

I made this code to make a circle follow my mouse but the movement is really mechanic and does not give the 360 degrees of movement I desired.
Here's the code:
mx, my = pygame.mouse.get_pos()
for event in pygame.event.get():
if event.type == pygame.QUIT:
Running = False
mpos = (mx, my)
X = (playerX)
Y = (playerY)
if mpos[1] >= Y:
playerY += vel
if mpos[1] <= Y:
playerY -= vel
if mpos[0] <= X:
playerX -= vel
if mpos[0] >= X:
playerX += vel

You need to calculate the direction vector, normalize it and multiply it with the desired speed, then apply that to the circles position.
The easiest way is to use pygame's built-in Vector2 class for this:
import pygame
def main():
pygame.init()
screen = pygame.display.set_mode((600, 600))
pos = pygame.Vector2()
clock = pygame.time.Clock()
speed = 10
while True:
for e in pygame.event.get():
if e.type == pygame.QUIT:
return
movement = pygame.mouse.get_pos() - pos
if movement.length() > 6: # some margin so we don't flicker between two coordinates
if movement.length() > 0: # when we move, we want to move at a constant speed
movement.normalize_ip()
pos += movement * speed # change the position
screen.fill((20, 20, 20))
pygame.draw.circle(screen, 'dodgerblue', pos, 30)
pygame.display.flip()
clock.tick(30)
main()

Related

How do I make sprites in pygame not move through eachother?

I'm making a game in pygame and I'm having some trouble with object collisions.
import pygame, sys
from pygame.math import Vector2
pygame.init()
screen = pygame.display.set_mode((500,500))
clock = pygame.time.Clock()
#Environment Variables
gravity = -1
jumpForce = 20
moveSpeed = 10
#Game Objects
playerPos = Vector2(230,230)
playerVelo = Vector2(0,0)
player = pygame.Rect(playerPos.x,playerPos.y, 20, 20)
boxPos = Vector2(350,480)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
def Clamp(var, minClamp, maxClamp):
if minClamp > maxClamp:
raise Exception("minClamp must be less than maxClamp")
if var < minClamp:
var = minClamp
if var > maxClamp:
var = maxClamp
return var
def Draw():
global player,box
screen.fill((255,255,25))
player = pygame.Rect(playerPos.x,playerPos.y, 20, 20)
pygame.draw.rect(screen,(0,100,255),player)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
pygame.draw.rect(screen,(10,200,20),box)
def PlayerVelocity():
global playerPos,playerVelo,player,box,boxPos
if player.colliderect(box):
playerPos = Vector2(boxPos.x,boxPos.y-20)
print("balls")
if playerPos.y < 499:
playerVelo.y += gravity
#if not pygame.Rect(playerPos.x+playerVelo.x,playerPos.y+playerVelo.y,20,20).colliderect(box):
playerPos -= playerVelo
while True:
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_SPACE or event.key == pygame.K_UP:
playerVelo.y = jumpForce
print(playerVelo)
keys = pygame.key.get_pressed()
if keys[pygame.K_a] or keys[pygame.K_LEFT]:
playerVelo.x = 1*moveSpeed
elif keys[pygame.K_d] or keys[pygame.K_RIGHT]:
playerVelo.x = -1*moveSpeed
else:
playerVelo.x = 0
#Draw things to the screen
Draw()
PlayerVelocity()
playerPos.x = Clamp(playerPos.x, 0, 480)
playerPos.y = Clamp(playerPos.y,0,480)
pygame.display.update()
clock.tick(30)
I've tried looking this up and the other solutions either don't work with the movement system I've implemented or I just plain don't understand them.
It is not enough to change the position of the player when the player hits the obstacle. You need to constrain the player's box (pygame.Rect object) with the obstacle rectangle and update the player's position. Clamp and calculate the position of the player before checking for collision:
def PlayerVelocity():
global playerPos,playerVelo,player,box,boxPos
prev_y = playerPos.y
if playerPos.y < 499:
playerVelo.y += gravity
playerPos -= playerVelo
playerPos.x = Clamp(playerPos.x, 0, 480)
playerPos.y = Clamp(playerPos.y, 0, 480)
player = pygame.Rect(playerPos.x, playerPos.y, 20, 20)
box = pygame.Rect(boxPos.x, boxPos.y, 20,20)
if player.colliderect(box):
if prev_y+20 <= box.top:
player.bottom = box.top
elif player.left < box.left:
player.right = box.left
else:
player.left = box.right
playerPos = Vector2(player.topleft)
print("balls")

Collisions in python ping pong game [duplicate]

This question already has answers here:
How do I detect collision in pygame?
(5 answers)
How to make ball bounce off wall with PyGame?
(1 answer)
Closed 1 year ago.
So, im having this trouble with collisions in my pygame Python Ping-pong game. Exactly in that fragment of code
#computer
if (x_ball > (x*9) and x_ball < (x*9)+15) and (y_ball < y+55 and y_ball > y-55):
print(f"col at x={x_ball} y= {y_ball}")
x_ball = 890
speed_x*=-1
So it is basic computer movement, repeating the y position of ball. But when i implemented AI movement, ball is going through the computer pallete, sometimes bouncing away. How to make it right? I lost my mind tbh.
import pygame
from pygame.constants import DROPTEXT
pygame.init()
#window
w_width = 1000
w_height = 600
screen = pygame.display.set_mode((w_width, w_height))
clock = pygame.time.Clock()
open = True
#player
x,y = 100,250
#define player action variables
speed = 5
speed_x,speed_y = -5,3
moving_down = False
moving_up = False
#define ball action variables
x_ball,y_ball = 500,250
radius = 10
class palette(pygame.sprite.Sprite):
global x,y
def __init__(self, x, y, speed):
self.speed = speed
pygame.sprite.Sprite.__init__(self)
self.player_rect = pygame.Rect(x,y,30,100)
def draw(self):
pygame.draw.rect(screen, 'White', self.player_rect)
def move(self, moving_up, moving_down):
#reset movement variables
global dy
dy = 0
#assing movement variables if moving up or down
if moving_up:
dy = -self.speed
if moving_down:
dy = self.speed
#update pallete possition
self.player_rect.y += dy
class EnemyPalette(pygame.sprite.Sprite):
global x,y
def __init__(self, x, y, speed):
self.speed = speed
pygame.sprite.Sprite.__init__(self)
self.enemy_rect = pygame.Rect(x,y,30,100)
def draw(self):
pygame.draw.rect(screen, 'White', self.enemy_rect)
#ai move
def move(self, speed,x_ball,y_ball):
self.speed = speed
self.x_ball = x_ball
self.y_ball = y_ball
global dy
dy = 0
dy = y_ball-50
#update enemy pallete possition
self.enemy_rect.y = dy
def ball():
global speed_x,speed_y,x_ball,y_ball
#update pos of bal
x_ball += speed_x
y_ball += speed_y
#basic colision with screen
y = player.player_rect.y
x = player.player_rect.x
#turn off left and right collisions
# if x_ball>=w_width-(0.5*radius) or x_ball <=0+(0.5*radius):
# speed_x*=-1
if y_ball>=w_height-(0.5*radius) or y_ball <=0+(0.5*radius):
speed_y*=-1
#set ball on middle when crosses screen
if x_ball>=w_width or x_ball<=0:
x_ball=500
#collision with pallettes
#player
if (x_ball < x+30 and x_ball > x) and (y_ball < y+120 and y_ball > y):
print(f"left paddle col at x={x_ball} y= {y_ball}")
x_ball = x+30
speed_x*=-1
#computer
if (x_ball > (x*9) and x_ball < (x*9)+15) and (y_ball < y+55 and y_ball > y-55):
print(f"right paddle col at x={x_ball} y= {y_ball}")
x_ball = 890
speed_x*=-1
pygame.draw.circle(screen, (255,255,255), (x_ball,y_ball), radius, 5)
return x_ball,y_ball
#ai movement
#build players and enemies
player = palette(x,y, speed)
enemy = EnemyPalette(x*9,y,speed)
#game
while open:
for event in pygame.event.get():
#quit game
if event.type == pygame.QUIT:
open = False
#keyboard presses
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_w:
moving_up = True
if event.key == pygame.K_s:
moving_down = True
if event.key == pygame.K_ESCAPE:
open = False
#keyboard button released
if event.type == pygame.KEYUP:
if event.key == pygame.K_w:
moving_up = False
if event.key == pygame.K_s:
moving_down = False
screen.fill((0,0,0))
clock.tick(60)
pygame.display.flip
#init players
player.draw()
enemy.draw()
player.move(moving_up, moving_down)
enemy.move(speed,x_ball,y_ball)
ball()
pygame.display.update()
You have to test against the enemy rectangle. Get the x and y coordinate form the enemy rectangle before the collision test:
y = enemy.enemy_rect.y
x = enemy.enemy_rect.x
if (x_ball > x and x_ball < x+15) and (y_ball < y+100 and y_ball > y):
print(f"right paddle col at x={x_ball} y= {y_ball}")
x_ball = 890
speed_x*=-1
Simplify the code with chained Comparisons:
y = enemy.enemy_rect.y
x = enemy.enemy_rect.x
if x < x_ball < x+15 and y < y_ball < y+100:
# [...]
Or even better pygame.Rect.collidepoint:
if enemy.enemy_rect.collidepoint(x_ball, y_ball):
# [...]
I recommend reading How do I detect collision in pygame? and Sometimes the ball doesn't bounce off the paddle in pong game.

How to attach an image to a rect in pygame?

I need help attaching an image to a rectangle in pygame.. I am trying to make a snake game in python(you know, the one that eats the apples and grows lol) and I wanna attach my teacher's face to head of the snake.
I've already tried defining a variable to import the image and then renaming the rectangle to be that image, but nothing seems to work.
snakeFace = pygame.image.load("Morrison.jpg").convert_alpha()
rect = snakeFace.get_rect()
screenWidth = 500
X=50
y= 50
height = 20
vel = 20
run = True
lastKey = None
while run:
pygame.time.delay(10) #1/2 milisecond delay
for event in pygame.event.get():
if event.type == pygame.QUIT:
run = False #this controls the "X" button
if event.type == pygame.KEYDOWN:
lastKey = event.key
keys = pygame.key.get_pressed()
if lastKey == pygame.K_LEFT and x > vel:
x-=vel
if lastKey == pygame.K_RIGHT and x< screenWidth - width -vel:
x+=vel
if lastKey == pygame.K_DOWN and y < screenWidth - height - vel:
y+= vel
if lastKey == pygame.K_UP and y > vel:
y-=vel
win.fill((0,0,0))
pygame.draw.rect(win, (255, 0, 0), (x, y, width, height))
pygame.display.update()
I expected there to be a square with my teachers face on it that runs around on the screen after a particular key is pressed on the screen but its just the regular old red square.
It's a red square because the code is drawing a red square:
pygame.draw.rect(win, (255, 0, 0), (x, y, width, height))
Paint the snakeFace bitmap instead:
win.blit(snakeFace, (x, y))
Here is the full example code that I use to add images to rect objects.
import pygame
vel = 5
pygame.init()
screen = pygame.display.set_mode((800,600))
pygame.display.set_caption('captions')
icon = pygame.image.load('test123.jpg')
pygame.display.set_icon(icon)
playerimg = pygame.image.load('test123.jpg')
playerx = 370
playery = 480
def player():
screen.blit(playerimg,(playerx,playery))
running = True
while running:
screen.fill((0, 0, 0))
for event in pygame.event.get():
if event.type == pygame.QUIT:
running = False
keys = pygame.key.get_pressed()
if keys[pygame.K_a] and playerx > vel:
playerx += vel
player()
pygame.display.update()

How to implement jump in Pygame without sprites?

I'm new to programming and to Python as well as Pygame. As such, I'm not yet comfortable with sprites in Pygame. I'm trying to make a game where a block jumps whenever the spacebar is pressed - similar to Mario.
My code doesn't work as desired because whenever the spacebar is pressed, the block incrementally moves up (I've added a gravity component), instead of "jumping".
import pygame
pygame.init()
game_display = pygame.display.set_mode((800, 800))
# fixed variables at the start
x_pos = 400
y_pos = 400
current_speed = 15
def jump_coords(y_position, speed):
if speed >= 0:
#to move up, reduce the y-coordinate
y_position -= speed
return y_position
game_exit = False
# main loop
while not game_exit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
y_pos = jump_coords(y_pos, current_speed)
# 1 represents gravity value
current_speed -= 1
rect_one = pygame.Rect(x_pos, y_pos, 10, 10)
pygame.draw.rect(game_display, (255, 0, 0), rect_one)
pygame.display.update()
I know that I have to somehow make y_pos keep updating in the while loop whilst speed >= 0 but I'm not sure how to implement it.
I made the minimal changes to your code to get the block to bounce:
import pygame
pygame.init()
game_display = pygame.display.set_mode((800, 800))
# fixed variables at the start
x_pos = 400
y_pos = 400
x_old = x_pos
y_old = y_pos
current_speed = 15
def jump_coords(y_position, speed):
# to move up, reduce the y-coordinate
y_position -= speed
if y_position > 400:
y_position = 400
global jump_flag
jump_flag = False
global current_speed
current_speed = 15
return y_position
game_exit = False
jump_flag = False
# main loop
while not game_exit:
for event in pygame.event.get():
if event.type == pygame.KEYDOWN:
if event.key == pygame.K_SPACE:
jump_flag = True
elif event.key == pygame.K_ESCAPE:
exit(0)
if jump_flag:
x_old = x_pos
y_old = y_pos
y_pos = jump_coords(y_pos, current_speed)
# 1 represents gravity value
current_speed -= 1
rect_old = pygame.Rect(x_old, y_old, 10, 10)
pygame.draw.rect(game_display, (0, 0, 0), rect_old)
rect_one = pygame.Rect(x_pos, y_pos, 10, 10)
pygame.draw.rect(game_display, (255, 0, 0), rect_one)
pygame.display.update()
The most important changes was the removal of the check for speed greater than zero. The speed has to go negative if the block is going to come back down. The next change was to save the old x and y coordinates so that we can draw a black square over the old position. I also made it possible to exit the program by pressing the Escape key.
I made this from scratch, I hope it's not too daunting!
import pygame,sys
pygame.init()
screen = pygame.display.set_mode((800, 800))
tm = 20 # Terminal Velocity
gravity = 1
class Player:
def __init__(self,speed,x,y):
self.speed = speed
self.x = x; self.y = y
self.yVelocity = 0
self.xVelocity = 0
def getKeys(self):
key = pygame.key.get_pressed()
if key[pygame.K_a]: self.xVelocity -= self.speed
if key[pygame.K_d]: self.xVelocity += self.speed
if key[pygame.K_SPACE]:
if isGround(self.x,self.y):
self.yVelocity -= 20
def move(self,dt):
if self.x < 0:
self.x = 0
if self.x > 800-15:
self.x = 800-15
if self.y < 0:
self.y = 0
if self.y > 800-10:
self.y = 800-10
self.x += self.xVelocity
self.y += self.yVelocity
if self.xVelocity != 0:
self.xVelocity /= 70*dt
if self.yVelocity < tm and not isBlocking(self.x,self.y+self.yVelocity):
self.yVelocity += gravity
if isBlocking(self.x,self.y):
self.yVelocity = 0
def draw(self):
screen.fill((255,0,0),(self.x,self.y,10,10))
def isBlocking(x,y):
if x < 0 or x > 800 or y < 0 or y > 800:
return True
elif y >= 400:
return True
else:
return False
def isGround(x,y):
if y >= 400:
return True
else:
return False
player = Player(1,400,400)
clock = pygame.time.Clock()
while True:
dt = clock.tick(60)/1000 # limit to 60 FPS.
screen.fill((0,0,0))
if pygame.event.poll().type == pygame.QUIT: pygame.quit(); sys.exit()
player.getKeys()
player.move(dt)
player.draw()
pygame.display.flip()
Hope it helps!

Python - Pygame bullet movements

I am trying to find a way to get the rotation of the ship and apply it to the beam so that it will travel in the direction it was shot in.. I really have no idea how I would do this but here is my code: Please excuse the messiness of it, I put comments in so you know whats what.
import sys, pygame, math, time;
from pygame.locals import *;
spaceship = ('spaceship.png')
mouse_c = ('crosshair.png')
backg = ('background.jpg')
fire_beam = ('beams.png')
pygame.init()
screen = pygame.display.set_mode((800, 600))
bk = pygame.image.load(backg).convert_alpha()
mousec = pygame.image.load(mouse_c).convert_alpha()
space_ship = pygame.image.load(spaceship).convert_alpha()
f_beam = pygame.image.load(fire_beam).convert_alpha()
f_beam = pygame.transform.scale(f_beam, (50, 50))
f_beam_rect = f_beam.get_rect()
clock = pygame.time.Clock()
pygame.mouse.set_visible(False)
space_ship_rect = space_ship.get_rect()
space_ship_rect.centerx = 375
space_ship_rect.centery = 300
speed = 3.5
pressed_down = 0
while True:
clock.tick(60)
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
pressed_down = 1
elif event.type == MOUSEBUTTONUP:
pressed_down = 0
if pressed_down == 1:
x, y = pygame.mouse.get_pos()
x1, y1 = x - space_ship_rect.x, y - space_ship_rect.y
angle = math.atan2(y1, x1)
dx = speed*math.cos(angle)
dy = speed*math.sin(angle)
movex = space_ship_rect.centerx = space_ship_rect.centerx + dx#ship x
movey = space_ship_rect.centery = space_ship_rect.centery + dy#ship y
if event.type == MOUSEMOTION:
x1, y1 = pygame.mouse.get_pos()
x2, y2 = space_ship_rect.x, space_ship_rect.y
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
display_s = pygame.transform.rotate(space_ship, (degs))#rotation of ship
if event.type == MOUSEBUTTONDOWN and event.button == 1:
#Is it possible for me to get the degree rotation of the space_ship and apply it to here so the beam will travel in the direction it was shot in?
screen.blit(display_s, (space_ship_rect.centerx, space_ship_rect.centery))
pos = pygame.mouse.get_pos()
screen.blit(mousec, (pos))
pygame.display.update()
I see you have problem with ship rotation.
If you create rotated spaceship display_s you get image with different size than space_ship size so you have to get display_s rectangle and assign spaceship center to display_s center.
display_s_rect = display_s.get_rect( center=spaceship_rect.center)
Now you have to use display_s_rect to display display_s
screen.blit(display_s, display_s_rect)
By the way:
blit() expect position where to put left top corner of blited image on screen.
With
screen.blit(display_s, (space_ship_rect.centerx, space_ship_rect.centery))
left top corner of display_s will be put in (space_ship_rect.centerx, space_ship_rect.centery) but I think you want to put display_s center in (space_ship_rect.centerx, space_ship_rect.centery)
Assign center values (as before) to (space_ship_rect.centerx, space_ship_rect.centery) but use (space_ship_rect.x, space_ship_rect.y) in blit().
You can use space_ship_rect in place of (space_ship_rect.x, space_ship_rect.y) in blit() with the same result.
I think you have the same problem with mousec position in blit().
Get mousec rectangle, assign mouse position to rectangle center and than use rectangle x,y to blit.
mousec_rect = mousec.get_rect( center = pygame.mouse.get_pos() )
screen.blit(mousec, mousec_rect)
EDIT:
your mainloop after my modification - now ship is rotate as it should
display_s = space_ship # default value at start when ship wasn't rotate
while True:
clock.tick(60)
screen.blit(bk, (0, 0))
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
elif event.type == MOUSEBUTTONDOWN and event.button == 3:
pressed_down = 1
elif event.type == MOUSEBUTTONUP:
pressed_down = 0
if pressed_down == 1:
x, y = pygame.mouse.get_pos()
x1, y1 = x - space_ship_rect.x, y - space_ship_rect.y
angle = math.atan2(y1, x1)
dx = speed*math.cos(angle)
dy = speed*math.sin(angle)
movex = space_ship_rect.centerx = space_ship_rect.centerx + dx#ship x
movey = space_ship_rect.centery = space_ship_rect.centery + dy#ship y
if event.type == MOUSEMOTION:
x1, y1 = pygame.mouse.get_pos()
x2, y2 = space_ship_rect.centerx, space_ship_rect.centery
dx, dy = x2 - x1, y2 - y1
rads = math.atan2(dx, dy)
degs = math.degrees(rads)
display_s = pygame.transform.rotate(space_ship, (degs))#rotation of ship
display_s_rect = display_s.get_rect(center = space_ship_rect.center)
if event.type == MOUSEBUTTONDOWN and event.button == 1:
#Is it possible for me to get the degree rotation of the space_ship and apply it to here so the beam will travel in the direction it was shot in?
pass
screen.blit(display_s, display_s_rect)
#screen.blit(display_s, space_ship_rect)
pos = pygame.mouse.get_pos()
mousec_rect = mousec.get_rect(centerx=pos[0], centery=pos[1])
screen.blit(mousec, mousec_rect )
pygame.display.update()
Before the While Loop:
beam_speed=<somevalue>
f_beam_rect=space_ship_rect #initialise beam position
fired=False #check if beam is fired or not
Inside While Loop
##If beam is fired(right click) blit images through-out the path
if fired:
b_angle = math.atan2(by1, bx1)
bdx = beam_speed*math.cos(b_angle)
bdy = beam_speed*math.sin(b_angle)
f_beam_rect.centerx = f_beam_rect.centerx + bdx
f_beam_rect.centery = f_beam_rect.centery + bdy
screen.blit(f_beam,f_beam_rect)
for event in pygame.event.get():
if event.type == QUIT:
pygame.quit()
sys.exit()
###If mouse is clicked then find the direction (mouse position and spaceship)
elif event.type == MOUSEBUTTONDOWN and event.button == 1:
bx, by = pygame.mouse.get_pos()
bx1, by1 = bx - space_ship_rect.x, by - space_ship_rect.y
fired=True
Now you can make a function which checks that beam collides with enemy objects and if True then stop blitting both the beam and that object.
Also reset the variable fired=False and reset beam position to start from spaceship again f_beam_rect=space_ship_rect

Categories