チキンレース〜キャットエスケープを分解しています。
現在目の疲労改善サプリを3種類ほど私で人体実験中です。
一つ効果があると実感できるものがあるので、他のものと成分の違いを確認し、何が効果があるのか、価格への反映はどの程度なのかを確認中です。
効果があると実感できるものは、1日分あたりの分量換算で他製品の1.5倍程度価格が高くなっています。
今後コロナをきっかけに、さらに人類の液晶画面と向き合う時間は増加するので、面白いマーケットだと見ています。
//tips
//簡単なゲームオーバーの表示方法
UIテキストにゲームオーバーを表示させたい時は、ゲーム中の表示条件またはゲームオーバーの条件が分かっていると作りやすい。
チキンレースの場合は、ゴールを超えてしまったらゲームオーバーと表示させたいため、逆にいうと
float length = this.flag.transform.position.x - this.car.transform.position.x;
length>=0であればゲーム続行で、マイナスになった際にゲームオーバーと表示させればいいことがわかる。
なので、if~elseで表され、
void Update()
{
float length = this.flag.transform.position.x - this.car.transform.position.x;
if(length >= 0)
{
this.distance.GetComponent<Text>().text = “ゴールまで” +length.ToString(“F2”)+”m”;
}
else
{
this.distance.GetComponent<Text>().text =“ゲームオーバー”;
}
}
//オブジェクトのコンポーネントを取得する際に例外表記が許されるtransform
transformはcar.transform.position.xのようにcarオブジェクトにアタッチされているTransformコンポーネントが持つ座標にアクセスできる。
しかし、一般的にオブジェクトにアタッチされているコンポーネントを取得したい時には
car.GetComponent<AudioSource>();
のようにGetComponentを使わなければならない。
transformはGetComponent<>()の使用を短縮してtransformで表すことができる。
//スクリプト内の作成済みメソッドの呼び出し方
オブジェクトにアタッチしたスクリプト内のメソッドを呼び出したいときには
car.GetComponent<CarController>().Run()
のように記述する。
carオブジェクトにアタッチされたCarControllerスクリプトをコンポーネントとして呼び出し、そのスクリプト内に書かれたメソッドであるRun()を呼び出している。
スクリプトもコンポーネントの一つであることを理解するとわかりやすい。
//ワールド座標系レイヤーの設定
初期時点で背景画像とプレイヤーオブジェクトの前後関係がうまくセットできないことがあるが、それはレイヤーの操作によって改善される。
ワールド座標系ではレイヤーが大きい番号であればあるほど最前面に表示され、最後面は0となっている。
プレイヤーオブジェクトのインスペクターSprite Render項目のOrder in Layerの数値を変更することで順番を変えることができる。
Sprite RenderのSpriteは画像のことで画像をどのように2D,3Dに表すのかを調整する項目となっている。
//GetMouseメソッドと同様のGetKeyメソッド
GetMouseメソッドと同様にキー操作もInput.GetKeyの形で表す。
GetKeyDown:押した瞬間
GetKey:押している間
GetKeyUp:離した瞬間
これらのメソッドの引数にとれるのが、
KeyCode.UpArrow:上矢印キー
KeyCode.Space:スペースキー
KeyCode.A:Aボタン
となっており、
GetKeyDown(KeyCode.LeftArrow)のように表される。
これに動かす力も加えて設定したい場合、ifで方向の条件を下記、trueを返した場合、動きを加える形に設定する。
void Update()
{
if(Input.GetKeyDown(KeyCode.LeftArrow))
{
transform.Translate(-3,0,0);
}
if(Input.GetKeyDown(KeyCode.RightArrow) )
{
transform.Translate(3,0,0);
}
}
//Physicsを使わずに落下動作を組む
Physicsの重力落下をアタッチしなくても、スクリプトで落下をさせることができる。
スクリプトの方が柔軟な設定ができるという利点がある。
例えばスクリプトにすることで下記のようなものを書くことができる。
void Update()
{
transform.Translate(0,-0.1f,0);
if(transform.position.y < -5.0f)
{
Destroy(gameObject);
}
}
マイフレーム下に0.1移動し、y座標が-5以下になった時にオブジェクトを破棄する。
//オブジェクト同士の衝突判定と衝突応答の設計
オブジェクトの輪郭線に接したかどうかを検出するのは計算量が膨大になってしまうので、円や円柱などの形で簡易的に表されることが多い。
円を使用した当たり判定の場合、円の半径を使って当たり判定をすることができる。
双方のオブジェクトの中心座標の距離が、双方のオブジェクトの半径を足したものより小さくなれば、それは各々の円が一部かぶっていることを示すので、当たり判定とする。
双方の中心座標の距離を三平方の定理magnitudeを使うことで求めることができる。
vector2 p1 = transform.position; //矢の中心座標
vector2 p2 = this.player.transform.position; //プレイヤーの中心座標
vector2 dir = p1 - p2;
float d = dir.magnitude;
float r1 = 0.5f; //矢の半径
float r2 = 1.0f; //プレイヤーの半径
if(d < r1 + r2)
{
Destroy(gameObject);
}
このifの中身で双方の半径の和より中心座標間の距離が小さければ、当たりとみなし、オブジェクトを消滅させている。
//ジェネレータースクリプトでPrefabをもとに1秒間隔でインスタンス生成
上から降る矢を1秒間隔で生成するスクリプトを考える。
public GameObject arrowPrefab;
float span = 1.0f;
float delta = 0;
1秒間隔で生成したいのでspanを1とし、間隔を数えるためにdeltaを作成する。
void Update()
{
this.delta += Time.deltaTime;
if(this.delta > this.span)
{
this.delta = 0;
GameObject go = Instantiate(arrowPrefab) as GameObject;
int px = Random.Range(-6,7);
go.transform.position = new Vector3(px, 7, 0);
}
}
Time.deltaTimeは直前のフレームと今のフレーム間で経過した時間[秒]を返す。
そのためthis.deltaの中にフレームが変わるごとに時間がどんどん蓄積され、spanで設定されている時間を超えるとifの結果の処理に移るようになっている。
処理内容としては
・蓄積されたthis.deltaの値を0に初期化する
・InstantiateメソッドでarrowPrefabのインスタンスを生成
・生成先を指定するためpx変数を作り、x座標のRandom.Rangeで-6以上7未満に範囲を指定
・生成したオブジェクトの位置を(x,y,z)で指定するためにnew Vector3で指定
ここでなぜnewではなくInstantiateを使用しなければいけないかというと、MonoBehaviorクラスを使用しているからで、
public class Rule : MonoBehaviour
{
public Rule(int i)
{
}
}
public Rule rulePrefab;
Rule rule2;
void Start()
{
rule2 = Instantiate(rulePrefab) as Rule;
}
ここまでがMonoBehaviorクラスを使用している場合の対応例。
If you don't inherit from MonoBehaviour, you should use the new keyword to create new instance of it. Now, you can use the parameter in the constructor if you want.
つまり、MonoBehaviour以外の場合は、下記。
public class Rule
{
public Rule(int i)
{
}
}
Rule rule2 = null;
void Start()
{
rule2 = new Rule(3);
}
で表すことができる。
英語だが下記の説明が参考になる。
https://stackoverflow.com/questions/37398538/unity-null-while-making-new-class-instance/37399263#37399263
https://stackoverflow.com/questions/44273613/unity3d-c-whats-the-difference-between-instantiating-a-prefab-as-a-gameobje
これでGameObject hogeGameObject = new GameObject("Hoge”);などのようにnewでオブジェクトを生成しない理由がわかった。
さらにas GameObjectとInstantiate()の後に続く理由は、Instantiate で返されるオブジェクトは Object型のためそのままでは操作できないからとされていたが、アップデートによって生成元と同じものが返されるようになり、特に必要なくなっているという話もある。
旧来のものの説明は下記が参考となる。
https://qiita.com/Gok/items/f732964d175bf99ffba9
//UIオブジェクトのImageにあるFill機能を利用して画像の表示領域を変化させる
Fill Methodは画像の表示領域を変化させることでゲージの増減を示すことができる。
Horizontal:横方向に画像を切り取る
Vertical: 縦方向に画像を切り取る
Radial 90:90度扇型に画像を切り取る
Radial 180:半円形に画像を切り取る
Radial 360:円形に画像を切り取る
UIのimageからimagetypeをFilledに選択し、FilledMethodをRadical 360などとする。
ダメージ判定をこのhpGaugeに反映させる必要があるので
UIを監督するスクリプトを作成し、DecreaseHP()メソッドを追加していく。
public class GameDirector:MonoBehavior
{
GameObject hpGauge;
void Start()
{
this.hpGauge = GameObject.Find(“hpGauge”);
}
public void DecreaseHp()
{
this.hpGauge.GetComponent<Image>().fillAmount -= 0.1f;
}
}
監督スクリプトの中でFindを使用してhpGaugeのアクセスを確保し、ダメージを受けたらfillAmountを減少させる新しいメソッドを作成している。
ただこれは減少が発生するトリガーについては記載されていないので、このメソッドが実行されるタイミングがない。
そのため、矢のコントローラスクリプトの当たり判定の際に一緒にこのDecreaseHpメソッドを呼び出す処理を行う必要がある。
これは
if(d < r1 +r2)
{
GameObject director = GameObject.Find(“GameDirector”);
director.GetComponent<GameDirector>().DecreaseHp();
Destroy(gameObject);
}
と追加処理を行うことで当たり判定時にうまく減少処理を行える。