示例#1
0
def _signed_distance(p1,p2,angle):
    distance = round(VMath.distance(p1,p2), constant.PRECISION)
    l_angle = math.atan2(p2[1]-p1[1],p2[0]-p1[0])
    diff_angle = VMath.angle_diff(angle,l_angle)
    if diff_angle > math.pi/2:
        distance *= -1
    return distance
示例#2
0
def SAT(o1, o2, moving=None):
    # Get Edges of the shape
    hitbox_o1 = o1.hitbox   # Store hitbox so no need to recompute them
    hitbox_o2 = o2.hitbox
    edges_o1 = _get_edges(hitbox_o1)
    edges_o2 = _get_edges(hitbox_o2)

    # Get the axis of the squares and normalize
    axises = edges_o1[0:2] + edges_o2[0:2]
    axises = [VMath.normalize(x) for x in axises]

    # Find the min and max projections
    for axis in axises:  
        mn = [math.inf]*2
        mx = [-math.inf]*2
        for p1,p2 in zip(hitbox_o1,hitbox_o2):
            proj1 = VMath.dot_product(axis, p1)
            proj2 = VMath.dot_product(axis, p2)
            mn[0] = min(mn[0], proj1)
            mx[0] = max(mx[0], proj1)
            mn[1] = min(mn[1], proj2)
            mx[1] = max(mx[1], proj2)
        if mx[1] <= mn[0] or mn[1] >= mx[0]:
            return 0
    return -1
示例#3
0
 def target(self, enemy_tank, grid):
     gz = constant.GRID_SIZE
     tank = grid.get_object(self._object_id)
     x,y = enemy_tank.position
     enemy_hitbox = enemy_tank.hitbox
     gx,gy = int(x/gz), int(y/gz)
     t_angle = tank.angle
     mna, mxa = math.inf,-math.inf
     a1, a2 = None, None
     for bullet_angle in self._2damap[gx][gy]:
         trajectory = int(bullet_angle/10)
         bounce = bullet_angle % 10
         if not self._asafe[trajectory]: continue
         l0 = self._amap[trajectory][bounce-1] if bounce !=0 else tank.position 
         l1 = self._amap[trajectory][bounce]
         line = [l0,l1]
         if (collision.line_square(line, enemy_hitbox)):
             bullet_dir = math.atan2(l1[1]-l0[1],l1[0]-l0[0])
             a_diff = VMath.angle_diff(bullet_dir, enemy_tank.angle)
             a_diff = VMath.astc(a_diff)
             if a_diff < mna:
                 a1 = trajectory
                 mna = a_diff
             if a_diff > mxa:
                 a2 = trajectory
                 mxa = a_diff
     if a1 is None and a2 is None: return False
     if a1 == a2: return [a1]
     else: return [a1,a2]
示例#4
0
def get_distance(o1, o2, moving=constant.FORWARD):
    min_dist = math.inf
    moving -= 1     # Forward: 0, Reverse: 1
    c_line = None   

    # X and Y Distances
    x,y = VMath.subtract(o1.position, o2.position)

    # Various angles
    a1 = o1.angle
    a2 = o2.angle
    a1_r = (a1 + math.pi) % math.tau    # Obj Velocity Direction (reverse)
    if moving: a1, a1_r = a1_r, a1             

    # Calculates and stores hitbox of objects for future use, saves recomputing.
    hitbox_obj1 = o1.hitbox   # So that code does not need to
    hitbox_obj2 = o2.hitbox   # recalculate hitboxes, saving computing time.

    # Get points & lines of both objects that have the potential of colliding.
    #    1  |  0       
    #   ---------      
    #    2  |  3
    #      
    q = int(2*(a1-a2+3*math.pi)/math.pi%4) # Quadrant of points that will collide w/ o2
    h2 = hitbox_obj2[q:q+3] + hitbox_obj2[:max(q-1,0)]
    lines_o1 = [hitbox_obj1[1:3], (hitbox_obj1[0],hitbox_obj1[3])]
    lines_o2 = [h2[0:2], h2[1:3]]
    # Rays from o1 -> o2
    for i in lines_o1:
        for j in lines_o2:
            p = line_intersection(i, j)
            if p and _point_in_line(p,j): 
                distance = _signed_distance(i[moving],p,a1)
                if distance >= 0 and distance < min_dist: 
                    min_dist = distance
                    c_line = j

    if not c_line: return math.inf, None

    # Rays from o2 -> o1
    l_o1 = hitbox_obj1[0+2*moving:2+2*moving]
    l_o2 = [h2[1], VMath.translate(h2[1], 1, a1)]
    p = line_intersection(l_o2,l_o1)
    if p and _point_in_line(p,l_o1):
        distance = _signed_distance(p,h2[1],a1)
        if distance >= 0 and distance < min_dist: 
            min_dist = distance
            if VMath.distance(p, l_o1[0]) < VMath.distance(p, l_o1[1]):
                c_line = lines_o2[moving]
            else: c_line = lines_o2[1-moving]

    if min_dist is not math.inf: min_dist = min_dist
    return min_dist, c_line
示例#5
0
文件: game.py 项目: dn54321/pytanks
    def render_entities(self, tick, time_step, stage):
        surface = self._bg.copy().convert_alpha()
        controllers = self._grid.get_controllers()
        show_name = (self._keybind.get_keys() & constant.SHOW_NAMES) | stage
        for i in range(len(controllers)):
            controller = controllers[i]
            obj = self._grid.get_object(controller.object_id)
            if isinstance(obj, tank.Tank):
                if not isinstance(controller, playerController.PlayerController): stage = False
                player = self._itop[controller.object_id]
                self._renderer.render_tank(surface, obj, player, time_step, show_name=show_name, show_arrow=stage)
            else:
                self._renderer.render_bullet(surface, obj, time_step, colour=None)



        gz = constant.GRID_SIZE
        for ff in temp:
            path = pygame.Surface((gz,gz))
            path.set_alpha(128)
            path.fill(ff[2])
            surface.blit(path,ff[0:2])
        
        for lin in line:
            pygame.draw.line(surface, (0,0,0), lin[0], VMath.translate(lin[0],500,lin[1]))
        return surface
示例#6
0
 def shoot(self, grid):
     tank = grid.get_object(self._object_id)
     x,y = VMath.translate(tank.position, tank.nozzle_length, tank.nozzle_angle)
     projectile = bullet.Bullet(x,y,tank.nozzle_angle)
     id = grid.add_object(projectile)
     if id: grid.add_controller(bulletController.BulletController(id, self))
     else: return False
     return True
示例#7
0
 def _bounce_object(self, obj, dist, line):
     id = self._ids[obj]
     if dist:
         obj.move(dist)
     x, y = VMath.subtract(line[0], line[1])
     l_angle = math.atan2(y, x) % math.pi
     #print(f"before collision ({obj.angle*180/math.pi}): {obj.hitbox}")
     obj.force_rotate(2 * l_angle - obj.angle)
示例#8
0
def _line_at_angle(line, angle):
    p1,p2=line
    x,y = VMath.subtract(p2,p1)
    l_angle = math.atan2(y,x)
    if l_angle < 0: l_angle += math.tau
    angle_diff = abs(angle - l_angle)
    angle_diff = min(angle_diff, math.tau-angle_diff)
    if abs(angle_diff) <= math.pi/2:
        return True
    return False    
示例#9
0
    def render_entity(self,
                      surface,
                      sprite,
                      entity,
                      time_step,
                      angle=None,
                      pivot=[0, 0]):
        if isinstance(sprite, pygame.Surface): tile = sprite
        else:
            i, j = sprite
            tile = self._sprite_sheet[i][j].copy().convert_alpha()
        if not angle: a1, a2 = entity.old_angle, entity.angle
        else: a1, a2 = angle[0], angle[1]
        angle = a1 + VMath.angle_diff(a1, a2) * time_step
        rot_tile = pygame.transform.rotate(tile, -math.degrees(angle))
        center = rot_tile.get_width() / 2, rot_tile.get_height() / 2
        x1, y1 = entity.position
        x0, y0 = entity.old_position
        entity_pos = x0 + (x1 - x0) * time_step, y0 + (y1 - y0) * time_step
        pos = VMath.subtract(entity_pos, center)

        # Account for pivot
        pos = VMath.subtract(pos, VMath.rotate([pivot], angle)[0])
        surface.blit(rot_tile, pos)
示例#10
0
    def beamV3(self, grid, pos=None, angle=None, hitbox=None, bounce=constant.BULLET_BOUNCE, old_angle=None):
        tank = grid.get_object(self._object_id)
        
        if pos is None: pos = tank.position
        if angle is None: angle = tank.nozzle_angle
        if hitbox is None: hitbox = tank.hitbox
        if old_angle is None: old_angle = angle

        new_position = None
        new_angle = None
        cpb = [0]*2             # collision point boundary (1: x, 2: y)
        hc = [0]*2              # left or right point of bullet wall, has it collided?
        ctr = -1                # counter, once a collision with one of the ray hits,
                                # the 2nd ray can move only up to two steps.
        bpt = []

        # Return Variables #
        bp = []                 # store the grid index of the bullet path
        bep = []                # stores the bullet end points
        ct = False              # Determines whether trajectory has collided with tank

        gz = constant.GRID_SIZE
        (bw,bh) = constant.BULLET_SIZE  # Bullet width/height
        
        # Get the starting position of the two rays which define the trajectory
        # the bullet will take and find the grid position of those two points.
        offset = VMath.rotate([(0.5*bw,0.5*bh),(0.5*bw,-0.5*bh)], angle)
        pts = VMath.sum(pos,offset[0]), VMath.sum(pos,offset[1])
        gpts = [int(pts[0][0]/gz),int(pts[0][1]/gz)],[int(pts[1][0]/gz),int(pts[1][1]/gz)]

        
        ### DEBUGGER ###
        '''
        if bounce == constant.BULLET_BOUNCE: 
            game.temp = []
            game.line = []
        game.line.append([pts[0], angle])
        game.line.append([pts[1], angle])
        #print(f"b: {bounce}, l1: y-{pts[0][1]} = {math.tan(angle)}(x-{pts[0][0]})")
        #print(f"b: {bounce}, l2: y-{pts[1][1]} = {math.tan(angle)}(x-{pts[1][0]})")
        '''

        # Find velocity vector of the bullet
        h_angle = math.pi/2 - angle
        vx,vy = sin(h_angle),cos(h_angle)
        sx,sy = int(math.copysign(1,vx)), int(math.copysign(1,vy))
        
        # Given a velocity vector V and intial position U, find dx, dy
        # such that U+V*dx and U+V*dy will be a point that intersect
        # the x and y grid edges respectively. Note, mxdx, mxdy is the 
        # distance the projectile needs to travel one horizontal/vertical grid tile.

        dx = [math.inf]*2
        dy = [math.inf]*2
        if round(vx,5):
            mxdx = gz/abs(vx)
            dx[0] = mxdx-(pts[0][0]*sx%gz)*sx/vx
            dx[1] = mxdx-(pts[1][0]*sx%gz)*sx/vx
            
        if round(vy,5):
            mxdy = gz/abs(vy)
            dy[0] = mxdy-(pts[0][1]*sy%gz)*sy/vy
            dy[1] = mxdy-(pts[1][1]*sy%gz)*sy/vy
            
        mx = 1 if dx[1]<dx[0] else 0        # Find min_x
        my = 1 if dy[1]<dy[0] else 0        # Find min_y

        # March dx/dy as specified in paper and stop marching when a collision
        # occurs. Keep track of what collision happened first and whether it
        # was a horizontal or vertical collision.

        while not (cpb[0] and cpb[1]) and ctr:
            if dx[mx] < dy[my]:
                gpts[mx][0] += sx
                if self.check_solid(grid[gpts[mx][0],gpts[mx][1]]):
                    cpb[mx] = 'x'
                    ctr = 3
                    hc[mx] = hc[1-mx]+1
                    if hc[my]: my = 1 - my
                else: 
                    dx[mx] += mxdx
                    bgpt = gpts[mx][0],gpts[mx][1]
                    if not bpt or bpt[-1] != bgpt: bpt.append(bgpt)
                if not hc[1-mx]: mx = 1 - mx
            else:
                gpts[my][1] += sy
                if self.check_solid(grid[gpts[my][0],gpts[my][1]]):
                    cpb[my] = 'y'
                    ctr = 3
                    hc[my] = hc[1-my]+1
                    if hc[mx]: mx = 1 - mx
                else: 
                    dy[my] += mxdy
                    bgpt = gpts[mx][0],gpts[mx][1]
                    if not bpt or bpt[-1] != bgpt: bpt.append(bgpt)
                if not hc[1-my]: my = 1 - my        
            ctr -= 1

        # From the three scenarios mentioned above
        # Find collision by X or Y axis
        # Find collsiion is at p1 or p2
        angles = [(math.pi-angle)%math.tau, (-angle)%math.tau]
        cpt = 0
        # Get position/angle based on scenario
        if VMath.equals(gpts[mx],gpts[my]) and cpb[0] and cpb[1] and cpb[0] != cpb[1]:
            grid_corner = (gpts[0][0]*gz+0.5*gz*(1-sx),gpts[0][1]*gz+0.5*gz*(1-sy))
            bp0 = collision.line_intersection2(pts[0],angle,grid_corner,angle-math.pi/2)
            bpd = VMath.distance(bp0, grid_corner)
            if VMath.distance(bp0, grid_corner) > 0.5*bh: ca = 1 if sx*sy>0 else 0
            else: ca = 0 if sx*sy>0 else 1
            new_pos = VMath.subtract(bp0, offset[0])
            new_angle = angles[1-ca]
        else:   
            if hc[0] == 1: ca = ord(cpb[0]) - ord('x')
            else: ca = ord(cpb[1]) - ord('x')                           
            if hc[1] == 1: cpt = 1 
            d = dy[cpt] if ca else dx[cpt]
            pt = (pts[cpt][0]+vx*d,pts[cpt][1]+vy*d)
            new_pos = VMath.subtract(pt, offset[cpt])
            new_angle = angles[ca]

        # Check if this point has passed the tank
        cpt = None
        pts2 = VMath.sum(new_pos,offset[0]), VMath.sum(new_pos,offset[1])
        if bounce < constant.BULLET_BOUNCE:
            cpt = collision.line_square((pts[0], pts2[0]), hitbox)
            if cpt: cpt = VMath.subtract(cpt,offset[0])
            else:
                cpt = collision.line_square((pts[1], pts2[1]), hitbox)
                if cpt: cpt = VMath.subtract(cpt,offset[1])

        if cpt: 
            bep.append(cpt)
            ct = True
            gx, gy = int(cpt[0]/gz), int(cpt[1]/gz)
        else:
            bep.append(new_pos)
            if bounce:
                bpf, bepf, ctf = self.beamV3(grid, new_pos, new_angle, hitbox, bounce-1, old_angle)
                bep.extend(bepf)
                bp.extend(bpf)
                if ctf: ct = True

        if not cpt: 
            for p in bpt: bp.append((p,bounce))
        else:
            for p in bpt:
                if p[0]*sx < gx*sx and p[1]*sy < gy*sy: bp.append((p,bounce))

        return bp, bep, ct
示例#11
0
 def _get_radius(self):
     r = 0
     for point in self._hitbox:
         dist = VMath.distance((0, 0), point)
         if dist > r: r = dist
     return r
示例#12
0
 def move(self, distance):
     self.position = VMath.translate(self.position, distance, self._angle)
示例#13
0
 def rotate(self, angle):
     self._hitbox = VMath.rotate(self._hitbox, angle)
     self._angle = (self._angle + angle) % math.tau
示例#14
0
def _get_edges(points):
    edges = []
    for i in range(4):
        edges.append(VMath.subtract(points[i],points[(i+1)%4]))
    return edges
示例#15
0
def _point_in_line(point, line):
    b,(a,c) = point,line
    d_ab = VMath.distance(a,b)
    d_bc = VMath.distance(b,c)
    d_ac = VMath.distance(a,c)
    return not round(abs(d_ab + d_bc - d_ac), constant.PRECISION) 
示例#16
0
def circle(o1, o2, extra=0):
    dist = VMath.distance(o1.position, o2.position)
    if o1.radius + o2.radius + extra> dist:
        return True
    return False