ユーザ用ツール

サイト用ツール


youtube:tetris-python-cocos2d

差分

このページの2つのバージョン間の差分を表示します。

この比較画面へのリンク

両方とも前のリビジョン前のリビジョン
次のリビジョン
前のリビジョン
youtube:tetris-python-cocos2d [2024/02/28 18:26] – 削除 - 外部編集 (不明な日付) 127.0.0.1youtube:tetris-python-cocos2d [2024/02/29 11:51] (現在) freemikan
行 1: 行 1:
 +====== テトリス Python+Cocos2d ======
 +
 +{{:youtube:tetris-python-cocos2d.png?300|}}
 +
 +
 +===== tetris.py =====
 +
 +
 +<file python>
 +"""Tetris Cocos2d."""
 +
 +import random
 +import cocos
 +from cocos.director import director
 +import pyglet
 +
 +
 +def arena_to_pixel(ax, ay):
 +    """Convert arena coord to pixel coord."""
 +    px = ax * (Cell.WIDTH + Cell.SPACING)
 +    py = ay * (Cell.HEIGHT + Cell.SPACING)
 +    return (px, py)
 +
 +
 +class Cell(cocos.sprite.Sprite):
 +    """Cell component."""
 +
 +    WIDTH = 32
 +    HEIGHT = 32
 +    SPACING = 1
 +    IMAGE = pyglet.resource.image('square.png')
 +
 +    def __init__(self, acoord, color):
 +        """Ctor."""
 +        super(Cell, self).__init__(Cell.IMAGE, color=color, anchor=(0, 0))
 +        self.scale_x = Cell.WIDTH / Cell.IMAGE.width
 +        self.scale_y = Cell.HEIGHT / Cell.IMAGE.height
 +        self.set_arena_position(acoord[0], acoord[1])
 +
 +    def set_arena_position(self, ax, ay):
 +        """Set position in arena coord."""
 +        self.ax = ax
 +        self.ay = ay
 +        self.position = arena_to_pixel(ax, ay)
 +
 +    def get_arena_position(self):
 +        """Get position in arena coord."""
 +        return self.ax, self.ay
 +
 +
 +class Piece(cocos.cocosnode.CocosNode):
 +    """Piece consits with four cells."""
 +
 +    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, pattern, position, drop_interval):
 +        """Ctor."""
 +        super(Piece, self).__init__()
 +
 +        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], position[1])
 +        self.clear_input()
 +
 +    def update(self, dt):
 +        """Update frame."""
 +        # 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, self.move_input_dy)
 +        if self._is_overlapping():
 +            # cancel
 +            self.move(-self.move_input_dx, -self.move_input_dy)
 +
 +        # free fall
 +        self.drop_time -= dt
 +        if self.drop_time <= 0:
 +            self.move(0, -1)
 +            self.drop_time = self.drop_interval
 +            if self._is_overlapping():
 +                self.move(0, 1)  # cancel drop
 +                self._detach_cells()
 +                self.alive = False
 +
 +        self.clear_input()
 +
 +    def set_arena_position(self, ax, ay):
 +        """Set position in arena coord."""
 +        self.ax = ax
 +        self.ay = ay
 +        self.position = arena_to_pixel(self.ax, self.ay)
 +
 +    def move(self, dx, dy):
 +        """Move by offset."""
 +        self.set_arena_position(self.ax + dx, self.ay + dy)
 +
 +    def rotate(self, rotation):
 +        """Rotate left (-1) or right (+1)."""
 +        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, new_y)
 +
 +    def set_drop_interval(self, drop_interval):
 +        """Set drop intenval."""
 +        self.drop_interval = drop_interval
 +
 +    def push_move_input(self, dx, dy):
 +        """Push move value for next update."""
 +        self.move_input_dx += dx
 +        self.move_input_dy += dy
 +
 +    def push_rotation_input(self, rotation):
 +        """Push rotation value for next update."""
 +        # -1 is counter clock-wise (left)
 +        # +1 is clock-wise (right)
 +        # otherwise is no rotation
 +        self.rotation_input = rotation
 +
 +    def clear_input(self):
 +        """Clear input values."""
 +        self.move_input_dx = 0
 +        self.move_input_dy = 0
 +        self.rotation_input = 0
 +
 +    def _detach_cells(self):
 +        """Detach child cells and pass to parent."""
 +        for cell in self.get_children():
 +            (ax, ay) = cell.get_arena_position()
 +            ax += self.ax
 +            ay += self.ay
 +            cell.set_arena_position(ax, ay)
 +            self.remove(cell)
 +            self.parent.add(cell)
 +
 +    def _is_overlapping(self):
 +        """Check if overlapping something."""
 +        # 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, Cell)
 +        ]
 +        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 True if this piece is alive (not landing on)."""
 +        return self.alive
 +
 +    def left(self):
 +        """Return left most cell position x."""
 +        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):
 +        """Return right most cell position x."""
 +        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):
 +        """Return down most cell position .y."""
 +        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):
 +    """Arena where cells put on."""
 +
 +    WIDTH = 10
 +    HEIGHT = 20
 +
 +    def __init__(self, acoord):
 +        """Ctor."""
 +        super(Arena, self).__init__()
 +        self.set_arena_position(acoord[0], acoord[1])
 +
 +    def set_arena_position(self, ax, ay):
 +        """Set position in arena coord."""
 +        self.ax = ax
 +        self.ay = ay
 +        self.position = arena_to_pixel(self.ax, self.ay)
 +
 +
 +class Game(cocos.layer.Layer):
 +    """Game player layer."""
 +
 +    is_event_handler = True
 +
 +    def __init__(self):
 +        """Ctor."""
 +        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, dt):
 +        """Update frame."""
 +        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, key, modifiers):
 +        """Handle key press events."""
 +        dx = 0
 +        if key == pyglet.window.key.LEFT:
 +            dx = -1
 +        if key == pyglet.window.key.RIGHT:
 +            dx = +1
 +        self.piece.push_move_input(dx, 0)
 +
 +        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):
 +        """Clean up cells on perfect line."""
 +        line_cell_count = [0] * Arena.HEIGHT
 +        cells = [c for c in self.arena.get_children() if isinstance(c, Cell)]
 +        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, ay - 1)
 +                    elif ay == linum:
 +                        removing_cells.append(cell)
 +                for cell in removing_cells:
 +                    cells.remove(cell)
 +                    cell.kill()
 +
 +    def _respawn_piece(self):
 +        """Respawn new piece."""
 +        self.piece = Piece(random.choice(Piece.PATTERN),
 +                           position=(Arena.WIDTH // 2, Arena.HEIGHT),
 +                           drop_interval=0.5)
 +        self.arena.add(self.piece)
 +
 +    def _setup_fence(self):
 +        """Set up left and right walls, bottom floor."""
 +        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="Tetris Cocos2d",
 +              autoscale=False)
 +scene = cocos.scene.Scene(Game())
 +director.run(scene)
 +</file>
  

特に明示されていない限り、本Wikiの内容は次のライセンスに従います: CC0 1.0 Universal
CC0 1.0 Universal Donate Powered by PHP Valid HTML5 Valid CSS Driven by DokuWiki