1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonのarcadeでスクロールする六角形マップを実装!

Posted at

はじめに

この記事では、PythonのArcadeライブラリを使用して、スクロール可能な六角形マップを作成する方法を説明します。ゲーム開発などで、六角形グリッドマップを扱う際に役立つでしょう。

コード解説

scrolling.py

このファイルでは、ウィンドウの生成とスクロール機能の実装を行います。

import arcade
from components.hexmap import Hexmap

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "Hex Map with Scrolling"
GRID_SIZE = 15
HEX_RADIUS = 30
SCROLL_SPEED = 25  # スクロール速度


class MyGame(arcade.Window):
    def __init__(self, width, height, title):
        super().__init__(width, height, title)
        arcade.set_background_color(arcade.color.AMAZON)
        self.hexmap = Hexmap(HEX_RADIUS, GRID_SIZE)
        self.view_x = 0
        self.view_y = 0
        self.last_key_press_time = 0
        self.mouse_x = 0
        self.mouse_y = 0


    def on_draw(self):
        arcade.start_render()

        # ビューポートを設定 (スクロール)
        arcade.set_viewport(self.view_x, SCREEN_WIDTH + self.view_x
                            , self.view_y, SCREEN_HEIGHT + self.view_y)

        self.hexmap.draw_hexagon(on_mouse_coordinates=(self.mouse_x, self.mouse_y))

    def on_key_press(self, key, modifiers):
        dx = 0
        dy = 0

        if key == arcade.key.LEFT:
            dx = -SCROLL_SPEED
        elif key == arcade.key.RIGHT:
            dx = SCROLL_SPEED
        elif key == arcade.key.UP:
            dy = SCROLL_SPEED
        elif key == arcade.key.DOWN:
            dy = -SCROLL_SPEED

        new_view_x = self.view_x + dx
        new_view_y = self.view_y + dy

        # スクロール範囲の制限
        self.view_x = min(400, max(-400, new_view_x)) # -400 ~ 400
        self.view_y = min(400, max(-300, new_view_y)) # -300 ~ 400

        print(f"view_x: {self.view_x}, view_y: {self.view_y}")
        
        if key == arcade.key.Q:
            arcade.close_window()

    def on_mouse_motion(self, x, y, dx, dy):
        """
        マウスの移動時座標をスクロールに合わせて補正
        
        Args:
            x: マウスのx座標
            y: マウスのy座標
        """
        self.mouse_x = x + self.view_x # 補正
        self.mouse_y = y + self.view_y


def main():
    game = MyGame(SCREEN_WIDTH, SCREEN_HEIGHT, SCREEN_TITLE)
    arcade.run()


if __name__ == "__main__":
    main()

SCROLL_SPEED変数でスクロールの速度を調整できます。on_key_press関数で、矢印キーの入力に応じてview_xview_y変数を更新し、スクロールを実現しています。スクロール範囲は-400~400、-300~400に制限されています。ここは適宜調節してください。

    def on_key_press(self, key, modifiers):
        # ... (コード省略)

        # スクロール範囲の制限
        self.view_x = min(400, max(-400, new_view_x)) # -400 ~ 400
        self.view_y = min(400, max(-300, new_view_y)) # -300 ~ 400

        print(f"view_x: {self.view_x}, view_y: {self.view_y}")

        if key == arcade.key.Q:
            arcade.close_window()

on_mouse_motion関数では、マウスの移動時座標をスクロールに合わせて補正し取得します。

    def on_mouse_motion(self, x, y, dx, dy):
        """
        マウスの移動時座標をスクロールに合わせて補正
        
        Args:
            x: マウスのx座標
            y: マウスのy座標
        """
        self.mouse_x = x + self.view_x # 補正
        self.mouse_y = y + self.view_y

components/hexmap.py

このファイルでは、六角形グリッドの生成と描画、ハイライト表示、マウス座標の取得、距離計算を行います。

import arcade
import math

class Hexmap:
    """
    六角形グリッドマップを生成、描画するクラス。
    ハイライト表示は、3hex分

    Attributes:
        radius (float): 六角形の半径。
        grid_size (int): グリッドのサイズ (行と列の数)。
        hex_centers (list): 各六角形の中心座標を格納するリスト。
    """
    def __init__(self, radius, grid_size):
        """
        Hexmapクラスのコンストラクタ。

        Args:
            radius (float): 六角形の半径。
            grid_size (int): グリッドのサイズ。
        """
        self.radius = radius
        self.grid_size = grid_size
        self.hex_centers = []

    # 頂点の計算
    def hexagon_vertices(self, x, y):
        """
        六角形の頂点座標を計算する。

        Args:
            x (float): 六角形の中心x座標。
            y (float): 六角形の中心y座標。

        Returns:
            vertices: 六角形の頂点座標のリスト。
        """
        vertices = []
        for i in range(6):
            angle = i * math.pi / 3
            vx = x + self.radius * math.cos(angle)
            vy = y + self.radius * math.sin(angle)
            vertices.append((vx, vy))
        return vertices

    # ヘックスマップの描画
    def draw_hexagon(self
                    , highlight_coordinates=None
                    , attack_coordinates = None
                    , on_mouse_coordinates=None):
        """
        六角形グリッドマップを描画する。

        Args:
            highlight_coordinates (tuple): ハイライトを表示するために必要なX,Y座標 例)クリックした位置から3hex分の周囲をハイライト表示したい場合は、クリックした位置のX,Y座標
            on_mouse_coordinates (tuple):  マウスのX,Y座標
        """
        root_3 = math.sqrt(3)
        horizontal_spacing = 3 * self.radius / 2
        vertical_spacing = root_3 * self.radius

        for row in range(self.grid_size):
            for col in range(self.grid_size):
                x = col * horizontal_spacing
                y = row * vertical_spacing

                if col % 2 == 1:
                    y += vertical_spacing / 2
                self.hex_centers.append((x, y))
                hex_vertices_list = self.hexagon_vertices(x, y)

                self.draw_single_hexagon(x, y, hex_vertices_list, highlight_coordinates, attack_coordinates, on_mouse_coordinates)

    # ハイライトの表示
    def draw_single_hexagon(self, x, y, hex_vertices_list, highlight_coordinates, attack_coordinates, on_mouse_coordinates):
        """
        個々の六角形を描画する。ハイライトの有無を判断。

        Args:
            x (float): 六角形の中心x座標。
            y (float): 六角形の中心y座標。
            hex_vertices_list (list): 六角形の頂点座標リスト。
            clicked_hex (tuple): クリックされた六角形の中心座標。
            highlight_trigger (bool): ハイライトを有効にするかどうか。
            on_mouse_point(): マウスの位置座標
        """
        
        if highlight_coordinates:
            distance = self.calcutate_distance(x, y, highlight_coordinates)
            if distance <= self.radius * 5.5:  # ハイライト範囲内
                self.draw_highlighted_hexagon(hex_vertices_list)
            else:
                self.draw_normal_hexagon(hex_vertices_list)
        else:
            self.draw_normal_hexagon(hex_vertices_list)
        
        if attack_coordinates:
            distance = self.calcutate_distance(x, y, attack_coordinates)
            if distance <= self.radius*2.0:
                self.attack_highlighted_draw(hex_vertices_list)
            else:
                self.draw_normal_hexagon(hex_vertices_list)
        else:
            self.draw_normal_hexagon(hex_vertices_list)
        
        if on_mouse_coordinates:
            distance = self.calcutate_distance(x, y, on_mouse_coordinates)
            if distance <= self.radius:
                self.draw_hex_on_mouse(hex_vertices_list)
            else:
                self.draw_normal_hexagon(hex_vertices_list)
        else:
            self.draw_normal_hexagon(hex_vertices_list)

    # ハイライトヘックスを表示
    def draw_highlighted_hexagon(self, hex_vertices_list):
        """
        移動範囲のハイライトされた六角形を描画する。

        Args:
            hex_vertices_list (list): 六角形の頂点座標リスト。
        """
        arcade.draw_polygon_filled(point_list=hex_vertices_list, color=(0, 0, 255, 50))
        arcade.draw_polygon_outline(point_list=hex_vertices_list, color=arcade.color.BLACK, line_width=2)
    
    def attack_highlighted_draw(self, hex_vertices_list):
        """
        攻撃範囲のハイライトされた六角形を描画する。

        Args:
            hex_vertices_list (list): 六角形の頂点座標リスト。
        """
        arcade.draw_polygon_filled(point_list=hex_vertices_list, color=(255, 0, 0, 50))
        arcade.draw_polygon_outline(point_list=hex_vertices_list, color=arcade.color.BLACK, line_width=2)
    
    # マウスがどこのhexにいるかを表示
    def draw_hex_on_mouse(self, hex_vertices_list):
        """
        hexmap上のhexにマウスが置かれた時に白色に描画する
        Args:
            hex_vertices_list (list): 六角形の頂点座標リスト。
        """
        arcade.draw_polygon_filled(point_list=hex_vertices_list, color=(255, 255, 255, 100))
        arcade.draw_polygon_outline(point_list=hex_vertices_list, color=arcade.color.BLACK, line_width=2)

    # 通常のヘックスを表示
    def draw_normal_hexagon(self, hex_vertices_list):
        """
        通常の六角形を描画する。

        Args:
            hex_vertices_list (list): 六角形の頂点座標リスト。
        """
        arcade.draw_polygon_outline(point_list=hex_vertices_list, color=arcade.color.BLACK, line_width=2)

    def get_hex_center_position(self, mouse_x, mouse_y):
        """
        マウス座標から最も近い六角形の中心座標を取得する。

        Args:
            mouse_x (float): マウスのx座標。
            mouse_y (float): マウスのy座標。

        Returns:
            tuple: 最も近い六角形の中心座標。見つからない場合はNone。
        """
        for center in self.hex_centers:
            distance = self.calcutate_distance(mouse_x, mouse_y, center)
            if distance <= self.radius:
                return center
        return None
    
    def calcutate_distance(self, hex_center_x, hex_center_y, point):
        """
        現在地の座標と、ヘックスの中心座標との距離を算出する
        
        Args:
            hex_center_x (float): hexmapの中心x座標
            hex_center_y (float): hexmapの中心y座標
            hex_center_point(tuple): tuple形式のx, y座標

        Returns:
            tuple: 最も近い六角形の中心座標。見つからない場合はNone。
        """
        # 距離の計算ロジックを入れる
        dx = hex_center_x - point[0]
        dy = hex_center_y - point[1]
        distance =  math.sqrt(dx**2 + dy**2)
        return distance

実行方法

  1. 必要なライブラリをインストールします。実装するときは仮想環境を作成することをお勧めします。
    pip install arcade
    
  2. scrolling.pyを実行します。
    python scrolling.py
    

まとめ

この記事では、PythonとArcadeライブラリを用いてスクロール可能な六角形マップを作成する方法を紹介しました。ゲーム開発などに応用できるでしょう。

今後の展望

  • スクロール機能の改善
  • より高度なマップ生成アルゴリズムの実装

ほかにもこんなのやってます

シューティングを作成してみました!ぜひ遊んでみてください^^

参考文献

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?