赤宿 = Red Inn

素人の試行錯誤と2Dゲームプログラミング

Top

『桟橋』が2Dゲームを作るブログ

過去に作ったゲーム

 ウディタローグライク:

  • 羊山ゴートの冒険 [2013/8]
     一作目。ウディタではゲームの流れを直接記述できたのが良かった。

  • ツギハギの行方 [2015/8]
     二作目。UIは良くなったが、ゲームと呼べない完成度。

現在

計画以前? Before Plans?

 意思を持って生きるには、計画が必要です。計画には計画性が必要です。計画性には、無数の意識、意図、思考といったものの保持が必要です。そして、意識や意図を保持するのに、キャッシュメモリのような記憶が重要なのではないか、というのが今回の気づきです。

 人間が短期間に記憶できる言葉の数は7だとか、辞書はコンピュータに入っているから、細かい知識を覚える必要は無いと言われています。しかし、記憶が重要なのは変わらないようです。記憶が人格を作っていることを思えば、当たり前ではあるのですけれど。

 言葉は7つしか覚えられなくても、映像や思考は、本来は100や200が平気で頭に残ります。それくらいは自分の脳みそに期待していいはずです。

 そして、計画性というものは、無数の記憶に支えられているはずです。記憶のキャッシュ領域が働くように頭を耕しておくことが、実は計画性に、ひいては意思の源になるではないでしょうか。

『本当にやる気ならとっくにやっているはず』という物言いがあります。正論です。ところで、自分の意図すら保持できないなら、『本当に本気になれない』という馬鹿げた事態になるでしょう。この虚ろさには覚えがあります……。

 保持というのは、思考の庭に埋まっているというイメージです。そのキャッシュは、回収されないかも知れませんが、浮かび上がってくるところに記憶があるのが重要なのだろうと直感します。ポテンシャルの差は大きいでしょうね。

 などと、今まで想定していなかったものの存在を捉えるだけでも効いてくる印象があります。

論理以前? Before Logics?

 脱線しながら書きました。

『論理』が重要だとよく聞ます。論理を学ぶことができるため、英語やプログラミングを勉強するのが良い、とも言われています。

 では、その重要とされる『論理』とは何なのでしょうか。ネットの解説を読みましたが、内容が限定され過ぎていたように思います。同値表現は得られませんでした。

脱線: 言葉との付き合い方

 元々、言葉とはそういうものかもしれません。人の持つ概念を丸ごと受け取るのは難しいため、自分で得た概念を言葉にマッチさせざるを得ませんし、その方が、自身も得でしょう。自分なりの理解を確立した上で、人の視点もトレースできれば理想的だと思います。

 概念の方が肝心なので、その言葉が何を意味するか『べき』かは、ある意味重要ではありません。自分にとって役に立つように扱って、定義は後回しで良い、という気持ちでいます。

『論理』を四つに分類してみた

 さて……。自分では、『論理』を四つに分類してみました。これも本題とは関係ありませんが、寄り道します。集合、手続き、推論、文脈/文化的態度に分けました。

 集合とは、命題の論理関係と、それらを視覚的に表すものです。専門的なことは知りません。手続きとは、アルゴリズムです。僕は一言で説明できません。推論とは、与えられた状況から可能性を絞ることです。物語を書くのにも使えます。分脈/文化的態度とは、常識とされる教養的な振る舞いです。日本と世界ではズレがあるようです。

 以上、『論理』という概念を、自分なりに解いてみました。もっと良い認識があるかもしれませんね。

 追記: 文章の論理は、n段論法に集約できるかも知れません。これで分類は5つですね。

『論理』以前で引っかかっているかも?

 本題ですが、あえて具体的なところから始めます。『黒玉と白球が両方入っている箱が3個以上』を『黒球が入っている箱と白球が入っている箱の両方が共に3個以上』と曲解する人が何人もいました。言葉が読めておらず、読み直しても気づかないようです。

 論理以前に、思考のレイヤが問題になっていないかと考えました。思考のレイヤが言葉だけだと、『意味や構造を考える』のではなく、『言葉を考える』傾向があり、錯誤しやすいのではないかと。

『言葉を考える』例を挙げます。最近見つけたものは、『宵闇(の時間)を襲う』という言葉遣いがあります。『を』は対象を指すので、『宵闇に襲う』でなければ、意味が分からないのでは、ということです。慣用的には正しいのかもしれませんが……。この違和感が無い場合、『言葉を考える』傾向にあるのではないかと思います。(四次元座標をポイントしているなら別ですが)

 続いて、『意味や構造を考える』例を挙げます。

『if and only if』(iff)と言う英語表現があります。前後に命題を配置すれば、『A if and only if B」という形になります。日本語では、この論理を言葉にするのは難しいですね。

 集合の記号で表すと『⊇ and ⊆』となります。同値ということですね。if, only ifは、論理的ですが、日本語に直すと直感的でないため、図を書きながら元の表現を理解すると良いと思います。

 このように、英語を使うと、必然的に『言葉を考える』のではなく『意味や構造を考える』ことになります。和訳や英訳の際も、自身が、言葉よりも下のレイヤでものを考えていることが感じられるでしょう。

まとめ

 論理が分からないのではなく、言葉が分からないのかもしれない。つまり、『言葉を考える』状態だと、反射的に意味を捉えるために、論理を表す言葉を理解するのが難しいのかもしれない。ということでした。

追記: 日本語文法

 僕の場合は、英語のおかげで言葉の感覚が回復しました。(たぶん)。日本語の構成に自覚的になることでも、同じ恩恵が受けられたはずです。検証目的で、この『よく分からない』母国語の文法の本をポチったところ、いいことが書いてありました。

 主題と解説、命題と陳述、主語と述語、格助詞と成分……。この辺りの判別ができると、文章を解析的に見られて面白いかもしれません。活用形なども考え始めたら、オタクですね! (褒め言葉だと補足しないと誤解されそうですが)。

Parsing in Amaranth

 Amaranthにおけるコンテンツのパースを把握する。
 単にソースを読めばいい気もするが……。自分の言葉を確かめつつ、備忘録として。

Overview

 regexによるmatchingを利用してファイルをパースし、文字列のデータをPropertyBagの木構造に変換する。個々のフィールド(: string)をパースし、特定のType Objectを生成する。

PropertyBag

 子を持てるstringのハッシュマップ。

Type Object

 オブジェクトの「型』を外部オブジェクトに依存させるデザインパタン。ここをデータ駆動にする。
 データをオブジェクトに変換するため、抽象化を挟められる。

余談: Property Bag Design Pattern
.propertyBag: HashMap をフィールドに加えることで、『サーバサイドのコードに手を加えずにプロパティの数を変更できる』。というのもデザインパタンらしい?

The File Format

 regexを用いて、以下の通り、パタンへの分解や、置換を行う。

  • '// comment'
  • '#include path'
  • 'name = value'
    • 名前を定義しなければ、プロパティの番号が名前(stringIndex)になる
  • 'name ='
    • 次の行からインデントしてa multi-line text property
    • 改行は空白文字に置換した形に変換される(インデントは削除)
  • 'name'
    • a collection property
    • 次の行からインデントすればchildの定義になる
  • ':: abstract' でcollection prop.の基底を定義
    • 'derivingCollection :: base' は基底に追記した形のデータとして振る舞う
      • 内部的にFlattenPropertiesに展開する形で実装
      • 当然、各データはoverridableになる

Regex (System.Text.RegularExpressions.Regex)

c.f. Character Classes in Regular Expressions
c.f. Best Practices for Regular Expressions in .NET

  • class Regex
    • ::Regex( pattern: string ) -> Self
    • .Match( input: string ) -> Match
  • class Match
    • .Groups: GroupCollection
      • Match.Groups[stringIndex] の形で用いられている
    • .Success: bool
      • : このKeyは見つかったか?
    • .Length: int
  • class GroupCollection

 なお、C#では@“string literal”の形で、エスケープ文字を使わずに文字列リテラルを書くことができる。(ただし”(double quotes)はエスケープされない)。

Parsing into PropertyBag-s

PropertyBag

 これの木構造に分解するのが目標。

  • .name, .value: string
  • .bases, .children: List<Self>
    • basesをあたってプロパティを遅延評価するような形
  • .[string] -> Self
class IndentaionTree

 まず、これでインデントによる木構造に分ける。

  • .Parse( lines: IEnumerable<string> )
    • Stackを利用しながら、インデントに応じたツリー構造を構築する
    • 最後に.NormalizeIndentation() とし、空白の数 -> インデンテーションレベルに変換
  • .mIndent: int
  • .mText: string
  • .mChildren: List<Self>
PropertyBagParser

 次に、これで継承などを実装しつつ、文字列の木構造に変換する。

  • .sXxxRegex: Regex
    • パース用のstatic objects
      • .Match() すれば、key-valueペアに分解してくれる
    • Comment, Include, Line
      • sLineRegexでは、『基底』『継承』も扱われる
  • .Parse()
    • まずコメントや空白行を削除
    • 次にインデントの木構造(IndentationTree)に分解
    • 最後に.ParseTree()サブルーチンに委ねる
  • .ParseTree( tree: IT, parents: Stack<IT>, abstracts: Stack<IT> )
    • IT: IndentationTree
    • regexで頑張る

Key Bindings for Axis Input : yuhjklbn | QWE AD ZXC

 Macさんラップトップを買った勢いが残っている。Coolなことをするためにコンピュータがある、そう思うとHot!!

オモイ……ダシテ……
アノ、ヒノ……オモ……イ……ヲ……

ToME4

 Macさんでは安定して動いてくれる。このゲームは、少しずつプレイしてクリアしたい。

Key Bindings for Axis Input

 ラップトップ型のMacにはテンキー(numpad)が無いため、代理が必要。何種類か検討したが、斜め移動キーを用意するより、numpadの替わりを用意する方針に決めた。

yuhjklbn

 Viなどと同様の、伝統的ローグライクのキーバインディング
 hjklは上下左右、yubnが斜め方向に対応する。h, lが左右, jは下(↓に似て見える)、残ったkは上を指す。

QWE AD ZXC

 変則的かもしれないが、自作品ではこちらを推奨したい。

Else
  • Inventory, Equipment
  • Run, Dir, RestForXTurns, RestForATurn

 適当に配列しよう

Data > Code ?

 コードよりも、データの設計や、ゲームの完成形の想定の方が重要に思えてきた。

 ゲームは、結局データを流し込む形で作ることになる。コードさんの役目は、データで書いたことを実現するに過ぎないのだろう……。(特に、非リアルタイムのゲームでは)

Data-Driven Type Objects

 データ駆動にしたい部分は、TypeObjectに対応する。ここでもBobの手法に習う。

 Amaranth(C#)やHauberk(Dart)では、Type Objectをデータ駆動にしてコンテンツを流し込んでいる。

Data Formats

 .txt, csv, yaml, json, tomlなどのフォーマットがある。CSVは表なので、値を比べながらデータを調整するのに向いている。他の形式でコンテンツを定義するなら、"基底"となる定義を”継承”させるのもアリ(Bobのように!)。

 Amaranthでは、独自のファイル形式が用意されている。

Parsing (in Amaranth)

 方法は次回。パッと見では、インデントでパース -> regexでパース -> データに応じてパーサを割り当て。データは、ハッシュマップで持たれているのをTypeObjectに変換する。

 アイテムの効果は、C#のコードとして動的にコンパイルして用いられる。(そんなのアリ!?)

Multi Langs

 会話文などはファイルを丸ごと差し替えればいいと思う。
 アイテム使用時のログなど、Typeの定義に埋め込む場合は困る。翻訳担当者がテキストだけを読めるのが理想だが、どうするのが良いだろう……?

Dynamic Texts

 言語によって必要な文脈の情報が異なる。例えば、英語なら時制や名詞の人称が必要になる。
 英語くらいは対応したい。

On Mac

 ラップトップのMacさんを買った。高DPIのディスプレイと良質なUIが特徴だと思う。自動変換が凄まじい高精度だったり、縦書きにできる軽量なエディタがあるのもありがたい。三点リーダダッシュの長音記号も、ショートカットで入力できる。

 WindowsでもLinuxでも放置される部分で先に進んでおり、評価されているのは最もだと感じた。目当てでは無かった部分でも不便さが取り除かれており、ディテールが心地良い。使っていて自由だ‥‥。

 Winではウィンドウを最大化して使うのに対し、Macでは最大化せずに重ねて使うことが多い。違和感が無いのは、ウィンドウにフレームが無いためかもしれない。その他、同じマウスを使っても、なぜかクリックの感覚がWinと違うなど、革新的なデザインだった。いつからMacはこうなっていたのだろうか(!)。

 開発環境もMacに移した。環境周りは常にトラブルがあると思った方が良さそう。

 Macは単にUIを洗練しているのではなく、根本的な便利さを変えているように感じた。

 Macのウィンドウはニュルっとした動きで開閉するのだが、比べてみると、単純な拡大縮小よりも見栄えが良く、意識への割り込みも小さくて、視線の誘導も素晴らしかった。短いアニメの妙で、実に快適になっている。横に並べればそうと分かるが、自分で考え出せる人がどれだけいるだろう? これを考えられる人が一人チームにいれば、製品の質は全く変わってしまうだろう。

 少し前のゲームには、UI関連のアニメーションが少なかったことを思い出す。ウディタ制ゲームのウィンドウの開閉が、とてもリッチに見えたものだった。あのモーションも、今では古くなっていたようだ。当時の感動を思えば、手持ちのアニメの古さは致命的に思うので、扱えるアニメのシリーズを更新したい。

 Macを買って、WindowsMacのUIを丸コピーするのが最善なのではないか、とすら思わされた。買って良かった!

Into Nez.UI [随時更新]

 PyQt5テキストエディタを組んでいたおかげで、UIフレームワーク全般への理解が深まった。そのため、自分のゲームでもNez.UIを導入することができそうだ。

 ちなみに、Nez.UIlibGDXのライブラリScene2Dを移植したもの。

英単語いろいろ

  • padding: コンテンツの内周の余白
  • margin: コンテンツの外周の余白
  • culling (area): ある範囲からはみ出た部分を描画しない謎技術

成果物 (クリックで拡大)

† Soul Garden † (PyQt5)

f:id:samba_4e:20180729203523p:plain:w320

 執筆用のテキストエディタ。フォルダは一つしか開けない。
 ツールバーの無い親切設計。こっそり配布中だが、.txtファイル以外を開くと死亡、検索・置換が無い、他アプリからテキストを編集した場合の対応が雑、など開発途中。

開発中のゲーム (Nez)

f:id:samba_4e:20180729203522p:plain:w320

 メニューはBuilderを用意し、MenuBuilder().begin().size(). .. .build()という形で作っている。Builderただ1つをSerializableかつDesearizableにすれば、全メニューがデータ駆動になる……はず。

(実は2つのTableを使っている。1つは背景を描画し、タイトルとアイテム欄を配列している。もう1つはアイテム欄のTableで、ScrollPaneを親に持つ。Tableの解説は後述)

UIフレームワーク

オブジェクトの親子関係

 UIフレームワークのオブジェクトは、シーングラフと呼ばれるオブジェクトの親子関係を築く。正確にはルートが存在するツリーなのだが、慣用的にグラフと呼ばれているらしい?

座標変換/レイアウト

 親オブジェクトからの相対座標で描画する。子オブジェクトを縦や横に配列したり、セルの中に入れたり、何枚も重ねて表示する親もある。

 Nez本体もTransform行列をかけることで、座標や拡大率を親から子へと伝播している。

キー入力の伝播

 フォーカスの合った子オブジェクトにキー入力を伝播する。

 Nez.UIのキー入力には手を加える必要がある。

keyboard, gamepad, mouse, touchに分かれており、keyboardEmulatesGamepad: boolもあるが、テンキーでフォーカスが移動しない、ゲームパッドの操作で画面外にフォーカスが移動したとき、自動的にスクロールしてくれない、などの問題も。

アニメーションを自在にする

 ?

Nez.UIの代表的なクラス

UICanvas : Component, IUpdatable

 StageをNez本体のECSフレームワークに参加させるためのComponent。Stageを更新するだけ。

Stage

 描画や更新の開始点。root: Groupを持つ。

Element

 シーングラフのNode基底クラスに相当。

Group : ELement

 子を持てるNode。

Container

 唯一の子の位置や大きさを設定する。background: Nez.UI.IDrawableを設定できる。

Table : Element

 Cellに分けて子を配列する。backgroundを設定できる。Cellのpaddingなども細かく設定できる。

TextButton : Table, IInputListener, IGamepadFocusable

 実装が楽なためか、Tableを継承して作られている。

ScrollPane

 子の真横にスクロールバーを表示し、cullingAreaの範囲内でのスクロールを可能にする。

  • elementA.addElement( new ScrollPane( elementB ) ) の形で使用する
     elementBの親が自動的にScrollPaneになることに注意。僕はここでハマりました……

  • スクロールバーの表示位置を調整するには
     僕は子のpadRightを設定することで、上記スクリーンショットのような位置に表示している。
     子の真横に表示される、というのが曲者。しかも自動的にリサイズされるのか、ScrollPane.setSize()では、スクロールバーの表示位置を設定することができない。

余談

IDEのショートカットを再設定すると便利だった

  • ctrl+shift+b/r: build/run
  • ctrl+g: go to definition
  • ctrl+tab: next tab
    などなど……

音楽聴いてます

Linkin Park -> Avenged Sevenfold -> System of a Down -> Kvelertak, Heavenly, Skeletonwitch, Theocracy, Kalmah, Rivers of Nihil

 HR/HMのポップさ(?)に気づくと、聴けるバンドが増えてきた。メタルが特に好きなわけではないが、学校でやるような音楽と比べてクセが小さいし、スケールが大きくて比較的面白いと思う。自然に流れ着いた人も多いのではないだろうか。

 たまに演歌好きや、ブラジル音楽ばかり聴く人などがいて、強者だ……。