1
\$\begingroup\$

I am stuck on a simple Box2D task. I have a box and a ball (in top-down view so no gravity), and I would like the ball to roll around the box.

Here is my code. Basically now I am trying to detect collision and when it occurs I change the direction of a force by 90 degrees, but this (obviously) does not work when the body reaches the edge of an obstacle, but I do not know how to fix it.

import random
import time
import Box2D
from Box2D.b2 import world, polygonShape, circleShape,edgeShape, staticBody, dynamicBody, kinematicBody, revoluteJoint, wheelJoint
from Box2D import b2Vec2, b2FixtureDef,b2PolygonShape, b2CircleShape, b2Dot,b2EdgeShape, b2Contact,b2ContactFilter,b2Filter,b2ContactListener,b2GetPointStates

import pygame 
from pygame import HWSURFACE, DOUBLEBUF, RESIZABLE, VIDEORESIZE
from pygame.locals import (QUIT, KEYDOWN, K_ESCAPE)
pygame.init()

# PyBox2D setup
PPM = 15
SCREEN_WIDTH, SCREEN_HEIGHT = 640, 480
pos_X = SCREEN_WIDTH/PPM/3
pos_Y = SCREEN_HEIGHT/PPM/3


# PyBox2D World initialization

BallGroup = 1
ObstacleGroup = -1

BallCategory =  0x0002
ObstacleCategory = 0x0004

BallMask = 0xFFFF
ObstacleMask = 0xFFFF


class myContactListener(b2ContactListener):
    def __init__(self):
        b2ContactListener.__init__(self)
    def BeginContact(self, contact):
        if 1.5 <= contact.fixtureB.body.mass <= 1.7 and contact.fixtureA.body.mass == 0: #Ball hit Rectangle
            contact.fixtureB.body.ApplyForce(b2Vec2(0,50), contact.fixtureB.body.worldCenter,True)
        else:
            pass
    def EndContact(self, contact):
        print('Dum Dum TSS, Dum Dum Tss')
    def PreSolve(self, contact, oldManifold):
        worldManifold = contact.worldManifold
        bodyA = contact.fixtureA.body
        bodyB = contact.fixtureB.body
    def PostSolve(self, contact, impulse):
        pass

My_World = world(contactListener = myContactListener(), gravity = (0.0, 0.0), doSleep = True)

class Ball():
    def __init__(self,My_World, position = None):

        if position == None:
            position = [5,5]

        self.position = position
        self.My_World = My_World
        self.Current_Position = []
        self.body = self.My_World.CreateDynamicBody(position = position, 
                                                       angle = 0.0,
                                                       fixtures = b2FixtureDef(
                                                            shape = b2CircleShape(radius = 0.5),
                                                            density = 2,
                                                            friction = 0.3,
                                                            filter = b2Filter(
                                                                groupIndex = BallGroup,
                                                                categoryBits = BallCategory,
                                                                maskBits = BallMask,
        )))

class Rectangle():
    def __init__(self, My_World,shape, position, sensor= None):
        self.My_World = My_World
        self.shape = shape
        self.position = position

        if sensor == None:
            sensor = False

        self.sensor = sensor
        self.rect = self.My_World.CreateStaticBody(position = position,
                                                           angle = 0.0,
                                                           fixtures = b2FixtureDef(
                                                               shape = b2PolygonShape(box=(self.shape)),
                                                               density = 1000,
                                                               friction = 1000))

Obstacle = Rectangle(My_World,shape = (5,5), position =  (pos_X,pos_Y))

############################################################## Pygame visualisation


screen = pygame.display.set_mode((SCREEN_WIDTH, SCREEN_HEIGHT), HWSURFACE|DOUBLEBUF|RESIZABLE)
pygame.display.set_caption('Top Down Car Using OOP')
colors = {dynamicBody: (133, 187, 101, 0),  staticBody: (15, 0, 89, 0)}
running = True
Every_Second = 0
FPS = 24
TIME_STEP = 1.0 / FPS
k = 0
Max_Amount_Balls = 8

############################### Drawing functions

SCREEN_OFFSETX, SCREEN_OFFSETY = SCREEN_WIDTH/16, SCREEN_HEIGHT

def fix_vertices(vertices):
    return [(int(SCREEN_OFFSETX + v[0]), int(SCREEN_OFFSETY - v[1])) for v in vertices]
def _draw_polygon(polygon, screen, body, fixture):
    transform = body.transform
    vertices = fix_vertices([transform * v * PPM for v in polygon.vertices])
    pygame.draw.polygon(
        screen, [c / 2.0 for c in colors[body.type]], vertices, 0)
    pygame.draw.polygon(screen, colors[body.type], vertices, 1)
polygonShape.draw = _draw_polygon


def _draw_circle(circle, screen, body, fixture):
    position = fix_vertices([body.transform * circle.pos * PPM])[0]
    pygame.draw.circle(screen, colors[body.type],
                       position, int(circle.radius * PPM))
circleShape.draw = _draw_circle

Body =Ball(My_World, position = (Obstacle.position[0] -random.randint(-Obstacle.shape[0],Obstacle.shape[0]),\
                        Obstacle.position[1] -random.randint(-Obstacle.shape[1],Obstacle.shape[1])))
Positive_Negative =[-1,1][random.randrange(2)]

Ticker = 0
############################### Main game loop
while running:
    Ticker += 1        
    # Draw the world
    screen.fill((255, 255, 255, 255))
    for event in pygame.event.get():
        if event.type == QUIT or (event.type == KEYDOWN and event.key == K_ESCAPE):
            # The user closed the window or pressed escape
            running = False
        elif event.type == KEYDOWN and event.key == pygame.K_KP_PLUS:
            PPM += 5
        elif event.type == KEYDOWN and event. key == pygame.K_KP_MINUS:
            PPM -= 5

        if event.type==VIDEORESIZE:
                screen=pygame.display.set_mode(event.dict['size'],HWSURFACE|DOUBLEBUF|RESIZABLE)

    for body in My_World.bodies:
        for fixture in body.fixtures:
            fixture.shape.draw(screen, body, fixture)


    # Generate Ball

    Body.body.ApplyForce(Positive_Negative*b2Vec2(5,0), Body.body.worldCenter,True)

    # Simulate dynamic equation in each step
    TIME_STEP = 1.0 / FPS
    My_World.Step(TIME_STEP, 10, 10)

    # Flip the screen and try to keep at the target FPS
    pygame.display.flip() # Update the full display Surface to the screen
    pygame.time.Clock().tick(FPS)
    Every_Second += 1

pygame.quit()
print('Done!')
print(Positive_Negative)

I am considering a PID controller but I do not think it will work, because it isn't moving in 2 dimensions. What algorithm would you suppose?

\$\endgroup\$
  • 1
    \$\begingroup\$ When you tell us only that "everything failed," we don't have very good leads on how to help you solve that problem. Any strategy we suggest could very well be one of the "various algorithms" you already tried without success, and without knowing exactly;*how* you did it and how it failed, we can't tell you what part to do differently. Can you edit your question to include more details of your approach so far? \$\endgroup\$ – DMGregory Nov 11 at 17:43

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.