Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- # full credit: http://www.pygame.org/project-Quadtree+test-1691-.html
- #only edit : cast to draw to int.
- import pygame
- from pygame.locals import *
- pygame.init()
- import time, math, random
- ### METHODS ###
- def n(x):
- y=1
- for z in xrange(x):
- y*=z+1
- return y
- def rect_quad_split(rect):
- w=rect.width/2.0
- h=rect.height/2.0
- rl=[]
- rl.append(pygame.Rect(float(rect.left), float(rect.top), float(w), float(h)))
- rl.append(pygame.Rect(float(rect.left+w), float(rect.top), float(w), float(h)))
- rl.append(pygame.Rect(float(rect.left), float(rect.top+h), float(w), float(h)))
- rl.append(pygame.Rect(float(rect.left+w), float(rect.top+h), float(w), float(h)))
- return rl
- def lerp(a, b, p):
- return(a+(b-a)*p)
- ### CLASSES ###
- class QuadLeaf(object):
- def __init__(self, world, rect, quadtree, level):
- self.world = world
- self.rect=rect
- self.quadtree = quadtree
- self.level=level
- quadtree.levels[level].append(self)
- self.branches=None
- self.entities=[]#a list of collidable objects on this leaf
- def subdivide(self):
- self.branches=[]
- for rect in rect_quad_split(self.rect):
- b=QuadLeaf(self.world, rect, self.quadtree, self.level+1)
- self.branches.append(b)
- def test_collisions(self, obj):
- for obj2 in self.entities:
- if obj2.rect.colliderect(obj.rect):
- if [obj,obj2] not in self.quadtree.collisions and [obj2,obj] not in self.quadtree.collisions:
- self.quadtree.collisions.append([obj, obj2])
- if self.level==self.quadtree.level_limit-1:
- return
- if self.branches!=None:
- for b in self.branches:
- b.test_collisions(obj)
- def add_entity(self, obj):
- for obj2 in self.entities:
- if obj2.rect.colliderect(obj.rect):
- if [obj,obj2] not in self.quadtree.collisions and [obj2,obj] not in self.quadtree.collisions:
- self.quadtree.collisions.append([obj, obj2])
- if self.level==self.quadtree.level_limit-1:#if it's hit the limit of levels
- self.entities.append(obj)
- obj.level=int(self.level)
- return
- if self.branches==None:
- self.subdivide()
- #the first thing it has to check is if the object fits in any branches
- fits=None
- for b in self.branches:
- if b.rect.contains(obj.rect):
- fits=b
- if fits==None:
- #doesn't fit in any of the branches, stays on this level
- self.entities.append(obj)
- obj.level=int(self.level)
- for b in self.branches:
- b.test_collisions(obj)
- else:
- #it DOES fit in a branch, hand it to the branch
- fits.add_entity(obj)
- def render(self):
- pygame.draw.rect(self.world.screen, [127,127,127], self.rect, 2)
- if self.branches!=None:
- for b in self.branches:
- b.render()
- class QuadTree(object):
- def __init__(self, world, world_rect):
- self.world = world
- self.world_rect=world_rect
- self.level_limit=3
- self.reset()
- def add_entity(self, obj):
- for obj2 in self.entities:
- if obj2.rect.colliderect(obj.rect):
- if [obj,obj2] not in self.collisions and [obj2,obj] not in self.collisions:
- self.collisions.append([obj, obj2])
- if self.level_limit<=0:
- self.entities.append(obj)
- obj.level=-1
- return
- #the first thing it has to check is if the object fits in any branches
- fits=None
- for b in self.branches:
- if b.rect.contains(obj.rect):
- fits=b
- if fits==None:
- #doesn't fit in any of the branches, stays on main level
- self.entities.append(obj)
- obj.level=-1
- for b in self.branches:
- b.test_collisions(obj)
- else:
- #it DOES fit in a branch, hand it to the branch
- fits.add_entity(obj)
- def test_collisions(self):
- #this means that each object will need a collide command
- for c in self.collisions:
- c[0].collide(c[1])
- c[1].collide(c[0])
- def reset(self):
- self.levels=[]
- for x in xrange(self.level_limit):
- self.levels.append([])
- self.branches=[]
- if self.level_limit>0:
- for rect in rect_quad_split(self.world_rect):
- b=QuadLeaf(self.world, rect, self, 0)
- self.branches.append(b)
- self.entities=[]#a list of all collidable objects on the main level
- self.collisions=[]
- def render(self):
- for branch in self.branches:
- branch.render()
- class Ball(object):
- def __init__(self, world, pos, radius):
- self.world = world
- self.pos = pos
- self.vector = [0,0]
- self.radius=radius
- self.set_rect()
- self.level=-1
- def set_rect(self):
- self.rect = pygame.Rect(self.pos[0]-self.radius, self.pos[1]-self.radius,self.radius*2,self.radius*2)
- def collide(self, obj):
- x=(self.pos[0]-obj.pos[0])
- y=(self.pos[1]-obj.pos[1])
- dist = math.sqrt(x**2+y**2)
- if dist<=self.radius+obj.radius and dist!=0:
- #we collided, act apon it
- x/=dist
- y/=dist
- collide_amount=(self.radius+obj.radius)-dist
- p1=collide_amount/float(self.radius+obj.radius)
- p2=math.sin(math.radians( lerp(0,90,p1) ))
- p=lerp(1,p2,0.5)#the bigger this number, the more squishy the balls are
- collide_amount*=p
- self.vector[0]+=x*collide_amount
- self.vector[1]+=y*collide_amount
- def update(self):
- ## HEY LOOK AT THIS!!!!
- #you can change these variables!
- self.level=-1
- self.vector[1]=self.vector[1]+0.0#<-- simulates gravity
- self.vector[0]=self.vector[0]*1.0#<-- simulates air resistance
- self.vector[1]=self.vector[1]*1.0#<--
- def move(self):
- dist = math.sqrt(self.vector[0]**2+self.vector[1]**2)
- if dist>self.radius:
- self.vector[0]/=dist
- self.vector[1]/=dist
- self.vector[0]*=self.radius
- self.vector[1]*=self.radius
- self.pos[0]+=self.vector[0]
- self.pos[1]+=self.vector[1]
- if self.pos[0]-self.radius<self.world.quadtree.world_rect.left or self.pos[0]+self.radius>self.world.quadtree.world_rect.right:
- self.vector[0]*=-1
- self.pos[0]+=self.vector[0]
- self.vector[0]*=0.8
- if self.pos[1]-self.radius<self.world.quadtree.world_rect.top or self.pos[1]+self.radius>self.world.quadtree.world_rect.bottom:
- self.vector[1]*=-1
- self.pos[1]+=self.vector[1]
- self.vector[1]*=0.8
- self.set_rect()
- self.world.quadtree.add_entity(self)
- def render(self, showLevel=False):
- if showLevel:
- if self.level==-1:
- c=[255,0,0]
- elif self.level==0:
- c=[255,255,0]
- elif self.level==1:
- c=[0,255,0]
- elif self.level==2:
- c=[0,255,255]
- elif self.level==3:
- c=[0,0,255]
- else:
- c=[255,0,255]
- else:
- c=[127,127,0]
- pos = (int(self.pos[0]), int(self.pos[1]))
- self.world.rects.append(pygame.draw.circle(self.world.screen, c, pos, int(self.radius)))
- pygame.draw.circle(self.world.screen, [0,0,0], pos, self.radius,1)
- ##########################################################
- ### MAIN ###########################################
- ##########################################################
- class World(object):
- def __init__(self):
- #SET UP __INIT__
- self.screen_size = (800,600)
- self.screen = pygame.display.set_mode(self.screen_size)
- self.clock = pygame.time.Clock()
- self.framerate=1000
- self.font = pygame.font.Font(pygame.font.get_default_font(),30)
- self.quadtree=QuadTree(self,pygame.Rect(0,0,self.screen_size[0], self.screen_size[1]))
- self.quadtree.level_limit=0
- self.balls=[]
- for x in xrange(600):
- radius=(x%3)*2+6
- self.balls.append(Ball(self, [random.randint(50,self.screen_size[0]-50), random.randint(50,self.screen_size[1]-50)], radius))
- self.bg_color = [0,0,0]
- self.screen.fill(self.bg_color)
- #SET UP ENTITIES
- self.reset()
- self.run()
- def reset(self):
- pass
- def run(self):
- stage=0
- print "The tree will start turned off. You will see the frame rate will be very low"
- next=time.time()+10.0
- avg_frame_rate=0
- prev_frame_rate=0
- avg_checks=0
- # RUN MAIN LOOP
- while True:
- self.clock.tick(self.framerate)
- self.events = pygame.event.get()
- self.keys=pygame.key.get_pressed()
- self.mouse_pos=pygame.mouse.get_pos()
- self.mouse_but = pygame.mouse.get_pressed()
- self.fps=self.clock.get_fps()
- self.rects=[]
- #UPDATE
- for b in self.balls:
- b.update()
- #MOVE
- for b in self.balls:
- b.move()
- #TEST COLLISIONS
- self.quadtree.test_collisions()
- #RENDER
- if stage!=None:
- self.quadtree.render()
- for b in self.balls:
- b.render(stage!=None)
- #renders framerate
- img=self.font.render(str(int(self.fps)),True, [255,255,255])
- rect = img.get_rect(topleft=[0,0])
- self.rects.append(rect)
- self.screen.blit(img, rect)
- #renders collision checks
- img=self.font.render(str(int(avg_checks)),True, [255,255,255])
- rect = img.get_rect(bottomleft=[0,self.screen_size[1]])
- self.rects.append(rect)
- self.screen.blit(img, rect)
- pygame.display.flip()
- self.screen.fill(self.bg_color)
- for rect in self.rects:
- self.screen.fill(self.bg_color, rect)
- avg_frame_rate=lerp(avg_frame_rate, self.fps,0.05)
- if stage!=None:
- avg_checks=lerp(avg_checks, len(self.quadtree.collisions),0.1)
- if time.time()>=next:
- print stage+1
- if avg_frame_rate>prev_frame_rate:
- #continues to test
- prev_frame_rate=avg_frame_rate
- next=time.time()+10.0
- stage+=1
- self.quadtree.level_limit=stage
- else:
- #found peak of preformance
- print "Found peak. Preformance for this computer will not be any better then it is now."
- M=(len(self.balls)-1)*len(self.balls)
- print "Would normally have",M,"collision checks,"
- print "but this quadtree reduces it to roughly",int(avg_checks),"collision checks"
- self.quadtree.level_limit=stage-1
- stage=None
- #EVENT HANDLER UPDATE
- for event in self.events:
- if event.type==MOUSEBUTTONDOWN:
- for b in self.balls:
- b.vector[0]+=random.randint(-10,10)
- b.vector[1]+=random.randint(-10,10)
- if event.type==KEYDOWN or event.type==QUIT:
- if event.type==QUIT or event.key==K_ESCAPE:
- pygame.quit()
- return
- self.quadtree.reset()
- world = World()
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement