Python(matplotlib)でアニメーションを書いてみた　第二弾

# はじめに
テストが終わって解放感がものすごいので，アニメーションで遊んで見ました

github:https://github.com/Shunichi09/Qiita/tree/master/Animation

# 目的
テストが終わった解放感での遊び

# 前回からの差分
[前回](https://qiita.com/MENDY/items/e1b432df1e0bfe8b680c)は単にボールをランダムで出現させただけですが
今度はシューティングゲーム的なものを．．．実装してみます

# イメージ
こんなんできたら面白いかなと画力のなさ笑
まぁよくあるただのゲームですがOpenGLとかでやりますよねーふつう とりあえずMatplotlibでどこまでいけるか笑
![image.png](https://qiita-image-store.s3.amazonaws.com/0/261584/55aa6e7e-5777-dab9-ba46-683b4f0e9998.png)

# 困難にぶち当たる
キーボードの入力を割り込みさせるにはどうすれば、、、、ということです
Funcanimationはそもそも勝手に回っているのでキーボードは割り込みが必要になります

最初、ネット調べてて以下のgetchを使おうと思ったのですが、
入力を待ってしまうので，うまく動かないという問題が発生（無知ですいません）
以下のコードだと，キーボードの入力を待ってしまう関係で，キーボードをおさないとアニメーションが動かないという．．．

```py
try:       
    key = ord(getch())
    if key == 77: #右矢印
        self.x += 0.1
    elif key == 75: #左矢印
        self.x -= 0.1
except:
    pass
```

いや，Pythonってキーボード割り込み的なものないんですか．．．
いろいろ探してもWindows用ではなかったり，pygameのようにちょっと大がかりだったり．．．

致し方なく強行手段にでます

```py
import signal
```

これで，割り込みさせます
Ctrl + C で右方向
限界まで行くと左の端から現れます
に動きます
あぁCtrlを押し続けなければならないという笑
なにかアイディアがある方がいれば，コメントをお願いします

# 結果
![anim (1).gif](https://qiita-image-store.s3.amazonaws.com/0/261584/1e0b4028-3616-9d21-19db-10e84b681368.gif)
速攻でゲームオーバーになっていますけど笑
保存するときは一回保存してからやるので，見えない状況で動かしてます許してください
保存しなければ普通にできます
GIFでも動いてるように，Ctrl + Cで動きますのでご確認ください（コマンドプロンプト上じゃないと動かないです！）

なんかちょっとしょうもないですね笑
まぁ勉強にはなりました！

# プログラム

```py:shooting_game1.py
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as ani
import math
import random
from msvcrt import getch
import signal
import sys
import os

# シューティングゲーム的なね
class Ball():
    def __init__(self):
        # 初期設定
        self.x = 0.1*random.randint(-50, 50)
        self.y = 5.0 # ここは固定
        self.v_x = 0.0 # 真下に落ちます 
        self.v_y = 0.1*random.randint(-50, -15)
        self.dt = 0.05

    def state_update(self):
        self.x += self.v_x * self.dt
        self.y += self.v_y * self.dt
        delete_flag = False

        # 範囲外に出たら，消滅
        if self.y < min_y:
            delete_flag = True

        return self.x, self.y, delete_flag

class Player():
    def __init__(self):
        # 初期設定
        self.x = 0.0
        self.y = -3.0 # ここは固定

    def state_update(self):
        # keybord関連
        # 右に動く
        signal.signal(signal.SIGINT, self.righter)

        if self.x > max_x:
            self.x = min_x

        return self.x, self.y
    
    def righter(self, signal, frame):
        self.x += 0.05

    def gameover_judge(self, balls_x, balls_y):
        gameover_flag = False
        for i in range(len(balls_x)):
            if abs(balls_x[i] - self.x) < 0.3 and abs(balls_y[i] - self.y) < 0.3:
                gameover_flag = True
                break

        return gameover_flag

class Drawing_ball():# 描画するクラス
    def __init__(self, ax):
        color_list = ['r', 'b', 'k', 'c', 'y', 'm', 'g']
        color_num = random.randint(0, len(color_list)-1)
        self.ball_img, = ax.plot([], [], color=color_list[color_num])
        self.ax = ax

    def draw_circle(self, center_x, center_y):
        # ボールの大きさ
        circle_size = 0.25

        steps = 50 #円を書く分解能はこの程度で大丈夫
        self.circle_x = [] #位置を表す円のx
        self.circle_y = [] #位置を表す円のy
            
        for j in range(steps):

            self.circle_x.append(center_x + circle_size*math.cos(j*2*math.pi/steps))
            self.circle_y.append(center_y + circle_size*math.sin(j*2*math.pi/steps))

        return self.circle_x, self.circle_y

    def set_graph_data(self):
        self.ball_img.set_data(self.circle_x, self.circle_y)

        return self.ball_img, 

class Drawing_player():# 描画するクラス
    def __init__(self, ax):
        self.player_head_img, = ax.plot([], [], color='k')
        self.player_body_img, = ax.plot([], [], color='k')
        self.ax = ax

    def draw_head_body(self, center_x, center_y):
        head_size = 0.25
        body_size = 0.5
        circle_size = [head_size, body_size]
        center_xs = [center_x, center_x]
        center_ys = [center_y+body_size+head_size, center_y]

        steps = 50 #円を書く分解能はこの程度で大丈夫
        self.circle_x = [[], []] #位置を表す円のx
        self.circle_y = [[], []] #位置を表す円のy
        for k in range(2):
            for j in range(steps):
                self.circle_x[k].append(center_xs[k] + circle_size[k]*math.cos(j*2*math.pi/steps))
                self.circle_y[k].append(center_ys[k] + circle_size[k]*math.sin(j*2*math.pi/steps))

        return self.circle_x, self.circle_y

    def set_graph_data(self):
        self.player_head_img.set_data(self.circle_x[0], self.circle_y[0])
        self.player_body_img.set_data(self.circle_x[1], self.circle_y[1])
        player_imgs = [self.player_head_img, self.player_body_img]

        return player_imgs,

def update_anim(i):# このiが更新するたびに増えていきます
    balls_imgs = []
    balls_x = []
    balls_y = []

    if i % 50 == 0: #ボール追加します
        balls.append(Ball())
        balls_drawers.append(Drawing_ball(ax))

    count = 0

    for ball in balls:
        # ボールの状態をアップデート
        temp_x, temp_y, delete_flg = ball.state_update()

        if delete_flg == True: #ボールが枠外に行ってたら消去
            balls.pop(count)
            balls_drawers.pop(count)
        else:
            balls_x.append(temp_x)
            balls_y.append(temp_y)
            balls_drawers[count].draw_circle(temp_x, temp_y)
            balls_imgs.append(balls_drawers[count].set_graph_data())

            count += 1

    # playerの状態をupdatee
    temp_x, temp_y = player.state_update()
    # 衝突判定
    gameover_flag = player.gameover_judge(balls_x, balls_y)    
    if gameover_flag == True:
        gameover_text.set_text('GAME OVER')

    player_drawer.draw_head_body(temp_x, temp_y)
    player_imgs = player_drawer.set_graph_data()

    # 時間
    step_text.set_text('step = {0}'.format(i))

    return balls_imgs, player_imgs

#############################################################
#########################__MAIN__############################
## plot 初期化
# グラフ仕様設定
fig = plt.figure()

# Axes インスタンスを作成
ax = fig.add_subplot(111)

# 軸
# 最大値と最小値⇒軸の範囲設定
max_x = 5
min_x = -5
max_y = 5
min_y = -5

ax.set_xlim(min_x, max_x)
ax.set_ylim(min_y, max_y)

# 軸の縦横比, 正方形，単位あたりの長さが等しくなります
ax.set_aspect('equal')

# 軸の名前設定
ax.set_xlabel('X [m]') 
ax.set_ylabel('Y [m]') 

# その他グラフ仕様
ax.grid(True) # グリッド

# ステップ数表示
step_text = ax.text(0.05, 0.9, '', transform=ax.transAxes)

# gameover
gameover_text = ax.text(0.15, 0.5, '', transform=ax.transAxes, fontsize=30)

# ボールだけ作ってる
balls_size = []
balls = []
balls_drawers = []

# player作成
player = Player()
player_drawer = Drawing_player(ax)

# アニメーション描画
animation = ani.FuncAnimation(fig, update_anim, interval=50, frames=500)

print('save_animation?')
shuold_save_animation = int(input())

if shuold_save_animation == True: 
    animation.save('anim.mp4', writer="ffmpeg")

plt.show()
```

# これで終わりかと思っていたら
Threadingというものがあるらしいですね！並列でプログラムを実行させます
そうすればいけるのではとおもって．．．ちょっと気力がないのでまた今度
そもそもMatplotlibはこういうの向いてないですね笑