def handle_key_pressed(key_map, elapsed_time, entities): mov_speed_player_vert = 2.0 mov_speed_player_horz = 2.0 if key_map[pygame.K_UP]: for e in entities: e.send_msg( UpdateTransform2DMessage( translate_vec=Vec2(0.0, -mov_speed_player_horz) ) ) if key_map[pygame.K_DOWN]: for e in entities: e.send_msg( UpdateTransform2DMessage( translate_vec=Vec2(0.0, mov_speed_player_horz) ) ) if key_map[pygame.K_LEFT]: for e in entities: e.send_msg( UpdateTransform2DMessage( translate_vec=Vec2(-mov_speed_player_vert, 0.0) ) ) if key_map[pygame.K_RIGHT]: for e in entities: e.send_msg( UpdateTransform2DMessage( translate_vec=Vec2(mov_speed_player_vert, 0.0) ) )
def init_ecs(screen_width, screen_height, verbose: bool = True): if verbose: print("Initializing ECS objects") # we put the player at 0,0 at let her view at positive x=+1 entities, components, systems = [], [], [] ctrans = CTransform2(pos=Point2(100, 100), v_dir=Vec2(1, 0)) cvel = CVelocity(velocity=5) cfov = CFieldOfView(fov_angle_rad=pi / 4.0, fov_distance=100.0) crnd = CRenderable(render_color=(64, 64, 228)) components.append(ctrans) components.append(cvel) components.append(cfov) components.append(crnd) e_player = EPlayer().add(ctrans).add(cvel).add(crnd).add(cfov) entities.append(e_player) box_pos_x = (50, 100, 200, 400, 450, 500, 550, 600, 620) box_pos_y = (100, 200, 50, 100, 300, 400, 250, 100, 225) box_width = (20, 50, 10, 40, 20, 30, 50, 10, 10) for i, p in enumerate(zip(box_pos_x, box_pos_y)): ctrans = CTransform2(p, Vec2(0, 0)) crnd = CRenderable( render_color=(200, 200, 200), shape=Shape.Square, width=box_width[i] ) components.append(ctrans) components.append(crnd) n1 = Vec2(0, 1) o1 = Vec2(p[0] + box_width[i] // 2, p[1]) p1 = Plane2(n1, o1) n2 = Vec2(1, 0) o2 = Vec2(p[0] + box_width[i], p[1] + box_width[i] // 2) p2 = Plane2(n2, o2) n3 = Vec2(0, -1) o3 = Vec2(p[0] + box_width[i] // 2, p[1] + box_width[i]) p3 = Plane2(n3, o3) n4 = Vec2(-1, 0) o4 = Vec2(p[0], p[1] + box_width[i] // 2) p4 = Plane2(n4, o4) planes = [p1, p2, p3, p4] ccol = CCollidable(planes=planes) components.append(ccol) e_box = EBox().add(ctrans).add(crnd).add(ccol) entities.append(e_box) systems.append(SInput()) systems.append(SRender(screen_width, screen_height)) return components, entities, systems
def __init__(self, w, h): super(BoxEntity, self).__init__() w2 = w // 2 h2 = h // 2 self._center = Point2(0, 0) self._width = w self._height = h self._w2 = w2 self._h2 = h2 self._p0 = self._center + Vec2(-w2, h2) self._p1 = self._center + Vec2(-w2, -h2) self._p2 = self._center + Vec2(w2, -h2) self._p3 = self._center + Vec2(w2, h2)
def setup_ecs() -> tuple: trans = Transform2DComponent(Point2(a=0, b=0)) player = PlayerEntity(r=20).add(trans) kb_sys = KeyboardInputSystem() t_sys = RenderSystem(screen_width=640, screen_height=480) entities = [player] components = [trans] # we add some very large boxes that are out of view initially, but gradually become visible if we move to the left or right # end of the world box_pos_x = (-300, -200, -50, 0, 100, 300, 250, 500, 1000, -1000) box_pos_y = (-300, 100, -200, 300, -300, 40, 40, 100, 200, 300) box_width = (10, 20, 50, 10, 30, 10, 40, 10, 20, 100, 100) debug = False if debug: box_pos_x = () box_pos_y = () box_width = () for (x, y, w) in zip(box_pos_x, box_pos_y, box_width): t = Transform2DComponent(pos=Vec2(x, y)) b = BoxEntity(w=w, h=w).add(t) entities.append(b) components.append(t) kb_sys.on_key_pressed = handle_key_pressed_event(kb_sys) systems = {"KEYBOARD": kb_sys, "TRANSFORM": t_sys} return entities, components, systems
def update(self, time_delta: float, entities: list): if not entities or len(entities) < 1: return None req_update = [e for e in entities if e.has_component_type("Transform2D")] self.clear_buffer(self._back_buffer, RenderSystem.C_WHITE) # before we update the entities in the world, we need to align the camera with the player. # here, we do a simple look for the player and position the camera at some offset from the player cam_offset = Vec2(-50, -50) player = [e for e in entities if isinstance(e, PlayerEntity)][0] # get the current transform - since this will determine our cam position p_trans: Transform2DComponent = player.get_of_type("Transform2D") p_pos = p_trans.position # since we want to align the camera with the players position, we transform him to the origin and apply the # camera local-world transformation. we do this by getting the inverse transformation of the player # and multiply this with the camera world transformation. # since the player is a translation only, we construct the inverse by hand, by taking the negative translation inverse_p = translate2D(-p_pos.x, -p_pos.y) cam_p = translate2D(cam_offset.x, cam_offset.y) final_p = cam_p * inverse_p # now here we set the camera world transformation, so that our overall transformation is ... # take the primitive, apply the normalized (0,0) to object-local transform # take the output and apply the object-local to world transform (final p) # we could achieve the same by multiplying final p with the transformation matrix stored in the entity's # transform component and applying that self._cam2d.mat_ws = final_p for e in req_update: # the transform component needs to be applied to the entity e_trans = e.get_of_type("Transform2D") # e_mat = final_p * e_trans.transform e_mat = e_trans.transform e_points = e.points eg_points = [e_mat * Vec3(p.x, p.y, 1.0) for p in e_points] ws_points = [self._cam2d.project_ws(p) for p in eg_points] nc_points = [self._cam2d.project_nc(p) for p in ws_points] fully_visible = all([self._cam2d.is_visible(p) for p in nc_points]) if not fully_visible: # print("{} is not fully visible".format(e)) next vp_points = [self._cam2d.project_vp(p) for p in nc_points] if isinstance(e, PlayerEntity): draw_player(self._back_buffer, e.radius, *vp_points) else: draw_box(self._back_buffer, *vp_points) self._screen_buffer.blit(self._back_buffer, (0, 0)) pygame.display.flip()
def handle_orientation_update(self, delta_angle: float): orientation = self.transform.viewing_direction rorientation = orientation.to_angle() % (pi * 2.0) aorientation = elisa.linalg.rad_to_angle(rorientation) s_angle = 1.0 if delta_angle >= 0 else -1.0 aorientation = aorientation + s_angle * elisa.linalg.rad_to_angle( abs(delta_angle) ) aorientation = aorientation % 360.0 orientation = elisa.linalg.angle_to_rad(aorientation) self.transform.viewing_direction = Vec2.from_angle(orientation)
def __init__(self, pos: Point2): super(Transform2DComponent, self).__init__("Transform2D") self._position = pos self._rotation = 0.0 self._scale = Vec2(1.0, 1.0)
def update(self, time_delta, entities): if len(entities) < 1: return None pvis = [e for e in entities if e.has_component_type("Collidable")] # for now we just render everyone having a position component # and do not care about the projection from world space coordinates to camera space renderables = [ e for e in entities if e.has_component_type("Renderable") and e.has_component_type("Transform2") ] # clear the backbuffer self.clear_back_buffer() for e in renderables: # get the position of the renderable and draw it - for now we assume that there is only one pos = e.get_of_type("Transform2").position rx = e.get_of_type("Renderable") col = rx.color if rx.shape == Shape.Point: pygame.draw.circle( self.back_buffer, col, (int(pos.x), int(pos.y)), 5, 0 ) elif rx.shape == Shape.Square: pygame.draw.rect( self.back_buffer, SRender.C_DARK_GRAY, (pos.x, pos.y, rx.width, rx.width), 1, ) else: pass fov_entity = [ e for e in entities if e.has_component_type("FieldOfView") and e.has_component_type("Transform2") ] if fov_entity: for e in fov_entity: transform = e.get_of_type("Transform2") fov = e.get_of_type("FieldOfView") pos, dvec = transform.position, transform.viewing_direction view_dist = fov.distance dvec_alpha = dvec.to_angle() fov_angle = fov.angle falpha1, falpha2 = (dvec_alpha - fov_angle / 2.0) % ( 2.0 * pi ), dvec_alpha + fov_angle / 2.0 % (2.0 * pi) N_sample = int(elisa.linalg.rad_to_angle(fov_angle)) v1 = Vec2.from_angle(falpha1) * view_dist v2 = Vec2.from_angle(falpha2) * view_dist dv = (v2 - v1) / N_sample pdvi = v1 pv1, pv2 = pos + v1, pos + v2 pygame.draw.polygon( self.back_buffer, SRender.C_VDARK_GRAY, [(pos.x, pos.y), (pv1.x, pv1.y), (pv2.x, pv2.y)], 0, ) for i in range(N_sample): # perform the collision test ppvi = pos + pdvi pdvi = pdvi + dv rayi = Ray2(origin=pos, direction=pdvi) for vis in pvis: visplanes = vis.get_of_type("Collidable").planes vpos = vis.get_of_type("Transform2").position vrx = vis.get_of_type("Renderable") intersects = False for planei in visplanes: r, t, _ip = elisa.linalg.intersection2(rayi, planei) if ( r and 0 <= t <= pdvi.length and elisa.linalg.inside_square2( vpos.x, vpos.y, vrx.width, _ip ) ): intersects = True break if intersects: pygame.draw.line( self.back_buffer, SRender.C_GRAY, (pos.x, pos.y), (ppvi.x, ppvi.y), 1, ) pygame.draw.rect( self.back_buffer, SRender.C_WHITE, (vpos.x, vpos.y, vrx.width, vrx.width), 1, ) pygame.draw.circle( self.back_buffer, SRender.C_DARK_GRAY, (int(pos.x), int(pos.y)), int(view_dist), 1, ) self.screen_buffer.blit(self.back_buffer, (0, 0)) pygame.display.flip() return None