読者です 読者をやめる 読者になる 読者になる

お米 is ライス

C#やらUnityやらを勉強していて、これはメモっといたほうがええやろ、ということを書くつもりです

Unityでパーティクルをドット絵風にするShader

この記事はKMC Advent Calendar 2016の十一日目の記事です。
昨日はnona7さんのあたらしくrootになったみなさまへ - /dev/nona (いっと☆わーくす!)でした。

おはこんばんちは。私は京大マイコンクラブ(以下KMC)に所属するspi8823と申します。
今回は表題の通り、UnityのParticleSystemをドット絵風にする方法についてお話しします。

理由

あなたはUnityというものを使ったことがあるでしょうか?
Unityというのはゲームを作るための開発環境のことで、とても高機能ですがお金儲けをするのでなければ無料で使用することのできる素晴らしいサービスです。
このUnityを使用すれば3Dグラフィックスなゲームをとても簡単に作ることができます。もちろん、2Dのゲームも同じように簡単に開発することができます。
さて、このUnityではゲーム中に描画される様々なエフェクト(炎の燃え盛る様子や、剣が空を切る軌跡など)を作成する機能として「Particle System」というものが用意されています。
これを用いると例えばこのようなものが作成できます。
gyazo.com
UnityではこのParticle Systemに用いるための炎や煙の画像があらかじめ用意されています。
しかし、これらはもともと3Dグラフィックスのゲームで用いるために用意されたもので、画像のサイズは256*256ほどあります。これをドット絵を使用した2Dゲーム内で用いようと思っても、周りのドット絵とうまくなじまないため、少なからず不自然な印象を与えてしまいます。
そこで、3Dグラフィックスゲームのために用意されたParticle System用の画像を2Dドット絵ゲームで使用するために、高解像度の画像をドット絵風にしてしまう描画機能を作ってしまおうと思い至ったわけです。

Shader

さきほど「高解像度の画像をドット絵風にしてしまう描画機能」といいましたが、このように何かをスクリーン上に描画する処理を行っているものを「Shader」といい、プログラムを書いてこの処理を指定することのできる言語を「シェーディング言語」といいます。
今回は、このシェーディング言語のうち、「GLSL」というものを書いてドット絵風のパーティクルシステムを実現することにします。

用意するもの

Unityは各自でインストールしていただくとして、Unityで作成したプロジェクトの中に用意するものを列挙します。

Shaderスクリプト

Projectビューを右クリックし、「Create→Shader→Standard Surface Shader」を選択して新しいShaderスクリプトを作成しましょう。ファイル名はなんでもいいのでとりあえず「DotPaintingParticleShader」とでもしておきましょうか。

Material

Projecyビューを右クリックし、「Create→Material」を選択して新しいMaterialを作成しましょう。ファイル名はなんでもいいのでとりあえず「DotPaintingParticleMaterial」とでもしておきましょう。

適当なパーティクル用画像

使用したい画像を自分で用意するなり、UnityのStandardAssetsのParticle Systemをインポートしたりしましょう。

やること

UnityでGLSLを使用できるようにする

まず初めに、UnityでGLSLを使用できるようにしなければなりません。
そのため、どこかにUnityの実行ファイルのショートカットを作成し、そのショートカットファイルのプロパティからリンク先の末尾にスペースを入れてから「-force-glcore40」と追記してください。
このショートカットを使用してUnityを開けばUnityでGLSLを使用できます。

Shaderスクリプトをコピペする

先ほど用意した「DotPaintingParticleShader」をダブルクリックしてください。なんらかのテキストエディタが開かれるはずです。
その中のテキストをすべて削除し、代わりに以下のスクリプトを貼り付けてください。

Shader "Custom/DotPaintingParticleShader"
{
	Properties 
    {
        _TextureWidth ("TextureWidth", int) = 32
        _TextureHeight("TextureHeight", int) = 32
        _ColorResolution("ColorResolution", int) = 16
        _MainTexture("MainTexture", 2D) = "white"{}
	}
	SubShader 
    {
        Tags
        {
            "Queue" = "Transparent"
        }
        Pass
        {
            Cull Back
            ZWrite Off
            
            BlendOp Add
            Blend SrcAlpha OneMinusSrcAlpha
            
            GLSLPROGRAM
            uniform int _TextureWidth;
            uniform int _TextureHeight;
            uniform int _ColorResolution;
            uniform sampler2D _MainTexture;
            
            #ifdef VERTEX
            out vec4 textureCoordinates;
            void main()
            {
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;
                textureCoordinates = gl_MultiTexCoord0;
            }
            #endif
            
            #ifdef FRAGMENT
            in vec4 textureCoordinates;
            void main()
            {
                float x = floor(textureCoordinates.x * _TextureWidth) / _TextureWidth;
                float y =  floor(textureCoordinates.y * _TextureHeight) / _TextureHeight;
                vec4 color = texture2D(_MainTexture, vec2(x, y));
                float r = floor(color.r * _ColorResolution) / _ColorResolution;
                float g = floor(color.g * _ColorResolution) / _ColorResolution;
                float b = floor(color.b * _ColorResolution) / _ColorResolution;
                float a = floor(color.a * _ColorResolution) / _ColorResolution;
                gl_FragColor = vec4(r, g, b, a);
            }
            #endif
            
            ENDGLSL
        }
	}
}

詳しいことは省きますが、このスクリプトでは画像内の座標や色の値の解像度を下げる、ということを行っています。

MaterialにShaderを適用

では次に、「DotPaintingParticleMaterial」をインスペクタで開いて、その上に先ほどの「DotPaintingParticleShader」をドラッグ&ドロップしてください。
そうするとこのような表示になると思います。
gyazo.com
そして、MainTextureという項目に、用意したパーティクル用の画像を適用してください。

適当なパーティクルを作成する

ヒエラルキビューを右クリックして「Particle System」を選択してください。
新しいパーティクルがゲーム上に出現したと思います。
このパーティクルの上に作成したMaterialをドラッグ&ドロップしてください。
そうするとどうでしょう、いい感じに粗くなったパーティクルが表示されたのではないでしょうか?

では、この方法を使わない場合と、この方法を使った場合のサンプルを以下に置いておきます。

  • 使用しない場合

gyazo.com

  • 使用した場合

gyazo.com

作成したMaterialの、TextureWidthやColorResolutionといった項目を編集することで、描画する際の「粗さ」を調節することができます。


ではみなさんも、このShaderを利用して快適なドット絵ライフを送りましょう!???
明日は、id:sorahさんの記事です。

KMCM

さて、今回使ったシェーディング言語というものですが、やはりこれもプログラミング言語ですのでいきなり初心者が使おうと思い立っても簡単に使えるものではありません。
私がこの記事のようなShaderを書くことができたのも、KMCにてGLSLの勉強会を行ったからです。
つまり、KMCに来ればこんなShaderが書けるようになるということです!!!
KMCは京都大学の学生だけでなく、すべての「コンピュータで何かをしたい人」に対して開かれています。
興味が沸いたという方はぜひ気軽にツイッター(京大マイコンクラブ(1日目西ほ40-b) (@KMC_JP) | Twitter)やメール(info@kmc.gr.jp)で連絡してみてください。
また、京大マイコンクラブ (KMC)から例会の日程などを確認することもできます。

最後に

パーティクル用にって思って書いたけど、よく考えたら(よく考えなくても)このShader、パーティクルに用途限ってないような気がする。。。。。。

おまけ

今回はUnityのバージョン5.3を使ったけど、最近バージョン5.5が正式にリリースされました。
バージョン5.5では今回使ったパーティクルシステムが結構アップグレードされたほか、「Collabolate」という機能が追加されたりもしました。
このCollabolateという機能を使えば、Gitなどのバージョン管理システムを使用せずともゲームプロジェクトのメンバー内での共有ができるようになります。
今までUnityプロジェクトの共有にGitを使ってきた人は知っていると思いますが、UnityとGitは相性があまりよくないことで知られていますが、Collabolateを使えばその不自由さから解放されるようです。
後日、別途このCollaborateに関する記事を書きたいと思います。

大国主・少彦名・大物主についての妄想

はじめに

さて、まず表題の名前が何だか知っているでしょうか?
そうです、日本神話の中でも特に有名な「国作り神話」に登場する神様たちの名前ですね。

大国主は、かの素戔嗚命の子孫で、日本の国を作ったそれはそれは偉い神様です。「国作り神話」の主人公でもあります。
少彦名は海のかなたから船に乗ってやってきて、大国主の国作りを手伝った神様です。
そうやって二人は国を作っていたのですが、少彦名はある日常世へ旅立ってしまいます。当然、大国主は困ってしまうわけなんですが、そんな時にふたたび海の向こうから「私を祀れば助けてやる」といって現れたのが大物主という神様です。

大国主は出雲の神様で、大物主は奈良の桜井・三輪の神様というのは日本神話好きな人ならばごく当たり前に知っている話なのですが、少彦名だけはどうもどこの神様なのかあいまいとしています。
今回はこの少彦名の出身地を勝手に想像し、それをもとに「国作り神話」というのが一体どういう物語なのか、ということについて妄想していきたいと思います。

少彦名の出身地

少彦名に関する記述というのは正直なところ、かなり少ないと言わざるを得ません。しかし、その中に重要なヒントが隠されています。

天満宮について

まずは一つ目、少彦名の登場時、少彦名の正体について、神産巣日神が「自分の手のひらから生まれた神だ」と言及する記述があります。
以下のサイト
吉田一氣の少彦名神考察
では、そこから「手間神社」で少彦名を祀るようになり、さらにそれが転訛して「天満神社」すなわち現在菅原道真を祭神としている「天満宮」のもととなったのだ、と言っています。
当然、インターネット上の情報というのは簡単に信用してはいけない、というのが常識なのですが、しかし「なるほどな」と思うところがあるのも確かです。また、この記事もインターネットの一部でありますので、難しいことは考えずにこの案を採用したいと思います。
すなわち、「天満宮」の総本社である「太宰府天満宮」でも、もともとは少彦名を祀っていた、ということにします。ここでさらに論理を飛躍させると、太宰府天満宮が影響力を持っていた九州(の一部の地域、特に北九州)では少彦名を信仰していた、ということになります。

神功皇后の和歌

二つ目に、神功皇后が詠んだ和歌について言及したいと思います。
つまり、以下のサイト
神功皇后 千人万首
にあるような、「この御酒は 我が御酒ならず 酒の司 常世にいます 石立たす 少名御神の 神祷ぎ 寿ぎ狂ほし 豊寿ぎ 寿ぎ廻し 献まつり来し 御酒ぞ 乾あさずをせ ささ」という和歌です。
この和歌について、素直に考えると「神功皇后の当時から少彦名は酒の神様として有名で、お酒の歌を詠むときに少彦名を絡めて詠んだのだ」程度にしか思いません。
しかし、本当にそうでしょうか?
日本酒の起源であるとされる物語には以下のサイト
日本酒の登場する神話・伝説
にもある通り、様々なものがあります。
今話題にしている三神(大国主・少彦名・大物主)についてだけでも、それぞれが酒の起源であるとする神話があります。
そんな中で、どうして神功皇后は酒の神様として少彦名を選んだのでしょうか?
というわけで、ここで少し神功皇后の物語についても触れておきましょう。

神功皇后の物語

神功皇后仲哀天皇の妃で、その名を気長足姫尊(おきながたらしひめのみこと)といいます。仲哀天皇熊襲(九州南部)を攻めようとしたときに占いによって神託を受け、「朝鮮半島のほうがいいぜ」と主張しますが仲哀天皇はそれを無視し、結果仲哀天皇は死んでしまいます。
そのとき神功皇后はすでに身ごもっていたのですが、身重の体でありながら朝鮮半島へ攻め入り、降伏させます。その後、九州の宇佐というところで出産して産まれたのが応神天皇です。
のちに神功皇后応神天皇を連れて畿内へと帰りますが、その時に応神天皇の異母兄である香坂皇子・忍熊皇子が反乱しますがこれを下します。
というわけで、無事に応神天皇畿内へ帰り、天皇になったのでした。

というのが神功皇后の物語です。
さて、この物語を見ると神功皇后が九州という土地に非常に関係の深い人物であることがわかります。「香椎宮」・「宇佐神宮」という、神功皇后を祭神とする神社も九州にあります。
さて、ここで話を戻しますと、神功皇后が数ある酒の起源神の中から少彦名を選んだのはなぜか、という疑問でしたが、「神功皇后は九州と縁が深いので少彦名を選んだ」というように推測すると、「神功皇后は少彦名を酒の神として信仰する九州出身の一族を出自としていたので、自然と和歌にも少彦名を選ぶことになった」と考えるのが妥当ではないでしょうか?
これに従えば、「少彦名は九州地方の人々によって信仰されていた神である」ということが言えるのではないでしょうか?

さらに、もう一つだけ神話を紹介しましょう。

道後温泉の起源神話

道後温泉というのは愛媛県松山市松山城のすぐ近くにある有名な温泉です。
この温泉についても起源神話というのはいくつかあるのですが、そのうちの一つに少彦名は登場します。
曰く、出雲から出発して伊予(愛媛県)のあたりを旅していた時のこと、少彦名は病に侵されてしまいますが、大国主大分県別府温泉から湯を引いてきて少彦名をそれに浸からせたところ、たちまち病が治った、という神話です。
この神話で注目してほしいのは、九州地方にある別府温泉から引いてきた温泉に入った結果、少彦名が回復したということです。
日本には「産土」という、土地ごとに産土神がおり、その土地で生まれたものを生まれてから死ぬまでずっと守ってくれているという考え方があります。
この神話はまさしく「産土」の思想により、故郷の土地の湯に宿る産土神の守護によって少彦名が回復した、という物語なのではないでしょうか?
こう考えるとますます少彦名が九州地方の神であるということが信憑性を持ってくるような気がします。


ここまで3つの話を通して少彦名が九州地方で信仰されていた神なのではないか、ということを推測しました。
あくまで推測であり、どこにも確証のない話ではあるのですが、かなり説得力のある主張ではないでしょうか?
というわけでここからは少彦名が九州地方の神様であるということで話を進めていきます。

国作り神話

ではやっと本題に入りまして、国作り神話について、紐解いていきましょう。

2神の出会い

さて、少彦名は九州の神であるということになりました。すなわち少彦名イコール九州勢力として神話を読み替えることができます。
とすると、国作り神話の大国主と少彦名の出会いの部分をこう説明することができるようになります。
つまり、「大国主は出雲地方の象徴であり、少彦名は九州地方の象徴である。少彦名は海の向こうから船に乗って大国主に協力しに来た。つまり海(瀬戸内海・関門海峡、あるいは日本海のことかも)を超えた同盟関係により出雲地方の人々と九州地方の人々が手を結び、日本を支配しようとした。」ということです。海の向こうから来たということで少彦名を渡来人ではないかとする説もあるようですが、こちらのほうがずっともっともらしいとは思いませんか?
「船に乗ってやってきた」という部分についても、神武東征にあるように「九州地方の人間が船に乗ってどこか別の土地へ行く」という話は他にも存在するので、これも少彦名が九州地方の神であるという説を支持するものになるのではないかと思います。

国作り

そうして出会った2神は国作りを進めていきます。
この国作りではどこが最終目的地であったかというと、後に天孫の子孫である天皇家が朝廷を開くことになった土地、すなわち大和のことでしょう。
九州地方の勢力の協力により奈良県北部・橿原の地にたどり着いた出雲地方の勢力はその土地の土着の豪族である三輪氏の人々と手を結びます。その前か後かはわかりませんが、せっかく仲良く国を作ったものの結局九州勢力と出雲勢力は仲違いをしてしまい、それが「少彦名が常世へと旅立った」という物語へと変わっていったのだと思われます。そうして九州勢力の代わりとなる土着の勢力である三輪氏と親密になったことが、「大物主を祀った」という伝説に変わったのではないでしょうか。
ここで一つ疑問が残ります。せっかく苦労をして大和の地へたどり着いた九州地方の勢力はどこへ行ったのでしょうか?みすみす九州まで引き返したとのでしょうか?
ここで登場するのが先ほど話題に上がった「神功皇后」という人物です。

九州勢力のその後

出雲勢力と実際に仲違いをしたのかどうかはわかりませんが、九州勢力は大和朝廷の主流派ではなくなりました。
しかし、大和朝廷のある橿原という土地から少し離れたところで依然として強い勢力を持っていました。それが、神功皇后をはじめとする人物の母体となっていたある一族です。
先ほど、神功皇后の名が気長足姫尊(おきながたらしひめのみこと)であるということを言いました。この名が示すのは、神功皇后が「息長(おきなが)氏」という一族の出身であるということです。息長氏は奈良県京都府の境目付近、あるいはそれより北の山城国などを本拠地とする豪族です。この豪族は天皇家外戚として強い権力を持っていました。神功皇后が九州と縁が深いのは周知の事実ですが、ということはつまり息長氏そのものが九州勢力に属する一族であるということではないでしょうか?大和までたどり着いた少彦名は大国主や大物主のいる橿原や桜井の地から少し離れた、奈良県北部・京都府南部のあたりを本拠地としたのではないでしょうか?そしてそれが息長氏となり、神功皇后の時代になってもずっと少彦名を信仰していた、ということでしょう。



さあ、ここまでで国作り神話の謎が解き明かされました。
つまり、大国主は出雲の人々であり、少彦名は九州の人々であった。そして彼らは協力して大和までたどり着いたがやがて出雲の人々と九州の人々は離れ離れになり、それが「少彦名の常世行き」という話になった、ということです。


ここまで来たんですからついでに日本神話の中でも最も重要な神話である「神武東征」についても見ていきましょう。

神武東征

さて、こうして国を作った大国主なのですが、のちの「国譲り」と言われる神話によって、天照大神の子孫である天孫「瓊瓊杵(ににぎ)尊」に国を譲ることになりますが、この瓊瓊杵尊が天界から地上へ降り立ったのはこれまた九州の高千穂という土地です。そして時代が下った神武天皇の時代になって、九州から瀬戸内海を渡って近畿地方へと渡り、その地にいた敵対する勢力を破って大和を手に入れます。
このときに神武天皇側には数々の協力者がいたことが語られるのですが、それは少し変な話ではないでしょうか?神話によると神武天皇は「海の向こうに素晴らしいところがあるらしい、そこを治めよう」と言って初めて紀伊半島のほうを目指します。それまで存在を知らなかったのに渡った先でいきなり協力者がいるというのはどう考えてもおかしいと言わざるを得ません。つまることろ、神武天皇側には紀伊半島内部とすでに何らかのつながりを持っていたというわけです。
それをこう解釈するとどうでしょうか?つまり、「国作りの段階で大和周辺に本拠地に構えることに成功した息長氏はしかしなおも九州地方と強いつながりを持っており、出雲勢力が王朝を築いた後に九州地方にいる本家と連携し出雲勢力を排除することによって大和の支配権を手に入れた」ということです。九州地方にいる本家というのがずばり神武天皇だったのです。実際、初めは橿原や桜井のあたりに集中していた古墳群がある時期を境にぱったりと無くなり、それと同時に奈良県北部の息長氏が本拠地としていたあたりに古墳が作られ始めた、というのは考古学上における事実です。

この部分をまとめるとこうなります。初めは三輪氏と結託した出雲勢力が奈良県橿原のあたりで権力を握っていたが、神武東征という物語にあらわれる九州勢力の巻き返しにより出雲勢力が排除され、権力の中心も奈良県北部へと移っていった、というわけです。

まとめ

どうでしょうか?
少彦名を九州地方の神であるという説を用いることによって今まであくまでそれぞれ独立した神話でしかなかった国作りや神武東征といった話をあるまとまった歴史として把握することができるようになったのではないでしょうか?
私は歴史学や考古学に関してはただの門外漢であり、当然今ここで述べた論にもなんの学術的根拠もありません。したがって、これを読んでくださった皆さんもどうかこの話は眉唾物として記憶しておいてください。
しかし、案外こういうのが真実なのかもしれませんよね。
間違っていることも多々あると思いますので、「ここはこうだからその説は成り立たない」という点があればバシバシコメントしてくださればと思います。
ありがとうございました。

UnityのInputが使いにくすぎるので、俺々ライブラリを書いて一人満足した

ダウンロード

まぁ君がこの記事を読んでいるのも何かの縁だ。
とりあえず下のページのcsファイルをダウンロードして君のUnityプロジェクトにインポートするんだ。
github.com

UnityのInput

Unityの入門をした人でInput.GetAxis()や、Input.GetKeyDown()とかの関数を使ったことのない人はいないと思う。
Unityでゲームを作るうえで、キーボードやゲームパッドなど、外部からの入力を受け取るには基本的にこれらの関数を使うことになる。

Inputの使いにくさ

はじめこそ、「おっ、これ簡単で使いやすいじゃん」って思うのだが、しばらく使っているとなんともかゆいところに手が届かないようになっているのである。

たとえば、アナログスティックのX軸やY軸の値をとるための、Axisに関する関数群がそうだ。
このAxisの値をとろうとすると、「Input.GetAxis("Horizontal")」という関数を使うことになるわけだが、見ての通りこの関数は引数としてAxisの名前の文字列を必要とする。
これは、Axisがその名前で管理されていることに起因するのだが、いちいちプログラム中にスペルミスを気にしながら文字列を打つのはかなりめんどくさいものがある。
それを避けるためには自分で列挙型を作ったり、文字列を変数に保存したりといろいろやり方があるのだろうが、いかんせんめんどくさい。
そもそも初めから引数に列挙型とかを使うことができればこんな苦労はしなくてもよいのだ。

また、Axisに対してはInput.GetAxisによって現在の値を取得するということしかできないため、たとえばメニュー画面で方向キーによって選択している項目を切り替えたいとき、つまり方向キーが押された瞬間を取得する、という行為でさえ少し煩雑な作業が必要になる。
「Input.GetButtonDown」はあるくせに、「Input.GetAxisDown」は無いのである。

とまぁこんな具合に日頃、Inputによって鬱憤を溜める羽目になっているUnityユーザーは多くいることだろう。
あるいは、そんなInputに嫌気がさして、もうすでに自分で汎用スクリプトを書いてある、という人もかなりいるに違いない。

さて、自分は後者の人間だったわけで、数か月前にUnityでInputを扱うための俺々ライブラリを書き、継ぎ足し継ぎ足し所々で使ってきたのだった。
ここではそんな俺々ライブラリについて紹介させてもらおうと思う。

InputController

っていう静的クラスを作った。
このクラスは「Controller」クラスの配列を持っており、このControllerはさらに「ButtonState」クラスや「AxisState」クラスの配列を持っている。InputControllerが何をしているのかというと、フレームごとにButtonクラスやAxisクラスのインスタンスに「更新しろ」という命令を送り、その結果を用いて種々の入力を返すメソッドを持っている、というわけだ。

といってもよくわからんので、InputControllerが持っている関数を下に紹介していく。

配下のいろいろ

Controller

複数のコントローラーに対応したかったので、Controllerというクラスを作ってそのインスタンスを配列で持つようにした。

ButtonState

Controllerのインスタンス連想配列で所持している。
InputControllerが何をしているかを簡単に説明すると、こいつを毎フレーム更新し、OnButtonDownとかを呼び出すことでボタンが押された瞬間などを取得する、という仕組みになっている。

AxisState

ButtonStateと大体いっしょ

Button

ボタンの列挙型。
Button.AとかButton.Startとかがある。

Axis

軸の列挙型
Axis.R_HorizontalやAxis.Cross_Verticalなどがある。

GamePad

ゲームパッドの列挙型。
1から4まである。

汎用関数

Update

InputControllerの要となる関数だ。
この関数を毎フレーム呼び出さないとこのライブラリは動かない。
したがって、どっかのコンポーネントのUpdateで一行「InputController.Update()」と空く必要がある。
う~ん、たぶんこの運用方法はダメなんだろうなぁ(Updateを呼び出すのがめんどくさい)と思いつつ、Singletonにしてもあまり手間は変わらない(いちいちGameObjectにアタッチしないといけない)ような気がするし、この使用のままにしている。
staticメソッドを毎フレーム呼び出せる、「ExucuteEveryFrame」みたいな属性があったらいいんだろうけど、ないものは仕方がない。
そういえばUniRxとかはどんな方法使ってるんだろうか。
ちなみに同じフレームに間違えて二度三度呼び出してしまっても大丈夫なようにはなっている。

GetAxisDown(Axis axis, GamePad pad = GamePad.One)

第二引数で指定したコントローラーの、第一引数で指定した軸が押された瞬間は1(or-1)を返す。それ以外は0。
GamePad.Allとかいうのもあって、これを指定するとすべてのコントローラーのいずれかの指定された軸が押されると1(or-1)を返すとかができる。

GetAnyButtonDown(GamePad pad =GamePad.One)

いずれかのボタンが押されたらその瞬間だけtrueを返す。

GetButtonStay

ボタンが押されている間はtrue、それ以外はfalseを返す。

GetAxisStayPeriodicly

軸がが押された瞬間、あるいは押されてから0.2秒以降の0.1秒ごとに1(or-1)が返される。
つまり、方向キーを押しっぱなしにしたときの「ピッ、ピピピピピ……」というカーソルの移動が容易に実装できるのだ!!

GetButtonStayTime

ボタンが押されている時間を取得する。

初期化関数

どのボタンをどこに割り当てるか、などの設定をする。
なにもしなかったら勝手に「SetForKeyboard」が呼び出される。

SetDefault

UnityでデフォルトでInputManagerに設定されてある内容を使えるようにする。
例えば、「InputController.GetButton(Button.A)」と「Input.GetButton("Jump")」は同じ動きをするようになる。
詳しくはコードを見ろ(ぶん投げ)

SetForKeyboard

キーボードを入力に使えるようにする。
例えば、「InputController.GetButtonStay(Button.A)」が「Input.GetKey(KeyCode.Space)」と同じ動作をするようになる。
詳しく書くのもめんどうなので、中で何をしてるか書くと、

        controller.Buttons[Button.A].SetKeyCode(KeyCode.Space);
        controller.Buttons[Button.B].SetKeyCode(KeyCode.Z);
        controller.Buttons[Button.X].SetKeyCode(KeyCode.X);
        controller.Buttons[Button.Y].SetKeyCode(KeyCode.C);

        controller.Buttons[Button.Start].SetKeyCode(KeyCode.Return);
        controller.Buttons[Button.Select].SetKeyCode(KeyCode.Escape);

        controller.Axes[Axis.R_Horizontal].SetKeyCodes(KeyCode.RightArrow, KeyCode.LeftArrow);
        controller.Axes[Axis.R_Vertical].SetKeyCodes(KeyCode.UpArrow, KeyCode.DownArrow);
        controller.Axes[Axis.L_Horizontal].SetKeyCodes(KeyCode.D, KeyCode.A);
        controller.Axes[Axis.L_Vertical].SetKeyCodes(KeyCode.W, KeyCode.S);
        controller.Axes[Axis.Cross_Horizontal].SetKeyCodes(KeyCode.RightArrow, KeyCode.LeftArrow);
        controller.Axes[Axis.Cross_Vertical].SetKeyCodes(KeyCode.UpArrow, KeyCode.DownArrow);
        controller.Axes[Axis.Side].SetKeyCodes(KeyCode.E, KeyCode.Q);

というわけで、Button.BがKeyCode.Zに対応してるとかいうことがわかるはず……。
自分が使いやすいように対応させているので、あまり一般的な対応ではなさそう。。。



とまぁ、こんな具合である。結局やりたかったことは何かと聞かれると、「GetAxisDown」関数がほしかったというだけのことなんだけど。。。
あ、あと「GetAxisStayPeriodicly」関数もお気に入りなので使ってみてね。
とりあえず、なるべく簡便に使えるように作ったつもりなので使い方さえわかればUnityでゲーム作るのがちょっとは楽になる(はず……?)

WPFのバグ?的なものを見つけたので報告

内容

Imageの所属を変えても変わっていなかったようで、もろもろのイベントが現在属しているWindow(あるいはCanvas)上で行われず、前のWindow(あるいはCanvas)上で発生していた。

状況

あるオブジェクトの子オブジェクトとしてImageを持たせて、必要に応じてCanvas上に描画するということをしていた。
MainWindowがあって、その上で別の子WindowをnewしてShowDialogしたものの中にあるCanvas上に、上記のImageを描画

子WindowをCloseしたのち、もう一度子WindowをnewしてShowDialogし、同じように上記のImageを描画

ImageのMouseDownイベントを実行したのち、CanvasのMouseMoveイベントで操作を行うようにしていたところ、うまく動作しない(イベントを書いているのはいずれもCanvasのコードの中)

ImageのMouseDownイベント上とCanvasのMouseMoveイベント上でGetHashCodeをしてその値を見比べてみたところ、それぞれ違う値が返ってきていた

さらに調べてみたところ、ImageのMouseDown上でGetHashCodeをした値は初めの子Window上で見た時と変わっていなかった

解決

Imageを持っているオブジェクトに、Imageを再読み込みするという関数を用意し、子WindowのClosingイベントの中で呼び出すようにしたところ上記のバグ?は解決した

結論

これがバグであるかどうかは微妙だけど、Imageを複数の親の間で使いまわしするなってことだろう

クォータービューのゲームのステージを作るエディタをリリースしました

用途

クォータービューという、世界を45度ほどの角度で見たような描画の仕方が、SRPGなどではよく用いられています。
このクォータービューを用いたゲームの多くでは、世界はマス目で区切られており、それぞれのマス目は高さを持っています。
今回作ったのは、そのようなステージを作るためのツールです。

導入

  • ダウンロードする
  • zipを任意の場所に解凍する
  • 「QuarterViewStageMaker.exe」を起動する

出力されるデータ

ステージはJsonで保存されています。
Jsonの中身はこんな感じです。

{
  "Name": "Sample",
  "Width": 4,
  "Depth": 4,
  "Squares": [
    [
      {
        "Position": {
          "X": 0.0,
          "Y": 0.0,
          "Z": 0.0
        },
        "Height": 1.0,
        "Blocks": [
          {
            "Position": {
              "X": 0.0,
              "Y": 0.0,
              "Z": 0.0
            },
            "Name": "Maptip_1"
          }
        ],
        "Discription": "",
        "Tag": ""
      },
    ],
  ]
  "Discription": "",
  "ID": 0
}

SerializeはNewtonsoftのJson.NETというライブラリを使っているのでそれを使えばちゃんとDeserializeできます。
その他のライブラリでもたぶん使えると思います。
詳細については以下のページでコードを見ればわかります。
QuarterViewStageMaker/Stage.cs at master · spi8823/QuarterViewStageMaker · GitHub
QuarterViewStageMaker/Square.cs at master · spi8823/QuarterViewStageMaker · GitHub
QuarterViewStageMaker/Block.cs at master · spi8823/QuarterViewStageMaker · GitHub


使い方

プロジェクトを作成

初回起動時、プロジェクト作成ウィンドウが表示されます。
プロジェクトはフォルダ単位で管理されているので、作成するプロジェクトフォルダの名前と、そのルートフォルダを指定して「プロジェクト作成」ボタンを押してプロジェクトを作成します。

マップチップをインポート

使用するマップチップはプロジェクト単位で管理されています。
プロジェクトにマップチップをインポートするために「Project - 素材を読み込む」を実行し、素材ファイル(pngファイル)をインポートしてください。
複数の素材ファイルを一枚の画像にまとめたファイルがある場合は、「Project - 素材を分割して読み込む」を実行すると、そのファイルを32×32の画像に分割してインポートします。
インポートした画像はプロジェクトフォルダの「Maptips」というフォルダに保存されています。
インポートしたマップチップは、画面左側のマップチップ一覧に表示されます。
ここに表示されているマップチップをクリックすることで、そのマップチップを選択することができます。

ステージを作成

画面上部中央、「新規作成」を実行するか、Ctrl-Nを入力するとステージ作成ウィンドウが表示されます。
作成するステージの名前と大きさを入力し、「作成する」を実行し、ステージを作成します。

ステージを編集

ステージを作成するとキャンバスに罫線とともにステージが表示されます。
ステージはマス目に分けられ、それぞれのマス目に乗っている複数のブロックで構成されています。

ブロックの追加

キャンバスを左クリックすることで、その位置に選択中のマップチップに基づくブロックを追加することができます。
ドラッグすることで複数のマス目にブロックを追加することもできます。

ブロックの削除

キャンバスを右クリックすることで、その位置のマス目の一番上のブロックを削除することができます。
ドラッグすることで複数のマス目のブロックを削除することもできます。

マス目の選択

Shiftキーを押しながらキャンバスをクリックすることで、その位置のマス目を選択することができます。
ドラッグすることで複数のマス目を選択することもできます。

また、Shiftキーの代わりにCtrlキーを押しながら同様の操作をすることで、部分選択をすることもできます。

Undo、Redo

Ctrl-ZやCtrl-Yを入力することでUndoやRedoをすることができます。
「戻る」・「進む」ボタンでも同様にすることができます。

その他

いい感じの機能を用意しました。
表示されてある説明通りの動作をします。

その他の情報

まだまだ開発中なのでうまく動かないところが多々あります。
そういうところを見つけたらコメントとかで報告してくださると非常に助かります。
ご意見・ご要望などもありましたら是非ともお願いします。

UnityでポストエフェクトをGLSLで書く方法

ポストエフェクトとは

ゲーム空間内のオブジェクトを、Vertex ShaderやFragment Shaderを通してスクリーンに描画する画像を作るという処理の後、スクリーンに描画される画像に対してさらに様々な効果を加えることをいう(あってるんかな)。
例えば、画面をぼかしたり、モザイクにしたり、エッジの検出をしたり、ということである。
Unityでは、この処理を実装するために一連の流れを作る必要がある。

ポストエフェクトをCgで書く方法

以下のサイトがわかりやすくてよい
fspace.hatenablog.com

ポストエフェクトをGLSLで書く方法

Shaderにはいくつかの言語があり、Unityで主に使われているのはCgという言語だ。上で紹介した記事のShaderScriptもCgを用いている。
しかし、あえてGLSLで書きたい!!という場合、どのようにして書けばよいのだろうか?探してみたがGLSLでポストエフェクトを書いているサイトは見当たらなかったので、上記の記事をGLSLに簡単に翻訳した。
それが以下である(本当に基本的な事柄のみ書いてある)。

Shader "Custom/PostEffectTestShader" 
{
    Properties 
    {
        _MainTex("Source", 2D) = "" {}
    }

    SubShader 
    {
        ZTest Always
        Cull Off
        ZWrite Off
        Fog{ Mode Off }
        
        Pass
        {
            GLSLPROGRAM
            
            uniform sampler2D _MainTex;
            
            #ifdef VERTEX
            out vec4 coord;
            void main()
            {
                coord = gl_MultiTexCoord0;
                gl_Position = gl_ModelViewProjectionMatrix * gl_Vertex;   
            }
            #endif
            
            #ifdef FRAGMENT
            in vec4 coord;
            void main()
            {
                gl_FragColor = texture2D(_MainTex, vec2(coord));
            }
            #endif
            
            ENDGLSL
        }
    }
}

UnityでGLSLを使う方法そのものについては

GLSL Programming/Unity - Wikibooks, open books for an open world

を参照されたい。

クオータービューとかの2Dゲームを作るときの描画順について(一般論およびUnityでの方法論)

初めに

この記事を開いたということはすでにお困りなのだと思うが、3次元座標を持つ2Dグラフィックのゲームを作る際、困ることの1つに「描画順」がある。
ここ数日間、うまいこといかなくて悩んでいたのだが、やっと望むような形になったので備忘録も兼ねてここに書いておく。

描画順の決め方

早速本題であるが、描画順の決め方というものをズバッと教えてやろう。

描画順に寄与する要素を列挙する

例えば、

  • そのオブジェクトがどのマス目にいるのか
  • そのオブジェクトの天頂までの高さはいくらか
  • そのオブジェクトがマス目内のどの位置にいるのか
  • そのオブジェクトが何であるか

みたいな感じである。

描画順に寄与する優先度で並び替える

今回の場合は先ほど列挙したとおりの順番であった。

それぞれの要素について、寄与度を数値化する

例えば、高さならば「何mか」など
ここで、優先度が一番高い要素でない場合は、数値の上限はきちんと定めておかなければならない.。
また、寄与度の数値は整数であったほうがよい(ような気がする)。

寄与度の数値を優先度に応じてオーダーを変えながら足し合わせていく

何が言いたいかというと、例えば、

  • 「どのマス目にいるのか」の寄与度が0~99
  • 「高さはいくらか」の寄与度が0~9
  • 「マス目内のどの位置にいるか」の寄与度が0~999
  • 「そのオブジェクトが何であるか」の寄与度が0~9

であった場合、7ケタの数字の特定のケタの部分に特定の寄与度の情報を埋め込んでいくということをする。
具体的に言うと、上から順に寄与度が「81、3、24、5」だったとすると、描画順の最終的な優先度は「8130245」という風にすればよい。

Unityでの実装方法

2Dモードにするやろ?そうするとPositionのZが小さくなるほど手前に表示されるようになる。
だから上の場合、Zに「-8130245」を指定するといい感じになる。値が大きすぎて嫌なんだったら定数で割ってあげればいい。