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.
My code is acting differently for negative velocities than it is positive ones
I'm trying to implement platformer physics, the player has velocity in the X direction, the velocity is increased or decreased when the user presses "A" or "D" respectively, or set to 0 when the player collides with a wall.
To simulate friction with the ground, the X Velocity of the player is multiplied with "self.drag" (a float less than 1)
I expected this code to reduce the players X Velocity, over time reducing it neglibly near to 0, without actually reversing the velocity (like subtracting a value would), this would stop the player sliding about uncontrollably when the user isn't imputing movement commands.
This works as intended when moving right, however when moving left it acts differently,
when moving to the left the player seems to continue floating for a while before coming to a stop.
Here's the code that takes player input, inside the player class, run each frame:
dx = 0
if pygame.key.get_pressed()[pygame.K_a]:
dx -= self.speed
if pygame.key.get_pressed()[pygame.K_d]:
dx += self.speed
# to slow down horizontal movement
self.vx *= self.drag
# Add change in velocity to total velocity
self.vx += dx
self.vy += dy
Maybe the concept works and I've implemented it incorrectly?
There's collision code that may be affecting the velocities in ways I haven't noticed?
Does this system work differently for positive and negative velocities?
Thanks!
Any help is much appreciated
The issue is caused, because pygame.Rect stores integral coordinates:
The coordinates for Rect objects are all integers. [...]
The fraction component of dx and dy is lost when you do:
self.Rect.x += dx
self.Rect.y += dy
You have to do the calculations with floating point accuracy. Add an x and y attribute to the class. Increment the attributes in move and synchronize the Rect attribute:
class Player:
def __init__(self, color):
self.Rect = pygame.Rect([50, 50], [30, 50])
self.x = self.Rect.x
self.y = slef.Rect.y
# [...]
def move(self, dx, dy, platforms):
# Test for collisions with platforms
# handle movement on the X axis
self.x += dx
self.Rect.x = round(self.x)
for platform in platforms:
if self.Rect.colliderect(platform.Rect):
if dx > 0:
self.Rect.right = platform.Rect.left
if dx < 0:
self.Rect.left = platform.Rect.right
self.x = self.Rect.x
# Reset velocity when collision with wall
self.vx = 0
# handle movement on the Y axis
self.Rect.y += dy
self.Rect.y = round(self.y)
for platform in platforms:
if self.Rect.colliderect(platform.Rect):
if dy > 0:
self.Rect.bottom = platform.Rect.top
if dy < 0:
self.Rect.top = platform.Rect.bottom
self.y = self.Rect.y
# Reset velocity when collision with floor or roof
self.vy = 0
# return correctly collided rect to draw()
return self.Rect
Related
Hi I am making a game and I have a problem. When player lands to the ground he goes a little bit under the ground, for example like this:
And I want someting like this:
Here is my gravity code:
if not player.player.collidelistall(MAPr) and player.yvel < 4:
player.yvel -= 0.3
if player.player.collidelistall(MAPr):
player.yvel = 0
player.y -= player.yvel
cy -= player.yvel
thanks!
Calculate the player's position as it would be if it had moved before the collision test. Only move the player if it doesn't collide. Set the player's position on the ground when you detect the collision:
player.y = cy - player.yvel
indices = player.player.collidelistall(MAPr)
if indices:
player.yvel = 0
for i in indices:
player.player.bottom = MAPr[i].top
cy = player.player.y
else:
cy -= player.yvel
if player.yvel < 4:
player.yvel -= 0.3
See collidelistall:
Returns a list of all the indices that contain rectangles that collide with the Rect
Full Code
self.VEL = 3
self.RAND = numpy.linspace(-self.VEL, +self.VEL, 100) # calculating value between -2 and 2, a total of 100 values
-----------------------------------------
if self.new_line:
self.deltax = random.choice(self.RAND)
self.deltay = random.choice(self.RAND)
self.new_line = False
self.move(self.deltax, self.deltay)
-----------------------------------------
def move(self, deltax, deltay):
self.x += deltax
self.y += deltay
I'm currently building an ant simulator in python with pygame. The ants start in the middle and move on from there randomly. The movement works like this. Every few milliseconds self.new_line is set to True. Then a new self.deltax and self.deltay are calculated. Now, as long as self.new_line is False my ant will move with self.x += deltax and self.y += deltay per iteration. When self.new_line is True again the ant will start a new line with new values for self.deltax and self.deltay. The values for deltax and deltay are between -2 and +2 meaning the ant could move in every angle.
Visualization
Questions:
However i want my ants only to move in a specific angle. Like This
How can i do this?
If you run the program with on ant you will see that the speed of the ant changes with new lines. How can make my ant to have one consistent speed?
How can i make my ant bounce of walls in a specific angle?
Thanks for your help.
These are multiple questions. The intent is to ask only one question. I will only answer 2 of them.
Use pygame.math.Vector2 and rotate() to generate a direction vector with constant length and angle in a specified range:
if self.new_line:
angle = random.randint(-45, 45)
direction_vector = pygame.math.Vector2(0, -self.VEL).rotate(angle)
self.deltax = direction_vector.x
self.deltay = direction_vector.y
I am attempting to create pong using Turtle, however, I am having an issue with the collision system. I am using a basic pythagorean function for this, however, when the ball hits a bumber, it gets stuck at the bumper and starts shaking. I am not sure how to fix this problem. Here is the collision and bumper code.
turtle.register_shape('bar.gif')
lbump = turtle.Turtle()
lbump.color('white')
lbump.shape('bar.gif')
lbump.penup()
lbump.speed(0)
lbump.setposition(-285,0)
rbump = turtle.Turtle()
rbump.color('white')
rbump.shape('bar.gif')
rbump.penup()
rbump.speed(0)
rbump.setposition(285,0)
ball = turtle.Turtle()
ball.color('white')
ball.shape('circle')
ball.penup()
ball.speed(0)
ball.setposition(0,0)
ballspeedx = -5
ballspeedy = 0 #To test collison#
def isCollision(t1, t2):
distance = math.sqrt(math.pow(t1.xcor()-t2.xcor(),2)+math.pow(t1.ycor()-t2.ycor(),2))
if distance < 30:
return True
else:
return False
def ball_move():
while True:
global ballspeedy
global ballspeedx
x = ball.xcor() + ballspeedx
y = ball.ycor() + ballspeedy
if y > 285 or y < -285:
ballspeedy *= -1
if x < -295 or x > 295:
x = 0
y = 0
if isCollision(lbump, ball):
ballspeedx *= -1
if isCollision(rbump, ball):
ballspeedx *= -1
ball.setposition(x,y)
The reason it's likely this: when a collision is detected (isCollision returns True) and the sign of the x velocity is switched, the ball does not have the time to gain enough distance from the bumper until the next iteration of the loop. Hence, next iteration isCollision is still detecting a collision and changes again the sign of the velocity.
As a result, x velocity sign is switched each iteration from positive to negative and viceversa, and you see the shacking effect.
If I'm right, this edit is the simplest way that come to my mind to solve the issue:
if isCollision(lbump, ball):
ballspeedx = abs(ballspeedx)
if isCollision(rbump, ball):
ballspeedx = -1 * abs(ballspeedx)
Of course more elaborate solutions can be implemented.
I'm making a pygame game designed so that bullets will shoot in the direction of the mouse. I'm using
a Class to define the bullets in a list like this:
class Bullet:
def __init__(self,pos,speed,size):
self.pos = pos
self.speed = speed
self.size = size
def move(self):
self.pos[0] = int(self.pos[0] + self.speed[0])
self.pos[1] = int(self.pos[1] + self.speed[1])
I'm using this trigonometry function to get the vector of the angle in which I'm going to be shooting bullets.
def getUnitVector(x1, y1, x2, y2):
delx = x2 - x1
dely = y2 - y1
m = math.sqrt(delx * delx + dely * dely)
unit = (delx / m, dely / m)
return unit
level = [
I'm not using angles because I have to work around a pygame rounding error.
these are the variables I'm plugging into the function.
mousex, mousey = pygame.mouse.get_pos()
startx = 50
starty = 400
aim = getUnitVector(startx, starty, mousex, mouse
This how i'm handling the aim and making the bullets shoot from the start x,y
if pygame.mouse.get_pressed()[0]:
if reload>10:
bx = BULLETSPEED * aim[0]
by = BULLETSPEED * aim[1]
bullet = Bullet([startx,starty], [bx,by],10)
bullets.append(bullet)
reload=0
reload = reload + 1
I just want to let you know. I'm working on a school assignment and I will be learning more in depth about vectors and trig next unit so I don't really want to spend too much time learning this stuff right now :L . Also if you know any active python forums that might be more helpful in answer this question please comment. I cant find any.
Thank you for your time.
I might just build a work-around by only allowing it to shoot if the mouse is within 20 pixels or something so the error is minimized.
The bullet class should have a rectangle attribute so that the bullet's direction of travel runs through the bullet's center.
Doing this would first require reading the Pygame documentation here: Pygame.Rect Docs
Centering the bullet rectangle on the direction of travel could be implemented in a movement method as such:
def move(self):
self.dir = self.get_direction(self.target) # get direction
if self.dir: # if there is a direction to move
self.trueX += (self.dir[0] * self.speed) # calculate speed from direction to move and speed constant
self.trueY += (self.dir[1] * self.speed)
self.rect.center = (round(self.trueX),round(self.trueY)) # apply values to bullet.rect.center
More information can be found in this helpful sprite movement example:Pygame Sprite Movement
I'm making a top down car racing game and I want to make the car rotate when you press the left and right keys (I’ve already done that part), the sprite's rotation is stored in a variable as degrees. I'd like to be able to make it move according to acceleration in the direction it is facing. I can figure out the acceleration part myself, it's just figuring out what pixel exactly is in that direction. Can anyone give me some simple code to help with this?
Here are the contents of the class that are relevant:
def __init__(self, groups):
super(Car, self).__init__(groups)
self.originalImage = pygame.image.load(os.path.join("Data", "Images", "Car.png")) #TODO Make dynamic
self.originalImage.set_colorkey((0,255,0))
self.image = self.originalImage.copy() # The variable that is changed whenever the car is rotated.
self.originalRect = self.originalImage.get_rect() # This rect is ONLY for width and height, the x and y NEVER change from 0!
self.rect = self.originalRect.copy() # This is the rect used to represent the actual rect of the image, it is used for the x and y of the image that is blitted.
self.velocity = 0 # Current velocity in pixels per second
self.acceleration = 1 # Pixels per second (Also applies as so called deceleration AKA friction)
self.topSpeed = 30 # Max speed in pixels per second
self.rotation = 0 # In degrees
self.turnRate = 5 # In degrees per second
self.moving = 0 # If 1: moving forward, if 0: stopping, if -1: moving backward
self.centerRect = None
def update(self, lastFrame):
if self.rotation >= 360: self.rotation = 0
elif self.rotation < 0: self.rotation += 360
if self.rotation > 0:
self.image = pygame.transform.rotate(self.originalImage.copy(), self.rotation)
self.rect.size = self.image.get_rect().size
self.center() # Attempt to center on the last used rect
if self.moving == 1:
self.velocity += self.acceleration #TODO make time based
if self.velocity > self.topSpeed: self.velocity = self.topSpeed # Cap the velocity
Trigonometry: The formula to get your coordinate is:
# cos and sin require radians
x = cos(radians) * offset
y = sin(radians) * offset
You use velocity for offset. (This means a negative velocity will drive backwards).
so:
def rad_to_offset(radians, offset): # insert better func name.
x = cos(radians) * offset
y = sin(radians) * offset
return [x, y]
loop_update is something like:
# vel += accel
# pos += rad_to_offset( self.rotation, vel )
math.cos, math.sin: uses radians, so
storing rotations as radians is simpler. If you want to define speed / etc as degrees, you still can.
# store radians, but define as degrees
car.rotation_accel = radians(45)
car.rotation_max_accel = radians(90)
I can't really do better than pointing you to this tutorial (*). In particular, the first part explains how to do rotation and make the sprites move in certain directions.
(*) Shameless plug :-) but very relevant to the question.