Unityでインディゲーム道!

プログラム、Unity初心者がインディゲーム制作を目指して日々思うことなどを書き綴ります。

なぜプログラミングが分からなかったのかを振り返る!<入門への道 >

そこそこプログラミング出来るようになった現在から、右も左もわからなかった頃、入門者時代を振り返り、なぜプログラミングは分からないのか?ということについての根本的原因を明らかにするのが本エントリの狙いです。一般的な観点(非プログラマ的観点)からの考察になります。特に具体的なコードは載せませんが、前提とする言語はC#としています。

※原稿用紙15枚分ほどで長いッ!!。

 

暗号だから読めない

 英語を使った人工言語だから、やはり英語力はそれなりに必要なのではないか?というエントリを以前書いたことがあります。 

 単純な単語力以上に、英語をリアルなスピードで使える力(ちから)、タイプしていく力がより大事なのではないか?という仮説です。もちろん英語が出来なくても、プログラミングは出来るんですが、日本人にとっての大きな壁はそこにある気がします。つまり多くの日本人にとって英語は暗号みたいなもんだし、"二重の暗号"になってしまうのではないか?ということです。

 

 で暗号というのはつまり"コード"です、プログラム・コードのコードと同じ"code"
暗号における"コード"というのは、つまり言葉の意味を入れ替える、というタイプの暗号を指します。普段使っている言葉が、違う場所では違う意味で使われているとしたら混乱しますよね?白が黒という意味だったら大変なことになってします。

 しかしプログラミングでは、そんなことがたくさんあるわけです。

 

現実社会(リアル)とは違う意味

 最も代表的な例が、" = "(イコール)でしょう。本来ならば、左辺と右辺は"等しい"という意味で、数学的事実を述べるための記号です。

a = b ;  //現実社会においてはaとbは等しい、という意味だが・・・

 しかし、プログラミングにおいては左辺にある"変数"に対して、左辺の"変数"の値を割り当てる、アサイン(asign)する処理を表す記号と成ります。つまり、ここではbの値をaに割り当てていることになり、aとbは等しい、という意味ではないのです。(処理の結果、等しくはなるわけですが)

 

 もう、この時点でワケわかんないですよね?しかし、そういう決まりなんです。だから納得してください!と言われます。でも、そういう決まりなんですって言われて直ぐ納得できる人は少ないわけで。ここがプログラミング入門者にとってキツイ。


 そしてこの"変数"自体も違う意味ですからね。メモリの一部分に名前をつけて、プログラム内でデータの保存に使えるように確保したものであり、数学における変数とは厳密には違います。プログラミングにおいて"変数"は何かを入れる箱のように例えられますが、そのような性質から単に"変数"という言葉を割り振られただけです。"関数"なども同じです。(コンピュータ上のモノを数学用語を使って抽象化した結果?)

 

 何かに例えることによって、分かりにくいコンピュータ上の機構を人間に分かりやすいようにしよう!ということは多くありますが、そのたとえ話を本気にしすぎてしまうとかえって分かりにくくなってしまうのではないかと思います。

 

 このような普段使われている言葉、記号がプログラミングにおいては違う意味、概念として使われている、というのが混乱の元です。

 自然言語プログラミング言語は近づけようとはしてますが違うものです。(言語によって、どれくらい近いかは変わる。)なので、最初はその意味を理解して覚えないといけないわけですが、ダブル・シンキングが必要になります。2 + 2 = 5なのです。

 

テキストだからこそ想像力が必要

 なので最初はその暗号の読み方、つまり文法を学んでいくことになります。しかし、ただ暗号の意味を分かるようになってもコードは読めません。なぜか?小説を読むように想像力を働かせないといけないからです。

 

 要は表層的な記述を読んだだけではコードを読んだとはいえない、ということです。そのコードは何がしたいのか?それを実現するためにどんな手段を採用しているか?(ソリューション、アルゴリズムを読み取らなければいけません。で他人のコードを読むことが大事とは言え、もちろん最初は文法も理解し切れてないし、そういう背後にある考えや論理を読む、ということも初心者には難しいわけです。

 

 視覚的にプログラミングしていくソフトもあるでしょうが、基本的にはプログラム・コードはテキスト主体で、文字の集合体です。小説は文字だけですが、私達はそれを読むことで、様々な世界や物語を頭の中に思い浮かべることが出来ます。

 

 コードも同様に単なる文字の集まりであっても、確かにコンピュータ上には何らかの世界が展開されており、なんらかの仕事がされています。それは画面に文字を映す、という具体的なものかもしれませんし、大きな仕事を達成するための細かい、人間にはよくわからない仕事かもしれません。それらを想像する力が、最初はないから読めません。

  

 目には見えなくても、確かに存在し、直接的には人間には分かりづらいことであっても、具体的な何らかの処理をしているわけです。そのことを感じ取れるように、想像できるように知識を付けていくことが求められます。プログラミングの文法というのはあくまで書式上のルールでしかなく、逸れも大事ですが、物事の仕組みや論理的な思考の方が重要だと思います。

 

コンピュータと人間のギャップ

 プログラミングというのは、人間が手作業でやろうとすると面倒くさい、時間がかかる、あるいは事実上不可能な仕事をやってもらうための手段です。ザックリ言ってしまえ、コンピュータの計算能力を利用して、現実世界における問題を解決するための機械への命令文を書くことがプログラミングです。

 

 iPhoneSiriみたく話してやってもらうのが理想ですが、現時点では不可能です。しかしプログラミングを始めたばかり、あるいはやったことがない人は、このことが理解できません。なぜならプログラミングという作業と普段コンピュータを使ってやっていることが、結びつかないからです。コンピュータと人間がいろんな意味でどれほどかけ離れた存在か?ということがよく分かってないから、ということになります。

 

 昨今はディープラーニング・ブームで人工知能への熱が高まり、また先述のSiriなどもあり、どんどん人工知能が日常のものになってきつつあります。人口知能によって仕事が奪われる!のもいよいよ現実になるか?みたい話題もよく見聞きするようになりました。

 


 そうした流れの中で見て取れるように、世間的にコンピュータは人間に相対する存在であり、超える存在だと捉えられている、と考えることが出来ます。これは、ある意味では正しいし、しかし過剰評価の部分もあります。

 

 人間の友達、あるいは敵としての存在するコンピュータ、AIは古いSFから現代のアニメまで、よくモチーフにされます。しかし、そのようなレベルの人工知能を実現するには、まだまだ十数年の時間がかかるようです。オバマ元大統領が任期末期にGoogleの中の人からそのようなテーマについて聞いたようなので、実際そうなのでしょう。

 

 人間は何にしても、擬人化して捉えるような部分があります。特に日本人はそこらへんの気質は強いのかもしれません。人工知能を過剰に擬人化して、人間のようにとらえてしまうのは、少なくとも現時点では、コンピュータというものへの理解を妨げます。

 

ではコンピュータとはどんなもので、人間とどう違うのか?

そしてコンピュータを利用するとはどういうことか?

 

 利用に関して、たとえ話を石油による火力発電で考えてみたいと思います。火力発電は石油そのもの自体をエネルギー化するのではなく、石油を燃やして湯を沸かしてタービンを回して発電しています。実はコンピュータによる仕事もこのような転用に近いです。コンピュータの莫大な計算を計算以外(つまり電卓以外)の仕事で使う、ということで、コンピュータが主体的に何かやっているわけではないんです。

 

使う言葉も考え方も違う

 コンピュータの言葉は01であり、考え方は全て計算です。これが全てであり、だとすると、やはり人間とは明らかに異なった存在であると分かります。しかし、最初はこの事実を受け入れるのは以外と難しいです。

 なぜならワタシ達現代人は日々の生活の中で、当たり前にコンピュータを使いこなしている(ように思わされている)からです。あらゆることをスマフォでやっています。なので、プログラミングを学び始めて直面する様々な課題とそうした普段使っているコンピュータとのギャップがとてつもなく大きいものなっています。 

 Hello World!プログラムは普段使っているようなソフトを自分で作ってみたい!という人にとっては退屈すぎますが、データ(この場合は文字列)を"入力"し、ライブラリにある標準関数を使い、"出力"するというプログラミングにおける基本的なことを短いながらに網羅しているプログラムであり、実は凄いプログラムなんですが、どうしても「なんだ!この詰まらないプログラムは!」となってしまいます。

 

 そして、どんなコンピュータであれ全て0と1に構成され、計算によってあらゆるソフトは動いています。あんだけいろいろ出来てんじゃん!?0と1だけであんないろいろ出来るのはおかしくない?みたいに思うのが普通です。それもやはり障壁となるギャップのひとつです。プログラミングを学ぶ上では、まずそうした手軽にコンピュータを使うということと自分でプログラミングするということの落差を呑み込めるようになるのが大事なのかもしれません。

 

 数字で表せるモノであれば、最終的にそれらは0と1だけで表すことができ、コンピュータ上で取り扱うことが出来るわけです。(10進法から2進法への変換)なにより、現代のコンピュータはアホみたいに莫大な計算力があります。一秒間に何十億回という計算が出来ます。コンピュータに暗算勝負を仕掛ける人はいないでしょう。

 

 数字で表すことが出来て、かつ計算することが出来る問題であれば基本的にはコンピュータは解決することが出来るということになります。しかし、そうでなければ、そもそもコンピュータがそうした問題、仕事を理解することすら出来ないのです。

 つまり計算によって答えが出せる問題ならば、0と1で構成された計算式による命令文をコンピュータに実行してもらうことで解決できる、ということになります。

 

プログラミングの目的

 さて、実際のプログラミングにおいて、問題なのは数字で表す、つまりデータ化や、そうしたデータをどう組み合わせて、どう計算するか?を考えるのは人間ということです。"上手いやり方"を人間がコンピュータに教えないといけません。

(そうしなくて済むようになるかも?というのがディープ・ラーニングらしい。)

 

 プログラミングは単にコードを書くだけでなく、そうした領域の問題解決も含むわけです。と同時に、既存のコードにはそうした部分が含まれているのであり、コードを読むということは、コードの背後にある概念を理解する、ということになります。

 つまり、目の前にあるコードというのは結果の産物であり、それに至るまでには様々な工程を経ているということです。だから、コードを読むというのは難しいのです。

 

絵が書けます。音楽が聴けます、作れます。映像を見れます、作れます。

夢のようなことを実現するための処理をコンピュータはしてくれています。

 

 しかし、どう処理するかの命令は人間が書いています。人間のやって欲しいことというのは、コンピュータには基本的に理解しがたいことであり、コンピュータに分かるような形で命令を書かなければいけません。

 

 つまり、人間とコンピュータのギャップを埋める、というのがプログラミングの本質だということになります。なので、そこをまず理解することがプログラミング入門の一歩の気がしますし、それなりの時間が掛かるのかなと思います。

  

じゃあ、どうするべきか?

注意点と対策をまとめます。

 

 プログラム・コードは一種の暗号であり、人間側の都合とコンピュータ側の都合が交じり合っているので、惑わされない。分かりにくくて当然と考える。

 コンピュータ上でことを人間が分かりやすいように何かに例えることが多いが、あくまで例え話なので本気にしない

 コードを読むというのは、表層的に読んでも意味がなく、何がしたいか?どう解決しているか?を理解する必要があり、その分野に文法は直接的には関係ない

 むしろ、理想論的にはコードを書き始めた時点で、コンピュータにどう指示するか?という問題解決の手段は見つかっており、その解決法を文法に沿って翻訳したものがコードと呼ばれるものに過ぎない。(現実的には試行錯誤が必須。)

 hello world!は詰まらないプログラムだけど、そこで諦めない。

 やはり、それなりに時間が必要なので焦らず自分で考える力を付ける。

 文法を覚えきるより、簡単なプログラムを自分で書いていく方がいい。

 コンピュータと人間の違いを理解し、その差を埋めるように"お願い"するということを意識する。

 

あくまで個人的な見解も入ってますが、こんな感じでしょうか。

 

最後に

 という感じで未熟者がいろいろ講釈たれたわけですが、とにかく言いたいのは、

プログラミングおもしろいから、諦めずに頑張ろうぜ!ということ。

まぁ仕事としてやってないからってこともあるのかもしれませんが、でも馴れるまでが大変だったとはいえ、単純にいろいろできるようになるとやっぱ楽しいんですよね。

 

 もちろんプログラミングは手段であり、それを使って何をするかが大事なんですが、それ自体がおもしろいなぁって。

 

 Unityなんかは特にプログラミングできなくてもゲーム作れるよ!という触れ込みではあるんですけど、逆にプログラミングできると何でもできますからね。なんかプログラミングをネガティブなものとして捉えるのはもったいないと思います。

いや、実際まともな動作にするには大変ではあるんですけどね。

 

  

プログラミングってこういうことだよね?というチャート図を描いてみた。

結局、プログラミングとはどういう作業なんだろう?ということを一覧できるような図を描いてみよう、というのは以前から思っていました。

 

プログラミングを一概に説明するのは無理でしょう。でも初心者の頃にそういうのが欲しかったんですよね。大体でいいので、プログラミングという作業の概要をなんとなく掴めるような図です。

 

これをチャート図といっていいのか分かりませんが、一応上から下へと順に工程を説明するような形にはなっているので、いいのかなー?

 

f:id:miur-us:20170913013402p:plain

かなりでかくなっちゃいました。

こんな感じで改善の余地アリアリですが、とりあえず自分が今理解している範囲でのプログラミングという作業の俯瞰図として描けたと思います。

まぁごちゃごちゃしてます。あとオブジェクト指向を念頭に置いた図になっています。

 

これを具体的にゲーム制作に当てはめた図も描くと、より分かりやすくなりますかね。

それも近々。

 

この図を描いて改めて思ったことをまとめると、人間とコンピュータのギャップを如何に埋めるか?という一言に尽きるのかもしれません。

そして、やはりコードを書くというのはプログラミングの中でも一部分に過ぎないということです。もっと物事の本質を掴むチカラが大事なんだなーと。

 

話は変わりますが、コードを書けるの当たり前で、おもしろいゲームを作れるかは別問題である!ということを最近痛感してきています。

コードによる処理も大事なんだけど、やっぱグラフィック面が大事なんですよね。音とか曲はまだ自分で何とか出来るんですけども。 

まぁ、そのためのアセットストアですけども!^_^;

 

最後、少し話がズレましたが、こんな感じで楽しくやってます。

 

 

C#で"FizzBuzz問題"に挑む!Main内で五行達成。

FizzBuzzとは英語圏における言葉遊びで、複数人で数字を順に言っていくわけですが、3の倍数の時は"fizz"5の倍数の時は"buzz"3と5の公倍数の時には"fizzbuzz"と言うのがルールになっています。

FizzBuzz問題とは、この遊びをプログラミングで再現するというものです。その人が本当にプログラミングが出来るかどうかを試すのにちょうどいい"簡単な問題"とされるもので、面接などで出題されるそうです。あるいはいろんな条件を課して、力試しとして取り組むこともあるようです。

 

きっかけはこちらの記事です。"fizzbuzz"という遊びはなんとなく聞いたことがあった気がしますが、プログラミングでそういうものがあるのは初めて知ったので、興味が出ました。ググってみた所、会社内でコレを使ってコンテスト開いた!というブログ記事なんてのもあったので、それを軽く読んでみたり。

 

どちらの記事もPythonでのコードです。ほぼ経験ナシのPythonとは言え、答えのコードを軽くは見てしまってはいるんですが、せっかくなのである条件を付け加えて、C#でこのfizzbuzz問題のコードを書いてみることにしました。

以下はどのような試行錯誤を経て、最終的なコードへと至ったかの思案の推移をまとめたドキュメントであり、なるべくどのように思考したかを順を追って逐一説明する形で文章化してあります。

 

条件とは?

目標としては可読性や見た目を損なわず、できる限り行の少ないコードにする!ことに設定しました。

1から100までの数字をコンソール画面に表示し、ルール通りに特定の数字の時にはfizzやらbuzzを変わりに表示する、というコードです。

いわゆるウィキペディアなどにあるような模範的なコードではなく、C#として使えるものは全部使って出来るかぎり短いコードにするということです。

 

試験ではないので制限時間もないし、コード実行して結果を見つつ改善していく、ということも特に禁止してません。あくまで自分で考えて、コードを書くという試みです。

 

問題の分割!

さて、この問題は大きく分けて2つの個々の問題に分割することが出来ます。

  • 繰り返し処理
  • 条件分岐

まずは1から100までを繰り返し画面に映す、という問題です。

これはループ文、イテレーションによって実現できます。whileかforのどちらかを選ぶか、ということについては、自分はwhile文の中でincrement(インクリメント)すれば短くできるかも?ということでwhile文を選びました。しかし、それは誤りで普通にfor文の方を使った方が簡単に短く出来るので最終版ではforを選択しました。

 

カウント変数、イテレータをそのままConsole.WriteLineを使って繰り返し画面に表示すれば、ひとまず1から100まで画面に映す、という問題は解決できます。

for(int i =1; i <= 100; i++){

Console.WriteLine( i );

}

 

次に解決すべき問題は、数字を判断して、3の倍数の時はfizzを、5の時はbuzzという風に数字の代わりに文字列を映す、という問題です。

  • 3の倍数            Fizz
  • 5の倍数            Buzz
  • 3と5の公倍数   FizzBuzz
  • それ以外の数   カウント変数を出力 

これは素直にif文でそれぞれ条件判断すればいいわけですが、自分の課題はなるべく行数を少なくすることです。なので三項演算子を使うことにしました。

 

三項演算子とは

(条件式) ? trueの時の値 : falseの時の値

というもの場合によってはif文よりシンプルに(あるいは読みづらく)条件判定が書き表わせられるというものです。

 

これを使えばConsole.WriteLine()のカッコ内、引数として三項演算子の式を入れることが出来ます。これによって行数を少なく出来ます。後で説明しますが、これは一回挫折します。しかし、最終的には三項演算子を使うというアイデアを実現することが出来ました。見当違いではなかったということになります。

Console.WriteLine( a > b ? a : b  );

//aの方が大きいならaがそうでないならbが表示される。

というわけで、この問題を解決するための準備は整いました。

 

解決に向けて その1

自分はwhile文を選んでしまったので、まずwhileの条件式の中でincrement出来るかの確認から始めました。一回試したかったというのもあります。

カウント変数は文字数を減らす意味でもnumberの頭文字のnとしました。

int n = 0;

while( n++ < 100){

Console.WriteLine(n);

}

です。

インクリメントは前置き後置きがあって、演算子や比較子を実行する前に増やすか後に増やすか選ぶことが出来ます。

どちらでも問題はないようです。後置きだと100との比較後にincrementが実行され、その後に、内部の処理が実行される、という流れになります。

ちなみにVisual StudioはConsole.ReadLine()などで受付待ちさせないと、処理終了後すぐ画面が閉じてしまうので付け足していますが、除外して行を数えています。

f:id:miur-us:20170822233900p:plain

f:id:miur-us:20170822233815p:plain

というわけでひとまずは最初の問題は解決です。しかし先ほど書いたように、最終的にはwhile文は捨てて、for文を使います。

 

解決に向けて その2

三項演算子です。それぞれの倍数は%(剰余算)を使って表現出来ます。倍数はつまり、その数で割り切れるということであり、%の答えが0になります。

6 % 3 == 0 (6を3で割った余りはゼロ)

 

で最初に思いついたアイディアを形にしたのが、この二行になります。

f:id:miur-us:20170822234305p:plain

キーポイントはConsole.Writeです。これは改行されません。つまり、3,5の倍数の時は続けてbuzzが表示されることで、Console.WriteLine("fizzbuzz")を省略出来ます。これは我ながら良いアイデアだと思いました。つまり3,5の公倍数の時には"FizzBuzz"を出力する!という個別の処理文を書く必要がなくなるので省スペースになります。 

f:id:miur-us:20170822234238p:plain

これを実行すると、Fizzの後ろに3が出力されてしまっていますが、それなりに上手くいっているように思えます。しかし、どうしても3の倍数を出力しない方法が、この時点では思い浮かびませんでした。

 


なので、諦めて素直にif文を使ったコードにすることにしました。

continue文で、その時点でループを強制終了して次のループへ移行します。

f:id:miur-us:20170822234441p:plain

3と5の公倍数を表すのに素直な表現は

if( n % 3== 0 && n % 5 == 0 ){}

になりますが、これは圧縮出来ます。

if( n%3 + n%5 == 0 ){}

どちらも0であるならば、その和も0になるのだから、これでも条件としてはオッケーなはずです。この後は素直にそれぞれの条件を判断し、相応しい数字、文字列を移すようにします。というわけで、完成かと思いましたが、最後の文が三項演算子で省略できると閃きました。上の写真の最後の文は元々は

if( n % 5 == 0 ) Console.WriteLine( "Buzz" );

else Console.WriteLine( n );

でした。

つまり、ここをConsole.WriteLine( n % 5 == 0 ? "buzz" : n.ToString() );としました。

三項演算子の場合は、nをToStringを使って文字列に変換することが求められます。

まさしく最初に思いついたアイデアをそれなりに活かすことが出来て満足でした。

f:id:miur-us:20170822234836p:plain

 

まだ終わってない!!

というわけで、できたー!と満足していたんですが、やはり三項演算子を活用した更に短いコードを諦めきれず、そしてやはりfor文の方が短く済むということで、再度挑戦しました。改めてというか、出来た後一時間後くらいなんですけども。

for ( int i = 1; i <= 100; i++ ){

//判断し、画面に映す処理

}

とりあえず、最初の改善案はfor文に変更することです。こちらの方が分かりやすい!

そして、最初のアイディアの大きな問題はfizzが打ち出された後、そのまま流れでカウント変数がプリントされてしまうことでした。

 

しかし、continueを使って次のループに飛ばす、という技を第二案で使っていたので、これを使えばいけるだろうと考えました。

 

何かもう一行、条件が必要です。fizzの条件を再確認しましょう。3の倍数であり、5の倍数出ない時です。つまり、複数の条件ですから、三項演算子は使えません。そうだ!別にif文使えばいいだけじゃん!と思いつきました。

f:id:miur-us:20170823001506p:plain

三項演算子の間にif文を書いてはいけない、なんていうルールはないです。自分自身の思い込みによって、視野が狭くなっていました。

if( i % 3 == 0 && i % 5 != 0){ Console.WriteLine(); continue;}

fizzは改行されませんが、Console.WriteLine()を実行し改行処理。continue;でイテレーションを飛ばします。これでcontinueも一個に削減。

以上で、FizzBuzz問題のコードをMain内で五行にすることが出来ました。 

 

まとめ!

今回の試みを経験したことで、やはりアイディアは大事だし、一回ダメでもいろいろ試してみると道は開ける、ということを強く実感しました。結果的に最初の三項演算子を使ってコードを圧縮できるかも!?という算段は外れてはいなかったので。

いろいろ勉強になりました! 

応用編として、%を使わない、とかいろんな縛りプレイがあるみたいですね。

そこらへんもいつか挑戦してみたいと思います。

 


と思ったら、記事を書いている間に案を思いつきました。

割り切れる、というのは余りがない、あるいは割った商の小数点以下がないということなので、%(剰余算)を使う代わりに次のように書き表せます。

もし i / 3.0f - i / 3 == 0 なら

3.0fつまりfloatにしないと、商が切り捨てられた整数になってしまいます。逆にi / 3とすることで、小数点以上のみに出来るのでこれを引けば、少数点以下を表せます。

もしiが1の場合、1.0 ÷ 3.0 = 0.33333...で1 ÷ 3は少数点以下切捨てで0でよって、少数点以下が存在するので(0.333... - 0 = 0.333...)1は3の倍数ではない、という風に判断することができます。

iが6の時は、(6.0 ÷ 3.0 = 2) - ( 6÷ 3 ) = 0.0よって、3の倍数である!となります。

あとは先ほどの三項演算子の条件式の中身と取り替えるだけです。

f:id:miur-us:20170822235029p:plain

というわけでFizzBuzz問題の研究を終わります。やっぱ自分で考えて解くと爽快感がありますね。

 

UnityでGetComponentを使って"参照"を得るということ。Unityにおけるオブジェクト指向とは、的な。

Unityでのスクリプト作成、コーディングにおいて重要であり、かつ基本的なメソッドである"GetComponent"について、まとめてみます。

そして、そもそも"参照(リファレンス)を得る"ということはどういうことか?という基本的な部分についても書いてみたいと思います。

 

GetComponentオブジェクトの参照を得るためのメソッド

 

オブジェクトとは

まず"オブジェクト"とは"クラス"の変数です。"クラス"は設計図のようなモノであり、そこから作られた実体、みたいな説明がありますが、結局は"クラス"は複数の変数、メソッドを保有できるデータ・タイプ(いわゆる"型")なワケで、そのタイプのデータを格納するための変数が"オブジェクト"です。

変数とは少し違うんだぞ!という区別をするために"オブジェクト"あるいは"インスタンス"といっているのであって、何らかのデータを格納するための器のようなものという性質は同じです。私達人間も様々なデータを内部に保有しています。

 

Unityでコードのコンポーネント"スクリプト"と呼んでいますが、要は"クラス化された、あるひとつのコード"であるということに注意が必要です。つまり、プログラム全体のコードの一部分です。またUnityのスクリプト上で、"スクリプト"を含む他のコンポーネントのオブジェクトを作るということは、他のクラスのオブジェクトを作るということにもなります。(ここは後で詳しく説明します。)

 

変数である以上、宣言して使うわけですが、それだけでは"中身"は入っていません。

int year = 2017;

このように値を代入しなければいけません。

 

オブジェクト指向とは"クラス"で分割し、そこからオブジェクトを作り、オブジェクトを中心にプログラムを動かしていくことで、現実世界の概念や自然言語に近づける、というものです。当然、オブジェクトには中身が必要です。

問題はUnityにおけるオブジェクトに入れる中身とは何か?ということです。つまり、それは実際のゲームオブジェクトにアタッチされているコンポーネントになります。

(もちろんプログラミングにおける一般的な意味でのオブジェクトも作られます。)

 

Unityにおける"コンポーネント"

直訳すれば『部品』となりますが、Unityでは何かしらの機能のまとまり、ゲームオブジェクトに機能を持たせるための部品になります。

要は物理演算のための"Rigidbody"や当たり判定の"コライダー"、音を出すための"オーディオ・ソース"、などのUnityで(比較的)手軽にゲームを作ることの出来る、有難い部品の数々です。

と同時にプログラミング的にこれらは全て"クラス"でもあります。つまり、スクリプト上でUnityのコンポーネントを動すには、そのコンポーネントの"クラス"からオブジェクトを作る必要があるということです。

 

そして、上記で書いたようにオブジェクトには中身が必要です。繰り返しますが、それがゲームオブジェクトに実際にアタッチされているコンポーネントになるわけです。

つまり、その中身がなければ、

Rigidbody rb; // Rigidbodyのオブジェクト、インスタンス

rb.AddForce( Vector3.up * jumpPower ); 

とコードを書いても、Unity上では何も起こらないわけです。ここでいう"rb"というRigidbodyのオブジェクトに中身を入れる必要があります。

 

つまり、オブジェクトにアタッチされているコンポーネント"参照"(リファレンス)を得る必要があります。(例えばプレイヤーのゲームオブジェクトにアタッチされているRigidbodyのことになります。)

 

そもそも"参照"とは?

参照する、と単純に言えば、何らかの本を参考にするみたいなことです。より詳しく言うと、"どこ"にある"どの本"の"何ページ目"の"何行目"を明示した上で、参考にする、というのが"参照する"ということです。

プログラミングにおいても同様で、より正確な所在地を指し示しつつ、参考にしている、引用している、ということになります。

 

プログラミングにおける"参照"(リファレンス)

少しプログラミングそのものに関する話です。C#では変数のデータの格納の仕方には二種類あります。値そのものを格納するデータ・タイプ、実際の値は格納せず、データ群が置かれたメモリ上のアドレスを格納するリファレンス・タイプの二つです。

 

オブジェクトはリファレンスタイプになります。つまり、オブジェクトは正確には該当する中身の"ある場所"を格納しているということになります。オブジェクトはメモリの住所を指し示している、とも言えます。どこにあるか分かっているからこそ、データやメソッドを動かすことができるということです。

 

GetComponentの働き

というわけで、いよいよ本題です。つまりGetComponentはUnity上の該当するコンポーネントどこにあるかをオブジェクトに示す(返す)ためのメソッドです。

スクリプト上のオブジェクトが真の実体を持つには、オブジェクトがUnity上のコンポーネントを指し示すことが出来なければいけないのです。

先ほどの例で言えば、

Rigidbody rb;

 

void Start(){

        rb = GetComponent<Rigidbody>();

}

 

これはRigidbodyのオブジェクトを作り、このスクリプトがアタッチされているゲームオブジェクトに同じくアタッチされているRigidbodyコンポーネントの場所を"rb"に指し示す、という内容になります。

注意としては、GetComponentの効力は、基本そのスクリプトがアタッチされているコンポーネントに限る、ということです。(ChildやParentは指定すれば可)

上の例が、プレイヤーキャラにアタッチされた"PlayerController"スクリプトだとすると、そのプレイヤーキャラにアタッチされたRigidbodyを呼び出していることになるのです。つまりrbというオブジェクトが、当コンポーネントの参照を得て、このコード上において実体をもった、ということになります。

 

アタッチされたゲームオブジェクト以外の参照

しかし、それだけでは参照を得るのに限界があります。例えば、敵キャラにアタッチされた"EnemyContoroller"等の他のスクリプト(他の"クラス")の参照を得ることはできないのでしょうか?でなければ、"PlayerController"側から、敵キャラを動かすことができないことになります。(攻撃したら吹っ飛ばす、みたいな)

 

スクリプトの中で、そのスクリプトのオブジェクトを作り、きちんと"参照"を得れば可能です。それにはいくつかの方法があります。

 

publicとして宣言して、インスペクター上にドラッグする。

public Rigidbody playerRb;

これは便利ですが、実際にシーンにおかれているゲームオブジェクトである必要があります。例えば、必ずシーンに存在するプレイヤーを参照する際に便利かもしれません。しかしドラッグし忘れるとnull(参照ないですよ!)エラーが発生します。

 

GameObject.FindWithTag()で引っ張ってくる。

タグの設定をする必要がありますが、確実な方法です。基本的にプレイヤーには、Playerタグを設定しておけば、FindWithTagでそのプレイヤーのゲームオブジェクトの参照を得られます。つまり、一回、ゲームオブジェクトのオブジェクトにそれを移して、そのオブジェクトからGetComponentする、という形になります。

GameObject playerObject = GameObject.FindWithTag("Player");

if(playerObject != null)

           rb = playerObject.GetComponent<Rigidbody>(); 

 

・接触したコライダーから参照を得る。

これはダイナミックな方法です。OnCollisionEnterなどで検出したコライダーからGetComponentするという方法です。応用するとPhysic.OverlapSphere等で検出したコライダーからを与えて、バクダンの爆発力を与えて吹き飛ばす、なんてこともできます。rigidbodyの場合は、collider.attachedRigidbodyから直接アクセスも可能です。

  

まとめ!

というわけで、Unity上で何かする際に必須な各コンポーネントスクリプト上でコントロールするためには、その実際のコンポーネントの参照を得る必要があり、そのためにGetComponentは必要である、という話でした。

基本ではありますが、大事な部分ではあると思うし、Unity上のC#を使う上での特徴的な所だと思うので、自分なりにまとめてみました。

   

 

下から叩くとアイテムが出るブロックの再現。二重コライダーの活用。

今回はUnity上で、マリオなどのアクションゲームで目にする"下から叩くとアイテムを出すブロック"を再現してみたいと思います。Unity2Dになりますので、3Dとの違いにご注意を。 

 

f:id:miur-us:20170813163431g:plain

完成版のGIFです。こんな感じでニュッと出るようにしました。明らかに敵っぽい目つきですが、アイテムのフラワーです。

Unityはcollider(コライダー)が準備されているので、それを各ゲームオブジェクトにアタッチすれば、簡単に"当たり判定"を付けることができます。

今回は2Dになるので、BoxCollider2Dという感じで後ろに"2D"がつきます。付け忘れると動かないので注意しましょう。

 

 で、ボックスにとりあえずコライダーを付ければ、プレイヤーキャラとの単純な衝突は検出できるようになるので、アイテムボックスのスクリプト側にOnCollisionEnter2Dを書いて、そこに"アイテムを飛び出させるコード"を書けば何とかなりそうです。

 

下からだけに反応させる!

しかし!このままでは、ボックスのどの位置にプレイヤーが当たってもアイテムが飛び出してしまいます。下から当たった時にだけアイテムが出るようにするにはどうすればいいか?自分が思いついたのは"二重コライダー"です。

つまり、ブロックにコライダーを二個設置することで、プレイヤーがブロックの下に当たったかを検出するという方法です。通常のコライダー以外にトリガーモードのコライダーを下側に設置します。

 

f:id:miur-us:20170813170020p:plain

 少し見えにくいですが、下に少しだけ飛び出している、コライダーがあるのが分かるかと思います。(緑の四角)

要はこのコライダーでプレイヤーが下にいることを確認しつつ、メインのコライダーに当たったか?という判定を行うわけです。そのために下のコライダーはトリガーモードでの使用、ということになります。 

 

 

 f:id:miur-us:20170811224644p:plain

Diamondというのは、赤い模様の部分なので無視してください。

"UnderTrigger"というのがそれです。コライダーのみアタッチされています。ヒエラルキー上では、ItemBoxのチャイルドに置かれています。

f:id:miur-us:20170811224835p:plain

IsTriggerにチェックをいれると、トリガーモードにできます。これにより単純にここに重なっているかどうかを検出できるようになります。"当たり"はなくなります。

↓アイテムボックス本体のインスペクターはコチラです。

f:id:miur-us:20170811225100p:plain

Box Collider 2Dがアタッチされています。これが単純にブロックとしての当たり判定になります。最終的なプレイヤーとの衝突はここが検出します。

下にある、Item Box Controllerが実際のスクリプトになります。

 

コードを確認してみる!

f:id:miur-us:20170811225350p:plain

f:id:miur-us:20170811225415p:plain

各変数から説明していきます。

"content"

まさしく箱の中身でアイテムです。今回はフラワーですが、Unityのインスペクター上でアイテムのプレファブをドラッグすれば、いろんなアイテムに入れ替えられます。

悩んだんですが、Startメソッド、つまりシーンがロードされた時点でプレファブをInstantiateしています。でSetActiveでfalseにして隠しておきます。

"movePoint"

アイテムの飛び出す位置です。本来はアイテムごとに飛び出る位置を調整する必要があるでしょうが、ここでは省略しています。Startメソッドで座標を指定しています。

ニュッと出すためにVector2.Lerpを使いますが、きちんと動くためにここで宣言しておく必要があります。(フィールドというやつです。)

"IsOpened"

これは開いたかどうか?当然最初は開いてないのでfalseです。プレイヤーがきちんと下にあたったらtrueにします。

"IsActive"

動かすのにVector2.Lerpを使うと移動し終えるまでUpdateでループさせる必要があるんですよね。で移動し終わった後もコードが実行され続けてパフォーマンス的にまずいと思うので、アイテムがきちんと出たらコレをfalseにして、つまりボックスの動作がオフになった、ということにしてコードを実行されなくするためのフラグになります。

 

"sr"

これはSpriteRendererのオブジェクトです。叩き終わった後、もうアイテムが入ってないよ!ってことを示すために色を変えたいんですが、その際に使います。

"m_Colliders"

このコードでもっとも重要な複数のコライダーを扱うためにBoxCollider2Dのオブジェクトを配列として宣言します。Startメソッド内でGetComponentsInChildrenを使って、ボックス本体のと下側の二つの参照を得ています。なので・・・

m_Colliders[0]はItemBoxのコライダー、m_Collider[1]は下部のトリガーをそれぞれ指すことになります。

"playerCollider"

プレイヤー自身のコライダも必要なので、オブジェクトを作り、StartメソッドでFindWityhTagを使い、参照を得ています。

 

実際の処理の流れ

というわけで準備は整いました!

プレイヤーがブロック下にぶつかると、どういう流れで処理が進んでいくかを、実際に説明していきたいと思います。

まずはOnCollisionEnter2Dが作動します。二つの条件を判定します。

  • 下部トリガーがプレイヤーのコライダーを検出しているか?
  • 本体に当たったゲームオブジェクトのタグが"Player"か?

最初の判定はm_Colliders[1].IsTouching(playerCollider)という文で行います。IsTouchingはコライダのメソッドで、引数として入力されたコライダが自分と触れ合っているかを判定し、触れていればtrueを返します。二番目は公式チュートリアルでも出てくるCompareTagで判定。

col.gameObject.CompareTag("Player")でプレイヤーのタグをチェックします。

 

どちらもtrueとなったら以下の文が実行されます。

content.gameObject.SetActive(true);

IsOpened = true;

StartCoroutine(WaitSwitchOff);

SetActiveでアクティブにし、IsOpenedをtrueにすることで移動の処理が始まります。

StartCoroutineIEnumratorを使って、WaitForSecondを含むメソッドを実行する際に必要ですが、コレによりアイテムが出た後、IsActiveがfalseになるようにしています。一秒もあれば移動も終わるので、1.0f秒待機、としています。

f:id:miur-us:20170811235740p:plain

さて!ではアイテムが飛び出るための処理です!

Updateメソッド上にあります。この処理自体をメソッド化してもよかったのですが、これだけなので、そのままにしています。

 if( IsOpend && IsActive) { //移動の処理 }

まずは箱がオープンされたか?アイテムボックスは仕事を終えてないか?という条件判定です。これについては、上記で説明したとおりです。

 

content.transform.position = Vector2.Lerp( ~ 以下略 ;

これはつまり、フレームごとにアイテム自身の位置目標地点を0.3fの割合で線形補間していく、というもので、要は単純に移動させるのではない、ニュッという感じの動きになります。生々しい動きになるわけです。

sr.color = Color.Lerp(sr.color, Color.gray, 0.4f);

については、正直ブロックを叩いた時点で実行していいですね。

ボックスの色を変える実行文です。

 

そして、先ほど言ったように移動し終わったころには、WaiSwitchOffメソッド内の

IsActive = false;

が実行され、移動の処理は行われなくなります。

if(IsOpend && IsActive)の部分がfalse(偽)になるからです。

 

まとめ!

以上で、プレイヤーが下から叩いたかを判定し、アイテムを出して、ボックス上部に移動させる、という動作の実現についてのレポートになります。

まだ改善点、付け加える部分はありますが、プレイヤーをジャンプさせてブロックを叩いて、その勢いのままにアイテムが飛び出してくる、という手ごたえのあるレスポンスは再現できたのかなと思います。

 

というわけで、Unity上でのまともな実践を扱ったエントリはこれが初になるんですかね?ネタはいろいろあるので、ドンドンやって生きたいと思います!! 

 

 

本ブログのカテゴリー(の定義)を整理しました!

本ブログのメインテーマはUnityでゲーム、あるいはアプリを作る!というものであり、それに付随するプログラミングに関することなども書き記していくのが、主な目的となっています。

それらを中心に、そこからズレ過ぎない程度にいろいろ書いていきたいんですが、きちんと方向性を決めておかないとブログとしてはブレるな、と思ったのでブログ内カテゴリの刷新、あるいは定義をしっかりやってみることにしました。

基本的に自分用メモですが、本ブログを読まれる方の参考にもなれば幸いです。

 

というわけで、より良いブログ運営を目指してGO!

 


 

プログラミング

これはそのままですね。純粋にプログラミングそのものに関することを書きます。基本的にはC#についての内容になります。学んだことのまとめになると思います。

 

UnityのためのC#

今回特にきちんとしたかったのがココです。従来だと"プログラミング"とごちゃ混ぜになっていて、きちんと内容的に分けられていなかったです。

なので、明確に定義しておきたいと思います。

Unity上でのC#に関することで、必ずUnityの具体的な動作を絡めること、とします。

単にコードのみ載せるとかではなく、その結果どういうことが出来るようになったか、などをきちんと明示する、ということになります。なので、どちらかというと実践寄りの内容になると思います。

 

Unity

アセットストアでこんなの買ったよ!とかアップデートでこんな新機能が搭載されてスゲー!などのUnity本体に関わる内容にすること。 

 

MadewithUnity

Unityで作られたゲームの紹介、レビューについてのカテゴリ。 

 

ゲーム評論

Unity関わらず、個人的に遊んだゲームについてのレビュー。MadeWithUnityでもレビュー的内容がある場合にはこれに含まれる。 

 

ゲーム制作

自身の具体的なゲーム作りに関することについて。近いうちに簡単なゲームについては公開してみたいと思ってますが、そうしたものの進捗などはココでやります。

"UnityのためのC#"はコーディング絡みになりますが、単純にUnity上で何か作ったり、アセットを試してみた!等の内容もココでやります。 

 

書籍レビュー

読んだ本の書評についての内容になります。もちろんプログラミングやゲーム制作に関わる本に限定します。 

 

GEEKネタ

"GEEK"とはアメリカにおけるサブカル寄りなオタクを意味するスラングで、任天堂のゲーム『MOTHER』シリーズにおける、ギーグの語源らしいです。アニメや漫画、その他、コンピュータやPCカルチャー的な内容を扱うカテゴリにしたいと思います。まぁ本ブログのテーマとはあまり関係ないようなモノをぶち込むカテゴリになります。 

 

ブログ運営(旧"お知らせ")

お知らせって知らせることなんてあるのかよ!って感じですが、ブログ自体の事について書く時に必要かと思うので、名称も改めつつ、運営に関わるようなことについてはココで書きたいと思います。

 


以上になりますが、さらに良いブログになるよう努めていきますので、これからも本ブログ、"Unityでインディーゲーム道"をよろしくお願いします。

 

"下から叩くとアイテムが出るブロック"の再現!!を近日公開!

 

 

アクセス修飾子やプロパティは、クラス連携のセーフ・ガード。Unity上での実例も含めて。

public, privateとはつまりは公開、非公開のことであります。

今回は"クラス"同士が連携する際のセーフ・ガードの役割を果たす、アクセス修飾子、プロパティについてまとめてみたいと思います。Unity上の使い方についても書いてみました。

 


プログラム全体を1つの大きな部屋だとすると、クラスは小部屋であり、その小部屋に自由に出入り出来るかどうか?ということになります。

これだけだと少し分かりづらいですが、より理解するためにはクラス及び、オブジェクト指向の本質、そしてカプセル化情報隠蔽を理解する必要があります。

では、なぜこのような修飾子が必要になるのか?について書いていきます。


 

いくつものクラスが組み合わさって

そもそもオブジェクト指向におけるクラスとは、何でしょうか。

設計図のようなもの、と言われますが、要はプログラムを分割するためのものです。

オブジェクト指向の本質とは、いかに仕事、機能ごとに分割して、どのようにそのクラス同士を連携させるか?ということになると思います。

ここでの問題は、まずどう分割するかの作業があり、かつそれを上手くやるには物事の本質を見極める能力が必要であるということ。ここについてはアクセス修飾子とは直接的な関係がないので、後日に"クラス"についてのエントリを書く時に触れたいと思います。そして次に"クラス"によって、全体のコードはバラバラにされているわけなので、実際にはそれらのクラスが連携し合うことで、プログラムが動くということです。

 

このいかにクラス同士を連携させるか?という問題が、本記事にとって重要な部分です。ここでの注意点は、別に連携させる必要がないなら無理に連携させる必要はないということです。つまり、あるクラスの中で処理が完結するのならば、それに越したことはない、ということになります。

クラス間の結合度を低く抑える、という風に言われます。

 

各クラスが独立していれば最高 

さて連携すると言っても、どの程度深く関わるか?という話にもなります。

その関わり方が浅い方がより良い、ということです。

結合度という言葉が出ましたが、これが高くなるとクラス同士が絡み合い、プログラムが複雑になってしまいます。分割して、各部をシンプルにするためのオブジェクト指向なのに、これでは本末転倒です。

ここら辺はクラスをいかに分割するか?というクラス設計も関わります。クラスをどう作るか?という問題が実は一番難しい、というような話もあるようです。

 

というわけでpublic, privateやプロパティというのは、クラス間の連携具合、アクセスを制御し、安全性を高めるためにものと言うことができます。

 

身近な例で考える 

さて公開、非公開とはどういうことでしょうか?

もし、あなたの家に友達が来たら、玄関を通り家に中に通します。

もしも宅急便の配達員が来たら、玄関で荷物を受け取り、家の中には入れさせません。

つまりクラスにおけるprivateとは家の中で、publicとは玄関あるいは外の世界ということになります。(玄関は"外との境界"という解釈です。) 

 

家の中はプライベートな部分なので、誰にでも見せるものではありませんし、親しい人にのみ許されます。要するにウチソトです。

 

プログラミングは何らかの事務処理なのですから、今度は市役所に例えてみます。

 


あなたがそこの市民であるなら、市役所に行って住民票を発行してもらうことが出来ます。しかし、それはあくまで写し(コピー)であり、原本をもらうことは出来ません。

そして、市役所の中には入ることはできませんし、内部に保管されている他人のデータを見ることもできません。あくまで入ることのできるのは窓口だけです。

 

プログラミングで言えば、

"市民class"のオブジェクトである太郎くんが、"市役所class"の住民票発行メソッドにアクセスして住民票の写しを取得できるが、"市役所class"内にある自分や他人のデータに直接アクセスすることはできないし、ましては変更することは禁じられている、という風に言うことが出来ます。

つまりクラス間のアクセスを制限するために、アクセス修飾子は必要なのです。

 

privateにすることで外部の人間に触らせないようにする、ということです。

外部の人間が利用したい機能などにはpublicをつけて利用できるようにするわけです。

 

プロパティは設定画面であり、窓口

プロパティも同様です。

変数をそのままpublicにしてしまうと、グローバル変数になってしまい危険です。つまり、どんなクラスからだろうとアクセスできてしまい、誤代入や干渉が発生してしまう恐れがあります。

 

クラスの中に収めつつ、外部から安全に変数にアクセスできるようにするにはプロパティが必要になります。市役所の例を先ほど出しましたが、まさに"クラス"における窓口のような存在ともいえる機能です。

 

変数そのものはprivateにします。しかし、それではクラスの外からアクセスできません。そんなprivate変数へのアクセスの窓口として機能するのが、プロパティです。

つまりプロパティを通してでしか、外部から変数の値を取得したり、変更できないようにすることで"クラス"のカプセル化を維持するわけです。またprivate setにすることで、外部からは変更出来ないようにすることもできます。例えば電話番号とかを勝手に変更されたら大変ですもんね。

private int _money; //privateにしてクラス内からしかアクセスできないようにする。

public int Money {

    get{ return _money;} //外部のアクセスに対し、代わりに変数にアクセスし渡す。

    private set{ _money = value; }

}

  

Unity内での実践

ということで、Unityにおいてprivate, publicをどのようにつかっていくかの簡単な実例を出してみたいと思います。

ここでは二つのクラスを考えます。

プレイヤーキャラの操作などを管理する、PlayerControllerと、

ゲーム全体、スコア、残り時間を管理する、GameControllerです。

 

さて残り時間がゼロになると、ゲームオーバーになるとします。

if( timeLeft <= 0 ) 

この時、プレイヤーが敵に当たって死ぬ時の演出を時間切れの際に、発動させたいわけです。マリオみたいな感じですね。

Death()メソッドとしますが、これはPlayerController内にあります。

しかし、残り時間の管理はGameControllerがやっているので、時間切れを判断した時点で、GameController側がPlayerControllerDeath()メソッドを呼び出す必要があります。

 

とにかくまずDeath()メソッドをpublicにしないといけません。

C#だと未指定は全てprivateになります。

public void Death(){ //死亡時の処理 }

それからGameControllerがアクセスするようにする必要があります。

もしstaticを使えば、直接GameController.Death()とかけますし、

  

あるいは、リファレンスからアクセスすることもできます。

PlayerController playerController;

void Start() {

    GameObject player = GameObject.FindWithTag("Player");

    if(player != null) playerController = player.GetComponent<PlayerController>();

}  //この際、プレイヤーにキャラに"Player"タグを設定しておく

Startメソッドで、シーンがロードされた時にFindWithTagを使って、プレイヤーのゲームオブジェクトを引っ張ってきます。そこからアタッチされているスクリプトに対して、GetComponentを使って、参照を得たPlayerControllerのオブジェクトplayerControllerからアクセスするという方法です。

playerController.Death(); という風に呼び出せるわけです。

このように面倒ではあるんですが、安全性のためには致し方ないというか。

 


またスコア表示についても考えてみたいと思います。

プレイヤーがコインを取った数を画面上に表示するとします。

コインの当たり判定はPlayer側でやって、カウントもそこでやっています。

 

画面に表示する処理はGameController側です。

なので、変数はint coinAmountとするとして、GameController側がこれを取得する必要があります。

もしpublic static int coinAmountとすれば、

PlayerController.coinAmountという風に直でアクセスできます。

あるいは、プロパティ自動実装を利用して、

public int CoinAmount{ get; private set;}

という風にして、前述のオブジェクトからアクセスする、という風にも出来ます。

coinText.Text = playerController.CoinAmount ;

という感じですね。

 

しかし、GameController側にスコアを表示するというメソッドを用意して、Player側がそれを利用する、というやり方も考えられますね。

どちらがいいかをどう判断するかが、オブジェクト指向の難しいところだと思います。 

 

まとめ!

クラス自体がよく分かっていない頃は、アクセス修飾子というものの存在意義がよくわかりませんでした。クラスというものの働きが分からないと、アクセスする、という概念自体がまず理解できないからです。

あとプロパティも意味不明で辛かったですね。要はいろんなソフトにもプロパティがあって、そこでいろいろ設定できる、あのプロパティと基本的に同じなんですけど、なんでわざわざ必要なんだろう、という感じだったので。 

 

でも基本的な考え方については、それなりに分かるようになってきたし、今回このような形で、学んできたことを自分の言葉でまとめられたのでよかったです。

で結論を言えば、やはりどう分けて、どう連携させるか?ということになってくるわけですが、まだまだ難しいですね。鍛錬を続けたいと思います。