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?