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.)
Related
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
This might be a more mathematical question, but I'm trying to get my head around how I can program an unbeatable AI for a ping pong game. From what I have read so far, it would be to simulate the trajectory of a ball when it is moving in the direction towards the AI Paddle.
In this game I have a ball and I can read its x and y position on the board, and then read it again in the next iteration which will allow me to calculate the velocity in the x and y direction.
But I'm not sure how to program how and where the ball will reach the AI paddle's goal position, and consider how many times the ball will bounce off the walls will requires me to use some geometry. But I can't get my head around it and how I will be programming it.
So far what I have thought of is the variables I've been given: the size of the table in x and y direction, the position of the ball "currently" and before in order to get its velocity in x and y direction. My first assumption is to find out a way to calculate whether the ball will hit the walls or the AI goal side instead?
There is a more direct way of doing this instead of repeated "raycasting":
def predict(x, y, vx, vy, h, b):
"""
:param x: ball x position
:param y: ball y position
:param vx: ball x velocity
:param vy: ball y velocity
:param h: the field height
:param b: the y position the prediction is for
:return: ball x position at y = b
"""
m = vy / vx # slope
c = -x * m + y # y-intercept
val = (m * b + c) % (2 * h)
return min(val, 2 * h - val)
Now, step by step
m = vy / vx # slope
c = -x * m + y # y-intercept
val = (m * b + c)
A simple linear function showing the ball's current path.
This works, but only if the ball never hits a side wall.
A Model
Imagine there were fields with the same height on both sides of the original one, stretching into infinity.
Now 'the number of bounces' has become 'the number of cells the ball travels'.
Additionally, if the number of bounces is even, the distance from the lower border of the cell it hits to the point of impact is the same as the height the actual ball would hit at in the real cell.
Therefore
(m * b + c) % (2 * h)
To cover odd bounces as well, you need to mirror the graph around h.
Here is a graphic explanation:
And since the irrelevant graph is the one with values above h, you take the minimum.
Possible Problems
In some languages, the % is a remainder operator, though not python.
If the predictions are negative in some cases add this.
val = ((m * b + c) % (2 * h) + 2 * h) % (2 * h)
This function depends on 'accurate' collision.
So if the bounces are handled in a way similar to this,
if y not in range(0, y_max):
vy *= -1
the predictions will be slightly off.
If you can change the core game, use
if y < 0:
y *= -1
vy *= -1
elif y > y_max:
y = 2 * y_max - y
vy *= -1
A divide by zero exception will be thrown if vx is 0, but since the ball will never hit a wall in this case, this should be handled by the ball movement logic.
Snippets are cool, but functions are better. I can't prove this works, but it seems to.
float pong_get_ball_endpoint(float xpos, float ypos, float xspeed, float yspeed)
{
// In the following, the fabs() mirrors it over the bottom wall. The fmod wraps it when it exceeds twice
// the top wall. If the ball ends up in the top half of the double height section, we reflect it back
auto deltaX = (xspeed > 0) ? (BAT2_X - xpos) : -(xpos - BAT1_X); // How far from ball to opponent bat
auto slope = yspeed / xspeed; // Rise over run, ie: deltaY per X
float newY = fmod(fabs(ypos + deltaX * slope), (2 * MATRIX_HEIGHT)); // New Y, but wrappped every 2*height
if (newY > MATRIX_HEIGHT) // If in top half, reflect to bottom
newY = 2 * MATRIX_HEIGHT - newY;
return newY;
}
I am creating a game where I want an enemy to path track onto the player - who can move in any direction on the 2D plane. At first, I tried...
self.bat_x += (player_rect.centerx - self.rect.centerx) / 60
self.bat_y += (player_rect.centery - self.rect.centery) / 60
Here the path-tracking works fine. I divide each value by 60 so that the enemy doesn't just appear and stick on to my player / to slow the movement of the enemy down. However, the further away the enemy is, the faster it is. The closer the bat gets, the slower the bat gets. This is because, using the x-axis for example, when the distance between the player and the enemy is smaller, player_rect.centerx - self.rect.centerxis smaller so less gets added to self.bat_x. Is there a way so that the path-finding still works but the speed is constant? Or does anyone know a different path-finding method and how to implement it?
Pythagoras is your friend
x = player_rect.centerx - self.rect.centerx
y = player_rect.centery - self.rect.centery
norm = (x**2 + y**2)**0.5
const = 1/60
self.bat_x += const * x / norm
self.bat_y += const * y / norm
One way would be using the locations of the player and enemy to find the slope/angle of the line connecting them.
Considering that enemy is at (x1, y1) and player is at (x2, y2).
Then
angle = arctan((y2 - y1)/x2-x1))
Note that x2 - x1 could be zero, so take care of that case.
After finding the line, you could use polar coordinates to find the next position
For eg
x += speed * sin(angle)
Y += speed * cos(angle)
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 son asked me if I could write a small program to have a ball bounce around the screen then have me explain it.
Spotting a neat father-son opportunity I said "Yes!, no problem". So I dug out my python skills and wrote this..
#!/usr/bin/python
#
# We have to tell python what stuff we're
# going to use. We do this by asking to import
# the code we'll be making use of
#
import curses
import random
import time
#
# We'll be using a screen handling
# package called "curses" so we need
# to initialise the screen
stdscr = curses.initscr()
# We need to make sure that
# keys we type will not show up
# on the screen spoiling the
# stuff we're drawing
curses.noecho()
# We need to tell curses that we don't
# want to wait when we ask for keys from
# the user, if there's non there then
# we want curses to carry on
stdscr.nodelay( True )
# This ones a bit tricky to explain.
# Basically it puts the keyboard into
# a mode which allows curses to do
# things with it...
curses.cbreak()
# Ok, now we can do the fun stuff
# First thing we need to do is to
# find out how big our screen is
max_y, max_x = stdscr.getmaxyx()
stdscr.box()
min_x = 1
min_y = 1
max_y = max_y - 2
max_x = max_x - 2
stdscr.addstr( min_y, min_x, "1" )
stdscr.addstr( min_y, max_x, "2" )
stdscr.addstr( max_y, min_x, "3" )
stdscr.addstr( max_y, max_x, "4" )
dir_x = 1
dir_y = 1
# Then we can pick a random point on the
# screen in both the y (up-down) and x
# (left-right) directions
y = random.randint( 0, max_y )
x = random.randint( 0, max_x )
# Ok, this basically says, while trying to
# get a key from the user keeps coming back
# with an error, meaning there aren't any keys
# to return, do the code inside the loop
while( stdscr.getch() == curses.ERR ):
# Ok, at the location we got, put a "0"
stdscr.addstr( y, x, "O" )
# stdscr.addstr( str( (y, x) ) )
# this just moves the cursor to a new location so we can see the "O" better
stdscr.move( 0, 0 )
# have the screen update
stdscr.refresh()
# now choose a new location
x = x + dir_x
y = y + dir_y
# have we gone too far
if x > max_x or x < min_x:
# change direction
dir_x = -dir_x
x = x + dir_x
if y > max_y or y < min_y:
# change direction
dir_y = -dir_y
y = y + dir_y
# wait a bit so we can see the screen we've drawn.
time.sleep( 0.05 )
# Ok, we're finished. Tidy up
curses.nocbreak()
stdscr.keypad( False )
curses.echo()
curses.endwin()
I ran the program and was quite pleased. However, when the ball hits the edge, it seems to "slide" and not bounce cleanly.
It's quite late in the evening so I couldn't get my head around it and I thought I'd throw it out there and see if anyone could explain why.
I'm not after fixing the code, I'm pretty sure that with <= or >= the tests would work. I just can't understand what it does what it does...
Thx
Mark.
You need to add twice dir_* in the "change direction" code.
You are figuring out a new x and y coordinate before you are testing. So, say the ball is at 20, 1 and is drawn, and assume the dir is North East with slope 1. The next position will be calculated at 21, 0. Here you see the y is out of range now. So you set it back to 1 when what you really want is 2, so you get a slide across the edge. Usually, what I do is test for the next conditions first, then add the new offsets. So...
if x + dir_x > max_x or x - dir_x < min_x:
Getting this kind of simulation to work properly at the edges is tricky. I ran it here and it looks nice to me (OS X). When it hits the edge you'll get two O's in a row at the edge but then it comes back. At a guess, this has to do with how far your virtual x,y point has to penetrate before reversal happens, which depends on how fast it's moving.
Took me a while to work out what you're meaning. You mean the double 'OO' on the wall when it changes direction? Seems to me the conditional allows the ball to go over (not up to) the line and then pulls it back again. I changed this bit:
# have we gone too far
if x >= max_x or x <= min_x:
# change direction
dir_x = -dir_x
#x = x + dir_x
if y >= max_y or y <= min_y:
# change direction
dir_y = -dir_y
#y = y + dir_y
(Note I changed > to >= and same with < and I also commented out the second increment - if you catch it on the line you don't need it.)
Nice little example by the way.
Not really too familiar with python, but is
dir_x = -dir_x
Acceptable? Maybe try
dir_x *= -1
or
dir_x = dir_x * -1
?