【技術書メモ】C#プログラミングのイディオム / 定石&パターン①
趣味で、Unityでゲームを作っているが、C#についてちゃんと勉強したことがない。
そのため、ここらでC#について基礎から学ぼうと思い、以下の技術書を読んだ。
uuc1h.hatenablog.jp
入門としては、最適な良書だと思う。
そこで、次に入門書の一歩先の技術書を読もうと思い、この本を選択した。
実戦で役立つ C#プログラミングのイディオム/定石&パターン
- 作者: 出井秀行
- 出版社/メーカー: 技術評論社
- 発売日: 2017/02/18
- メディア: 大型本
- この商品を含むブログ (1件) を見る
文法を説明するだけでなく、どういったときにこういう書き方をしなければならないのかという、一歩踏み込んだところまで書いてくれている。
ていうことで、読んだだけでは頭に残らないので、読んだ内容をブログにまとめていきます。
プロパティ
プロパティとは、Javaでいうセッター、ゲッターメソッドの代わりだと認識していた。
ただ、C#3.0で導入された自動実装プロパティのことは知らなかった。こっちの方が凄い簡単。
// 自動実装プロパティを使った書き方 public int Code { get; set; } // 自動実装プロパティを使わない書き方 private int code; public int Code { get { return this.code; } set { this.code = value; } }
これまで6行で書いていたところを、たった1行で済んでしまう。。
※ここでちょっとした参考になったこと。
なぜ値型と参照型が必要なのか。
参照型と値型の違いは分かっているのだが、使い分けが分からなかった。
よく入門書では、大きなオブジェクトをコピーする際、値型だったらコピー処理が大変だが、参照型だったらアドレスをコピーするだけですむ。と書いてあるが、これは分かる。
けど、この本には小さなオブジェクトの場合、参照型だとオブジェクトを格納する領域と、アドレスを格納する領域と2つ必要になってしまう。値型なら、変数の領域そのものにオブジェクトを格納できる利点があると書いてあり、腹落ちした。
静的クラスについて
静的クラスの定義方法などは分かっているので、ここでは静的クラスを使う理由について。
インスタンスは、1つのクラスから複数を生成できるので、たとえば以下のようなクラスがあったとする。
class Product { public int Price { get; set; } public double getTaxPrice() { return this.Price * 1.08; } }
1つのクラスから複数のインスタンスを生成できるので、車オブジェクトやバイクオブジェクトなどを生成でき、それぞれ個別の値を変数Priceに設定できる。
そのため、Productクラスを静的クラスにしてしまうと、上記利点が消えてしまう。
そのため、静的クラスは特定の値に左右されない場合に適用する。代表的なのが、DateTime.Todayなど。
継承について
// 基底クラス class Person { // 名前 public string Name { get; set; } // 誕生日 public DateTime Birthday { get; set; } // 年齢を返却 public int GetAge() { DateTime today = DateTime.Today; int age = today.Year - Birthday.Year; if(today < Birthday.AddYears(age)) { age--; } return age; } }
// 派生クラス class Employee : Person { // 社員ID public int Id { get; set; } // 部署名 public string DivisionName { get; set; } }
class Program { public static void Main(string[] args) { Employee em = new Employee { Id = 100, Name = "山田", Birthday = new DateTime(1980, 1, 1), DivisionName = "プログラミング1課", }; Console.WriteLine("{0}({1})は、{2}に所属しています", em.Name, em.GetAge(), em.DivisionName); } }
継承については、文法的にはJavaとほぼ一緒の印象。なお、基底クラスの変数に派生クラスのインスタンスを代入可能だが、その場合基底クラスのプロパティやメソッドしか使用できない。(これもJavaと一緒)
ちなみにis a 関係というものを知らなかったのでメモ。
継承は「〇〇は△△である」という関係が成り立つときに使用する。上の例でいえば、「社員は人である」という関係が成り立つので、継承を使用して可。
逆に、「人は社員である」という関係は成り立たないので、継承を使ってはいけないとのこと。
【技術書メモ】確かな力が身につくC#「超」入門
Unityをメインにアプリ開発をしているが、Unityで使用する言語はC#だ。
自分は仕事でJavaを使っているからC#も何となく理解できるが、ここらで一度C#を学んでおきたいと思う。
そこで手に取ったのが、この「確かな力が身につく」シリーズだ。
確かな力が身につくC#「超」入門 (Informatics&IDEA)
- 作者: 北村愛実
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2017/07/26
- メディア: 単行本
- この商品を含むブログを見る
この本から学んだことを、ブログにまとめていく。
プロジェクトの作成
Visual Studioを立ち上げて、新規作成 / プロジェクト を選択する。
そこでプロジェクトの種類が色々出てくるが、ただC#の文法を確認するだけなら「コンソールアプリ」を選べば十分だ。
なお、プログラムの実行はデバッグ / デバッグなしで開始 を選ぶ。
if文、for文、switch文、while文
これらも基本はJavaと一緒。
ただし、foreach文だけはメモしておこう。Javaの拡張for文とちょっと違うので。
float[] weights = { 41.2f, 42.5f, 44.9f, 43.0f, 29.2f }; foreach (float f in weights) { Console.WriteLine(f); }
コレクション
まずはList型から。
List型
List<float> weights = new List<float>();
Dictionary
次はDictionary型。Map型に近い印象。
Dictionary<string, float> weights = new Dictionary<string, float>(); weights.Add("2019/12/10", 41.2f); Console.WriteLine(weights["2019/12/10"]);
LINQとラムダ式
ラムダ式とは
戻り値を返す短いメソッドを、より簡潔に書く方法のこと。
(引数) => 戻り値の計算式
int Add(int n) { return n + 5; } // これをラムダ式で書くと n => n + 5
とかなり簡潔に書ける。
LINQとは
統合言語クエリ。配列やコレクションの中から条件を満たす値だけ取り出せる。まさにクエリです。
Whereメソッド
int[] hp = { 420, 120, 600, 0, 1200 }; // 600, 1200が取得できる var newHP = hp.Where(n => n >= 500);
※var型とは
代入された値によってコンパイラが自動的に型を判断。LINQのメソッドの戻り値は基本var型。
Selectメソッド
int[] hp = { 420, 120, 600, 0, 1200 }; // すべての要素の値を100減らす var newHp = hp.Select(n => n - 100);
Countメソッド
int[] hp = { 420, 120, 600, 0, 1200 }; // HPが0の要素を取り出し、その数をカウントする var newHp = hp.Where(n => n == 0).Count();
慣れたら、かなり使いやすそう。
プロパティ
通常、他クラスのメンバー変数にアクセスする場合、そのクラスのセッター、ゲッターメソッドを使用しなければならない。
だが、プロパティを使うと、直接メンバー変数にアクセスするような感覚でできる。
private int hp; // プロパティ public int Hp { set { this.hp = value; } get { return this.hp; } } Player player = new Player("たかし", 500); // Hpプロパティに代入 player.Hp -= 2000; // セッター player.Hp; // ゲッター
※ちょっと番外編。staticキーワードについて
いつも忘れてしまうので、ちょっとメモ。
staticをつけると、インスタンスではなくクラス自体に属するようになり、インスタンス間で共有される値になる。「クラス名.メンバ変数」で値を代入、取得できる。また、メソッドでも「Player.GetTeamName()」のように、「クラス名.メソッド名」でも使用できる。
アプリ開発初心者が作ったアプリは、本当に売れるのか?③_アプリを作る場合、自分ができる範囲を自覚することが重要
まさに表題の通りです。GWが終わり仕事が始まると、そもそも家で開発できる時間がなかなか確保できない。
けど、ここでやめてしまうとせっかく勉強を始めたのに勿体ないので、何とか少しずつでも開発を行っていました。
ちなみに完成したアプリは、こんな感じ。
いわゆるジャンケンゲームです。
この画面で自分のジャンケンの手を選ぶと、次画面に遷移します。
そこでジャンケンの結果が表示され、戻るボタンを押すともう一度ジャンケンができるというゲームです。
このアプリって、「はじめてのANDROIDプログラミング第4版」のサンプルアプリとほぼ一緒なんやけど、自分で工夫したのが、連勝回数をトップ画面に表示させるようにしたこと。
アプリを作る場合、「自分ができる範囲内」だけでやることが重要だと思っている。
これで、画面遷移と共有プリファレンスの使い方はなんとなくわかった気がする。
- 作者: 金田浩明
- 出版社/メーカー: SBクリエイティブ
- 発売日: 2019/04/20
- メディア: 単行本
- この商品を含むブログを見る
アプリ開発初心者が作ったアプリは、本当に売れるのか?②_さっそくアプリを1つ作り、Google Play Storeへ公開した
前回の記事がこちら。
さて、Androidを買ったし、PCはWindowsだし、Javaならちょっと分かるしということで、Android Studioでアプリ開発をやっていこうと、前回のブログで決意表明しました。
そこで、さっそくAndroid開発の本を購入。それがこちら。
サンプルとなるアプリがたくさん載っているのが、購入に至った一番の動機だ。
最初から読み進めていくと、ここである事実が一つ。
Android Studio 3.0のリリースとともに新言語Kotlinのサポートが開始されました。Googleは今後KotlinをAndroidの標準プログラミング言語にすることを明言していますので、将来的には完全に置き換わるかもしれません。
引用元:はじめてのAndroidプログラミング 第4版
えーJavaじゃないの!!ってのが第一の感想。ただ、Javaとは100%互換性があるみたいで、Java開発経験は無駄になるわけではないとのこと。しかも、Kotlinの方がなにかと簡潔にすむらしい。
よし、では自分もKotlinで開発していこう。というか、この本がKotlinで開発しているから、Kotlinで開発するしかないのだが。。
この本は、8つのサンプルアプリが収録されている。ということで、1つのサンプルアプリをまず本の通りに作ってみて、そこで得た知識をもとに自分のアプリを作り、リリースまでやってしまおうと思う。今日はその第一弾。
得た知識
・プロジェクトの作成時、Use AndroidX artifactsにチェックを入れる。(AndroidXライブラリを使用するため)
・画像を使用する場合は、res/drawableに保存する。
・Androidは画面をレイアウトする方法として、「ContraintLayout」がある。ビューに対して、制約を設定する。
・リソースの設定方法
Resources画面を開き、Add new resources / New string valueより行う。
または、Translations Editorから行う
・アクティビティの追加
appを右クリックし、New / Activity / Empty Activityより追加する。ktファイルとxmlファイルが作成される。
・Intentを使った画面遷移方法
・インポートのショートカット(Alt + Enter)
・インテントにデータを格納し、遷移先でデータを取得する方法
・finishメソッドを使った、前のアクティビティに戻る動作
・共有プリファレンスの使い方
成果物
Randomメソッドを使って、ただ単に「大吉」、「中吉」、「凶」のいづれかを画面上に表示するだけの単純なアプリです。
ただ、今回の一番の目的はAndroid Studioで作ったものを、リリースすること。一度Google Playにリリースしたことはあるが、そのときはUnityで作ったものだ。
Android Studioから、apkファイルの作成は以下記事の通りにやったら無事にできた。
https://akira-watson.com/android/apk.html
つづいて、Google Play Consoleからのリリースだが、以下記事を参考にした。
https://qiita.com/android_develop_hunt/items/c57319dc3012cb310c6c
そうして、公開したアプリがこちら。
https://play.google.com/store/apps/details?id=jp.uuch1.fortune_telling
うん、このサイクルを続けていけば、いい感じになりそうな気がする。
アプリ開発初心者が作ったアプリは、本当に売れるのか?①
もう何度も本ブログで書いているが、自分は30歳からプログラミングを始めた。始めたきっかけは、IT会社に転職したからだ。
せっかくプログラミングを覚えたから、プライベートでも何か作ってみたいと挑戦中なのが、Unityを使ったゲーム製作だ。素人なりにいろいろ考えて、Applo Storeにも何本かリリースした。
ただ、ゲーム作りはプログラミングだけでなく、プログラミング以外にも色々な要素が必要になってくる。例えば絵だったり、音楽だったり、アニメーションだったりと。いや、たしかにゲーム作りは楽しいんだけど、自分はもっとサクッと作って、リリースまでしたい。
あと、iOSアプリを作っていてしんどいのが、Appleの審査。なかなか通らず、リジェクトされまくりの日々がこれまたストレスになってくる。
そこで今度はAndroidについて調べると、なんとAndroidはかなり審査がゆるいという。また、リリースも結構簡単にできるとのこと。
これはと思い、生粋のiPhoneユーザーだった自分は生まれて初めてAndroid買いました。テスト機としてね。それが、これ。
なんと、1万円ちょっとでスマホが買える。Androidやっぱりすげーっと、妙な興奮を覚えながら、さっそくリリースしたアプリがこちら。
Android用アプリを公開しました。ブロック崩しとシューティングゲームをミックスしたカジュアルゲームです。広告なし、無料なので、みなさまぜひとも遊んでください。#unity #アプリ開発 #gamedev https://t.co/JBu3M2gwdm
— UUC1H (@HHII221188) 2019年5月5日
ちなみにこのアプリは、Unityで作りました。たしかに、developer登録からリリースまで、かなり簡単にできた。うぉーついにリリースした!って感動してたのだが、ここで問題がひとつ。
まったくダウンロードされない。
このアプリ、ブロック崩しとシューティングゲームを混ぜたようなゲームで、自分の中ではかなり頑張って作った。しかし、マジで誰もダウンロードされない。ダウンロード数は、自分がテスト機にダウンロードした1回だけだ。
もちろん自分はアプリ開発で食べていこうなんて思ってないのだが、やはり何の反応もないと、モチベーションが保てない。アプリ開発はレッドオーシャンと呼ばれているが、本当に初心者が個人開発したアプリなんて、まったく見向きもされないのだろうか。
と、後ろ向きになりつつあったが、冷静に考えると自分が作ったアプリのクオリティーはかなり低い。これじゃあ使ってもらえないし、またダウンロードされる工夫もしていない。まずは絶対的に、アプリを作った数が少ないと思う。
ということで、これからしばらくはAndroidアプリ開発に集中しようと思う。また、ダウンロードされることを目標にしているので、Unityを使ったゲーム開発ではなく、Android Studioでのツール系アプリ開発に挑戦していく。
100万ダウンロードくらいされないかなー。
Kotlinまとめ
変数と定数
// 変数 var a : Int = 10000 // 定数 val c : Double = 12345.6
文字列
// 文字列比較は「==」で行う if (str == "Kotlin") // 文字列テンプレート val i = 10 print("i = $i") // i = 10と出力される // 文字列テンプレート2 val s = "Kotlin" printl("$s length is ${s.length}) // Kotlin length is 6 と出力される。{}をつけないと、Kotlin length is Kotlin.lengthと表示される
型変換
// 文字列からInt型へ val str = "64" val intVal: Int = str.toInt() // 文字列からInt型へ val str = "Kotlin" val intVal: Int? = str.toIntOrNull() // 変換できない場合は変数にNULLを設定 // 数値から文字列へ val num = 128 val intStr: String = num.toString() // 数値同士の変換 val i: Int = 100 val l: Long = i.toLong() // 明示的に変換が必要
null許容型とnull非許容型
// 以下の場合はエラーになる var text: String = null // 正しくは以下のような記述が必要 var text: String? = null // null許容型の変数の場合、String型のメソッドやプロパティをそのままでは使用できないなどの制限が発生する
配列
// Arrayクラスを使い、arrayOf関数で配列の生成と初期化を実行 val arrayofInt: Array<Int> = arrayOf(1,2,3,5,8) for (i in arrayofInt) print("$i,")
if式
val c = 10 val d = 20 if (c > d) { print("c") } else { print("d") }
when式
val a = 4 when(a) { 1 -> println("a == 1") 2,3 -> println("a == 2 or 3") in 10..20 -> println("a is in the range") !in 20..30 -> println("a is outside the range") else -> println("otherwise") }
for文
for(i in 1..4) // 配列を使う場合 val a = arrayOf(1,2,3,10,100) for(i in a.indices) { print("a[$i]=${a[i]}") }
while文
var x = 0 while(x < 10) // do-while文 var i = 7 do { }while(i > 4)
リスト
// 読み込み専用リスト val items: List<Int> = listOf(1, 2, 3) println(items.get(0)) println(items[1]) println(items.size) // 変更可能なリスト val numbers:MutableList<Int> = mutableListOf(1,2,3) numbers.add(4) // リストの末尾に4を追加 numbers.remove(4) // 一致する要素を削除
セット
// 特徴:要素の順番をもたず、要素の重複も不可 // 読み取り専用セット var strings:Set<String> = setOf("A", "B", "C") println(strings) // 変更可能セット var strings: MutableSet<String> = mutableSetOf("x", "y", "z") strings.add("A") strings.remove("x")
マップ
// 読み取り専用のマップ val fruits:Map<String, Int> = mapOf("apple" to 1, "orange" to 2, "banana" to 3) // 変更可能なマップ val fruits: MutableMap<String, Int> = mutableMapOf("apple" to 1, "orange" to 2, "banana" to 3) println(fruits.get("apple")) println(fruits["orange"]) fruits.put("melon",4) // 要素の追加 fruits.remove("banana") // 要素の削除
関数
// fun 関数名(引数:型,・・・):戻り値 fun times(a: Int, b: Int): Int { return a * b } // 関数の呼び出し // Kotlinではクラスに属さない関数を作成できる times(2, 5) // 戻り値がなく、2番目の引数をデフォルト設定する場合 fun printTimes(a: Int, b:Int = 2):Unit{} printTimes(10)
ラムダ式と関数型
ラムダ式:{引数 -> 処理}
// ラムダ式は変数に格納可能。この時の変数を関数型と呼ぶ var minus = {x: Int, y: Int -> x - y} minus(3, 1) // 結果は2 // 変数plusは「(Int, Int) -> Int」型 var plus:(Int, Int) -> Int = {x:Int, y:Int -> x + y} plus(5, 2) // 暗黙の引数it // 引数一つの場合は、引数を省略し引数itを使用できる // itを使用しない場合はvar double:(Int)->Int = {x:Int -> x * 2}とかく var double: (Int)->Int = {it * 2} double(5) // 関数型を引数に持つ関数 fun doLambda(x: Int, y:(Int) -> Int) = y(x) doLambda(5, {it * 2})
クラスとインターフェイス
クラスの定義:class クラス名{}
※publicがデフォルト
// インスタンス生成 class dog{} val dog = Dog()
インターフェイスの定義:interface インターフェイス名{定義}
Kotlinのインターフェイスは、抽象メソッドだけでなく具体的な処理も記述できる
プロパティ
Javaのフィールドとセッター/ゲッターを合わせたような機能
var dog = Dog() dog.name = "Pochi" // プロパティに値を設定 println(dog.name) // プロパティから値を取得
コンストラクタ
プライマリコンストラクタ:クラス宣言でクラス名の後ろにコンストラクタの引数のみを記載する。処理ブロックをもたないため、処理を行う場合はinitブロックに記載。
セカンダリコンストラクタ:処理ブロックを持つため、その他の処理も記載可能。
class Dog(val name: String) {}
イニシャライザ
class Dog(val name: String) { init{ print("The dog's name is $name") } val dog = Dog("Pochi") // ここでイニシャライザが呼ばれる
クラスの継承
class クラス名(プライマリコンストラクタ):親クラス名(コンストラクタ引数)
open class Person(val name: String) {} // 親クラス class Student(name: String, var degree: String): Person(name) {} // 子クラス val student = Student("Sato", "Bechelor") // インスタンスの取得
インターフェイスの実装
class クラス名(プライマリコンストラクタ):インターフェイス名
※Kotlinのインターフェイスでは抽象メソッドだけでなく、具体的な処理を含むメソッドも定義可能。
interface Pet { fun eat() fun showName() = println("I'm Pet") } class Cat : Pet { override fun eat() = println("I'm eating") }
型チェックとスマートキャスト
fun getLen(obj: Any) : Int? { // isでStringかどうかチェック if ( obj is String) { // チェック後はStringとしてふるまう return obj.length } return null }
継承関係のあるオブジェクトのキャスト
// 親クラス open class Person(val name: String) { } // 子クラス class Student(name: String, var degree: String): Person(name) {} // 親クラスの変数に子クラスのインスタンスを生成 var person: Person = Student("Sato", "Bechelor") // 子クラスへキャスト var student: Student = person as Student // as?の使い方 var person: Person = Student("Sato", "Bechelor") // 例外が発生せずに、nullがセットされる var student = person as? Int
objectキーワード
objectキーワードを使うと、クラスの宣言とそのインスタンス生成を一気に行う。
■シングルトンの実現
object MyProfile { 処理内容 } // objectを使用するにはオブジェクト名を使う MyProfile.メソッド名
■コンパニオンオブジェクト
class Person(val name: String) { companion object { fun printMe() = println("Hello") } } // スタティックメソッドのように扱える Person.printMe()
■オブジェクト式
無名インナークラスを簡単に記述するための機能
SAM変換
Javaのインターフェイスで、メソッドを1つしか持たないものをSAM(Simgle Abstract Method)インターフェイスと呼ぶ。
KotlinではSAMインターフェイスを引数としたメソッドを、ラムダ式で置き換えることをSAM変換と呼ぶ。
NULL安全
var s: String? = null // The length of null is nullと出力される print("The length of $s is ${s?.length}")
// 変数sがNULLでなければsを返し、sがNULLならdefaultという文字列を返す val t : String = s ?: "default"
スコープ関数
■with関数
with(対象オブジェクト){処理}
with(Dog("Pochi")) { age = 10 // プロパティに値を設定 message() // メソッドの利用 }
■apply関数
対象オブジェクト.apply{処理}
Dog("Pochi").apply{ age = 10 }.message()
■let関数
対象オブジェクト.leg{処理}
var output = name?.let{ var upperCase = it.toUpperCase() var len = it.length "$upperCase $len" } print(output)
■run関数
対象オブジェクト.run{処理}
var output = name?.run{ "${toUpperCase()} $length" } print(output)
Pair
val p = Pair("apple", 1) println("the number of ${p.first} is ${p.second} // the number of apple is 1と出力される
拡張関数
fun 型名.関数名(引数):戻り値{処理}
隻狼 SEKIRO_どんなことでも、楽しむためには成長が必要
朝食:サーモンアボカド丼、味噌汁
昼食:つけ麺
夕食:ホタルイカと春キャベツのサラダ、焼き鳥
待ち焦がれていた「SEKIRO」、発売日に買いました。
それから、4~5時間ほどやってるけど、ホントに死んで死んで死にまくる。自分はこれまでDarkSoulsシリーズや、Bloodborneをやったことがなくて、FROMSOFTWAREの死にゲー具合が全く分からなかった。けど、これほど死にまくるとは。まさに、心折れる。。
けど、不思議なことに、こんな敵絶対倒せないと思っていながら、何回もやってるといつの間にか倒せるようになってくる。これこそ、ゲームをやって「成長」が実感できる瞬間だ。
自分はゲームに求めるものは、「成長」だ。分かりやすくいうと、RPGの「レベル」だ。何十時間とやることで、レベルがあがり、強くなるし、使える技や魔法が増える。できることが増えると、さらにゲームが楽しくなり、プレイするという具合だ。
コマンド式のRPGは、アクションゲームなどのように反射神経を駆使したテクニックなどは必要なく、誰もが平等に時間をかければ必ず強くなれるゲームだ。だから、RPGでストレスなどは感じないと思う。ここでいうストレスとは、コントローラーをぶん投げてしまうようなことだ。
だが、このSEKIROはアクションゲームである。そのため、誰もが平等に成長できるわけではない。センスのいい人、悪い人ではっきりと成長速度に差が生じてしまう。
また、コマンド式のRPGなどのように、ただ時間をかければいいというものではない。アクションをマスターするために、操作の練習が必要だ。このSEKIROでも、「弾き」「回避」「見切り」などの、敵を倒すうえで必須なアクションが様々ある。これらは、相手の攻撃に合わせ、タイミングよく操作しないと発動しないアクションなのだが、これがとても難しい。
敵によって、攻撃パターンが違えば、こちらのコマンドを押すタイミングも違ってくる。敵の攻撃パターンを把握し、それらを見て、反射的に適切なアクションのコマンド操作が必要になってくる。なお、これらのアクションが何度でも練習できるシステムがゲームに組み込まれているのだが、これはプレイヤーの努力なしでは先に進めないことを物語っているともいえる。
FROMSOFTWAREが以前インタビューで答えていたのだが、「プレイヤーにはゲームで達成感を感じてもらいたい」。この考えは、SEKIROにも十分反映されていると思う。
SEKIROはたしかに高難易度だが、絶対にクリアできないなんてことはない。プレイヤーが努力し、プレイヤー自身が「成長」すれば、必ず先に進める。そうすると、新たな技を覚えたり、武器を入手できたりして、さらに強くなる。そして、またゲームに深くのめり込んでいく。
これ、ゲームだけにとどまらず、現実世界でも同じことだと思う。何事も楽しむためには、「成長」が必要。
さて、しばらくはSEKIROで死んで死んで死にまくる生活を送ります。