def shoot_shotgun(self, bodies): shots = list() for i in range(7): shotpos = copy.copy(self.pos) shotvel = 300 + (2 * random.random() - 1) * 100 shotdir = copy.copy( self.rot[0]) + (2 * random.random() - 1) * c.PI / 12 shotpos[0] += Vector(math.cos(self.rot[0]), math.sin( self.rot[0])) * self.radius[0] shotpos[1] += Vector(math.cos(shotdir), math.sin(shotdir)) * shotvel shot = Shot(ipos=shotpos, imass=[0.001], iradius=[3], friction=1, elasticity=0.2, colour=self.colour, damage=20, health=20, duration=100) bodies.append(shot) self.pos[1] -= Vector( math.cos(shotdir), math.sin(shotdir)) * shotvel * shot.mass[0] / self.mass[0] self.colliding_with.append(shot) shot.colliding_with.append(self) shots.append(shot) for shot in shots: others = copy.copy(shots) others.remove(shot) shot.colliding_with += others self.shot_cooldown = self.ishot_cooldown
def get_col_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: if self.test_collision(other): selfpara, selfperp = self.pos[1].resolve_about(self.pos[0] - other.pos[0]) otherpara, otherperp = other.pos[1].resolve_about(self.pos[0] - other.pos[0]) m1, m2 = self.mass[0], other.mass[0] selfparanew = (m1 - m2) / (m1 + m2) * selfpara + 2 * m2 / ( m1 + m2) * otherpara vf = selfparanew * self.elasticity + selfperp a += (vf - self.pos[1]) / c.DT if self.health is not None and other.damage is not None: if isinstance(self, Player_class.Player) and isinstance( other, Player_class.Player): damage = round(other.damage * (self.pos[1] - other.pos[1]).norm / 1200) if other.pos[1].norm > self.pos[1].norm * 4: damage *= 2 else: damage = other.damage self.health -= damage if isinstance(self, Player_class.Player): Body.damage_markers.append([ damage, self.pos[0] - Vector(20, 20), other.colour[0], max(min(damage, 100), 30) ]) return a
def __init__(self, ipos=[Vector(), Vector()], imass=[0], icharge=[0], iradius=[2], iseparation=[0], ialignment=[0], icohesion=[0], iview_radius=[0], friction=0, elasticity=1, colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)), health=None, damage=None): """self.pos is a list of all derivatives of an object's position up to the constant term etc""" self.prev_pos, self.pos, self.next_pos = None, ipos, None # (x, dx,...) self.prev_mass, self.mass, self.next_mass = None, imass, None # (m, dm,...) self.prev_charge, self.charge, self.next_charge = None, icharge, None # (q, dq,...) self.prev_radius, self.radius, self.next_radius = None, iradius, None # (r, dr,...) self.prev_cohesion, self.cohesion, self.next_cohesion = None, icohesion, None # (coh, dcoh,...) self.prev_alignment, self.alignment, self.next_alignment = None, ialignment, None # (ali, dali,...) self.prev_separation, self.separation, self.next_separation = None, iseparation, None # (sep, dsep,...) self.prev_view_radius, self.view_radius, self.next_view_radius = None, iview_radius, None # (rad, drad,...) self.friction = friction # linear friction self.elasticity = elasticity # collision elasticity self.colour = colour # (light, middle, dark) self.health = health self.damage = damage """nonzero masses have extra collision acceleration from any masses they are touching""" self.colliding_with = list( ) # list of objects currently being collided with
def get_ctrl_acc(self, other_bodies): a = Vector(0, 0) if self.controls is not None: if self.thrust == 1: a += self.controls[0][1] * Vector(math.cos(self.rot[0]), math.sin(self.rot[0])) if self.thrust == -1: a += self.controls[2][1] * Vector(math.cos(self.rot[0]), math.sin(self.rot[0])) return a
def draw(self, screen, fixed_to_screen=False, colour=None): if self.pos is not None: if colour is None: colour = self.colour if fixed_to_screen: P = self.pos[0] else: P = self.pos[0] - Body.cmp pygame.draw.circle(screen, colour[0], round(P + Vector(*screen.get_size()) / 2), round(self.radius[0])) if self.radius[0] >= 3: pygame.draw.circle(screen, colour[1], round(P + Vector(*screen.get_size()) / 2), round(self.radius[0] - 2))
def get_sep_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.SEP * self.separation[0] * other.separation[ 0] / distance**2 return a
def get_grav_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.GRAV * other.mass[0] * ( distance**2 + radii**4 * distance**-2)**-1 return a
def get_coh_acc(self, other_bodies): a = Vector(0, 0) sum_position = sum_cohesion = 0 for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: sum_position += other.cohesion[0] * other.pos[0] sum_cohesion += other.cohesion[0] if sum_cohesion > 0: a = c.COH * sum_position / sum_cohesion - self.pos[0] return a
def set_next(self, screen, dt, other_bodies): """ calculates a body's attributes after a frame using its derivatives """ self.next_pos = list() for i in range(max(len(self.pos) - 1, 2)): if len(self.pos) > 2 or i == 0: dxi = self.pos[i + 1] * dt else: dxi = Vector(0, 0) if i == 1: # change in velocity for acc_func in self.acc_funcs: # self.draw_vector(screen, acc_func(self, other_bodies)) a = acc_func(self, other_bodies) dxi += a * dt self.next_pos.append(self.pos[i] + dxi) if len(self.pos) > 2: self.next_pos.append(self.pos[-1]) self.next_mass = list() for i in range(len(self.mass) - 1): dxi = self.mass[i + 1] * dt self.next_mass.append(self.mass[i] + dxi) self.next_mass.append(self.mass[-1]) self.next_charge = list() for i in range(len(self.charge) - 1): dxi = self.charge[i + 1] * dt self.next_charge.append(self.charge[i] + dxi) self.next_charge.append(self.charge[-1]) self.next_radius = list() for i in range(len(self.radius) - 1): dxi = self.radius[i + 1] * dt self.next_radius.append(self.radius[i] + dxi) self.next_radius.append(self.radius[-1]) self.next_cohesion = list() for i in range(len(self.cohesion) - 1): dxi = self.cohesion[i + 1] * dt self.next_cohesion.append(self.cohesion[i] + dxi) self.next_cohesion.append(self.cohesion[-1]) self.next_alignment = list() for i in range(len(self.alignment) - 1): dxi = self.alignment[i + 1] * dt self.next_alignment.append(self.alignment[i] + dxi) self.next_alignment.append(self.alignment[-1]) self.next_separation = list() for i in range(len(self.separation) - 1): dxi = self.separation[i + 1] * dt self.next_separation.append(self.separation[i] + dxi) self.next_separation.append(self.separation[-1])
def get_ali_acc(self, other_bodies): a = Vector(0, 0) sum_velocity = sum_alignment = 0 for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: sum_velocity += other.alignment[0] * other.pos[1] sum_alignment += other.alignment[0] if sum_alignment > 0: a = c.ALI * sum_velocity / sum_alignment - self.pos[1] return a
def get_stat_acc(self, other_bodies): a = Vector(0, 0) if self.mass[0] != 0: for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.STAT * self.charge[0] * other.charge[ 0] / (distance**2 + radii**4 * distance**-2) / self.mass[0] return a
def update_cmp(cls, bodies, fast=False): cmp = Vector(0, 0) if len(bodies) != 0: cmm = 0 for body in bodies: cmp += body.mass[0] * body.pos[0] cmm += body.mass[0] if cmm > 0: cmp /= cmm if fast: Body.cmp = cmp else: Body.cmp = (7 * Body.cmp + cmp) / 8
def get_mag_acc(self, other_bodies): a = Vector(0, 0) if self.mass[0] != 0: for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: radius = (self.pos[0] - other.pos[0]).normalize() direction = self.pos[1].rotate(c.PI / 2) radii = self.radius[0] + other.radius[0] axb = other.pos[1].cross(radius) a += direction * c.MAG * self.charge[0] * other.charge[ 0] * axb / (distance**2 + radii**4 * distance**-2) / self.mass[0] return a
def shoot_gun(self, bodies): shotpos = copy.copy(self.pos) shotvel = 225 shotdir = self.rot[0] shotpos[0] += Vector(math.cos(shotdir), math.sin(shotdir)) * self.radius[0] shotpos[1] += Vector(math.cos(shotdir), math.sin(shotdir)) * shotvel shot = Shot(ipos=shotpos, imass=[0.1], iradius=[5], friction=1 / 2000, elasticity=0.2, colour=self.colour, damage=40, health=60, duration=300) bodies.append(shot) self.pos[1] -= Vector( math.cos(shotdir), math.sin(shotdir)) * shotvel * shot.mass[0] / self.mass[0] self.colliding_with.append(shot) shot.colliding_with.append(self) self.shot_cooldown = self.ishot_cooldown
def delta_angle_test(): pid = PID() wanted = Vector(0.0, 0.0) current = Vector(0.0, 0.0) piv = Vector() pid.set_wanted(wanted) for i in range(0, 360, 1): wanted.set(0.0, i) pid.set_wanted(wanted) for j in range(0, 360, 1): current.set(0.0, j) piv = pid._get_delta_angle(current) if j in (45, 90, 135, 180, 225, 270, 315, 360) and i in (45, 90, 135, 180, 225, 270, 315, 360): print("current angle: ", j, " | wanted angle: ", i) print("closest angle: ", round(piv, 0)) print("")
def draw_bodies(cls, screen, bodies, fixed_to_screen=False, colour=None): for body in bodies: body.draw(screen, fixed_to_screen, colour) if not fixed_to_screen: for damage_marker in cls.damage_markers: damage_marker[3] -= 1 if damage_marker[3] >= 0: surface = pygame.font.SysFont("Consolas", 15).render( str(damage_marker[0]), True, damage_marker[2]) screen.blit( surface, round(damage_marker[1] - Body.cmp + Vector(*screen.get_size()) / 2)) else: cls.damage_markers.remove(damage_marker)
def __init__(self, ipos=[Vector()], imass=[0], icharge=[0], iradius=[2], iseparation=[0], ialignment=[0], icohesion=[0], iview_radius=[0], friction=0, elasticity=1, irot=[0, 0], colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)), rotfriction=0, controls=None, shot_type=0, health=None, damage=None, stocks=None): self.ipos = ipos self.imass = imass self.icharge = icharge self.iradius = iradius self.icohesion = icohesion self.ialignment = ialignment self.iseparation = iseparation self.iview_radius = iview_radius self.irot = irot self.ihealth = health self.istocks = stocks self.ishot_cooldown = (40, 120)[shot_type] """initiates a player body with a rotation and controls""" super().__init__(ipos, imass, icharge, iradius, iseparation, ialignment, icohesion, iview_radius, friction, elasticity, colour, health, damage) self.thrust = 0 self.turning = 0 self.prev_rot, self.rot, self.next_rot = None, irot, None self.rotfriction = rotfriction self.controls = controls self.shot_cooldown = (40, 120)[shot_type] self.shot_type = shot_type self.stocks = stocks
def draw(self, screen, fixed_to_screen, colour=None): if colour is None: colour = self.colour Body.draw(self, screen, fixed_to_screen, colour) if self.radius[0] >= 3: P = self.pos[0] + self.radius[0] * Vector(math.cos(self.rot[0]), math.sin(self.rot[0])) if not fixed_to_screen: P -= Body.cmp pygame.draw.circle(screen, colour[1], round(P + Vector(*screen.get_size()) / 2), 3) if self.stocks is not None: for i in range(self.stocks + 1): pygame.draw.circle( screen, colour[0], round(self.ipos[0] + Vector(*screen.get_size()) / 2 + i * Vector(20, 0) + Vector(-30, -15)), 6) if self.health > 0: P1 = round(self.ipos[0] + Vector(*screen.get_size()) / 2 + Vector(-50, 0)) P2 = round(self.ipos[0] + Vector(*screen.get_size()) / 2 + Vector(max(self.health, 0) - 50, 0)) pygame.draw.line(screen, colour[1], P1, P2, 2)
def __init__(self, ipos=[Vector()], imass=[0], icharge=[0], iradius=[2], iseparation=[0], ialignment=[0], icohesion=[0], iview_radius=[0], friction=0, elasticity=1, colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)), health=None, damage=None, duration=None): super().__init__(ipos, imass, icharge, iradius, iseparation, ialignment, icohesion, iview_radius, friction, elasticity, colour, health, damage) self.duration = duration
def _set_derivator(self, derivator = Vector(0.0,0.0)): self.Derivator = derivator
def _set_integrator(self, integrator = Vector(0.0,0.0)): self.Integrator = integrator
def __init__(self): '''Initialises values for future PID calculations''' self.Kp=Vector(0.85,1.0) #mindre self.Ki=Vector(0.0,0.0) #set for test self.Kd=Vector(0.1,0.0) #set for test self.Derivator=Vector() self.Integrator=Vector() self.Integrator_max=Vector(50.0,45.0) self.Integrator_min=Vector(-50.0,-45.0) self.pid_max = Vector(5.0, 45.0) self.wanted_vector=Vector() self.error=Vector() self.delta_angle = 0.0
class PID : '''Class to serve ass a PID regulator between wanted and current vector''' def __init__(self): '''Initialises values for future PID calculations''' self.Kp=Vector(0.85,1.0) #mindre self.Ki=Vector(0.0,0.0) #set for test self.Kd=Vector(0.1,0.0) #set for test self.Derivator=Vector() self.Integrator=Vector() self.Integrator_max=Vector(50.0,45.0) self.Integrator_min=Vector(-50.0,-45.0) self.pid_max = Vector(5.0, 45.0) self.wanted_vector=Vector() self.error=Vector() self.delta_angle = 0.0 def update(self, current_vector): '''Calculates new PID value based on current vector input Args: current_vector: Vector containing current movement and bearing Returns: Vector containing new speed and angle to boat calculated with PID ''' self.error.magnitude = self.wanted_vector.magnitude - current_vector.magnitude self.error.angle = self._get_delta_angle(current_vector) self.P_value = self.error * self.Kp self.D_value = ( self.error - self.Derivator) * self.Kd self.Derivator = self.error self.Integrator = self.Integrator + self.error #look at what value we get if self.Integrator.magnitude > self.Integrator_max.magnitude or self.Integrator.angle > self.Integrator_max.angle: self.Integrator = self.Integrator_max elif self.Integrator.magnitude < self.Integrator_min.magnitude or self.Integrator.angle < self.Integrator_min.angle: self.Integrator = self.Integrator_min self.I_value = self.Integrator * self.Ki pid = self.P_value + self.I_value + self.D_value #linearity fix pid.magnitude = current_vector.magnitude + pid.magnitude #max speed fix if pid.magnitude > self.pid_max.magnitude: pid.magnitude = self.pid_max.magnitude elif pid.magnitude < -1.0*self.pid_max.magnitude: pid.magnitude = -1.0*self.pid_max.magnitude if pid.angle > self.pid_max.angle: pid.angle = self.pid_max.angle elif pid.angle < -1.0*self.pid_max.angle: pid.angle = -1.0*self.pid_max.angle return pid def set_wanted(self, wanted): '''Function for setting wanted vector for PID object Args: wanted: Vector containing wanted movement in speed and angle relative to North ''' if isinstance(wanted, Vector) or isinstance(wanted, tuple): self.wanted_vector = wanted self.Integrator.set(0.0,0.0) #For resetting if isinstance(wanted, list) or isinstance(wanted, float): self.wanted_vector.set(wanted[0], wanted[1]) def _set_integrator(self, integrator = Vector(0.0,0.0)): self.Integrator = integrator def _set_derivator(self, derivator = Vector(0.0,0.0)): self.Derivator = derivator def _get_delta_angle(self, current_vector): deltaXY = m.radians(self.wanted_vector.angle - current_vector.angle ) self.delta_angle = m.degrees(m.atan2(m.sin(deltaXY), m.cos(deltaXY))) return self.delta_angle
if len(active_players) >= 1: for body in bodies: body.update(1 / FPS, bodies, G, Q, keys) for body in bodies: body.step(bodies, screen_dims, frame, damage_markers) screen.fill((0, 0, 0)) if not in_play: for i, player in enumerate(players): if player not in bodies and player not in active_players: player.draw(screen, bodies, players) screen.blit( FONT.render(player_names[players.index(player)], True, player.colour), player.defP + Vector(-37, -50) + (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0)) screen.blit( FONT.render(player.shot_type, True, player.colour), player.defP + Vector(-37, 18) + (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0)) screen.blit( FONT.render("{} wins".format(wins[i]), True, player.colour), player.defP + Vector(-37, 40) + (-1, 1)[players.index(player) in (1, 2)] * Vector(50, 0)) if winner is not None: surface = FONT.render( "{} wins as {}!".format(player_names[winner[0]], winner[2]), True, winner[1]) rect = (int(screen_dims[0] / 2 - surface.get_rect().w / 2), int(screen_dims[1] / 2 - surface.get_rect().h / 2))
class Body(object): cmp = Vector(0, 0) damage_markers = list() def __init__(self, ipos=[Vector(), Vector()], imass=[0], icharge=[0], iradius=[2], iseparation=[0], ialignment=[0], icohesion=[0], iview_radius=[0], friction=0, elasticity=1, colour=((255, 255, 255), (170, 170, 170), (85, 85, 85)), health=None, damage=None): """self.pos is a list of all derivatives of an object's position up to the constant term etc""" self.prev_pos, self.pos, self.next_pos = None, ipos, None # (x, dx,...) self.prev_mass, self.mass, self.next_mass = None, imass, None # (m, dm,...) self.prev_charge, self.charge, self.next_charge = None, icharge, None # (q, dq,...) self.prev_radius, self.radius, self.next_radius = None, iradius, None # (r, dr,...) self.prev_cohesion, self.cohesion, self.next_cohesion = None, icohesion, None # (coh, dcoh,...) self.prev_alignment, self.alignment, self.next_alignment = None, ialignment, None # (ali, dali,...) self.prev_separation, self.separation, self.next_separation = None, iseparation, None # (sep, dsep,...) self.prev_view_radius, self.view_radius, self.next_view_radius = None, iview_radius, None # (rad, drad,...) self.friction = friction # linear friction self.elasticity = elasticity # collision elasticity self.colour = colour # (light, middle, dark) self.health = health self.damage = damage """nonzero masses have extra collision acceleration from any masses they are touching""" self.colliding_with = list( ) # list of objects currently being collided with @classmethod def update_cmp(cls, bodies, fast=False): cmp = Vector(0, 0) if len(bodies) != 0: cmm = 0 for body in bodies: cmp += body.mass[0] * body.pos[0] cmm += body.mass[0] if cmm > 0: cmp /= cmm if fast: Body.cmp = cmp else: Body.cmp = (7 * Body.cmp + cmp) / 8 @classmethod def update_bodies(cls, screen, dt, bodies, presses=None): """ the main function of the class to iterate through its members updating their attributes in realtime """ for body in bodies: body.update(screen, dt, bodies, presses) for body in bodies: body.step_next() """ y -> dy -> y + dy = y2 -> dy2 -> (dy + dy2) / 2 = better dy -> y + dy = better y2 (VALID) y -> dy -> y + dy = y2 -> dy2 -> y2 + dy2 = y3 -> (y2 + y3) / 2 = better y2 (NOT VALID) (y3 - y) / 2 = better dy -> y + dy = better y2 = (y + y3) / 2 """ def update(self, screen, dt, bodies, presses): other_bodies = copy.copy(bodies) other_bodies.remove(self) self.set_next(screen, dt, other_bodies) def set_next(self, screen, dt, other_bodies): """ calculates a body's attributes after a frame using its derivatives """ self.next_pos = list() for i in range(max(len(self.pos) - 1, 2)): if len(self.pos) > 2 or i == 0: dxi = self.pos[i + 1] * dt else: dxi = Vector(0, 0) if i == 1: # change in velocity for acc_func in self.acc_funcs: # self.draw_vector(screen, acc_func(self, other_bodies)) a = acc_func(self, other_bodies) dxi += a * dt self.next_pos.append(self.pos[i] + dxi) if len(self.pos) > 2: self.next_pos.append(self.pos[-1]) self.next_mass = list() for i in range(len(self.mass) - 1): dxi = self.mass[i + 1] * dt self.next_mass.append(self.mass[i] + dxi) self.next_mass.append(self.mass[-1]) self.next_charge = list() for i in range(len(self.charge) - 1): dxi = self.charge[i + 1] * dt self.next_charge.append(self.charge[i] + dxi) self.next_charge.append(self.charge[-1]) self.next_radius = list() for i in range(len(self.radius) - 1): dxi = self.radius[i + 1] * dt self.next_radius.append(self.radius[i] + dxi) self.next_radius.append(self.radius[-1]) self.next_cohesion = list() for i in range(len(self.cohesion) - 1): dxi = self.cohesion[i + 1] * dt self.next_cohesion.append(self.cohesion[i] + dxi) self.next_cohesion.append(self.cohesion[-1]) self.next_alignment = list() for i in range(len(self.alignment) - 1): dxi = self.alignment[i + 1] * dt self.next_alignment.append(self.alignment[i] + dxi) self.next_alignment.append(self.alignment[-1]) self.next_separation = list() for i in range(len(self.separation) - 1): dxi = self.separation[i + 1] * dt self.next_separation.append(self.separation[i] + dxi) self.next_separation.append(self.separation[-1]) def step_next(self): """ uses the calculated next attributes to shift to the next step """ self.prev_pos, self.pos, self.next_pos = self.pos, self.next_pos, None self.prev_mass, self.mass, self.next_mass = self.mass, self.next_mass, None self.prev_charge, self.charge, self.next_charge = self.charge, self.next_charge, None self.prev_radius, self.radius, self.next_radius = self.radius, self.next_radius, None self.prev_cohesion, self.cohesion, self.next_cohesion = self.cohesion, self.next_cohesion, None self.prev_alignment, self.alignment, self.next_alignment = self.alignment, self.next_alignment, None self.prev_separation, self.separation, self.next_separation = self.separation, self.next_separation, None def get_grav_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.GRAV * other.mass[0] * ( distance**2 + radii**4 * distance**-2)**-1 return a def get_stat_acc(self, other_bodies): a = Vector(0, 0) if self.mass[0] != 0: for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.STAT * self.charge[0] * other.charge[ 0] / (distance**2 + radii**4 * distance**-2) / self.mass[0] return a def get_mag_acc(self, other_bodies): a = Vector(0, 0) if self.mass[0] != 0: for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: radius = (self.pos[0] - other.pos[0]).normalize() direction = self.pos[1].rotate(c.PI / 2) radii = self.radius[0] + other.radius[0] axb = other.pos[1].cross(radius) a += direction * c.MAG * self.charge[0] * other.charge[ 0] * axb / (distance**2 + radii**4 * distance**-2) / self.mass[0] return a def get_coh_acc(self, other_bodies): a = Vector(0, 0) sum_position = sum_cohesion = 0 for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: sum_position += other.cohesion[0] * other.pos[0] sum_cohesion += other.cohesion[0] if sum_cohesion > 0: a = c.COH * sum_position / sum_cohesion - self.pos[0] return a def get_ali_acc(self, other_bodies): a = Vector(0, 0) sum_velocity = sum_alignment = 0 for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: sum_velocity += other.alignment[0] * other.pos[1] sum_alignment += other.alignment[0] if sum_alignment > 0: a = c.ALI * sum_velocity / sum_alignment - self.pos[1] return a def get_sep_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: distance = self.pos[0].distance_to(other.pos[0]) if distance > 0 and distance <= self.view_radius[0]: direction = (other.pos[0] - self.pos[0]).normalize() radii = self.radius[0] + other.radius[0] a += direction * c.SEP * self.separation[0] * other.separation[ 0] / distance**2 return a def get_fric_acc(self, other_bodies): a = -self.pos[1] * self.friction return a def get_col_acc(self, other_bodies): a = Vector(0, 0) for other in other_bodies: if self.test_collision(other): selfpara, selfperp = self.pos[1].resolve_about(self.pos[0] - other.pos[0]) otherpara, otherperp = other.pos[1].resolve_about(self.pos[0] - other.pos[0]) m1, m2 = self.mass[0], other.mass[0] selfparanew = (m1 - m2) / (m1 + m2) * selfpara + 2 * m2 / ( m1 + m2) * otherpara vf = selfparanew * self.elasticity + selfperp a += (vf - self.pos[1]) / c.DT if self.health is not None and other.damage is not None: if isinstance(self, Player_class.Player) and isinstance( other, Player_class.Player): damage = round(other.damage * (self.pos[1] - other.pos[1]).norm / 1200) if other.pos[1].norm > self.pos[1].norm * 4: damage *= 2 else: damage = other.damage self.health -= damage if isinstance(self, Player_class.Player): Body.damage_markers.append([ damage, self.pos[0] - Vector(20, 20), other.colour[0], max(min(damage, 100), 30) ]) return a def test_collision(self, other): distance = self.pos[0].distance_to(other.pos[0]) if distance > 0: if distance <= self.radius[0] + other.radius[0]: if other not in self.colliding_with: self.colliding_with.append(other) return True elif other in self.colliding_with: self.colliding_with.remove(other) return False acc_funcs = [ get_grav_acc, get_stat_acc, get_mag_acc, get_coh_acc, get_ali_acc, get_sep_acc, get_fric_acc, get_col_acc ] @classmethod def draw_bodies(cls, screen, bodies, fixed_to_screen=False, colour=None): for body in bodies: body.draw(screen, fixed_to_screen, colour) if not fixed_to_screen: for damage_marker in cls.damage_markers: damage_marker[3] -= 1 if damage_marker[3] >= 0: surface = pygame.font.SysFont("Consolas", 15).render( str(damage_marker[0]), True, damage_marker[2]) screen.blit( surface, round(damage_marker[1] - Body.cmp + Vector(*screen.get_size()) / 2)) else: cls.damage_markers.remove(damage_marker) def draw(self, screen, fixed_to_screen=False, colour=None): if self.pos is not None: if colour is None: colour = self.colour if fixed_to_screen: P = self.pos[0] else: P = self.pos[0] - Body.cmp pygame.draw.circle(screen, colour[0], round(P + Vector(*screen.get_size()) / 2), round(self.radius[0])) if self.radius[0] >= 3: pygame.draw.circle(screen, colour[1], round(P + Vector(*screen.get_size()) / 2), round(self.radius[0] - 2)) def draw_vector(self, screen, vector): pygame.draw.line(screen, self.colour[1], round(self.pos[0]), round(self.pos[0] + vector), 2)