youtube:tetris-python-cocos2d
差分
このページの2つのバージョン間の差分を表示します。
両方とも前のリビジョン前のリビジョン次のリビジョン | 前のリビジョン | ||
youtube:tetris-python-cocos2d [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1 | youtube:tetris-python-cocos2d [2024/02/29 11:51] (現在) – freemikan | ||
---|---|---|---|
行 1: | 行 1: | ||
+ | ====== テトリス Python+Cocos2d ====== | ||
+ | |||
+ | {{: | ||
+ | |||
+ | |||
+ | ===== tetris.py ===== | ||
+ | |||
+ | |||
+ | <file python> | ||
+ | """ | ||
+ | |||
+ | import random | ||
+ | import cocos | ||
+ | from cocos.director import director | ||
+ | import pyglet | ||
+ | |||
+ | |||
+ | def arena_to_pixel(ax, | ||
+ | """ | ||
+ | px = ax * (Cell.WIDTH + Cell.SPACING) | ||
+ | py = ay * (Cell.HEIGHT + Cell.SPACING) | ||
+ | return (px, py) | ||
+ | |||
+ | |||
+ | class Cell(cocos.sprite.Sprite): | ||
+ | """ | ||
+ | |||
+ | WIDTH = 32 | ||
+ | HEIGHT = 32 | ||
+ | SPACING = 1 | ||
+ | IMAGE = pyglet.resource.image(' | ||
+ | |||
+ | def __init__(self, | ||
+ | """ | ||
+ | super(Cell, self).__init__(Cell.IMAGE, | ||
+ | self.scale_x = Cell.WIDTH / Cell.IMAGE.width | ||
+ | self.scale_y = Cell.HEIGHT / Cell.IMAGE.height | ||
+ | self.set_arena_position(acoord[0], | ||
+ | |||
+ | def set_arena_position(self, | ||
+ | """ | ||
+ | self.ax = ax | ||
+ | self.ay = ay | ||
+ | self.position = arena_to_pixel(ax, | ||
+ | |||
+ | def get_arena_position(self): | ||
+ | """ | ||
+ | return self.ax, self.ay | ||
+ | |||
+ | |||
+ | class Piece(cocos.cocosnode.CocosNode): | ||
+ | """ | ||
+ | |||
+ | PATTERN = [ | ||
+ | [(0, 0), (1, 0), (1, 1), (2, 1)], # S | ||
+ | [(1, 0), (2, 0), (0, 1), (1, 1)], # Z | ||
+ | [(0, 0), (1, 0), (0, 1), (0, 2)], # L | ||
+ | [(0, 0), (1, 0), (1, 1), (1, 2)], # J | ||
+ | [(1, 0), (0, 1), (1, 1), (2, 1)], # T | ||
+ | [(0, 0), (1, 0), (0, 1), (1, 1)], # O | ||
+ | [(0, 0), (1, 0), (2, 0), (3, 0)], # I | ||
+ | ] | ||
+ | |||
+ | def __init__(self, | ||
+ | """ | ||
+ | super(Piece, | ||
+ | |||
+ | self.ax = 0 | ||
+ | self.ay = 0 | ||
+ | self.drop_interval = drop_interval | ||
+ | self.drop_time = drop_interval | ||
+ | self.alive = True | ||
+ | |||
+ | for p in pattern: | ||
+ | c = Cell(p, (255, 255, 255)) | ||
+ | self.add(c) | ||
+ | |||
+ | self.set_arena_position(position[0], | ||
+ | self.clear_input() | ||
+ | |||
+ | def update(self, | ||
+ | """ | ||
+ | # handle rotation | ||
+ | self.rotate(self.rotation_input) | ||
+ | if self._is_overlapping(): | ||
+ | # cancel | ||
+ | self.rotate(-self.rotation_input) | ||
+ | |||
+ | # move left or right | ||
+ | self.move(self.move_input_dx, | ||
+ | if self._is_overlapping(): | ||
+ | # cancel | ||
+ | self.move(-self.move_input_dx, | ||
+ | |||
+ | # free fall | ||
+ | self.drop_time -= dt | ||
+ | if self.drop_time <= 0: | ||
+ | self.move(0, | ||
+ | self.drop_time = self.drop_interval | ||
+ | if self._is_overlapping(): | ||
+ | self.move(0, | ||
+ | self._detach_cells() | ||
+ | self.alive = False | ||
+ | |||
+ | self.clear_input() | ||
+ | |||
+ | def set_arena_position(self, | ||
+ | """ | ||
+ | self.ax = ax | ||
+ | self.ay = ay | ||
+ | self.position = arena_to_pixel(self.ax, | ||
+ | |||
+ | def move(self, dx, dy): | ||
+ | """ | ||
+ | self.set_arena_position(self.ax + dx, self.ay + dy) | ||
+ | |||
+ | def rotate(self, | ||
+ | """ | ||
+ | if rotation != -1 and rotation != 1: | ||
+ | return | ||
+ | |||
+ | for c in self.get_children(): | ||
+ | cx, cy = c.get_arena_position() | ||
+ | new_x = +rotation * cy | ||
+ | new_y = -rotation * cx | ||
+ | c.set_arena_position(new_x, | ||
+ | |||
+ | def set_drop_interval(self, | ||
+ | """ | ||
+ | self.drop_interval = drop_interval | ||
+ | |||
+ | def push_move_input(self, | ||
+ | """ | ||
+ | self.move_input_dx += dx | ||
+ | self.move_input_dy += dy | ||
+ | |||
+ | def push_rotation_input(self, | ||
+ | """ | ||
+ | # -1 is counter clock-wise (left) | ||
+ | # +1 is clock-wise (right) | ||
+ | # otherwise is no rotation | ||
+ | self.rotation_input = rotation | ||
+ | |||
+ | def clear_input(self): | ||
+ | """ | ||
+ | self.move_input_dx = 0 | ||
+ | self.move_input_dy = 0 | ||
+ | self.rotation_input = 0 | ||
+ | |||
+ | def _detach_cells(self): | ||
+ | """ | ||
+ | for cell in self.get_children(): | ||
+ | (ax, ay) = cell.get_arena_position() | ||
+ | ax += self.ax | ||
+ | ay += self.ay | ||
+ | cell.set_arena_position(ax, | ||
+ | self.remove(cell) | ||
+ | self.parent.add(cell) | ||
+ | |||
+ | def _is_overlapping(self): | ||
+ | """ | ||
+ | # check for left or right wall | ||
+ | if self.left() < 0 or self.right() >= Arena.WIDTH: | ||
+ | return True | ||
+ | |||
+ | # check landing on bottom | ||
+ | if self.bottom() < 0: | ||
+ | return True | ||
+ | |||
+ | # check for cells | ||
+ | arena_cells = [ | ||
+ | c for c in self.parent.get_children() if isinstance(c, | ||
+ | ] | ||
+ | for pcell in self.get_children(): | ||
+ | px, py = pcell.get_arena_position() | ||
+ | px += self.ax | ||
+ | py += self.ay | ||
+ | for acell in arena_cells: | ||
+ | ax, ay = acell.get_arena_position() | ||
+ | if px == ax and py == ay: | ||
+ | return True | ||
+ | |||
+ | return False | ||
+ | |||
+ | def is_alive(self): | ||
+ | """ | ||
+ | return self.alive | ||
+ | |||
+ | def left(self): | ||
+ | """ | ||
+ | children = self.get_children() | ||
+ | if len(children) > 0: | ||
+ | return self.ax + min([c.get_arena_position()[0] for c in children]) | ||
+ | else: | ||
+ | return self.ax | ||
+ | |||
+ | def right(self): | ||
+ | """ | ||
+ | children = self.get_children() | ||
+ | if len(children) > 0: | ||
+ | return self.ax + max([c.get_arena_position()[0] for c in children]) | ||
+ | else: | ||
+ | return self.ax | ||
+ | |||
+ | def bottom(self): | ||
+ | """ | ||
+ | children = self.get_children() | ||
+ | if len(children) > 0: | ||
+ | return self.ay + min([c.get_arena_position()[1] for c in children]) | ||
+ | else: | ||
+ | return self.ay | ||
+ | |||
+ | |||
+ | class Arena(cocos.cocosnode.CocosNode): | ||
+ | """ | ||
+ | |||
+ | WIDTH = 10 | ||
+ | HEIGHT = 20 | ||
+ | |||
+ | def __init__(self, | ||
+ | """ | ||
+ | super(Arena, | ||
+ | self.set_arena_position(acoord[0], | ||
+ | |||
+ | def set_arena_position(self, | ||
+ | """ | ||
+ | self.ax = ax | ||
+ | self.ay = ay | ||
+ | self.position = arena_to_pixel(self.ax, | ||
+ | |||
+ | |||
+ | class Game(cocos.layer.Layer): | ||
+ | """ | ||
+ | |||
+ | is_event_handler = True | ||
+ | |||
+ | def __init__(self): | ||
+ | """ | ||
+ | super(Game, self).__init__() | ||
+ | self._setup_fence() | ||
+ | |||
+ | self.arena = Arena((1, 1)) | ||
+ | self.add(self.arena) | ||
+ | |||
+ | self._respawn_piece() | ||
+ | |||
+ | self.schedule(self.update) | ||
+ | |||
+ | def update(self, | ||
+ | """ | ||
+ | if self.piece.is_alive(): | ||
+ | self.piece.update(dt) | ||
+ | else: | ||
+ | self.piece.kill() | ||
+ | self._cleanup_perfect_lines() | ||
+ | self._respawn_piece() | ||
+ | |||
+ | def on_key_press(self, | ||
+ | """ | ||
+ | dx = 0 | ||
+ | if key == pyglet.window.key.LEFT: | ||
+ | dx = -1 | ||
+ | if key == pyglet.window.key.RIGHT: | ||
+ | dx = +1 | ||
+ | self.piece.push_move_input(dx, | ||
+ | |||
+ | rot = 0 | ||
+ | if key == pyglet.window.key.UP: | ||
+ | rot = -1 | ||
+ | if key == pyglet.window.key.DOWN: | ||
+ | rot = +1 | ||
+ | self.piece.push_rotation_input(rot) | ||
+ | |||
+ | if key == pyglet.window.key.SPACE: | ||
+ | self.piece.set_drop_interval(0) | ||
+ | |||
+ | def _cleanup_perfect_lines(self): | ||
+ | """ | ||
+ | line_cell_count = [0] * Arena.HEIGHT | ||
+ | cells = [c for c in self.arena.get_children() if isinstance(c, | ||
+ | for cell in cells: | ||
+ | ay = cell.get_arena_position()[1] | ||
+ | if ay < Arena.HEIGHT: | ||
+ | line_cell_count[ay] += 1 | ||
+ | |||
+ | for linum in range(Arena.HEIGHT - 1, -1, -1): | ||
+ | if line_cell_count[linum] == Arena.WIDTH: | ||
+ | removing_cells = [] | ||
+ | for cell in cells: | ||
+ | ax, ay = cell.get_arena_position() | ||
+ | if ay > linum: | ||
+ | cell.set_arena_position(ax, | ||
+ | elif ay == linum: | ||
+ | removing_cells.append(cell) | ||
+ | for cell in removing_cells: | ||
+ | cells.remove(cell) | ||
+ | cell.kill() | ||
+ | |||
+ | def _respawn_piece(self): | ||
+ | """ | ||
+ | self.piece = Piece(random.choice(Piece.PATTERN), | ||
+ | | ||
+ | | ||
+ | self.arena.add(self.piece) | ||
+ | |||
+ | def _setup_fence(self): | ||
+ | """ | ||
+ | fence_color = (88, 88, 188) | ||
+ | for i in range(Arena.HEIGHT + 1): | ||
+ | left = Cell((0, i), fence_color) | ||
+ | right = Cell((Arena.WIDTH + 1, i), fence_color) | ||
+ | self.add(left) | ||
+ | self.add(right) | ||
+ | |||
+ | for i in range(1, Arena.WIDTH + 1): | ||
+ | bottom = Cell((i, 0), fence_color) | ||
+ | self.add(bottom) | ||
+ | |||
+ | |||
+ | game_width = (Arena.WIDTH + 2) * (Cell.WIDTH + Cell.SPACING) - Cell.SPACING | ||
+ | game_height = (Arena.HEIGHT + 1) * (Cell.HEIGHT + Cell.SPACING) - Cell.SPACING | ||
+ | |||
+ | director.init(width=game_width, | ||
+ | height=game_height, | ||
+ | caption=" | ||
+ | autoscale=False) | ||
+ | scene = cocos.scene.Scene(Game()) | ||
+ | director.run(scene) | ||
+ | </ | ||