ワイ「Pythonista3 のscene モジュールを紹介するんだよ？」

## Pythonista3 Advent Calendar 2022 の7日目の記事を書き始めるワイ

ワイ「まずは書き出しのテンプレからやな、、、」ｶﾀｶﾀ、、、

- - -
この記事は、[Pythonista3 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pythonista3) の07日目の記事です。

https://qiita.com/advent-calendar/2022/pythonista3

一方的な偏った目線で、Pythonista3 を紹介していきます。

ほぼ毎日iPhone（Pythonista3）で、コーディングをしている者です。よろしくお願いします。

以下、私の2022年12月時点の環境です。

```sysInfo.log
--- SYSTEM INFORMATION ---
* Pythonista 3.3 (330025), Default interpreter 3.6.1
* iOS 16.0.2, model iPhone12,1, resolution (portrait) 828.0 x 1792.0 @ 2.0
```

他の環境(iPad や端末の種類、iOS のバージョン違い)では、意図としない挙動(エラーになる)なる場合もあります。ご了承ください。

ちなみに、`model iPhone12,1` は、iPhone11 です。
- - -

ワイ「これでええな！」ｯﾀｰﾝ

ワイ「さてと、、、」

ワイ「`scene` モジュールを紹介すると言っても、何をどう紹介したらええんやろか、、、」

ワイ「どうしたの？ネタ切れなのかな？」

ワイ「ネネネ、ネタ切れちゃうわ！！」

ワイ「書きたいことはたくさんあるけど、書く速度と時間が全く釣り合ってないんや！」

ワイ「じゃあ、大好きなShader をやればいいんだよ？」

ワイ「それば、次回の予定や！そもそも前回も、`ui` モジュールでニッチに`draw` メソッドでクリエイティブコーディングをメインお届けしてしまったんや！」

ワイ「少ないPythonista3 読者層の中で隙間産業すなよ！」

ワイ「Shader はあくまで、`scene` モジュールの副産物みたいなもんやろ」

ワイ「そうですわねぇ、SpriteKit Framework がベースですものねぇ」

[SpriteKit | Apple Developer Documentation](https://developer.apple.com/documentation/spritekit)

https://developer.apple.com/documentation/spritekit

ワイ「不要な情報を増やすなや、、、」

ワイ「そういったことは、`objc_util` のときにまとめて説明しようと思ってたんや、、、」

ワイ「ワンチャン、Pythonista3 をはじめようと読んでくれてた人が、混乱するやろ、、、」

ワイ「ごめんなさいだよ？（たぶんAPI の説明を前にしてたんだよ？）」

ワイ「紹介する順番をそれなりに考慮して進めてるんやから水差さんようにお願いしますやで」

## 充実のチュートリアル

ワイ「とにかく、`scene` モジュールは、Pythonista3 の中でもコードサンプルが多いんや」

ワイ「しかも、細かくパート分けされてたり、順を追って機能追加をしていったりと手厚くサポートされているなと思うで」

### Game Tutorial

ワイ「サンプルコードがたくさん入っている`Examples` にも、`Examples/Game Tutorial` にpart7 まで用意されてるんや！」

ワイ「`Tutorial Part 1.py` なんて、実装コードよりもコメント解説の方が多いんやで」

ワイ「フォントが`Ubuntu Mono` で、Size はPythonista3 最小の`8` でやっと全体像がギリギリ見えるくらいや」

![img221123_191121.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/ed0ca14e-48a1-c138-4d14-611e544a4db9.png)

ワイ「すごいね！壮観なのだね？」

ワイ「ゲームで使えるイラストなんかもPythonista3 に準備されてるんやで」

![img221123_193042.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/a3bc78ee-1710-d1f9-261c-0f76fc00394e.png)

ワイ「キーボードの右上にある`(+)` をタップするとアイコンや画像、サウンドを簡単に選べるんや」

![img221123_193101.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/c1c3b75b-35ca-5c99-5e3e-c7ea237b948a.png)

![img221123_193109.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/f1600458-e013-835d-9394-b2fc8d4afd6a.png)

ワイ「ゲームつくる前に、材集めのためのサイト巡りが不要になるで！！」

ワイ「いたせりつくせりなんだね？」

ワイ「英語に抵抗あるなら、前に紹介した翻訳スクリプトを使えば少し楽になるやで」

[Pythonista3 内だけで、英語を日本語に機械翻訳 | Pythonista3 のeditor module を使い、Pythonista3 のコーディングを楽にする - Qiita](https://qiita.com/pome-ta/items/c3902a0f6a0de691df8d#pythonista3-%E5%86%85%E3%81%A0%E3%81%91%E3%81%A7%E8%8B%B1%E8%AA%9E%E3%82%92%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AB%E6%A9%9F%E6%A2%B0%E7%BF%BB%E8%A8%B3)

<https://qiita.com/pome-ta/items/c3902a0f6a0de691df8d#pythonista3-%E5%86%85%E3%81%A0%E3%81%91%E3%81%A7%E8%8B%B1%E8%AA%9E%E3%82%92%E6%97%A5%E6%9C%AC%E8%AA%9E%E3%81%AB%E6%A9%9F%E6%A2%B0%E7%BF%BB%E8%A8%B3>

### `scene` モジュールのDocumentation

ワイ「Documentation の充実っぷりも最高やで、特にintroduction は、適宜かいつまんでサンプルが用意されているんや」

[introduction | scene — 2D Games and Animations — Python 3.6.1 documentation](https://omz-software.com/pythonista/docs/ios/scene.html#introduction)

https://omz-software.com/pythonista/docs/ios/scene.html#introduction

 ワイ「`[Open in Editor]` でサンプルをひとつづつ挙動を確認すれば、`scene` モジュールの雰囲気が掴めるで」

 ワイ「サンプルから、自分が思い描く改造チャレンジをしてみるのもええと思うで」

## それにしても

ワイ「コードの内容にも触れずに、ただただPythonista3 にある事実を伝えているだけやんけ。。。」

ワイ「`ui` モジュールだと、`add_subview` が`add_child` になっていたり、混同しないように気をつけるんやでー！しか伝えることがなさそうやで。。。」

ワイ「。。。」

ワイ「過去の完成してない、リポジトリの紹介しか道はないのかもしれんな。。。。」

ワイ「改めてコードをみてみると、意味不明なところやペンディングなところもあるから、いろいろ修正したいンゴ」

ワイ「だけど、そんなことしたら、時間が全然足りない。。。」

ワイ「納期という確定完成が存在しない、趣味プログラミングの悲しき性かもしれんな」

ワイ「そもそも、ゲームも下手だし。ゲーム作りたい。ってモチベーションもさほど持ってない系男子なんや、、、」

ワイ「もう前置きの言い訳は、いいからはやく紹介をするんだよ？」

ワイ「わかりましたやで、、、」

### エアーホッケーゲーム

ワイ「昔のiPhone から引っ張り出してきたやで、、、」

![img221124_123447.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/7f084cdd-72bf-15f3-82b3-98180256cf0f.png)

[pystaAirHockeyGame.py](https://gist.github.com/pome-ta/069d01b47b19fe2f1176a4f2989a82c0)

https://gist.github.com/pome-ta/069d01b47b19fe2f1176a4f2989a82c0

ワイ「2人プレイのエアーホッケーゲームやな」

ワイ「2019年の4月頃にぽちぽち作ってたみたいやで」

#### テストプレイ

![img221124_123559.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/61917744-d83a-949f-410c-6b6b27db4b87.gif)

ワイ「点数が入ると自エリアがピカピカする」

ワイ「パドルは自エリアしか移動できひんようになってるな」

![img221124_123642.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/2eb56b64-d854-1f8e-7be2-7c2b6ccc1e53.gif)

ワイ「ボールがガクガクしとるのは、衝突判定がガバってる証拠やな」

ワイ「まあ必殺技みたいでカッコええからヨシ！やで」

ワイ「ヨシ！じゃないんだよ？しっかり修正をしてほしいんだよ？」

#### 実装

ワイ「これは、コードを見返して気がついたんじゃなく、記憶が蘇ってきただけなんやが」

ワイ「パドルやゴールを個別に判断させたくて、ゴニョゴニョしてたのを思い出したわ」

ワイ「`super` で呼んでるところかな？継承の概念はないのかな？」

```py
class Oval_obj(scene.ShapeNode):
  def __init__(self, _x, _y, wh, player, color, parent=None):
    p_path = ui.Path.oval(0, 0, wh, wh)
    super(Oval_obj, self).__init__(
      p_path, position=(_x, _y), fill_color=str(color), parent=parent)
    self.touch = None
    self.player = player
```

```py
class Field(scene.Node):
  def __init__(self, parent=None):
    super(Field, self).__init__(parent=parent)

  def setup(self):
    r_path = ui.Path.rect(0, 0, x, y_cent)
    self.play1 = scene.ShapeNode(r_path, 'pink')
    self.play1.position = (x_cent, y_cent / 2)
    self.play2 = scene.ShapeNode(r_path, 'cyan')
    self.play2.position = (x_cent, y_cent + y_cent / 2)
```

ワイ「参考にしたコードがこんな感じだったのかも知れんな、もうその辺は覚えてないわ」

ワイ「2つのものを捜査させて無駄感が半端ないねん」

```py
  def update(self):
    self.ball.update()
    if self.ball.position[1] > y - self.ball.size[0] / 2:
      if self.goal1.position[0] - goal_var / 2 <= self.ball.position[0] < self.goal1.position[0] + goal_var / 2:
        #print('赤に加点')
        self.p1_points += 1
        self.field.p1_action()
        self.scr_p1.text = str(self.p1_points)

    if self.ball.position[1] < self.ball.size[0] / 2:
      if self.goal2.position[0] - goal_var / 2 <= self.ball.position[0] < self.goal1.position[0] + goal_var / 2:
        #print('青に加点')
        self.p2_points += 1
        self.field.p2_action()
        self.scr_p2.text = str(self.p2_points)

```

ワイ「class 名や、変数名も適当すぎるんだよ？」

ワイ「命名関係と、2プレイヤーとしての処理を書き換える。衝突判定をしっかり書く。とかの書き直しが必要そうなコードやな」

ワイ「実装時の記憶じゃなくて、コードから意図を読み取れる書き方を目指したいやで」

### テトリス目コピ

ワイ「これは、Pythonista3 で実装するネタ探しの時に『なんかネタくださいな』と相談したときに『テトリスでも作れば？』と言われたんや」

![img221124_151922.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/a8defee2-78ed-3bf4-fc85-3624e331c267.png)

ワイ「色々な言語で実装されてそうだったから、そんな情報を一切遮断したるでーと、Wikipedia と当時公式で出てたアプリを見るだけで作ってみたやつやね」

[pome-ta/tetrisPysta: Pythonista3 (ios app) use scene module](https://github.com/pome-ta/tetrisPysta)

https://github.com/pome-ta/tetrisPysta

#### テストプレイ

![img221124_152523.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/2953777/a6324667-e935-a6a4-9d89-a406c1fd2d65.gif)

ワイ「ブロックが着地する。もしくは、壁にぶつかると、画面が揺れるエフェクトをつけてる」

ワイ「当時、触覚フィードバックを知らんかったから、視認てきなフィードバックを実装したんやね」

ワイ「テトリスが下手すぎてキャプチャ撮るのに時間かかってしもうたわ」

#### 実装面

ワイ「今回の記事用に、試しにプレイしてみたんやけど、低確率でエラー出てしまうんよ」

ワイ「コードがスパゲッティ過ぎて、深追いできず諦めましたや」

ワイ「あと、下ボタン長押し処理を実装せずに終えてるみたいやな」

ワイ「ぽちぽち、下ボタン連打するんがホンマめんどくさいわ」

ワイ「いまは、なんとなくどう処理すればいいかイメージはつくな」

ワイ「ボタン周りだと、ボタン一つ一つを`MainScene` に個別で配置しとるから、ガバッとレイアウトの移動調節できへんのが面倒やな」

```py
    # --- btn start
    path = ui.Path.oval(0, 0, 72, 72)
    self.d_btn = scene.ShapeNode(path=path, parent=self, fill_color='white')
    self.d_btn.position = self.size * .5
    self.d_btn.position -= (0, self.size[1] / 2 - self.d_btn.size[1] / 2)
    self.l_btn = scene.ShapeNode(path=path, parent=self, fill_color='red')
    self.l_btn.position = self.size * .5
    self.l_btn.position -= (+(self.l_btn.size[0]),
                            self.size[1] / 2 - self.l_btn.size[1])
    self.r_btn = scene.ShapeNode(path=path, parent=self, fill_color='blue')
    self.r_btn.position = self.size * .5
    self.r_btn.position -= (-(self.r_btn.size[0] * 1),
                            self.size[1] / 2 - self.r_btn.size[1])
    self.e_btn = scene.ShapeNode(path=path, parent=self, fill_color='yellow')
    self.e_btn.position = self.size * .5
    self.e_btn.position -= (0, self.size[1] / 2 - self.e_btn.size[1] * 1.5)

    self.s_btn = scene.ShapeNode(parent=self, fill_color='pink')
    self.s_btn.path = ui.Path.oval(0, 0, 32, 32)
    self.s_btn.position = self.size * .5
    self.s_btn.position -= (self.s_btn.size[0] - self.size[0] / 2,
                            self.size[1] / 3)
    # --- btn end
```

ワイ「地獄かな？」

ワイ「当時は、iPhone6 plus だったから良かったかもしらんが、画面レイアウトの柔軟性に欠けとる」

ワイ「ブロックの回転は、もっとスマートならないのかな？」

```py
class SetUpMinos:
  def __init__(self):
    i_rotate = ([[-1, 0], [0, 0], [1, 0], [2, 0]], [[1, -2], [1, -1], [1, 0],
                                                    [1, 1]],
                [[-1, -1], [0, -1], [1, -1], [2, -1]], [[0, -2], [0, -1],
                                                        [0, 0], [0, 1]])
    mino_i = {'name': 'i', 'color': 'cyan', 'rotate': i_rotate}

    o_rotate = ([[0, 0], [0, 1], [1, 0], [1, 1]])
    mino_o = {'name': 'o', 'color': 'yellow', 'rotate': o_rotate}

    s_rotate = ([[-1, 0], [0, 0], [0, 1], [1, 1]], [[0, 1], [0, 0], [1, 0],
                                                    [1, -1]],
                [[-1, -1], [0, -1], [0, 0], [1, 0]], [[-1, 1], [-1, 0], [0, 0],
                                                      [0, -1]])
    mino_s = {'name': 's', 'color': 'green', 'rotate': s_rotate}

    z_rotate = ([[0, 1], [1, 1], [1, 0], [2, 0]], [[1, 0], [1, 1], [2, 2],
                                                   [2, 1]],
                [[0, 2], [1, 2], [1, 1], [2, 1]], [[0, 0], [0, 1], [1, 2],
                                                   [1, 1]])
    mino_z = {'name': 'z', 'color': 'red', 'rotate': z_rotate}

    j_rotate = ([[0, 0], [1, 0], [2, 0], [0, 1]], [[1, 1], [1, 0], [2, 1],
                                                   [1, -1]],
                [[0, 0], [1, 0], [2, -1], [2, 0]], [[1, 1], [1, 0], [0, -1],
                                                    [1, -1]])
    mino_j = {'name': 'j', 'color': 'blue', 'rotate': j_rotate}

    l_rotate = ([[0, 0], [1, 0], [2, 0], [2, 1]], [[1, 1], [1, 0], [2, -1],
                                                   [1, -1]],
                [[0, 0], [1, 0], [0, -1], [2, 0]], [[1, 1], [1, 0], [1, -1],
                                                    [0, 1]])
    mino_l = {'name': 'l', 'color': 'orange', 'rotate': l_rotate}

    t_rotate = ([[0, 0], [1, 0], [2, 0], [1, 1]], [[1, -1], [1, 0], [2, 0],
                                                   [1, 1]],
                [[1, -1], [1, 0], [2, 0], [0, 0]], [[1, -1], [1, 0], [1, 1],
                                                    [0, 0]])
    mino_t = {'name': 't', 'color': 'purple', 'rotate': t_rotate}

    self.mino_list = [mino_i, mino_o, mino_s, mino_z, mino_j, mino_l, mino_t]
```

ワイ「せやな、結局答え合わせ的に、他のテトリスのコードと見合わせてないねん」

ワイ「独自実装過ぎて、他のコードを読むの諦めた記憶があるわ」

ワイ「壁付近のブロック回転は、公式テトリスから動きをキャプチャして、その通りに動くように調整した記憶も出てきたわ」

ワイ「写真アルバムのテトリスキャプチャ汚染すごそうなんだよ？」

## 過去のコード

ワイ「なかなか、ガバガバでヘビーやったな、、、」

ワイ「一つのファイルで完結させるのをやめたらいいんだよ？」

ワイ「それは、モジュール設計が下手くそというのが本音。表向きは、試してみたい人が1ファイルの方がハードル低く参入できる意図があるんやで」

ワイ「本音と建前の説明順番が逆なんだよ？」

ワイ「`frame_interval` に依存して、ゲーム速度が変化してしまうのもよくないんだよ？」

ワイ「最近だと120Hz の端末もあるんよね。ゲーム内時間を管理するのを入れんとあかんなぁ」

ワイ「ちょいちょい、過去のを引っ張り出してリファクタリングするのも、勉強になるかもなぁ」

ワイ「とりあえず酷くてもいいから、GitHub にあげてさらし首にすれば、意識してコード書くようになるかもな」

ワイ「つまりGitHub にあるコードは、意識してコード書いてるんだよ？」

ワイ「もうええわ、やめさせてもらうわ」

## 結局`scene` モジュールの紹介はどうすんねや？

ワイ「っは！？」

ワイ「夢ならよかったのに、、、」omz

ワイ「はやく`scene` モジュールでのShader 遊びをしたいやで、、、」

- - -

ワイ、俺「皆さんここまで、読んでいただきありがとうございました！」

ワイ「って、なんで俺くんが！？
改めまして、ありがとうございました！」

本当の本当に終わり

## せんでん

### Discord

Pythonista3 の日本語コミュニティーがあります。みなさん優しくて、わからないところも親身に教えてくれるのでこの機会に覗いてみてください。

https://t.co/0IOW2hn1VC

### 書籍

iPhone/iPad でプログラミングする最強の本。

- [Python や Jupyter で iPhone/iPad 先端機能を簡単･自由にプログラミング！「活用篇」：hirax](https://techbookfest.org/product/1WiyV4LEev3f4YrzesRKWV?productVariantID=axX32srsSgtLtZWUtjkj99)

https://techbookfest.org/product/1WiyV4LEev3f4YrzesRKWV?productVariantID=axX32srsSgtLtZWUtjkj99

- [Python や Jupyter で iPhone/iPad 先端機能を簡単･自由にプログラミング！「土台篇」：hirax](https://techbookfest.org/product/wTZTyeibm5GQ5XgdfMrEBV?productVariantID=kRDmN1udbEYZUWbETdwL8r)

https://techbookfest.org/product/wTZTyeibm5GQ5XgdfMrEBV?productVariantID=kRDmN1udbEYZUWbETdwL8r

### その他

- サンプルコード

[Pythonista3 Advent Calendar 2022](https://qiita.com/advent-calendar/2022/pythonista3) でのコードをまとめているリポジトリがあります。

コードのエラーや変なところや改善点など。ご指摘やPR お待ちしておりますー

https://github.com/pome-ta/Pythonista3AdventCalendar2022sampleCode

- Twitter

なんしかガチャガチャしていますが、お気兼ねなくお声がけくださいませー

https://twitter.com/pome_ta93

<blockquote class="twitter-tweet"><p lang="ja" dir="ltr">やれるか、やれないか。ではなく、やるんだけども、紹介説明することは尽きないと思うけど、締め切り守れるか？って話よ！（クズ）<br><br>Pythonista3 Advent Calendar 2022 <a href="https://t.co/JKUxA525Pt">https://t.co/JKUxA525Pt</a> <a href="https://twitter.com/hashtag/Qiita?src=hash&amp;ref_src=twsrc%5Etfw">#Qiita</a></p>&mdash; pome-ta (@pome_ta93) <a href="https://twitter.com/pome_ta93/status/1588570697777152006?ref_src=twsrc%5Etfw">November 4, 2022</a></blockquote> <script async src="https://platform.twitter.com/widgets.js" charset="utf-8"></script>

- GitHub

基本的にGitHub にコードをあげているので、何にハマって何を実装しているのか観測できると思います。

https://github.com/pome-ta