2018/08/14

書籍紹介: Unity ゲーム プログラミング・バイブル

書籍『 Unity ゲーム プログラミング・バイブル 』の一部を出版社ボーンデジタルさんよりご恵贈いただきました。中身を読み実践してみたので、かんたんに書籍の紹介とレビューをしてみたいと思います。

書籍について


『 Unity バイブル プログラミングブックス 』はゲーム開発用ソフトウェア(=開発環境)である Unity でゲームを作る方法を解説した書籍です。サンプルを使った実践的な内容になっているので、読むタイミングとしては「入門書をひと通り終え Unity の基本的な使い方に慣れた後」に読むのが最適だと思います。




今回はその書籍のうち「 TensorFlow を組み合わせて AI プレイヤーを作る」というテーマの章(= Python が関わっている章)の抜粋をご恵贈いただきました。

※ Unity そのものは Python で利用するソフトウェアではありません

やったこと


書籍の内容をざっと読むだけではなかなか評価しづらいところがあるので、 Unity をインストールし、紹介されているプログラムを試しながら読んでみました。この文章を書いている時点で、ご提供いただいた章の内容の半分強ぐらいを動かしました。




出版社さんの公式ページでサンプルの Unity プロジェクト(≒サンプルコード)が公開されているので、それを利用するとコードを書かずともとりあえずプログラムを試すことが可能です。


ちなみに私は Unity / C# に不慣れなので、今のところコードはちらっと確認する程度で、ありもののファイルをほぼそのままの形で動かすだけで読み進めています。

感想


Unity を使ったゲーム作り、または、 Unity 縛りがなくても一般にゲーム作りに関心のある方にはとても興味深い内容ではないかと思います。画像が多く解説も丁寧なので、お勉強感覚ではなく雑誌感覚で楽しみながら読み進めることができます。

こんな方におすすめです。

  • プログラミングの基礎を学んだ後に、「何かおもしろいことがしたいな」と思っている方
  • Unity でどんなことができるのかを知りたい方
  • Unity の基礎を押さえた後に実践的な制作に取り組みたい方
  • ゲーム作りに興味がある方

プログラミングを日頃行っている方であれば高校生――もしかしたら中学生の方でも楽しく読めるかもしれません。

ちなみに、私の印象では、プログラミングがまったくわからないと、スムーズなときは別に問題ありませんが、書いてあることを Unity 上でどう実践すればよいかわからない場合や罠にハマった場合等、イレギュラーが起こったときに前に進めずにつらいと思います。なので、最低限「変数とは何ぞや」「関数とは何ぞや」「クラスとは何ぞや」ぐらいのベースの知識がある方が読むのがよいと思います。

いまひとつなところ

よかったところだけをあげるとアレなので、いまひとつなところをあげてみます。

書かれている操作を実際にどうやればよいのかわからない: これは GUI ソフトウェアの操作説明の常でもありますが、説明されている操作を実際にどうやればよいかわからないところがときどきありました。

概念と操作の説明が混ざっていて迷子になる: これも章によるかと思いますが、概念的な説明と具体的な操作の説明とが混ざっているところがあって、読んでいて「これは何の説明がされているんだろう」「この一連の操作をすると何が起こるんだろう」となることがありました。ただ、このあたりは Unity をよく知る読者であれば行間を読んでカバーできるところなのかもしれません。

よかったところ

画像が多くてわかりやすい: 画像が多めで細かなところにも丁寧な説明があったりするので、わかりやすかったです。文字ばかりのモノクロの技術書は読むのにエネルギーが要るので、「カラーの書籍はわかりやすくていいなぁ」と改めて思いました。

サンプルプロジェクトが公開されている: 上述のとおり、本書の中で紹介されているサンプルプロジェクト(≒サンプルコード)が公式サイト他で公開されています。「コードを書かずにサンプルをさっと動かしたい」「実際に動くプロジェクトを見て学びたい」というときにはとてもよいと思います。私が読んだのはごく一部ですが、書籍全体では数多くのサンプルが紹介されているので、それぞれからアイデアをもらうだけでちょっとしたオリジナルゲームを作ることが可能でしょう。

というわけで、書籍『 Unity ゲーム プログラミング・バイブル 』のご紹介でした。

私自身はゲーム作りはやったことがありませんがこれを機に少しやってみたくなりました。ゲームや Unity に興味のある方はぜひお手に取ってみてください :)

尚、最近同出版社さんが Unity の中でも特に「 Unity ✕ 機械学習 」というテーマに絞った書籍を出版されたそうです(具体的には Unity ML-Agents Toolkit を利用した Unity ✕ 機械学習の書籍です)。ゲーム作りに興味のある方にかぎらず、機会学習・ AI に興味のある方はチェックしてみるとよいかもしれませんよ。



2018/08/10

Python Tips: Enum 型を使いたい

Python で Enum 型(列挙型)を使う方法について、手短に説明してみます。

お断り: 以下に記載するコードについては動作確認はしていますが、私はたくさん Enum 型を使ってきたわけでは無いので、理解が間違っている可能性もあります。ご了承ください。

Python の Enum 型


Python では Python 3.4 から enum という標準モジュールが追加され、 Python 本体に Enum 型が同梱されるようになりました。

import enum

Enum 型の基本的な使い方


定義

オリジナルの Enum 型は enum.Enum 型を継承して作成します。

from enum import Enum

class Status(Enum):
    ACTIVE = 1
    INACTIVE = 0
    CANCELED = -1

アイテム(=インスタンス)はクラスアトリビュートとして定義します。このサンプルの場合は、 Status.ACTIVE Status.INACTIVE Status.CANCELED という 3 つのアイテムを定義していることになります。

ふるまい

Enum 型のふるまいを unittest で確認してみましょう。

from enum import Enum
from collections import OrderedDict
import unittest


class Status(Enum):
    ACTIVE = 1
    INACTIVE = 0
    CANCELED = -1


class TestStatus(unittest.TestCase):
    def test_types(self):
        # クラスのアトリビュートは自動的にインスタンスとして扱われる
        self.assertIsInstance(Status.ACTIVE, Status)
        self.assertIsInstance(Status.INACTIVE, Status)
        self.assertIsInstance(Status.CANCELED, Status)

    def test_for(self):
        # iterable プロトコルを持っているので for ループで回せる
        it = iter(Status)
        self.assertIs(next(it), Status.ACTIVE)
        self.assertIs(next(it), Status.INACTIVE)
        self.assertIs(next(it), Status.CANCELED)
        with self.assertRaises(StopIteration):
            next(it)

    def test_in(self):
        # iterable プロトコルを持っているので in 演算子も使える
        self.assertIn(Status.CANCELED, Status)

    def test_name_and_value(self):
        # キーと値はアトリビュート `name` と `value` で取得できる
        self.assertIs(Status.INACTIVE.name, 'INACTIVE')
        self.assertIs(Status.INACTIVE.value, 0)

    def test_accessors(self):
        # 各アイテムにアクセスする方法として次の 3 種類が要されてている
        self.assertTrue(Status['CANCELED'] == Status(-1) == Status.CANCELED)

    def test_inequality_with_value(self):
        # キーや値と勝手に等しくなったりはしない
        self.assertNotEqual(Status.CANCELED, 'CANCELED')
        self.assertNotEqual(Status.CANCELED, -1)

    def test_dunder_members(self):
        # クラスの __members__ アトリビュートには OrderedDict が入っている
        self.assertEqual(
            Status.__members__,
            OrderedDict(
                {
                    'ACTIVE': Status.ACTIVE,
                    'INACTIVE': Status.INACTIVE,
                    'CANCELED': Status.CANCELED,
                }
            ),
        )

なんだか直感的なふるまいをしてくれる印象がありますが、いかがでしょうか。 enum.Enum を使って定義できる Enum 型は私が Enum 型に期待する機能をひととおり揃えていてかつシンプルなので、私にはとても使いやすい印象です。

自動採番

Python 3.6 以降なら、 enum.auto() を使って各アイテムの値を自動でセットすることができます。

from enum import Enum, auto
import unittest


class Prefecture(Enum):
    HOKKAIDO = auto()
    AOMORI = auto()


class TestPrefecture(unittest.TestCase):
    def test_auto_values(self):
        # auto() を使うと 1 始まりで整数が自動的に振られる
        self.assertEqual(Prefecture.HOKKAIDO.value, 1)
        self.assertEqual(Prefecture.AOMORI.value, 2)

シンプル定義

Enum 型の定義方法としてメジャーなのはおそらく上の enum.Enum クラスを継承する方法ですが、次のように enum.Enum を関数のように使用してワンラインで Enum 型を定義する方法も用意されています。

PublishedStatus = Enum('PublishedStatus', 'PUBLISHED DRAFT')


class TestPublishedStatus(unittest.TestCase):
    def test_instances(self):
        self.assertIsInstance(PublishedStatus.PUBLISHED, PublishedStatus)
        self.assertIsInstance(PublishedStatus.DRAFT, PublishedStatus)

    def test_values(self):
        self.assertEqual(PublishedStatus.PUBLISHED.value, 1)
        self.assertEqual(PublishedStatus.DRAFT.value, 2)

この形で Enum 型を定義する場合は、 enum.Enum の第 1 引数に Enum 型の名前を、第 2 引数にアイテムをスペースや , で区切りの文字列で渡します。第 2 引数は listdict で渡すことも可能です。詳細は公式ページの Functional API の節でご確認ください。


というわけで、 Python の Enum 型の使い方についてでした。

シンプルな用途で Enum 型を使う場合は以上の内容を知っていれば十分ではないかと思いますが、その他細かな機能がもろもろ用意されているので、詳細を知りたい方は公式ページをご覧になってから使うのがよいかと思います。

参考

2018/07/23

Python Tips:パッケージの開発版をインストールしたい

Python でパッケージの開発版をインストールする方法についてご紹介します。

開発版のインストールは、バグ報告やテスト等でパッケージに貢献したいときや、自分でパッケージを開発したいとき等に必要になってきます。

(尚この記事では「パッケージ」ということばは、 pip でインストールできる「ディストリビューションパッケージ」という意味で使っています)

  • Git リポジトリにあるパッケージをインストールしたい
  • ローカルにあるパッケージをインストールしたい

Git リポジトリにあるパッケージをインストールしたい


Git で管理されたパッケージをインストールしたいときは次の形で pip コマンドを使えば OK です。

$ pip install git+[リポジトリ URL]

例えば GitHub にリポジトリがある Requests の場合だと次のようになります。

$ # パターン A/B のどちらでも OK
$ # パターン A
$ pip install git+https://github.com/requests/requests.git
$ # パターン B
$ pip install git+git://github.com/requests/requests.git

バージョンやブランチを指定するには末尾に @ をつけてその後に指定します。

$ # ブランチ master
$ pip install git+https://github.com/requests/requests.git@master

$ # タグ v2.18.4
$ pip install git+https://github.com/requests/requests.git@v2.18.4

$ # コミットハッシュ 4ea09e49f7d518d365e7c6f7ff6ed9ca70d6ec2e
$ pip install git+https://github.com/requests/requests.git@4ea09e49f7d518d365e7c6f7ff6ed9ca70d6ec2e

コミットハッシュで指定する場合はハッシュ値を省略せずすべて渡す必要があるようです。

URL 等から pip がプロジェクト名(≒パッケージ名)を特定できない場合等は末尾に #egg=[プロジェクト名] を付ける必要があります。例えば、 GitHub のリポジトリの URL の末尾に .git を付けない場合は #egg=[プロジェクト名] をつけないとインストールができません。

$ # プロジェクト名を指定しない → 失敗
$ pip install -e git+https://github.com/requests/requests
Could not detect requirement name for 'git+https://github.com/requests/requests', please specify one with #egg=your_package_name

$ # プロジェクト名を指定する → 成功
$ pip install -e git+https://github.com/requests/requests#egg=requests

ちなみに pip は Git の他にも有名 VCS をひととおりサポートしており、 URL の前の git+ を他のものに変更すればその他の VCS で管理されたパッケージもインストールすることができます。

  • git+: Git
  • hg+: Mercurial
  • bzr+: Bazaar
  • svn+: Subversion

ただし各 VCS のリポジトリを利用するには対応するコマンド( githg 等)がその環境で利用できる必要があります。

参考:

ローカルにあるパッケージをインストールしたい


続いて、ローカルにあるパッケージをインストールする場合についてです。ローカルにファイル一式が置いてあるパッケージをインストールしたいときは次の形で pip コマンドを使えば OK です。

$ pip install [パッケージのパス]

ここの パッケージのパス には setup.py ファイルが含まれたディレクトリを指定します(あるいは、 sdist / wheel フォーマットのアーカイブファイルを指定する形も可能なようです)。

シンプルな hello という名前のパッケージを作ってインストールしてみた場合のイメージは次のとおりです。

$ tree hello/
hello/
├── hello.py
└── setup.py

0 directories, 2 files

$ cat hello/hello.py
def say_hello(name=None):
    msg = 'Hi, {}!'.format(name) if name else 'Hi!'
    print(msg)

$ cat hello/setup.py
import setuptools

setuptools.setup(
    name='hello',
    version='0.1',
    packages=setuptools.find_packages(),
)

$ pip install hello/
Processing ./hello
Building wheels for collected packages: hello
  Running setup.py bdist_wheel for hello ... done
  Stored in directory: [var ディレクトリのどこか]
Successfully built hello
Installing collected packages: hello
Successfully installed hello-0.1

おまけ


おまけ A: editable mode でのインストール

pip でのインストールには editable mode というモードが用意されています。これはパッケージ開発時に便利な機能で、コードが編集可能な状態でパッケージをインストールできるものです。

editable mode でインストールされたパッケージのコードに変更を加えると、再インストールをしなくてもそのまま実行環境に反映されます。

editable mode を有効にするには -e--editable )オプションを使用します。

$ pip install -e hello/

editable mode は、ローカルにファイルのあるパッケージだけでなく、通常のディストリビューションパッケージや Git リポジトリ上のパッケージの場合でも利用することができます。その際は pip install 実行時に出力されるパッケージのダウンロード先パスを確認してそこのファイルを編集すると OK です。

参考:

おまけ B: Pipenv でのインストール

Pipenv を使った場合でも上のパターンを利用することができます。

$ # Git リポジトリのパッケージを editable mode を有効にしてインストール
$ pipenv install -e git+https://github.com/requests/requests#egg=requests

$ # ローカルのパッケージをインストール
$ pipenv install hello/

$ # Pipfile には Git リポジトリや editable mode であること等がきちんと記録されている
$ cat Pipfile
[[source]]
url = "https://pypi.org/simple"
verify_ssl = true
name = "pypi"

[packages]
hello = {path = "./hello"}
requests = {editable = true, git = "https://github.com/requests/requests"}

[dev-packages]

[requires]
python_version = "3.6"

おまけ C: インストールし直す

インストールし直したいときは pip install-U--upgrade )オプションを指定すると OK です。

...

ということで、 Python パッケージの開発版をインストールする方法についてでした。このあたりはいろいろと洗練されておりすばらしいですね。

参考: