Pythonのarcadeでクリックでユニットをスムーズに移動

# コードの実行結果
初期画面
![スクリーンショット 2024-11-04 21.12.25.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769985/2f762c61-61f9-8f03-47e3-1fb106159960.png)

画面上の適当な位置を押すとユニットが移動
![スクリーンショット 2024-11-04 21.12.38.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3769985/88ab86da-e3df-9bc8-7bf4-77d0475542d3.png)

# コードの概要
マウスをクリックした位置に作成したユニットが移動します。
ユニットの.png画像は適宜好きなものを使ってください。

# コード
```python
import arcade
import arcade.color
import arcade.key
import math

SCREEN_WIDTH = 800
SCREEN_HEIGHT = 600
SCREEN_TITLE = "ユニット移動の例"
UNIT_FILEPATH = "xxxx.png"
UNIT_SCALE = 0.5
UNIT_SPEED = 3

class MyUnit(arcade.Sprite):
    def __init__(self, filename, scale, speed):
        super().__init__(filename, scale)
        self.speed = speed
        self.destination_x = None # destinationは方向という意味。
        self.destination_y = None
    
    def update(self):
        # 方向先が指定されたら、ユニットを移動
        if self.destination_x is not None and self.destination_y is not None:
            # 移動方向の正規化
            """
            現在地とマウスクリック時の距離を計算し、移動するときに反映
            
            # Geminiさんに移動の正規化をするようにした理由を説明
            ユニットがクリックされた地点へ一定の速度で滑らかに移動するようにするためです。
            正規化によって、移動ベクトル (dx, dy) の長さを1に保ち
            方向のみを表す単位ベクトルに変換しています。
            """
            dx = self.destination_x - self.center_x
            dy = self.destination_y - self.center_y
            distance = math.sqrt(dx**2 + dy**2)
            
            if distance > self.speed:
                # 移動方向を正規化
                # 移動のスケールをmax1にしている
                dx = dx/distance
                dy = dy/distance
                
                # ユニットの移動
                self.center_x = self.center_x + dx
                self.center_y = self.center_y + dy
            else:
                self.center_x = self.destination_x
                self.center_y = self.destination_y            
                # 初期値に戻す
                self.destination_x = None
                self.destination_y = None

class MyWindow(arcade.Window):
    def __init__(self, width, height, title):
        super().__init__(width, height, title)
        self.unit_list = arcade.SpriteList()
        
    def setup(self):
        # 背景の設定
        arcade.set_background_color(arcade.color.AQUAMARINE)
        
        # ユニットの設定
        self.unit = MyUnit(filename=UNIT_FILEPATH, scale=UNIT_SCALE, speed=UNIT_SPEED)
        self.unit.center_x = SCREEN_WIDTH / 2
        self.unit.center_y = SCREEN_HEIGHT / 2
        self.unit_list.append(self.unit)
    
    def on_draw(self):
        arcade.start_render()
        self.unit_list.draw()
    
    def on_mouse_press(self, x, y, button, modifiers):
        self.unit.center_x = x
        self.unit.center_y = y
    
    def on_update(self, delta_time):
        self.unit.update()
    
    def on_key_press(self, symbol, modifiers):
        if symbol == arcade.key.Q:
            print("Q key press")
            arcade.close_window()

def main():
    mywindow = MyWindow(width=SCREEN_WIDTH, height=SCREEN_HEIGHT, title=SCREEN_TITLE)
    mywindow.setup()
    arcade.run()

if __name__ == "__main__":
    main()
```

# ほかにもこんなのやってます
- [pythonのarcadeでhexmapを作成してみた！
](https://qiita.com/nami_fumi/items/12904d2572d7f5f3fe23)
- [Python&arcadeで簡単！レトロ風シューティングゲームを作ろう！🚀](https://qiita.com/nami_fumi/items/dc679b47f57fa5a18f68)
- [pythonのarcadeを使って、ユニット選択とボタンの表示をやってみた](https://qiita.com/nami_fumi/items/c819bd71dac6123f942d)

# 参考文献
- [The Python Arcade Library](https://api.arcade.academy/en/latest/index.html)
    - arcade公式サイト
- Gemini
    - コードをよりよくするときにいいです
- [perplexity](https://www.perplexity.ai/)
    - 初期コードを書かせる時にいいです