def __init__(self, world: 'World', level: int, start_pos: Tuple[int, int], scroll: Vector = Vector(0.05, 0)): self.level = level self.start_pos = Vector( start_pos[0] * BLOCK_SIZE, (GRID_SIZE[1] - start_pos[1] - 1) * BLOCK_SIZE ) self.offset = Vector(0, 0) self.scroll = scroll self.items: List[LevelItem] = [] self.finished = False self.counter = 0 self.world = world self.world.source.last_active_level = self self.window_size = world.window.get_size() # Just some initialisation stuff here; less to compute later. self.background_offset = self.window_size[0] / 2 self.background_image = load_image(LEVEL_BACKGROUND_IMAGE) self.bg_size = (self.background_image.get_width(), self.background_image.get_height()) self.bg_center = (self.bg_size[0] / 2, self.bg_size[1] / 2)
def __init__(self, window: Window): super().__init__(window) from constants import BUTTON_SIZE window_size = window.get_size() window_center = (window_size[0] / 2, window_size[1] / 2) dpi_factor = window.hidpi_factor button_size = (BUTTON_SIZE[0] * dpi_factor, BUTTON_SIZE[1] * dpi_factor) button_font = Font('sans-serif', 16, dpi_factor) self.bg_image = util.load_image('assets/background.png') back_btn = Button(window, '[ Back ]', Vector(window_center[0] - button_size[0] / 2, window_size[1] * 2 / 3), Vector(*button_size), Color(0, 102, 255), Color(255, 255, 255), Color(0, 80, 230), Color(255, 255, 255), font=button_font) back_btn.set_click_handler(self.back) self.children.append(back_btn)
def get_bounds(self) -> BoundingBox: size = self.window.get_size() return BoundingBox( Vector(0, 0), Vector(size[0], 0), Vector(size[0], size[1]), Vector(0, size[1]), )
def spline_eval_based_on_y(spNodes, y_value): """ Given a spline represented by a bezier and a y value of interest. Return the (first) point on the spline matches that y value. """ # Find bezier segment of interest seg = -1 n_seg = len(spNodes) / 4 for i in range(n_seg): lower = spNodes[i * 4 + 0].y upper = spNodes[i * 4 + 3].y if y_value >= lower and y_value <= upper: seg = i break if seg == -1: print 'Did not find y value in the spline interval' print 'y= ', y_value if y_value < spNodes[0].y: # We are below the minimum y return ('FAIL', spNodes[0].y, 'FAIL') else: # We are abov the maximum y_value return ('FAIL', spNodes[-1].y, 'FAIL') b0 = Vector(spNodes[seg * 4 + 0].x, spNodes[seg * 4 + 0].y, spNodes[seg * 4 + 0].z) b1 = Vector(spNodes[seg * 4 + 1].x, spNodes[seg * 4 + 1].y, spNodes[seg * 4 + 1].z) b2 = Vector(spNodes[seg * 4 + 2].x, spNodes[seg * 4 + 2].y, spNodes[seg * 4 + 2].z) b3 = Vector(spNodes[seg * 4 + 3].x, spNodes[seg * 4 + 3].y, spNodes[seg * 4 + 3].z) Bx = [b0.x, b1.x, b2.x, b3.x] By = [b0.y, b1.y, b2.y, b3.y] Bz = [b0.z, b1.z, b2.z, b3.z] B = mySpline(Bx, By, Bz) funData = myData(B, y_value) for guess in init_guesses: x0 = guess[0] x1 = guess[1] t = secant(zero_fun_y, x0, x1, funData, limits=[0.0, 1.0]) if t != 'FAIL': break if t == 'FAIL': print 'There is a problem finding the t-parameter' sys.exit(-1) return bezier3D_eval(t, Bx, By, Bz)
def walk(steps): loc = Point(0, 0) direc = Vector(0, 1, 0) for turn, dist in moves(steps): if turn == 'R': direc = -Vector.k().cross(direc) else: direc = direc.cross(-Vector.k()) loc += dist * direc return loc
def __init__(self, window: Window): super().__init__(window) from constants import BUTTON_SIZE self.window_size = window.get_size() self.window_center = (self.window_size[0] / 2, self.window_size[1] / 2) dpi_factor = window.hidpi_factor button_size = (BUTTON_SIZE[0] * dpi_factor, BUTTON_SIZE[1] * dpi_factor) button_font = Font('sans-serif', 16, dpi_factor) self.bg_image = util.load_image('assets/background.png') self.bg_size = (self.bg_image.get_width(), self.bg_image.get_height()) self.bg_center = (self.bg_size[0] / 2, self.bg_size[1] / 2) self.logo = util.load_image('assets/logo.png') self.logo_size = (self.logo.get_width(), self.logo.get_height()) self.logo_center = (self.logo_size[0] / 2, self.logo_size[1] / 2) self.max_score = 0 self.last_active_level = None self.text_font = Font('monospace', 20, window.hidpi_factor) self.text_font_color = Color(255, 255, 255) # Template to create new button start_btn = Button(window, '[ Start ]', Vector(self.window_center[0] - button_size[0] / 2, self.window_center[1] - button_size[1]), Vector(*button_size), Color(0, 102, 255), Color(255, 255, 255), Color(0, 80, 230), Color(255, 255, 255), font=button_font) start_btn.set_click_handler(self.start) self.children.append(start_btn) help_btn = Button(window, '[ Help ]', Vector(self.window_center[0] - button_size[0] / 2, self.window_center[1] + button_size[1]), Vector(*button_size), Color(0, 102, 255), Color(255, 255, 255), Color(0, 80, 230), Color(255, 255, 255), font=button_font) help_btn.set_click_handler(self.help) self.children.append(help_btn)
def _on_click(self, pos: Tuple[int, int]): """ Called whenever the window receives a mouse click event. Passes the event to the handler. """ if self.handler is not None: self.handler.on_click(Vector(*pos)) if self._mouse_down: self._mouse_down = False if self.handler is not None: self.handler.on_mouse_up(Vector(*pos))
def walk2(steps): loc = Point(0, 0) visited = {loc} direc = Vector(0, 1, 0) for turn, dist in cycle(moves(steps)): if turn == 'R': direc = -Vector.k().cross(direc) else: direc = direc.cross(-Vector.k()) for _ in range(dist): loc += direc if loc in visited: return loc else: visited.add(loc)
def _on_drag(self, pos: Tuple[int, int]): """ Called whenever the window receives a mouse drag event. Passes the event to the handler. """ if not self._mouse_down: self._mouse_down = True self._last_mouse_pos = pos if self.handler is not None: self.handler.on_mouse_down(Vector(*pos)) else: if self.handler is not None: self.handler.on_drag(Vector(*self._last_mouse_pos), Vector(*pos))
def on_collide(self, player: 'Player'): # Perform a "hopping off" animation and disable collision player.on_ground = False player.is_dying = True player.vel = Vector( math.copysign(PLAYER_DEATH_VELOCITY[0], player.vel.x), -PLAYER_DEATH_VELOCITY[1])
def find_location(cp): right = Vector(1, 0, 0) down = Vector(0, 1, 0) loc = Point(0, 0) size = 100 right_edge = Vector(size - 1, 0, 0) bottom_edge = Vector(0, size - 1, 0) while not (cp(loc + right_edge) and cp(loc + bottom_edge)): while not cp(loc + bottom_edge): loc += right while not cp(loc + right_edge): loc += down return loc
def __init__(self, window: Window, text: str, pos: Vector, size: Vector = Vector(150, 50), bg: Color = Color(200, 200, 200), fg: Color = Color(20, 20, 20), bg_over: Color = Color(220, 220, 220), fg_over: Color = Color(20, 20, 20), border_size: int = 0, font: Font = Font('sans-serif', 15, HIDPI_FACTOR)): super().__init__(window) self._click_handler = None self.text = text self.pos = pos self.size = size # Initial colours self.bg = bg self.fg = fg self.bg_over = bg_over self.fg_over = fg_over # Extra self.font = font self.border_size = border_size # Center self.center = (self.pos[0] + self.size[0] / 2, self.pos[1] + self.size[1] / 2) # Get corners self.bounds = BoundingBox(pos, pos + size)
def combine_chains(chain1: EpicycleChain1D, chain2: EpicycleChain1D, tag_name, color='black'): assert chain1.canvas == chain2.canvas assert chain1.num_vectors == chain2.num_vectors rotation = (chain1.rotation + chain2.rotation) / 2 result_chain = EpicycleChain1D(chain1.canvas, chain1.origin_point, rotation, tag_name=tag_name) N = chain1.num_vectors c = result_chain.canvas result_chain.vectors = [Vector(Point(0, 0), Point(0, 0)) for i in range(N)] result_chain.epicycles = [ ( c.create_line(0, 0, 0, 0, tag=tag_name, fill=color), # , arrow=tk.LAST), c.create_oval(0, 0, 0, 0, tag=tag_name, outline=color)) for i in range(N) ] for i in range(N): result_chain.vectors[i] = chain1.vectors[i] + chain2.vectors[i] result_chain.num_vectors = N return result_chain
def get_bounds(self) -> BoundingBox: pos = self.get_pos() size = self.get_size() pos = Vector( pos[0] * BLOCK_SIZE, (GRID_SIZE[1] - pos[1] - 1) * BLOCK_SIZE, ) size = Vector( size[0] * BLOCK_SIZE, size[1] * BLOCK_SIZE, ) pos = pos - self.world.level.offset size = size return BoundingBox(pos, pos + size)
def paint(codes, start=0): c = Computer(codes) loc = Point(0, 0) direc = Vector(0, 1, 0) panels = {loc: start} while c.running: c.run([panels.get(loc, 0)]) turn = c.output.pop() color = c.output.pop() panels[loc] = color if turn == 0: # Turn left direc = Vector.k().cross(direc) elif turn == 1: # Turn right direc = direc.cross(Vector.k()) loc += direc return panels
def get_text_bounds(self, text: str) -> Vector: """ Returns to bounds of the text in this font. """ font_name = self.face if font_name in simplegui._SIMPLEGUIFONTFACE_TO_PYGAMEFONTNAME: font_name = simplegui._SIMPLEGUIFONTFACE_TO_PYGAMEFONTNAME[ font_name] bounds = pygame.font.SysFont(font_name, int(self.size * self.hidpi_factor)).size(text) return Vector(bounds[0], bounds[1])
def __init__(self, world: 'World'): super().__init__(world.window) self.world = world self.size = Vector(*PLAYER_SIZE) self.pos = world.level.start_pos self.last_pos = self.pos.copy() self.last_x = 0 self.vel = Vector(0, 0) self.accel = Vector(0, 0) self.on_ground = False self.jumping = False self.moving_x = False self.is_dying = False self.desired_platform = None self.score = 0 self.lives = 3 self.sprite_cols = 8 self.sprite = Sprite('assets/player.png', self.sprite_cols, 1) self.roll = 0
def merge_vectors(vectors1, vectors2): assert vectors1[0].p_from == vectors2[0].p_from assert len(vectors1) == len(vectors2) vectors = [] x_prev, y_prev = list(vectors1[0].p_from.get_iter()) for i in range(len(vectors1)): rel_vec1 = (vectors1[i].p_to[0] - vectors1[i].p_from[0], vectors1[i].p_to[1] - vectors1[i].p_from[1]) rel_vec2 = (vectors2[i].p_to[0] - vectors2[i].p_from[0], vectors2[i].p_to[1] - vectors2[i].p_from[1]) rel_vec = rel_vec1[0] + rel_vec2[0], rel_vec1[1] + rel_vec2[1] x_cur, y_cur = x_prev + rel_vec[0], y_prev + rel_vec[1] vectors.append(Vector(Point(x_prev, y_prev), Point(x_cur, y_cur))) x_prev, y_prev = x_cur, y_cur return vectors
def _init_epicycles(self, color): c = self.canvas self.epicycles = [ ( c.create_line(0, 0, 0, 0, tag=self.tag_name, fill=color), # , activefill=color, arrow=tk.LAST, arrowshape="10 20 10" c.create_oval(0, 0, 0, 0, tag=self.tag_name, outline=color, width=2)) for i in range(self.num_vectors) ] self.vectors = [ Vector(Point(0, 0), Point(0, 0)) for i in range(self.num_vectors) ]
def walk_scaffold(layout, robot): direcs = { '>': Vector(1, 0, 0), '<': Vector(-1, 0, 0), '^': Vector(0, -1, 0), 'V': Vector(0, 1, 0) } scaffold = '#' cur_loc = Point(*robot[:2]) cur_dir = direcs[robot[-1]] cur_run = 0 moves = [] while True: #print(cur_loc, cur_dir) next_loc = cur_loc + cur_dir if valid_index( layout, next_loc) and layout[next_loc.y][next_loc.x] == scaffold: cur_loc = next_loc cur_run += 1 else: if cur_run: moves.append(cur_run) cur_run = 0 # All of the directions are kinda flipped from expected because we're in a coordinate system # where +y is down. if cur_dir.x == 1: left = cur_loc.down right = cur_loc.up elif cur_dir.x == -1: left = cur_loc.up right = cur_loc.down elif cur_dir.y == 1: left = cur_loc.right right = cur_loc.left elif cur_dir.y == -1: left = cur_loc.left right = cur_loc.right if valid_index(layout, left) and layout[left.y][left.x] == scaffold: moves.append('L') cur_dir = cur_dir.cross(Vector.k()) elif valid_index(layout, right) and layout[right.y][right.x] == scaffold: moves.append('R') cur_dir = Vector.k().cross(cur_dir) else: break return ','.join(str(i) for i in moves)
def draw(self, canvas: simplegui.Canvas, pos: Vector, size: Optional[Vector] = None, index: Tuple[int, int] = (0, 0)): """ Draw the sprite on the canvas. """ source_center = [ self.sprite_size[i] * index[i] + self.sprite_center[i] for i in [0, 1] ] if size is not None: size = size.into_tuple() else: size = self.sprite_size canvas.draw_image(self.image, source_center, self.sprite_size, pos.into_tuple(), size)
def update_vectors_by_time(self, time): x_prev, y_prev = self.x0, self.y0 j = 0 self.fourier_vectors = sorted(self.fourier_vectors, key=lambda i: i.amp, reverse=True) for f_vec in self.fourier_vectors: # for f_vec in self.fourier_vectors: rad = f_vec.amp x_cur = x_prev + rad * math.cos(time * f_vec.freq + f_vec.phase + self.rotation) y_cur = y_prev + rad * math.sin(time * f_vec.freq + f_vec.phase + self.rotation) self.vectors[j] = Vector(Point(x_prev, y_prev), Point(x_cur, y_cur)) x_prev, y_prev = x_cur, y_cur j += 1 return Point(x_cur, y_cur) # last point of chain
def on_death(self): self.lives -= 1 if self.lives == 0: self.world.window.handler = self.world.source else: self.vel.x = 0 self.vel.y = 0 if self.desired_platform is None: self.pos = Vector(PLAYER_RESPAWN_X_OFFSET, -self.size.y) else: bounds = self.desired_platform.get_bounds() if bounds.max.x <= 0: self.pos = Vector(PLAYER_RESPAWN_X_OFFSET, -self.size.y) else: # Place player on platform they last touched self.pos = Vector( bounds.max.x - self.size.x - PLAYER_RESPAWN_X_OFFSET, bounds.min.y - self.size.y) self.is_dying = False
def bezier_3_spline(m, p): """ Given m+1 interpolation points, determine the m-segment, Bezier polyline that interpolates these points. Unlike the C-function, this returns a list of the beizer control points used to represent the spline. Further comments are in the accompanying C-file. Coded in Python: 06-Dec-2004 """ assert len(p) >= (m + 1) if m <= 0: print 'Cannot attempt to find a Bezier representation' print 'with ', m, ' segments.' return 'FAIL' tolerance = 1.0e-10 # Setup the initial guess for the weight points by using the # supplied points. This also sets the end points correctly. d = [] for node in p: # Use a Vector object which will not be stored in the Node.nodeList. d.append(Vector(node.x, node.y, node.z)) # Apply the Gauss-Seidel interation until the internal # weight points converge. for j in range(50): max_diff = 0.0 for i in range(1, m): old_p = d[i] d[i].x = 0.25 * (6.0 * p[i].x - d[i - 1].x - d[i + 1].x) d[i].y = 0.25 * (6.0 * p[i].y - d[i - 1].y - d[i + 1].y) d[i].z = 0.25 * (6.0 * p[i].z - d[i - 1].z - d[i + 1].z) dx = fabs(d[i].x - old_p.x) dy = fabs(d[i].y - old_p.y) dz = fabs(d[i].z - old_p.z) diff = dx + dy + dz if (diff > max_diff): max_diff = diff # end for i if (max_diff < tolerance): break # end for j if j >= 50: print 'Iterations did not converge while using the Guass-Seidel technique' print 'to find a bezier representation of a spline.' return 'FAIL' # Finally, calculate the Bezier segments an pack them away. bcp = range(4 * m) for i in range(m): bcp[i * 4 + 0] = copy(p[i]) bcp[i * 4 + 1] = Vector() bcp[i * 4 + 1].x = (2.0 * d[i].x + d[i + 1].x) / 3.0 bcp[i * 4 + 1].y = (2.0 * d[i].y + d[i + 1].y) / 3.0 bcp[i * 4 + 1].z = (2.0 * d[i].z + d[i + 1].z) / 3.0 bcp[i * 4 + 2] = Vector() bcp[i * 4 + 2].x = (d[i].x + 2.0 * d[i + 1].x) / 3.0 bcp[i * 4 + 2].y = (d[i].y + 2.0 * d[i + 1].y) / 3.0 bcp[i * 4 + 2].z = (d[i].z + 2.0 * d[i + 1].z) / 3.0 bcp[i * 4 + 3] = copy(p[i + 1]) return bcp
def test_vector(self): v1 = Vector(7, 4) v2 = Vector(-6, 3) self.assertEqual("%s" % v1, "Vector(7.0, 4.0)") self.assertEqual("%s" % v2, "Vector(-6.0, 3.0)") # length should be sqrt(7**2 + 4**2) = 8.062 self.assertAlmostEqual(v1.norm(), 8.062, places=3) # Create a unit vector and test its length v1u = v1.unit_vector() self.assertAlmostEqual(v1u.norm(), 1.00, places=3) v3 = v1 * 6.4 self.assertTrue(isinstance(v3, Vector)) # v3 length = 8.062(6.4) = 51.597 self.assertAlmostEqual(v3.norm(), 51.597, places=2) # v1 + v2 = (1, 7); length = 7.071 v4 = v1 + v2 self.assertAlmostEqual(v4.norm(), 7.071, places=3) # Dot product of v1, v2 = (7)(-6) + (4)(3) = -30 self.assertAlmostEqual(v1.dot(v2), -30.0, places=3) self.assertAlmostEqual(v2.dot(v1), -30.0, places=3) # Cross product of v1 x v2 = (7)(3) - (-6)(4) = 45 self.assertAlmostEqual(v1.cross(v2), 45.0, places=3) # Cross product of v2 x v1 = (-6)(4) - (7)(3) = -45 self.assertAlmostEqual(v2.cross(v1), -45.0, places=3) v5 = v1.perp() self.assertAlmostEqual(v5[0], -4.0, places=3) self.assertAlmostEqual(v5[1], 7.0, places=3)
class Level(object): """ A game level. """ def __init__(self, world: 'World', level: int, start_pos: Tuple[int, int], scroll: Vector = Vector(0.05, 0)): self.level = level self.start_pos = Vector( start_pos[0] * BLOCK_SIZE, (GRID_SIZE[1] - start_pos[1] - 1) * BLOCK_SIZE ) self.offset = Vector(0, 0) self.scroll = scroll self.items: List[LevelItem] = [] self.finished = False self.counter = 0 self.world = world self.world.source.last_active_level = self self.window_size = world.window.get_size() # Just some initialisation stuff here; less to compute later. self.background_offset = self.window_size[0] / 2 self.background_image = load_image(LEVEL_BACKGROUND_IMAGE) self.bg_size = (self.background_image.get_width(), self.background_image.get_height()) self.bg_center = (self.bg_size[0] / 2, self.bg_size[1] / 2) def get_score(self): return self.world.player.score def add_item(self, item: LevelItem): """ Adds the item to the level. """ self.items.append(item) def finish(self): """ Finishes the level. """ self.finished = True def render(self, world: 'World', canvas: simplegui.Canvas): """ Called on every game tick to render the level. """ # Draw background if LEVEL_USE_BACKGROUND: center_dest1 = ( -(self.offset.x % self.window_size[0]) + self.background_offset, self.window_size[1] / 2 ) center_dest2 = ( center_dest1[0] + self.window_size[0], center_dest1[1] ) canvas.draw_image(self.background_image, self.bg_center, self.bg_size, center_dest1, self.window_size) canvas.draw_image(self.background_image, self.bg_center, self.bg_size, center_dest2, self.window_size) self.counter += 1 if self.counter % BLOCK_SIZE == 0: self.counter = 0 world.player.score += 1 dpi_factor = world.window.hidpi_factor font = world.text_font font_color = world.text_font_color score_text = "SCORE // {0:d}".format(world.player.score) lives_text = "LIVES // {0:d}".format(world.player.lives) canvas.draw_text(score_text, (10 * dpi_factor, 20 * dpi_factor), font.get_size(), str(font_color), font.get_face()) canvas.draw_text(lives_text, (10 * dpi_factor, 40 * dpi_factor), font.get_size(), str(font_color), font.get_face()) # Render items for item in self.items: bounds = item.get_bounds() if bounds.max.x > 0 and bounds.min.x < WINDOW_SIZE[0]: item.render(canvas) # Render player world.player.render(canvas) # Add the level scroll, mutating the current offset. self.offset.add(self.scroll * BLOCK_SIZE) # Load next level. if self.finished: levels = world.levels next_level = self.level + 1 if len(levels) >= next_level: target_level = levels[next_level - 1] world.player.pos = target_level.start_pos world.level = target_level else: world.level = None
def get_cursor_pos(self) -> Vector: x, y = pygame.mouse.get_pos() return Vector(x, y)
class Player(Renderable): def __init__(self, world: 'World'): super().__init__(world.window) self.world = world self.size = Vector(*PLAYER_SIZE) self.pos = world.level.start_pos self.last_pos = self.pos.copy() self.last_x = 0 self.vel = Vector(0, 0) self.accel = Vector(0, 0) self.on_ground = False self.jumping = False self.moving_x = False self.is_dying = False self.desired_platform = None self.score = 0 self.lives = 3 self.sprite_cols = 8 self.sprite = Sprite('assets/player.png', self.sprite_cols, 1) self.roll = 0 def jump(self): self.on_ground = False self.vel.y = -PLAYER_VELOCITY[1] self.accel.y = -ACCEL_GRAVITY def get_bounds(self) -> BoundingBox: pos = self.pos size = self.size return BoundingBox(pos, pos + size) def get_render_bounds(self) -> BoundingBox: bounds = self.get_bounds() dpi_factor = self.window.hidpi_factor return BoundingBox(bounds.min * dpi_factor, bounds.max * dpi_factor) def on_key_down(self, key: int): if not self.is_dying: if key == Key.SPACE: self.jumping = True # Allow holding jump button. if self.on_ground: self.jump() elif key == Key.KEY_A: self.vel.x = -PLAYER_VELOCITY[0] elif key == Key.KEY_D: self.vel.x = PLAYER_VELOCITY[0] def on_key_up(self, key: int): if not self.is_dying: if key == Key.SPACE: self.jumping = False elif key == Key.KEY_A: if self.vel.x < 0: self.vel.x = 0 elif key == Key.KEY_D: if self.vel.x > 0: self.vel.x = 0 def on_death(self): self.lives -= 1 if self.lives == 0: self.world.window.handler = self.world.source else: self.vel.x = 0 self.vel.y = 0 if self.desired_platform is None: self.pos = Vector(PLAYER_RESPAWN_X_OFFSET, -self.size.y) else: bounds = self.desired_platform.get_bounds() if bounds.max.x <= 0: self.pos = Vector(PLAYER_RESPAWN_X_OFFSET, -self.size.y) else: # Place player on platform they last touched self.pos = Vector( bounds.max.x - self.size.x - PLAYER_RESPAWN_X_OFFSET, bounds.min.y - self.size.y) self.is_dying = False def render(self, canvas: simplegui.Canvas): bounds = self.get_bounds() dpi_factor = self.window.hidpi_factor # Draw player. if PLAYER_POTATO: dest_center = self.pos + self.size / 2 index = (self.roll // self.sprite_cols) % self.sprite_cols self.sprite.draw(canvas, dest_center * dpi_factor, self.size * dpi_factor, (index, 0)) else: point_list = [p.multiply(dpi_factor).into_tuple() for p in bounds] color = Color(120, 120, 200) canvas.draw_polygon(point_list, 1, str(color), str(color)) # Update position. self.last_pos = self.pos.copy() self.pos.add(self.vel) self.roll += self.vel.x * 2 / PLAYER_VELOCITY[0] self.roll += 1 if abs(self.vel.x) > PLAYER_VELOCITY[0]: self.vel.x = math.copysign(PLAYER_VELOCITY[0], self.vel.x) # Check collisions position. self.on_ground = False bounds = self.get_bounds() if not self.is_dying: for item in self.world.level.items: if item.collides_with(bounds): item.on_collide(self) self.vel.add(self.accel) # Do gravity and platform collision. if self.on_ground: self.vel.y = 0 self.accel.y = 0 else: self.accel.y = -ACCEL_GRAVITY if bounds.max.y >= WINDOW_SIZE[1] or bounds.min.x <= 0: self.on_death()