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.
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
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
I have a ball and once it hits the floor, I want it to bounce back up to say half the original bounce, until it can't bounce anymore. I added a pass where the ball just stops at the bottom. How do I make it bounce back up?
kickoff_location = [WIDTH/2 - ball_size[0] / 2, 210]
gravity = 2
ball_bounce = False
if not(ball_bounce):
if kickoff_location[1] < player_one_pos[1] + (player_width / 2) + ball_size[0]:
kickoff_location[1] += gravity
elif kickoff_location[1] == player_one_pos[1] + (player_width / 2) + ball_size[0]:
kickoff_location[1] -= gravity / 2
pass
You need a speed vector for your ball, ie. vel = [0, 0] # [dX, dY]
You need simple gravity acceleration constant, ie. grav = 0.1
You need enviroment resistance constant, ie. resist = 0.8
You need mentioned elasticity factor, ie. elast = 0.5
Now, in every update cycle you:
update Y part of velocity by gravity: vel[1] += grav
update both velocity parts by resistance: vel[0] *= resist ; vel[1] *= resist
check for collisions, vertical and horizontal separately. If collision is vertical (floor/ceiling) reverse Y speed and modify it by elasticity: vel[1] = -vel[1] * elast. The same for horizontal collisions, but for X part of velocity.
update ball position by speed vector: ball_loc[0] += vel[0] ; ball_loc[1] += vel[1]
And tinker with constants to get realistic ball movement...
My problem: My goal is to create a variable that is a line of code that later on I can just call the variable other than just using that line of code (making the code neater).
Comment: ball_x_pos and ball_y_pos change in the code
My current code:
CASE_1 = ball_y_pos + RADIUS >= WINDOW_HEIGHT # Hitting bottom floor(need to increase Y)
CASE_2 = ball_y_pos - RADIUS <= 0 # Hitting top floor(need to decrease Y)
CASE_3 = ball_x_pos + RADIUS >= WINDOW_WIDTH # Hitting right side(need to decrease X)
CASE_4 = ball_x_pos - RADIUS <= 0 # Hitting left side(need to decrease X)
if CASE_1: # Ball it hitting the bottom floor
Y_CHANGER = 1
if CASE_2: # Ball is hitting the top floor
Y_CHANGER = -1
if CASE_3: # Ball is hitting the right side
X_CHANGER = -1
if CASE_4:
X_CHANGER = 1
What I think is happening: I'm pretty sure that right now the code is defining the values of the cases as False on assignment. I was wondering if there is any way I can still do this.
Thanks in advance!
I'm not sure if I misunderstood the question or not, but it seems you're looking to create a function, whose output changes based on inputs. Maybe something like this would help?
Based on your comment below, you're looking to inline your functions to emulate macro-esque behavior. You can't do this, but some compilers like PyPy will automatically optimize your code, so I wouldn't worry too much about it. Below are examples of functions that oculd do the trick for you:
def CASE_1():
return ball_y_pos + RADIUS >= WINDOW_HEIGHT # Hitting bottom floor(need to increase Y)
def CASE_2():
return ball_y_pos - RADIUS <= 0 # Hitting top floor(need to decrease Y)
def CASE_3():
return ball_x_pos + RADIUS >= WINDOW_WIDTH # Hitting right side(need to decrease X)
def CASE_4():
return ball_x_pos - RADIUS <= 0 # Hitting left side(need to decrease X)
if CASE_1(): # Ball it hitting the bottom floor
Y_CHANGER = 1
if CASE_2(): # Ball is hitting the top floor
Y_CHANGER = -1
if CASE_3(): # Ball is hitting the right side
X_CHANGER = -1
if CASE_4():
X_CHANGER = 1
This defines four functions, each of which , when called, evaluates its statement and returns True or False based on its result. Note that this implies global variables (which are poor practice) - since you also mentioned ball_x_pos and ball_y_pos change in the code, you likely want to pass the variables in. Something like this would be better practice:
def CASE_1(y_pos):
return y_pos + RADIUS >= WINDOW_HEIGHT # Hitting bottom floor(need to increase Y)
def CASE_2(y_pos):
return y_pos - RADIUS <= 0 # Hitting top floor(need to decrease Y)
def CASE_3(x_pos):
return x_pos + RADIUS >= WINDOW_WIDTH # Hitting right side(need to decrease X)
def CASE_4(x_pos):
return x_pos - RADIUS <= 0 # Hitting left side(need to decrease X)
if CASE_1(ball_y_pos): # Ball it hitting the bottom floor
Y_CHANGER = 1
if CASE_2(ball_y_pos): # Ball is hitting the top floor
Y_CHANGER = -1
if CASE_3(ball_x_pos): # Ball is hitting the right side
X_CHANGER = -1
if CASE_4(ball_x_pos):
X_CHANGER = 1
. . . a variable that is a line of code that later on I can just call the variable other than just using that line of code . . .
This is essentially describing a function.
To write one, you could have, for example:
def is_hitting_bottom():
return ball_y_pos + RADIUS >= WINDOW_HEIGHT
if is_hitting_bottom():
Y_CHANGER = 1
The benefits of using functions here are minimal though. Really, this is just giving the code a nicer name.
You probably want code like the following, because if you have a ball with an x and a y position, you are likely to have more of them later on:
def get_changes(px, py):
dx, dy = 0, 0
if py + RADIUS >= WINDOW_HEIGHT:
dy = 1
if py - RADIUS <= 0:
dy = -1
if px + RADIUS >= WINDOW_WIDTH:
dx = 1
if px - RADIUS <= 0:
dx = -1
return dx, dy
def update_position(px, py):
dx, dy = get_changes(px, py)
return px+dx, py+dy
ball_x_pos, ball_y_pos = update_position(ball_x_pos, ball_y_pos)
(btw I think you have the changes the wrong way round, eg if the Y pos is so large that it is in danger of going over the window height, which is actually the floor, then I think that Y should be reduced.)
I wanted to increase the speed of the ball after each "goal" scored, you can see under #1 and #2 are points where I have tried and failed to do this, speedx and speedy was orignally just
Firstly I tried to increase by adding on to the already defined if loop in the tutorial, the #1 is what I added on the end
if ball.xcor() > 390:
ball.goto(0, 0)
ball.dx *= -1
score_a += 1
pen.clear()
pen.write("Big Man : {} Pussio: {}".format(score_a, score_b), align="center", font=("Courier", 24, "normal"))
#1 speedy += 0.1
#1 speedx += 0.1
#1 ball.dx = speedx
#1 ball.dy = speedy
I then tried to make my own for loop, however the problem I have is when I increase the speed, it will always make the ball go in a (x,y) (+,+) direction after a goal is scored, i think because I am adding +1 to the ball.dx it sets the ball in a (+, +) direction, when as you can see from my code, after a goal is scored i want to reverse the starting direction, using:
if ball.xcor() > 390:
ball.goto(0, 0)
ball.dx *= -1 - this
score_a += 1
so basically i want to increase the speed of the ball in increments everytime a goal is scored and also I want to keep the ball going in different directions after scoring,
Here is the for loop i tried and also got no luck with
#2if ball.xcor() < -390 or ball.xcor() > 390:
#2 ball.dx = speedx
#2 ball.dy = speedy
#2 speedy *= -1
#2 speedx *= -1
#2 speedy += 0.1
#2 speedx += 0.1
Maybe this cant be done the speed increment as turtle is too basic I'm not sure?
Below is full code:
To accelerate the ball, the simplest way is probably to multiply dx and dy by a factor > 1 (to decrease it, use a 0 < factor < 1) :
In this example, the velocity is increased by 10%.
acc_factor = 1.1
...
dx *= acc_factor
dy *= acc_factor
If you want a fixed increment, you can do like this:
acc_factor = 1.1
...
dx_increment = abs(dx)/dx * acc_factor
dx += dx_increment
dy_increment = abs(dy)/dy * acc_factor
dy += dy_increment
You should introduce time to make this easier as the game gets more complex.
self.vx should be equal to self.dx * unit_of_time. You can then increment self.vx over time in order to introduce acceleration. Note that if accelleration is constantly changing, you might also want to add self.ax to control accelleration more easily.