Python Pygame forces - python

I've been wanting to create a physics engine for my game, and decided I should first cover forces. I'm using pygame on python to accomplish this.
For example, I want to be able to program a force of a certain magnitude, that acts on an object. I haven't been able to think of any way to do this. After I get the basic strategy down, it should be easy to subtract magnitudes of forces and add all kinds of different properties. Here is my source code now:
import pygame
pygame.init()
display_width = 800
display_height = 600
gameDisplay = pygame.display.set_mode((display_width, display_height))
pygame.display.set_caption('Physics Engine')
clock = pygame.time.Clock()
x = display_width / 2
y = display_height / 2
gameExit = False
def force():
# force function
# Code here
y_change = 0
x_change = 0
while not gameExit:
for event in pygame.event.get():
if event.type == pygame.QUIT:
quit()
elif event.type == pygame.KEYDOWN: # check for key presses
if event.key == pygame.K_LEFT: # left arrow turns left
x_change = -10
y_change = 0
elif event.key == pygame.K_RIGHT: # right arrow turns right
x_change = 10
y_change = 0
y += y_change
x += x_change
print(x, y)
gameDisplay.fill((255, 255, 255))
pygame.draw.rect(gameDisplay, (255, 0, 0), [x, y, 30, 30])
pygame.display.update()
clock.tick(25)
pygame.quit()
quit()
As of now, it simply is a red square moving left and right. I want it to be that if I hold down the arrow key it moves, and when I release it stops. I've tried using a boolean in the event, but it doesn't seem to want to work.
I would like to implement a simple gravity factor.
Thanks so much for reading!

There are a few points I can see. Firstly, if you want an effect to occur while you hold a key down then you should query if that key is down, rather than just pressed. Ignore KEYDOWN events and use http://www.pygame.org/docs/ref/key.html#pygame.key.get_pressed
instead. Secondly, you should define or use library classes for positions, velocities, etc... which will give you a concrete set of operations.
From there the basic idea is to query the keys, set this update's acceleration from that and gravity, set velocity by updating old velocity with acceleration, set position by updating old position with velocity, and repeat forever.

When a force is applied, it adds an acceleration to the object in terms of the mass in the direction of the force.
First of all, I would suggest creating variables for your object's mass, x and y velocity and its x and y resultant force components. When a force is added to the object, it would be easier to add it in terms of its x and y components ( if you wanted the force to be defined with an angle and magnitude you could use opposite = sin(angle) * hypotenuse and adjacent = cos(angle) * hypotenuse to split the force into components. Careful though -- the math module uses radians, and pygame uses degrees ) .
To apply the force on the screen, you can use force = mass * acceleration to find the x and y accelerations on the object, and apply those changes to the x and y velocities of the object (that's your x_change and y_change). Then apply the x and y velocities to the position of the object.
If you want it to work in terms of meters and seconds, instead of pixels and frames, you'll need to work out conversions for that as well: just use your clock.tick value to work out frames per second, and you can make up a value for pixels per meter.
As for gravity, the force applied = mass * gravitational field strength (the gravitational field strength on earth is 9.81 Newtons / kilogram ). Using this formula, you can add gravity to your object like any other force. The acceleration due to gravity is always the same as the gravitational field strength though, which might be useful if no other vertical forces are being applied.

Related

Generate a connected line with different amplitude

I'm trying to make a game like Line, but with a horizontal and not vertical wave. The problem is making that the wave continues even after changing its amplitude (I will change the frequency later). So far I have reached this part of wave:
import pygame
import pygame.gfxdraw
import math
import time
DISPLAY_W, DISPLAY_H = 400, 800
clock = pygame.time.Clock()
pygame.init()
SCREEN = pygame.Surface((DISPLAY_W, DISPLAY_H))
GAME_DISPLAY = pygame.display.set_mode((DISPLAY_W, DISPLAY_H))
class Line():
def __init__(self):
self.pointsList = [0]*800
self.listIndex = 0
def game(self):
while True:
clock.tick(60)
SCREEN.fill((0, 0, 0))
self.listIndex += +1
self.generateWave()
self.drawWave()
for event in pygame.event.get():
if (event.type == pygame.QUIT):
quit()
pygame.display.update()
GAME_DISPLAY.blit(SCREEN, (0, 0))
def drawWave(self):
for Y_CORD in range(len(self.pointsList)):
pygame.gfxdraw.pixel(
GAME_DISPLAY, self.pointsList[Y_CORD]-55, DISPLAY_H-Y_CORD, (255, 255, 255))
pygame.gfxdraw.pixel(
GAME_DISPLAY, self.pointsList[Y_CORD]-350, DISPLAY_H-Y_CORD, (255, 255, 255))
def generateWave(self):
waveAmplitude = 50
waveFrequency = 1
XCord = int((DISPLAY_H/2) + waveAmplitude*math.sin(
waveFrequency * ((float(0)/-DISPLAY_W)*(2*math.pi) + (time.time()))))
if self.pointsList[-1] != 0:
self.pointsList.pop(0)
self.pointsList.append(XCord)
else:
self.pointsList[self.listIndex] = XCord
if __name__ == "__main__":
game = Line()
game.game()
I thought about having another function to change the amplitude, but then there would be a gap:
Obligatory reference: https://en.wikipedia.org/wiki/Spline_(mathematics)
Splines have many nice properties, on purpose,
such as matching the slope of inbound and outbound
curve segments. But you're not using splines
in the current code, so let's switch gears.
For current y
you can phrase this in terms of hidden_x
and display_x coordinates.
The trouble with the hidden X is, as you mentioned,
it sadly is discontinuous.
Which would make for an ugly display.
So we won't do that.
But it's a perfectly good ideal to shoot for.
So let's run with that notion.
Initialize display_x to equal hidden_x.
Now as Y advances, the hidden value can jump
around disconcertingly, even discontinuously.
Define some parameter alpha on the unit interval 0 .. 1.
Compute
delta = hidden_x - display_x
For each new Y, advance display_x
in the right direction
by alpha * delta.
Thus, no matter how the hidden X jumps around,
the display X will always smoothly be chasing it.
display_x += alpha * delta
Imagine that alpha was set to 1.0.
Then we're essentially assigning the hidden value
to the display value, right?
For positive values less than 1,
the display value will head in the right direction,
and soon will almost catch up to where
the hidden variable is.
Choosing an appropriate alpha value is up to you.
Largely it's an aesthetic decision,
given that OP did not disclose how errors affect
a cost function.
Play with it, have fun!
For more details on this technique,
look into PID controllers.
Feel free to ignore the integration term.
What we've been looking at are
the Position and Derivative terms.
The PID literature might give you
ideas on how to formalize "goodness of fit"
in your use case.
Non-linear transforms might be applicable,
as well.
assert delta >= 0
display_x += max(max_delta, alpha * delta)
Sometimes we describe this as clipping
the derivative to a maximum slew rate.
(If you choose to adopt this approach,
be sure to use something like sgn()
so that both positive and negative deltas
are handled appropriately.)
Some folks might even prefer to use a quadratic
relation with delta,
in order to catch up to "big" swings more quickly.

How to create Signed Distance function for geometric shapes other than circle? [duplicate]

I'm making a 3D maze Game in pyGame and made the environment in an OBJ file and used the OBJFileLoader found on the pyGame website to load it into my game. But when I implemented the movement controls the camera passes through the model. How do I detect collisions between my camera and the model so that I can fix that?
I thought about using rectangular collision detections but all the implementations I could find were in 2D and had a hard time trying to do it in 3D also using obj files didn't make it any easier. I also read somewhere that I can use raytracing but I couldn't find anything related to using raycasting in pyGame.
Below is the code I have in the main loop for controlling the in-game movement
while 1:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYUP and event.key == K_ESCAPE:
sys.exit()
time_passed = clock.tick()
time_passed_seconds = time_passed / 1000.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
pressed = pygame.key.get_pressed()
rotation_direction.set(0.0, 0.0, 0.0)
movement_direction.set(0.0, 0.0, 0.0)
if pressed[K_a]:
rotation_direction.y = +1.0
elif pressed[K_d]:
rotation_direction.y = -1.0
if pressed[K_w]:
movement_direction.z = -1.0
elif pressed[K_s]:
movement_direction.z = +1.0
# Calculate rotation matrix and multiply by camera matrix
rotation = rotation_direction * rotation_speed * time_passed_seconds
rotation_matrix = Matrix44.xyz_rotation(*rotation)
camera_matrix *= rotation_matrix
# Calcluate movment and add it to camera matrix translate
heading = Vector3(camera_matrix.forward)
movement = heading * movement_direction.z * movement_speed
camera_matrix.translate += movement * time_passed_seconds
# Upload the inverse camera matrix to OpenGL
glLoadMatrixd(camera_matrix.get_inverse().to_opengl())
glCallList(obj.gl_list)
pygame.display.flip()
I'm very confused as to how I can actually block the user from moving through the walls and I need to do that for the maze game I'm making to actually work.
Generally speaking using a 3D geometry mesh as your collision detection data isn't a great idea. Usually the way we do this is to have a much simpler collision geometry (ideally composed of simple primitives such as boxes, cylinders, spheres, planes - although tri meshes can be used in some cases). The player/camera is then usually represented as a capsule, and we test the capsule against the geometry. Implementing this form of collision detection in 3D is REALLY hard.
If it is possible to boil the maze game down to a 2D problem (e.g. a top down view), then define a set of 2D lines that define the outline of the maze, and treat the player/camera as a circle.
Even the 2D scenario though is difficult. The best system would need to solve a load of simultaneous equations in order to determine the closest intersection. As the size of the maze increases, the complexity of solving this becomes extremely unpleasant (i.e. you'd need to use some form of spatial partition, such as BSP tree, quad trees, etc)
It is a very hard problem, which is why most sane people would simply make use of a physics engine to perform these calculations for them. I'd probably recommend trying to get your head around a simple 2D physics engine first, e.g. Box2D. If you absolutely need 3D collision detection, then you probably want to be looking at PhysX, bullet, or Havok.

How to implement object collision on imported wavefront OBJ files in 3D

I'm making a 3D maze Game in pyGame and made the environment in an OBJ file and used the OBJFileLoader found on the pyGame website to load it into my game. But when I implemented the movement controls the camera passes through the model. How do I detect collisions between my camera and the model so that I can fix that?
I thought about using rectangular collision detections but all the implementations I could find were in 2D and had a hard time trying to do it in 3D also using obj files didn't make it any easier. I also read somewhere that I can use raytracing but I couldn't find anything related to using raycasting in pyGame.
Below is the code I have in the main loop for controlling the in-game movement
while 1:
for event in pygame.event.get():
if event.type == QUIT:
sys.exit()
if event.type == KEYUP and event.key == K_ESCAPE:
sys.exit()
time_passed = clock.tick()
time_passed_seconds = time_passed / 1000.
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
glLoadIdentity()
pressed = pygame.key.get_pressed()
rotation_direction.set(0.0, 0.0, 0.0)
movement_direction.set(0.0, 0.0, 0.0)
if pressed[K_a]:
rotation_direction.y = +1.0
elif pressed[K_d]:
rotation_direction.y = -1.0
if pressed[K_w]:
movement_direction.z = -1.0
elif pressed[K_s]:
movement_direction.z = +1.0
# Calculate rotation matrix and multiply by camera matrix
rotation = rotation_direction * rotation_speed * time_passed_seconds
rotation_matrix = Matrix44.xyz_rotation(*rotation)
camera_matrix *= rotation_matrix
# Calcluate movment and add it to camera matrix translate
heading = Vector3(camera_matrix.forward)
movement = heading * movement_direction.z * movement_speed
camera_matrix.translate += movement * time_passed_seconds
# Upload the inverse camera matrix to OpenGL
glLoadMatrixd(camera_matrix.get_inverse().to_opengl())
glCallList(obj.gl_list)
pygame.display.flip()
I'm very confused as to how I can actually block the user from moving through the walls and I need to do that for the maze game I'm making to actually work.
Generally speaking using a 3D geometry mesh as your collision detection data isn't a great idea. Usually the way we do this is to have a much simpler collision geometry (ideally composed of simple primitives such as boxes, cylinders, spheres, planes - although tri meshes can be used in some cases). The player/camera is then usually represented as a capsule, and we test the capsule against the geometry. Implementing this form of collision detection in 3D is REALLY hard.
If it is possible to boil the maze game down to a 2D problem (e.g. a top down view), then define a set of 2D lines that define the outline of the maze, and treat the player/camera as a circle.
Even the 2D scenario though is difficult. The best system would need to solve a load of simultaneous equations in order to determine the closest intersection. As the size of the maze increases, the complexity of solving this becomes extremely unpleasant (i.e. you'd need to use some form of spatial partition, such as BSP tree, quad trees, etc)
It is a very hard problem, which is why most sane people would simply make use of a physics engine to perform these calculations for them. I'd probably recommend trying to get your head around a simple 2D physics engine first, e.g. Box2D. If you absolutely need 3D collision detection, then you probably want to be looking at PhysX, bullet, or Havok.

Gravity Problems

I've been trying to make a code with pygame to simulate simple gravity. At the moment, there is only one object (HOM) which is orbiting the sun. However, for reasons unknown to me, whenever I run the code, HOM travels round the sun in an orbit at the start, but then accelerates away from the sun when it reaches ~135 degrees from vertical.
Does anyone know why this is happening and how I can fix it? I have been printing some variables to try and source the problem, but have had no luck so far.
Code:
import pygame,sys,time
from math import *
screen=pygame.display.set_mode((800,600))
G = 5
class Object: #Just an object, like a moon or planet
def __init__(self,mass,init_cds,init_vel,orbit_obj='Sun'):
self.mass = mass
self.cds = init_cds
self.velocity = init_vel
self.accel = [0,0]
self.angle = 0
self.orb_obj = orbit_obj
def display(self):
int_cds = (round(self.cds[0]),round(self.cds[1]))#Stores its co-ordinates as floats, has to convert to integers for draw function
pygame.draw.circle(screen,(255,0,0),int_cds,10)
def calc_gravity(self):
if self.orb_obj == 'Sun':
c_x,c_y = 400,300
c_mass = 10000
else:
c_x,c_y = self.orb_obj.cds
c_mass = self.orb_obj.mass
d_x = self.cds[0]-c_x
d_y = self.cds[1]-c_y
dist = sqrt(d_x**2+d_y**2) #Find direct distance
angle = atan(d_x/d_y) #Find angle
print(d_x,d_y)
print(dist,degrees(angle))
if dist == 0:
acc = 0
else:
acc = G*c_mass/(dist**2) #F=G(Mm)/r^2, a=F/m -> a=GM/r^2
print(acc)
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
self.accel = [acc_x,acc_y]
print(self.accel)
self.velocity = [self.velocity[0]+self.accel[0],self.velocity[1]+self.accel[1]] #Add acceleration to velocity
print(self.velocity)
self.cds = (self.cds[0]+self.velocity[0],self.cds[1]+self.velocity[1]) #Change co-ordinates by velocity
print(self.cds)
print('-------------------') #For seperating each run of the function when printing variables
HOM = Object(1000000,(400,100),[10,0]) #The problem planet
clock = pygame.time.Clock()
while True:
for event in pygame.event.get():
if event.type == pygame.QUIT:
pygame.quit()
sys.exit()
screen.fill((0,0,0))
pygame.draw.circle(screen,(255,255,0),(400,300),25)
HOM.display()
HOM.calc_gravity()
clock.tick(30)
pygame.display.flip()
Your main issue has to do with this line:
angle = atan(d_x/d_y) #Find angle
The atan function is very limited in its ability to compute angles because it can't tell the signs of the coordinates you combined in your division. For instance, it will give the same result for atan(1/1) and atan(-1/-1), since both divisions compute the same slope (1).
Instead you should use atan2, and pass the coordinates separately. Since this will let the code see both coordinates, it can pick an angle in the right quadrant of the circle every time.
But there's an even better fix. Instead of computing an angle and then immediately converting it back to a unit vector (by calling sin and cos on it), why not compute the unit vector directly? You already have the original vector's length! Instead of:
acc_x = acc*sin(angle) #Convert acceleration from magnitude+angle -> x and y components
acc_y = acc*cos(angle)
Use:
acc_x = acc * d_x / distance
acc_y = acc * d_y / distance
The d_x / distance and d_y / distance values are the same as the sin and cos values you were getting before (for the angles when they were working correctly), but there's no need for the trigonometry. You can get rid of the line I quoted up top completely!
Note that you might need to reverse the way you're computing d_x and d_y, so that you get a vector that points from the orbiting object towards the object it's orbiting around (instead of pointing the other way, from the center of the orbit towards the orbiting object). I'm not sure if I'm reading your code correctly, but it looks to me like you have it the other way around right now. That means that you were actually getting the wrong results from atan in the cases where your current code was working the way you expected, and the bad behavior (flying off into nowhere) is the code working "correctly" (from a mathematical point of view). Alternatively, you could compute acc to be negative, rather than positive.
As several commenters mentioned, you may have other issues related to your choice of integration algorithm, but those errors are not going to be as large as the main issue with the acceleration angle. They'll crop up as you run your simulation over longer time periods (and try to use larger time steps to make the simulation go faster). Your current algorithm is good enough for an orbit or two, but if you're simulating dozens or hundreds of orbits, you'll start seeing errors accumulate and so you should pick a better integrator.

Python 2.7 - Pygame - move_ip() too granular

I'm practicing making an android app in pygame, and decided to do your classic bubble shooter. I have run into a problem though where the projectile launched by the player isn't accurate enough.
def move (self, time_passed):
x = (math.sin(self.angle) * self.speed) * time_passed
y = (math.cos(self.angle) * self.speed) * time_passed
c = self.rect.center
self.rect.center = (c[0]+x, c[1]+y)
def update(self, screen, time_passed):
if self.launched:
if not self.mpos:
self.mpos = pygame.mouse.get_pos()
diffx, diffy = (self.mpos[0]-self.rect.center[0],
self.mpos[1]-self.rect.center[1])
self.angle = math.atan2(diffx, diffy)
self.move(time_passed)
The screen and time_passed arguments are passed from the main loop and are the display and returned value of the Clock class's tick(60)/1000.0, for the sake of clarity.
If I print the values of x and y from move() it's very accurate: x: 1.0017224084 y: -21.9771825359, or something similar depending on where the mouse is. However, it seems pygame or move_ip only work with integers. I can click between what would be 1.00000001 and 1.9999999 and the ball will shoot in the exact spot, being 1 (in reference to the x coordinate). This is bad for my game. Is there a solution to this so I can get my objects to move very precisely?
EDIT: I realized I pasted in the wrong code. self.rect.center = (c[0]+x, c[1]+y) is where self.rect.move_ip(x, y) should be. Both methods yield the same result, however.
Keep track of your own positions using precise float values, and then instead of incrementally moving your Rects from frame to frame, just place them at their proper (integer-ized) positions each frame. That way you don't suffer from the sum of each frame's rounding error, but instead only have a single rounding involved at any given time.

Categories

Resources