Reputation: 818
I would like to implement a code which has non colliding pairs with colliding objects. My pairs are 2 balls which are connected to each other. I want those 2 pair ball to collide. However I don't want those 2 balls to collide with other pairs.
How can I implement a mask with this much of categories in pymunk ShapeFilter? Should I use bitwise operators? As you can see in my shape filter I tried to ignore values which is out of their category except their pairwise catergory, BUT it doesnt work for 4 + number of balls?
My code cannot handle those states of balls with each other.
import pygame
from pygame.locals import *
from pygame.color import *
import pymunk
import pymunk.pygame_util
from pymunk import Vec2d
import sys
#import tensorflow as tf
from time import sleep
import time
import numpy as np
from math import exp
from random import seed
from random import random
import datetime
import operator
class Game:
def __init__(self):
# initialize game window
pygame.init()
self.screen_x= 1500
self.screen_y= 200
# Pool Hyper Parameters
# BE CAREFULL CHANGING THESE VARIABLES
self.pool_size = 2
self.pool_time = 15
#########################################################################
# NN lists
#Pygame fonts
self.font = pygame.font.SysFont("Arial", 16)
self.screen = pygame.display.set_mode((self.screen_x,self.screen_y+200)) #screen display
self.clock = pygame.time.Clock() ## init clock
self.running = True
# pymunk init
self.space = pymunk.Space()
self.space.gravity = (0.0, -1200.0) #gravity setup
self.draw_options = pymunk.pygame_util.DrawOptions(self.screen)#adds add physics to the screen
def new(self):
# start a new game
## Balls
self.balls = []
## creating walls
self.static_body = self.space.static_body
self.static_lines = [pymunk.Segment(self.static_body, (0.0,50.0), (1500.0, 50.0), 0.0) ## road
,pymunk.Segment(self.static_body, (1499.0, 150.0), (1499.0, 700.0), 0.0)
,pymunk.Segment(self.static_body, (1800, 50.0), (1800, 170.0), 0.0)
,pymunk.Segment(self.static_body, (0.0,50.0), (0.0, 700.0), 0.0) ## wall 1
]
## set walls
for line in self.static_lines:
line.elasticity = 0.95
line.friction = 01.5
#line.filter = pymunk.ShapeFilter(categories=np.uint8(0))
self.space.add(self.static_lines)
# Go to run
self.run()
def run(self):
# Game Loop
self.playing = True
self.no_ball = True
while self.running:
self.events()
if self.no_ball == True:
self.unit(self.pool_size)
self.update()
def update(self):
self.screen.fill(THECOLORS["white"])
### Draw stuff
self.balls_to_remove = []
for ball in self.balls:
if ball.body.position.y < 0: self.balls_to_remove.append(ball)
for ball in self.balls_to_remove:
self.space.remove(ball, ball.body)
self.balls.remove(ball)
self.space.debug_draw(self.draw_options)
### Update physics
self.dt = 1.0/60.0
for k in range(1):
self.space.step(self.dt)
### Flip screen
pygame.display.flip()
self.clock.tick(50)
pygame.display.set_caption("fps: " + str(self.clock.get_fps()))
def events(self):
for event in pygame.event.get():
if event.type == QUIT:
self.running = False
if event.type == KEYDOWN:
if event.key == K_y: ## Creates a ball
self.running = False
pass
elif event.type == pygame.MOUSEMOTION:
(self.mouse_x, self.mouse_y) = pymunk.pygame_util.get_mouse_pos(self.screen)
def unit (self, number_balls=None):
#Mass And Radius units
self.mass = 20
self.radius = 10
#Mass and Radius for joint
self.mass_joint = 2
self.radius_joint = 2
#for n in number_balls
self.objs = list()
for i in range(number_balls):
self.objs.append(pymunk.Body())
self.objs.append(pymunk.Body())
self.objs.append(pymunk.Body())
#Inertia
self.inertia = pymunk.moment_for_circle(self.mass, 0, self.radius, (0,0))
self.inertia_joint = pymunk.moment_for_circle(self.mass_joint , 0, self.radius_joint, (0,0))
#Body
self.objs[i], self.objs[i+1] = pymunk.Body(self.mass, self.inertia), pymunk.Body(self.mass, self.inertia)
self.objs[i+2] = pymunk.Body(self.mass_joint, self.inertia_joint)
#Position
x = 100
self.objs[i].position = x , 90
self.objs[i+1].position = x+24, 90
self.objs[i+2].position = x+12, 90
#Links
self.link_1 = pymunk.PinJoint(self.objs[i], self.objs[i+2], (0, 0), (0, 0))
self.link_2 = pymunk.PinJoint(self.objs[i+1], self.objs[i+2], (0, 0), (0, 0))
# Adding Body links
self.space.add(self.link_1)
self.space.add(self.link_2)
#First ball shape
self.shape_first = pymunk.Circle(self.objs[i], self.radius, (0,0))
self.shape_first.elasticity = 0.4
self.shape_first.friction = 0.9
self.space.add(self.objs[i], self.shape_first)
#Fsecond ball shape
self.shape_second = pymunk.Circle(self.objs[i+1], self.radius, (0,0))
self.shape_second.elasticity = 0.4
self.shape_second.friction = 0.9
self.space.add(self.objs[i+1], self.shape_second)
#Joint shape
self.shape_joint = pymunk.Circle(self.objs[i+2], self.radius_joint, (0,0))
self.shape_joint.elasticity = 0.4
self.shape_joint.friction = 0.9
#self.shape_joint.sensor == True
self.space.add(self.objs[i+2], self.shape_joint)
body_first_category = (i*3)+1#"{0:b}".format(int(i+1))
body_second_category = (i*3)+2
body_joint_category = (i*3)+3
print (body_first_category )
print(body_second_category )
print(body_joint_category )
#"{0:b}".format(int(i+2))
self.shape_first.filter = pymunk.ShapeFilter(categories=body_first_category, mask=(body_joint_category and body_second_category) )
self.shape_second.filter = pymunk.ShapeFilter(categories=body_second_category, mask=(body_first_category and body_joint_category) )
self.shape_joint.filter = pymunk.ShapeFilter(categories=body_joint_category, mask=(body_first_category and body_second_category))
self.balls.append(self.shape_first)
self.balls.append(self.shape_second)
self.balls.append(self.shape_joint)
self.no_ball = False
g = Game()
while g.running:
g.new()
##pg.quit()
Please help :)
P.S: Example Library shape filter class:
http://www.pymunk.org/en/latest/pymunk.html#pymunk.ShapeFilter
Upvotes: 3
Views: 725
Reputation: 4603
You can solve this with collision callbacks, I think begin callback is a good option for you. On each pair of shapes you want to collide you set a common identifier, and then check that in the callback, and return True only when the two objects colliding belong to the same pair.
Something like this:
def only_collide_same(arbiter, space, data):
a, b = arbiter.shapes
return a.pair_index == b.pair_index
h = space.add_collision_handler(1,1)
h.begin = only_collide_same
for i in range(10):
# create shapes and bodies ...
# then for each pair of shapes:
shape1.pair_index = i
shape1.collision_type = 1
shape2.pair_index = i
shape2.collision_type = 1
Upvotes: 2