Created
July 20, 2018 14:33
-
-
Save bytezen/68cb262659b86396d20fcfeb919c6df8 to your computer and use it in GitHub Desktop.
Day4 - Seek Steering Behavior
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| from pygame.math import Vector2 | |
| import util | |
| class Behavior: | |
| NONE = 0 | |
| SEEK = 1 | |
| ## id_map = {0:'NONE',1:'SEEK'} | |
| ## | |
| ## def __init__(self,entity,behavior_type): | |
| ## self.type = behavior_type | |
| ## self.entity = entity | |
| ## | |
| ## def calculate(self, **kwargs): | |
| ## pass | |
| ## | |
| ## def __repr__(self): | |
| ## return self.id_map[self.type] | |
| ## | |
| class SteeringBehaviors: | |
| """ | |
| Class that encapsulates a host of steering behaviors that can be applied to | |
| an entity. Entitiies are of type Actor. See the Actor documentation for how to use | |
| """ | |
| def __init__(self, entity): | |
| """ | |
| Create a steering behaviors for entity. Entity is expected to be of | |
| type MovingEntity. | |
| """ | |
| self.entity = entity | |
| self._steering_force = Vector2() | |
| self._flags = Behavior.NONE | |
| def turn_on(self, behavior): | |
| self._flags |= behavior | |
| def turn_off(self, behavior): | |
| if self.is_on(behvior): | |
| self._flags ^= behavior | |
| def is_on(self,behavior): | |
| return self._flags & behavior > 0 | |
| def calculate(self): | |
| self._steering_force *= 0 | |
| if self.is_on(Behavior.SEEK): | |
| self._steering_force += self.seek(self.entity.seek_target) | |
| # clamp the steering_force to the maximum if necessary | |
| util._clamp_vector(self._steering_force,0, self.entity.max_force) | |
| return self._steering_force | |
| def seek(self,target): | |
| """Compute the steering force required to seek a target | |
| Args: | |
| target (Vector2, tuple, list): The coordinates of the target | |
| Returns: | |
| force (Vector2): The steering force for this behavior | |
| """ | |
| # our desired velocity is the velocity that would take us to the target | |
| # from our current position | |
| desiredVelocity = Vector2(target) - self.entity.pos | |
| # avoid dividing by 0 by making sure the length of the desired velocity | |
| # is greater than 0 | |
| if desiredVelocity.length() > 0.0001: | |
| desiredVelocity.normalize_ip() | |
| # scale the vector by our max_speed | |
| desiredVelocity *= self.entity.max_speed | |
| # the resulting force is the difference between where we want to go | |
| # and our current velocity | |
| return desiredVelocity - self.entity.vel | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pygame as pg | |
| from pygame.math import Vector2 | |
| import pygame.gfxdraw | |
| from behaviors import * | |
| import util | |
| pg.init() | |
| IMAGE_PATH = 'images/' | |
| MAX_SPEED = 100 | |
| MAX_FORCE = 1000 | |
| class Boid: | |
| """Class representing a game agent that can move | |
| Attrs: | |
| color ('red',''purple','green','blue') - color of the agent | |
| pos (Vector2) - the position of the agent | |
| mass (float) - the mass of the agent | |
| vel (Vector2) - velocity of the agent | |
| heading (Vector2) - heading of the agent | |
| side(Vector2) - the vector perpendicular to the agent | |
| max_force (float) - the maximum force that can be exerted on the agent | |
| max_speed (float) - the maximum speed that the agent can attain | |
| steering (Steering Behavior) - manages the behavior of the agent | |
| steering_force (Vector2) - the force resulting from the behaviors that | |
| are controlling the agent | |
| Mehtods: | |
| update(dtime) - updates the agents position using the time since the last update | |
| as the time value for the physics calculations | |
| draw(surface) - renders the agent to the screen | |
| seek_on () - turns seek behavior on | |
| seek_off () - turns seek behavior off | |
| """ | |
| _ID = 0 | |
| def __init__(self, pos=(0,0), vel=(0,0), mass = 1.0, color='red'): | |
| self.pos = Vector2(pos) | |
| self.mass = mass | |
| self.vel = Vector2(vel) | |
| self.heading = Vector2(1,0) | |
| self.side = self.heading.rotate(-90) | |
| self._orig_surface = _get_image(color) | |
| _,self.angle = self.heading.as_polar() | |
| self.steering = SteeringBehaviors(self) | |
| self.steering_force = Vector2() | |
| self.seek_target = Vector2(100,100) | |
| self.max_force = MAX_FORCE | |
| self.max_speed = MAX_SPEED | |
| @property | |
| def vel(self): | |
| return self._vel | |
| @vel.setter | |
| def vel(self,value): | |
| """sets the velocity, heading and side vectors | |
| copies the value to the velocity vector. If the value | |
| is the zero vector then heading and side are set to | |
| zero vectors too. | |
| """ | |
| self._vel = Vector2(value) | |
| #calculate the heading angle from the velocity | |
| _,self.angle = self._vel.as_polar() | |
| if self._vel.length() > 0.001: | |
| #calculate the heading from the velocity | |
| self.heading = self._vel.normalize() | |
| #calculate the side vector (perpendicular to the heading) | |
| self.side = self.heading.rotate(90) | |
| else: | |
| self.heading = Vector2() | |
| self.side = Vector2() | |
| def update(self, dtime): | |
| # get the steering force | |
| self.steering_force = self.steering.calculate() | |
| # accel = Force / mass | |
| accel = self.steering_force / self.mass | |
| # velocity | |
| # velocity = initialVelocity + acceleration * changeInTime | |
| self.vel = self.vel + accel * dtime | |
| # make sure that we don't exceed the speed limit | |
| util._clamp_vector(self.vel,0, self.max_speed) | |
| #position | |
| # position = positionInitial + velocity * changeInTime | |
| self.pos += self.vel * dtime | |
| #update the angle | |
| _,self.angle = self.vel.as_polar() | |
| def draw(self, surface): | |
| _surface, _rect = util._rotate(self._orig_surface, | |
| self.angle, | |
| self.pos, | |
| Vector2(0,0)) | |
| surface.blit(_surface, _rect) | |
| #draw the steering force | |
| f = self.steering_force / self.max_force | |
| f *= 50 | |
| pg.gfxdraw.line(surface, | |
| int(self.pos.x), | |
| int(self.pos.y), | |
| int(self.pos.x + f.x), | |
| int(self.pos.y + f.y), | |
| pg.Color(200,0,0)) | |
| def seek_on(self): | |
| self.steering.turn_on(Behavior.SEEK); | |
| def seek_off(self): | |
| self.steering.turn_off(Behavior.SEEK); | |
| def _get_image(color): | |
| if color == 'green': | |
| return pg.image.load(IMAGE_PATH+'boid3_small.png').convert_alpha() | |
| if color == 'blue': | |
| return pg.image.load(IMAGE_PATH+'boid2_small.png').convert_alpha() | |
| if color == 'purple': | |
| return pg.image.load(IMAGE_PATH+'boid4_small.png').convert_alpha() | |
| return pg.image.load(IMAGE_PATH+'boid1_small.png').convert_alpha() | |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pygame as pg | |
| from pygame.locals import * | |
| from pygame.math import Vector2 | |
| from boid import Boid | |
| import random | |
| ##from game_world import * | |
| import sys | |
| ##import game_world as world | |
| BOID_NUMBER = 1 | |
| pg.init() | |
| window = pg.display.set_mode((500,500), SRCALPHA | HWSURFACE) | |
| clock = pg.time.Clock() | |
| # load some images of boids | |
| ##boid_orig_surface = pg.image.load('images/boid1_small.png').convert_alpha() | |
| ##boid_surface = pg.transform.rotozoom(boid_orig_surface, -45, 1.0) | |
| ##offset = Vector2(-boid_orig_surface.get_width() * 0.5, | |
| ## -boid_orig_surface.get_height() * 0.5) | |
| ##offset.rotate_ip(-45) | |
| # load the target image | |
| target = pg.image.load('images/target.png').convert_alpha() | |
| target_pos = (250,250) | |
| boids = [] | |
| for i in range(BOID_NUMBER): | |
| boid = Boid(color=random.choice(['red','blue','green','purple'])) | |
| boid.pos = (random.randint(0, 500), random.randint(0,500)) | |
| boid.max_speed = random.randint(100,100) | |
| boid.seek_target = target_pos | |
| boid.seek_on() | |
| boids.append(boid) | |
| frame = 0 | |
| while True: | |
| window.fill(pg.Color('white')) | |
| for event in pg.event.get(): | |
| if event.type == MOUSEBUTTONUP: | |
| target_pos = event.pos | |
| for b in boids: | |
| b.seek_target = target_pos | |
| if event.type == QUIT: | |
| pg.display.quit() | |
| pg.quit() | |
| sys.exit() | |
| # draw the target on the screen | |
| window.blit(target,target.get_rect(center=target_pos)) | |
| # update and draw the agent on the screen | |
| for b in boids: | |
| b.update(clock.get_time() * .001) | |
| b.draw(window) | |
| clock.tick(60) | |
| pg.display.update() |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import pygame as pg | |
| from pygame.math import Vector2 | |
| def _rotate(surface, angle, pivot, offset): | |
| """Rotate the surface around the pivot point | |
| Args: | |
| surface (pygame.Surface): The surface that is to be rotated | |
| angle (float): Rotate by this angle | |
| pivot (tuple, list, pygame.math.Vector2): The pivot point | |
| offset (pygame.math.Vector2): This vector is added to the pivot point after the rotation | |
| Returns: | |
| rotated image (pygame.Surface): the new rotated surface | |
| rect (pygame.Rect): A rect with proper positioning for the rotated surface | |
| """ | |
| #rotate the image | |
| rotated_image = pg.transform.rotozoom(surface, -angle, 1) | |
| #rotate the offset vector | |
| rotated_offset = offset.rotate(angle) | |
| #Add the rotated offset vector to the center point to shift the rectangle | |
| rect = rotated_image.get_rect(center = pivot + rotated_offset) | |
| return rotated_image , rect | |
| def _clamp_vector(vector,min_length, max_length): | |
| """make sure that a vectors length is between minimum and maximum | |
| Args: | |
| vector (Vector2) - the vector to scale | |
| min_length (positive float) - the minimum length of the vector | |
| max_length (positive float) - the maximum length of the vector | |
| Return: | |
| the vector passed in is changed if necessary | |
| """ | |
| length = vector.length() | |
| if length > .001: | |
| if length < min_length: | |
| return vector.scale_to_length(min_length) | |
| elif length > max_length: | |
| return vector.scale_to_length(max_length) | |
| else: | |
| return vector | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment