0
\$\begingroup\$

I am trying to make game but first I want to make simple simulator. The goal is to have a ball fall onto a surface and then roll off the edge.

Here is my code:

import os,sys, math,pygame, pygame.mixer
from pygame.locals import *
import random
import euclid
import pdb
import math


#Defining some basic colors

black = 0, 0, 0
white = 255, 255, 255
red = 255, 0, 0
green = 0, 255, 0
blue = 0, 0, 255

colors = [black, red, green, blue]

gravity = euclid.Vector2(0.0,80.0)
drag = 0.1
initial_velocity = 20

#Defining the screen size
screen_size = screen_width, screen_height = 600, 400

class MyCircle:
    def __init__(self, position , size, color = (255, 255, 255),velocity=euclid.Vector2(0,0), accel=euclid.Vector2(0,0),width = 1):
        self.position=position
        self.size=size
        self.color = color
        self.width = width
        self.velocity=velocity
        self.accel = accel
        #__set_initial_velocity()

    def display(self):
        rx, ry = int(self.position.x), int(self.position.y)
        pygame.draw.circle(screen,self.color,(rx,ry), self.size,self.width)

    def move(self):
        self.position += self.velocity * dtime + 0.5*(self.accel * (dtime **2))
        self.velocity += self.accel * dtime
        #self.velocity -= self.velocity * drag * dtime
        #self.bounce()

    def change_velocity(self, velocity):
        self.velocity = velocity

    def bounce(self):
        if self.position.x <= self.size:
            self.position.x = 2*self.size-self.position.x
            self.velocity = self.velocity.reflect(euclid.Vector2(1,0))
        elif self.position.x >= screen_width - self.size:
            self.position.x = 2*(screen_width - self.size) - self.position.x
            self.velocity = self.velocity.reflect(euclid.Vector2(1,0))
        if self.position.y <= self.size:
            self.position.y = 2*self.size - self.position.y
            self.velocity = self.velocity.reflect(euclid.Vector(0,1))
        elif self.position.y >= screen_height - self.size:
            self.position.y = 2*(screen_height - self.size) - self.position.y
            self.velocity = self.velocity.reflect(euclid.Vector2(0,1))

    #Equation for distance between surfaces
    #d(t) = |A(t) - B(t)| - (Ra + Rb)
    def surface_distance(self, other, time):
        radiiAB = self.size + other.size
        posA = self.position + self.velocity * time + 0.5*(self.accel * (time**2))
        posB = other.position + other.velocity * time + 0.5*(other.accel * (time**2))
        posAB = abs(posA-posB)
        return posAB - radiiAB

    def collide(self,other):
        if self.surface_distance(other,dtime) <= 0:
            collision_vector = self.position - other.position
            collision_vector.normalize()
            self.velocity = self.velocity.reflect(collision_vector)
            other.velocity = other.velocity.reflect(collision_vector)


class MySurface():
    def __init__(self, size, color = (255, 255, 255),angle=math.pi/2,width = 0):
        self.theta=angle
        self.position1=[50,100]
        self.position2=[self.position1[0]+50*math.cos(self.position1[0]),
            self.position1[1]+50*abs(math.sin(self.position1[1]))]
        self.color = color
        self.width = width
        self.accel = euclid.Vector2(gravity.y*math.cos(self.theta),-1*gravity.y*math.sin(self.theta))
        #pdb.set_trace()

    def display(self):
        line = pygame.draw.aaline(screen, (0,0,0), self.position1,self.position2,5)

    def surface_distance(self,circ, time):
        theta=self.theta
        pos_ball = circ.position+circ.velocity*time+0.5*(circ.accel*(time**2)) 
        #map coordinates to surface that the ball is going to contact
        ball_y = self.position1[1]-(-1*pos_ball.x*math.sin(theta) + pos_ball.y*math.cos(theta))
        #map the actual surface to its own coordinate system
        pos1y_adjusted = self.position1[1]-(-1*self.position1[1]*math.sin(theta) + self.position1[1]*math.cos(theta))
        ydelta = ball_y-pos1y_adjusted
        return ydelta

    def collide(self,circ,counter):
        ydelta = self.surface_distance(circ,dtime)
        if ydelta <= circ.size:
             circ.accel=self.accel # readjust acceleration based on angle of line
             if counter<1:
                 circ.velocity=euclid.Vector2(0,0)
                 counter=counter+1
             else:
                 counter+=1
        return counter
        #     pdb.set_trace()


#Setting the display and getting surface object
screen = pygame.display.set_mode(screen_size)
#Getting clock object
clock = pygame.time.Clock()
#Setting a title to the window
pygame.display.set_caption('First Class!')

number_of_circles = 10
my_circles = []
#create ball
size = 10
color = colors[0]
my_circle = MyCircle(euclid.Vector2(70,40), size, color, euclid.Vector2(0,0),gravity)
my_circle.velocity = euclid.Vector2(0,10)
my_circle.velocity.normalize()

#Create surface
angle = -1*math.radians(30)
my_surface = MySurface(screen,color,angle)


direction_tick = 0.0
counter = 0;
#Defining variables for fps and continued running
fps_limit = 60
run_me = True
while run_me:
    #Limit the framerate
    clock.tick(fps_limit)

    for event in pygame.event.get():
         if event.type == pygame.QUIT:
             run_me = False

    #Limit the framerate
    dtime_ms = clock.tick(fps_limit)
    dtime = dtime_ms/1000.0


    #Clear the screen
    screen.lock()
    screen.fill(white)



    counter=my_surface.collide(my_circle,counter)
    my_circle.move()
    my_circle.display()
    my_surface.display()
    screen.unlock()
    #Display everything on the screen
    pygame.display.flip()

#Quit the game
pygame.quit()
sys.exit()

Everything starts out fine as shown below: Starting game

Once you let the game go on the ball keeps going in the same direction: Ball rolling off

Now I know I can adjust acceleration vectors again once the ball reaches the edge of the surface, but is there something in pygame that already does the appropriate physics for me?

I.E. In Unity there is a concept of RigidBody and the ball hits the RigidBody and rolls off of it and once it reaches the edge of the body the ball changes acceleration.

Here is the link for euclid: https://github.com/xocoatzin/pyeuclid/blob/master/euclid.py

\$\endgroup\$
  • 1
    \$\begingroup\$ Sounds to me like your gravity acceleration is too weak, so it's not deflecting the ball's path as much as you expect after it rolls off the edge. Have you tried increasing your acceleration due to gravity? \$\endgroup\$ – DMGregory Jan 7 '19 at 12:05
  • 1
    \$\begingroup\$ You should use pyBox2D \$\endgroup\$ – Juanpa Jan 7 '19 at 17:28
  • \$\begingroup\$ Based on your title, it sounds like you are switching from Unity. Out of curiosity, may I ask why? \$\endgroup\$ – Evorlor Jan 8 '19 at 10:07
  • 1
    \$\begingroup\$ @Juanpa I will look into pyBox2D \$\endgroup\$ – ahat Jan 9 '19 at 15:54
  • 1
    \$\begingroup\$ @Evorlor I am switching from Unity because: 1) When I am designing my game I want to make various levels by simulating different solutions using physics simulation (I don't know if unity can already do that?) 2) I run into issues when using Linux on Unity 3)I know Python pretty well so it would be a easier to work with (but I could be wrong). I can try Unity again if I can simulate various solutions for different levels using a physics and try to get around #2 #3. \$\endgroup\$ – ahat Jan 9 '19 at 16:04

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Browse other questions tagged or ask your own question.