Amazing 2D Physics Engine Tutorial

and I’ll just throw in my notes for the sector where he starts with the 2D stuff because I had to do a lot of learning on the side to get what he is doing.

I’ll just mention that I didn’t just do all his assignments from beginning to where I am now. I don’t think that will be the best way to learn for a lot of people. I mean I did a lot of reading and figuring it all out. Then trying the code out and a bit of fiddling with it to understand it better. Also for each assignment I at least tried to see if I could piece together in my head where all the new stuff would fit in as he puts out of order code pieces for to make you think. Really though I am always focusing being able to apply this knowledge to my own games, which requires the 2D tutorials more than anything, the others are needed to ramp up and understand his thought process.

The next thing is that he starts doing server client stuff and I don’t want to get into all that right now. So that is where I have decided to just recreate his assignments but without the client server stuff. So I had to figureout what was important and what wasn’t. Next is the actual phsyics stuff which has taken some reading online to figure out. So these notes are what I have so far for tutorial 10.

# So I realize that this tutorial is teaching a whole
# lot of physics. So I want to make basically a list
# or summary of the concepts that I would need to have
# on hand to be able to make one of these without the
# help of at least this tutorial, or at least to understand
# it to that extent.
# The hard stuff really comes from the collisions
# for each puck to puck collision:
# we create a tangent AXIS vector point and same for normal
# we get a point that is paralell to the normal
# by subtracking the two radiuses which yealds
# a third with a diff magnitude but same direction

# then to get the tangent to that, we shift the x and y of
# the normal point by 90 which uses cos and sin angle addition
# but really it’s just -y,x because that is for 90 degrees on
# the unit circle.

# we check for collisions
# if the distance between radiuses is less than the sum of two
# radius magnitudes(Like from center to edge of circle)
# then we know the circles overlap
# we take squared differences of the x values and squared
# differences of the y values, and add those, and then compare
# against a squared sum of the radius magnitudes, to avoid using
# square root whcih is slow

# That is the easy part….

# Then we need tangential and normal velocity for both objects
# so we use projection of a velocity vector(with x and y) onto
# the tan and norm axis vector points for each ship.

# then we subtract objectnormvel from hitobjectnormvel to get
# a relative velocity, I guess that means it will know which
# velocity was stronger for the x and y directions.
# it will be the resulting velocity for two velocities that collided?

# even if hitobject had vel of 8 and object had vel 7
# 8-7 = 1 small positive over all velocity.

# first we need speed for some reason
# speed is scalar and direction unaware
# velocity is a vector, x and y, so direction aware
# also displacement aware

# relative normal speed is the length of the vector of relative norm vel.

# sum of radiuses(magnitudes) – (sqrt(sum of squared diff of radiuses))
# so if sum of radiuses is 40 + 40 = 80
# and the difference between the positions of the two is only 2
# then the penetradion is 78 which iunno, I guess that might work
#although it seems like double?

#there is an condition that speed has to be faster that 0.000001

# then calc penetration time as penetration/speed
# ?? why not use velocity here? Maybe because with penetration
# it’s all about magnitude?

# he adds in a penetration time scalar which I think allows us to increase
# how much we back up penetration by.

# then back both the objects up to when they just hit, before penetration
# their new positions calculated by…

# object position – (object norm vel * penetration time * pentimescalar)
# hitobject position – (object norm vel * penetration time * pentimescalar)

# then calculate the new velocities for each object
# by sending them both to AandBnormal

# then calculate the new positions based on the new velocities and
# again the penetration time


# again we have the coefficient of resitution

#send object velocites again to AandBnormal to get normal velocities
# set normal vels to equal the calced normalaftervels

# do this for each object

Code Comment notes on what is a fixed ratio and what is not

self.movex = math.cos(self.angle) * self.amplitude
self.movey = math.sin(self.angle) * self.amplitude

#I couldn’t just add movex/y to the rect because that calculation
#means the rectx/y values are being calculted by the vector PLUS
#the rectx/y meaning the x and y sums probably wont be in line
#with the ratio the vectore intended.
#So I add the vector to the ships position and give rect THAT.
#the difference is that I use the ship’s position as a reference
#so every time I give rectx/y a new value, that value keeps the
#ratio, but just adds the ships starting x/y as a reference point.
#The original didn’t HAVE a refereence point, it just added values
#to rectx/y which means it will change ….

#ok it’s because we keep calculating the vector again because
#we it’s amplitude! If each time we change the vectors amplitude
#we add it to the rectx/y which changes the rectx/y, then we
#are dealing with a ship in a new position and trying to add
#the same vector(but at a new amplitude, point on the line) to
#that NEW starting position.

#let’s take rectx by it’s sef for example. If you change it’s position
#and then add a vectore of a different length to it, you are
#adding to a different rect/x position so it’s …

#I mean, the longer the amplitude, the more it seemed to be in
#line, but without any amplitude, you’re looking at#the cos and sin
#values, and adding those to rect x and y, and then adding them
#again etc.
#We need to go „5+1, 5+2, 5+3
#not „5+1, 6+2, 8+3
#because if cos was 2 but sin was 1
#then it wouldn’t be the right ratio to do things the second
#way for cos and sin, because while you are increasing both
#cos and sin values by the right ratio, by adding them to values
#that then increment at different rates, well then you have values
#of rect x and rect y that GROW at different rates! where as the cos
#and sin grow at the same rates.

#x 5+1 = 6 5+2 = 7 5+3 = 8
#y 5+2 = 7 5+4 = 9 5+6 = 11
#the rate for x is 1 6, 7, 8
#the rate for y is 2 7, 9, 11

#x 5+1 = 6 6+2 = 8 8+3 = 11
#y 5+2 = 7 7+4 = 11 11+6 = 17 y get’s larger FASTER than x
#rate for x is 1, but 6, 8, 11 is not 1 it’s closer to 2.something
#rate for y is 2, but 7, 11, 17 is not 2 it’s closer to 5
#it seems CLOSE to the 1/2 but it’s NOT because…
#11+4 = 15 15+5 = 20
#17+8 = 35 35+10 = 45

#the rates of growth for x here is 5 + 15
#and for y it’s 5+40 because we need to take into account
#what has been added as well for each.
#in the right exampe it will always be x = 5+something
#and y = 5+something*2
#in the wrong example you can see that by the 5th iteration
#15/40 != 1/2

So  this works:
self.rect.x = self.x1 + self.movex
self.rect.y = self.y1 + self.movey

#self.x1 and self.y1 act as references to „5“ in the examples above.

This does not

self.rect.x+= self.movex
self.rect.y+= self.movey

Because the ratio will not not remain fixed.

Pygame level creation: experimenting with 2d arrays text maps

The video just shows the results of my fidgeting lol. I have a level super class which feeds into a sector class with instantiates item, obstacle, and enemy class objects. Then the game loop instantiates the sector class and uses it’s draw method.

Also I have enemies moving around just to make sure I could do that from the 2d array maps initialization. I have these blue guys that close in on the enemy really fast which was my first attempt at what I show in other posts which is inspired by the game Axley fro SNES.

Python’s atan and atan2 functions

This is needed to get an angle based on the rise and run of a vector. So I need this in situations where I need to aim say a turret, and a player sprite and fire. I need to calculate the vector’s rise y2-y1 and run  x2-x1 and put those two parameters into the atan2 function.

There are some things I can’t quite visualize yet but I’ll get to that.

First, there are positive and negative angles. Positive angles are when you go counter clockwise around the unit circle, and negative angles are when you go clockwise. Also, the angle in radians if you go counter clockwise starting from zero, will be the same as that angle if it were positive and going clockwise. enter image description here

so – 45 degrees is positive 315 degrees.

This is important because in aiming something like a turret in the game, having negative angles means you can turn it both ways, instead of having to turn 315 counter clockwise, it can just go clockwise -45 degrees.

So atan2 allows for this by having the parameters for rise and run separate. atan, another math. function, only accepts the already calculated ratio, and so if rise and run were both negative, the result would be positive.

Also for atan, this means the angle then could never be in the 4rd quadrant, the part of the cartesian plane where both y and x are negative.

So the issue in pygame is we a y axis that starts from 0 and goes down from there. So if y1 was 5 and y2 was 10, y2 would be lower than y1 on the screen. I’m trying to get an idea of why the options for fixing this work the way they do.

The first way people fix this is  dy = y1-y2 and then set dy to negative before passing it to atan2. atan2(-dy, dx).

The other way that works for some reason is just flipping the two numbers, atleast when using atan anyways, people are doing atan(dx/dy) and say that works…

I’m going to take a break from it for now but that’s something I’d like to figure out.

Notes on my game programming workflow

1. I create the basic game loop
that displays a titled screen

2. I create the basic classes


3. I define the functions for each clas
trying to use the same name in multiple classes
if I can such as „update“ „animate“, etc.
(because I think that kind of thing comes in handy)
##object images###

4. I create a basic image for a temp class object that has images
using variations of this

width = 40
height = 60
self.image = pygame.Surface([width, height])

5. I set a basic rect for each temp class object
and assign a position for it.

5b. set the updating for each temp class object
so the image remains on each game loop ???

###image initializations###
6. level initialization

####Temp Level initializations
1. in init function init object lists for non player objects
3. create lists with for loops and rand locations
4. add all lists with „.draw“ in draw function
5. update lists „.update“ in update function*not important yet
6. in main before loop instatiate level

level = Level(player) #don’t think „player“ is needed yet

7. In game loop before flip „level.draw“
##(((movement first, then updating becomes needed)))####

7. player initialization

1. in main before loop instatiate player

player = Player()

###initialize temp player locations
player.rect.x = 340
player.rect.y = SCREEN_HEIGHT – player.rect.height

2. in game loop blit starting location

screen.blit(player.image, (340, 500))

8. create add the level shift_world function to
increase the rect.y of all the object so that
they fall off screen away from the player’s ship.

when I do movement I will be able to have acceleration
and reverse affect these

and enemies should be able to ajust for these but not
affect them

9. create „levels“ which will be just sectors and each will initialize
enemies, obstacles, and items for it’s location.
(the „location“ actually doesn’t increase, just that the level
objects increase in rect y until they are off the screen)

10. create a sector list to hold all sectors
create a world_shift var to hold amount world is shifted
if >= 1000 go to next sector of sector list

(although I think each sector should be initialized to start somewhere above
the screen)

In main

###level instantiations/initializations
sector_list = []
temp_sector_1 = sector_1(player)
temp_sector_2 = sector_1(player)

#set current sector
current_sector_no = 0
current_sector = sector_list[current_sector_no]

In loop


if level.world_shift >= 1000:
level.world_shift = 0
current_sector_no += 1
current_sector = sector_list[current_sector_no]

screen.blit(player.image, (340, 500))

Then I add an offest to all sector objects rect.y +1000 so they
are initialized well about the screen
and I change the world_shift conditional to >= 1500

I can clone sectors now to add more to the level and
set and end sector where world shift stops being called and you
just have to collide with the wormhole.
####in no particular order

Now though the drawing has to happen in the sector class instead of the
level class.

obstacles wont have too much to them yet
and I know how to make a basic player
so I’ll focus on all the different types of enemies
…or maybe just 3 to start.

I’m starting to think a scrolling map isn’t a good idea
if this is to big and done fast. I need something more
modular and that I can work on in a more routined way so
I don’t want to have to do so much playing around with
level design at this point.

So I want to make this game instead a series of rooms (although in space,
still rooms) I guess like decent or something iunno)
So I can kind of batch work on all the rooms at once, instead of having
to working on one super long room, and one super big map.
For the enemies a player movement, I have an idea that will also allow
me to do things in a batch like fashion. I will make it so there is one big „ship“
class that holds all possible movement patterns of all ships in the game. Then each
enemy will have one or a few of those and the player ship will be able to gain the
ability to learn those movement patters.

8. movement

I will use the „animation“ function on each class save the „item“ class
and start with basic side to side movement.
9. weapons (need to make a bullet class and maybe have weapons as options
in def shoot)

10. collisions and boundaries (physics engine?)

11. actual map outlines

12. gamestates

13. appearances

14. enemy variation

15. camera (world shift,
for enemies I want to set their location on a map and then
have the world shift and as enemies enter into screen area
they are initialized and attack/move/etc.
I might end up doing collisions last
and movement second to last…

Trigonometry for programming enemies and trajectories

Starting from this site:

I get to the part where he defines:

def move(self):
    self.x += math.sin(self.angle) * self.speed
    self.y -= math.cos(self.angle) * self.speed

I am working on my own little project at the same time and my issue is finding a way to make a circle of enemies close in on the player at their fixed angles. (Like in the first level of Axley for snes!)

On my way home from the library my idea was that the x1-x2/y1-y2 would give a ratio, and then I can subtract a fixed amount from I guess x, and then multiply y by the ratio so that x and y keep the same ratio which should mean the same angle.

However, I want to get this trig stuff cause I feel it will keep being important and in my face. So I wiki’d it and found, „And for a given angle, cos and sin give the respective x, y coordinates on a unit circle.“

Then I wiki’d „unit circle“ and found out that it is like a circle generalized to a radius of one and the angle is at the 0,0 in Cartesian format so the middle of an x,y axis. So sine(self.angle) gives the x value out of a radius of 1, and cos(self.angle) the same, for a given angle. This means that the self.x and self.y that result will be…I can’t think of the word but it’s like generalized lol. It’s that it’s based on a standardized value and has nothing to do with the size of your grid or screen or anything. It’s x and y values based on a unit circle with radius 1. 

This might help. So the max y value is 1, the min y value is also 1. Any other values are somewhere in between including the values calculated in our example.

ok, so self.angle is in radians. So with a fixed angle being initialized, the x and y values, will also be fixed as generalized x and y values. Then incrementing by the sin(self.angle) and cos(self.angle) will result in movement along the fixed angle (self.angle) because incrementing the x and y based on their ratio to a fixed angle maintains that angle.

self.angle = math.pi/2

Make the angle pi/2 = 90 degrees , and you will have a (y) value of zero because 90 degrees is completely horizontal. Oh, and hear I based on how the angle is drawn here startingat the y line and going down. From there 90 degrees is hitting the x line and 180 is hitting the y line at the bottom.

The mathematics of movement

The picture above this one has 90 degrees as the y line which is different I guess.