Unity勇者の冒険の書

Unityで学んだことや便利なものなど作ったら紹介します

【Unity勇者の奮闘記】Mathf.Clampを使いこなす

こんにちは。

今回はMathf.Clampの使い方等について書いていきたいと思います。

概要

まず、Mathf.Clampの機能ですが「値を指定範囲内に収めてくれるもの」と覚えていいと思います。使い方は、

範囲内に収めた値=Mathf.Clamp(範囲内に指定したい値,最小値,最大値);

という感じで使います。
型はfloat型int型で使えます。

一応リファレンスも載せておきますね。
docs.unity3d.com


言葉では分かりにくいと思うので、使用例などを入れながら説明していきたいと思います。

使用例1(値を制限)

f:id:LightGive:20171101162849p:plain
とりあえずここにHPゲージの付いたシューティングがあります。クリックしたらランダムで1~4のダメージを受けるようにしました。この時、値の制限をかけないとこのようになります。
f:id:LightGive:20171101163145g:plain


見てもらったら分かるように、値に制限をかけていないので、HPがマイナスになっています。

現状はこうなっています。

hp -= Random.Range(1.0f, 4.0f);

これをMathf.Clampを使えば簡単に制限がかけられます。使う方法と使わない方法を書いてみます。

Mathf.Clampを使わない方法

hp -= Random.Range(1.0f, 4.0f);
if(hp < 0)
{
    hp = 0;
}

Mathf.Clampを使う方法

hp = Mathf.Clamp(hp - Random.Range(1.0f, 4.0f), 0.0f, MAX_HP);

これで、マイナスにならず値を制限内に収める事が出来ました。
f:id:LightGive:20171101183004g:plain
こんな感じで使うことが出来ます。

使用例2(座標を制限)

よく使われる例としては、transfom.positionの値に制限をかける事が多いと思います。今回はシューティングで自機が画面内に収まるような処理を作りたいと思います。Colliderで制限をかける方法もありますが、今回は直接座標を移動させる方法で実装していきたいと思います。

f:id:LightGive:20171101154225p:plain
とりあえず3種類の処理を作ってみます。

Mathf.Clampを使わない方法1

//入力を取得
var hor = Input.GetAxis("Horizontal");
var ver = Input.GetAxis("Vertical");

transform.position += new Vector3(hor, ver, 0.0f) * speed * Time.deltaTime;

//画面端から出たら画面内に戻す
if (transform.position.x > limitWidth)
	transform.position = new Vector3(limitWidth, transform.position.y);
else if (transform.position.x < -limitWidth)
	transform.position = new Vector3(-limitWidth, transform.position.y);
if (transform.position.y > limitHeight)
	transform.position = new Vector3(transform.position.x, limitHeight);
else if (transform.position.y < -limitHeight)
	transform.position = new Vector3(transform.position.x, -limitHeight);

Mathf.Clampを使わない方法2

//入力を取得
var hor = Input.GetAxis("Horizontal");
var ver = Input.GetAxis("Vertical");

//現在の座標から足されるベクトル
var addvec = new Vector3(hor, ver, 0.0f) * speed * Time.deltaTime;

//次の座標
var nextPos = transform.position + addvec;
if (nextPos.x > limitWidth || nextPos.x < -limitWidth)
	addvec.x = 0.0f;
if (nextPos.y > limitHeight || nextPos.y < -limitHeight)
	addvec.y = 0.0f;
transform.position += addvec;

Mathf.Clampを使った方法

//入力を取得
var hor = Input.GetAxis("Horizontal");
var ver = Input.GetAxis("Vertical");

transform.position = new Vector3(
	Mathf.Clamp(transform.position.x + (hor * speed * Time.deltaTime), -limitWidth, limitWidth),
	Mathf.Clamp(transform.position.y + (ver * speed * Time.deltaTime), -limitHeight, limitHeight));

という事で、実装しました。下の画像の通り、画面右端より右には移動出来ないようになっています。
f:id:LightGive:20171101152426g:plain

3種類の方法の違いは、こんな感じです。

Mathf.Clampを使わない方法1
f:id:LightGive:20171101155043p:plain
この方法は最初に、入力した方向にとりあえず自機を動かした後、もし画面から出ていた時に画面内へ座標を移動させる方法です。デメリットとしては、処理が重くなってきた時、実際に一度画面外に出ているので、一瞬だけ画面外に出ている瞬間が見えてしまうかもしれないですね。


Mathf.Clampを使わない方法2
f:id:LightGive:20171101155050p:plain
この方法は、次の移動する座標を先に求めておいて、その位置が画面外だった時に移動しない、という方法です。デメリットは、単純に移動距離が大きいときにまだ画面外まで余裕があっても止まってしまう所ですね。

Mathf.Clampを使った方法
f:id:LightGive:20171101155056p:plain
これがMathf.Clampを使用した方法です。うまく1つ目と2つ目のデメリットが無く、指定した画面端ピッタリに移動してくれます。

ざっと座標を制限する時にはこんな感じで使います。

Mathf.Clamp01

あと似ているので一緒に紹介しておきます。
Mathf.Clamp01は、関数の名前の通りMathf.Clampの最小値が0で最大値が1の関数です。ゲームを作る上で結構0~1に制限したい!という事が多いので割と頻繁に使います。

使い方は、Mathf.Clamp01(制限したい値)で使う事が出来ます。

GitHubのリンク

github.com
今回のプロジェクトはまるっとここにあるので、何か分かりにくかったらここからダウンロードして中身を覗いてください。

では。('_')

【Unity勇者の奮闘記】Unityで物理の勉強6(指定した地面の位置に落ちる初速を計算する)

こんにちは。

前回は、初速を加えた時に落ちる位置を予測しましたね。

www.lightgive.net

今回は前回の逆で指定した位置に落とすように初速を計算するようにしたいと思います。

f:id:LightGive:20171031000706g:plain

とりあえず、シーンはこんな感じす。
現状は、発射する座標からクリックした位置の方向のベクトルに直接飛んでいくようにしています。これでは見てもらったら分かる通り、重力があるので指定した位置に到着する前に落ちて、少し手前の下に落ちていますね。これではだめです。

最終的にはクリックした位置に指定したn秒数後に落ちるようにしていきたいと思います。

今回使う変数はこんな感じです。

  • sp(球体を発射する座標)
  • ep(落とす座標)
  • t(到着するまでの時間)
  • g(重力加速度)


まず初めに決めておかなければいけないのが、その位置に到着するまでの時間です。これが無いと、無数にパターンがあるので、今回はtとして1秒で指定の位置にたどりつくようにしていきます。

とりあえず比較的簡単なx,zを求めて生きたいと思います。
x,yは等速直線運動なので、距離÷時間で初速が出ます。

多分小学生の時にやったと思いますがあの「きはじ」がとうとう役に立ちますね。笑
f:id:LightGive:20171031001412p:plain

ので、yを除くとこんな感じになると思います。

var x = (ep.x - sp.x) / t;
var z = (ep.z - sp.z) / t;
var vec = new Vector3(x, 0.0f, z);

f:id:LightGive:20171031002122g:plain


まだyが0なので指定の座標まで飛ばないので、初速のyを求めていきたいと思います。
いつもの鉛直投げ上げの公式を使います。

今回はv0を求めたいので、それ以外を埋めていきます。

球体が発射される位置は0ではないので公式にy座標を開始位置の分ずらすようにしておきます。

 y = v0t -{\frac{1}{2}}gt^2 + sp.y

v0t = y + {\frac{1}{2}}gt^2 - sp.y

v0 = {\frac{g}{t}}+{\frac{1}{2}}gt - {\frac{sp.y}{t}}

こんな感じに式を変形できました。
後はこれをプログラムにしていきます。

こんな感じになりました。


これでテストしてみたいと思います。

地面に落ちるまでの時間:1秒
f:id:LightGive:20171031093809g:plain
地面に落ちるまでの時間:2秒
f:id:LightGive:20171031095137g:plain

うまく計算出来ているみたいですね。
これで敵のロケットとか、大砲とか色々使えそうですね。

では。('_')

【Unity勇者の奮闘記】2Dで敵の方向を向く

こんにちは。

今回は、Unityの2Dで「敵の方向を向く」処理をC#で実装していきたいと思います。 

自機の画像はスプライトで表示しています。
真ん中のが自機で、右上のがですね。
f:id:LightGive:20150714195203p:plain 


こんな感じで敵が周りにいるとします。
これは自機が敵を向いて撃たないといけないですね…
まずは敵がマウスの位置に来る処理を作ります。

var pos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
pos.z = 0.0f;
Teki.transform.position = pos;


f:id:LightGive:20150714195719g:plain

これでマウスの位置に敵が来るようになりました。 
あとは自分のキャラが敵の方向に向いてほしいですね!


ここで、Unityの特徴なのですが、実装の方法がいくつかあります。

実装方法がいくつもあると複雑に思いますが、沢山知っておくと、片方の実装方法が何らかの原因で実装できないときにもう片方の実装方法で何とかなる、という事があります。とりあえず両方知っておくのが吉ですね。

とりあえず知ってる方法を載せます。

 

【方法1】
var vec = (Teki.transform.position - Jiki.transform.position).normalized;
var angle = (Mathf.Atan2(vec.y, vec.x) * Mathf.Rad2Deg) - 90.0f;
Jiki.transform.rotation = Quaternion.Euler(0.0f, 0.0f, angle);

 

【方法2】
var vec = (Teki.transform.position - Jiki.transform.position).normalized;
Jiki.transform.rotation = Quaternion.FromToRotation(Vector3.up, vec);

f:id:LightGive:20150714200318g:plain
 

無事、敵の方向を向くようになりました!
Unityの回転関係は色々あって、難しいので、とにかく勉強すべし!

では。('_')

 

【Unity勇者の奮闘記】Unityで物理の勉強5(初速を加えた時に落下地点を予測する)

こんにちは。

前回は初速を加えた時に地面に落ちる時間を求めました。今回はそこにちょこっと変更をして、初速を加えた時に地面に落ちる位置を予測してみようと思います。

まず、前回もやりましたが、地面に落ちるまでの時間を初速と投げる位置から計算で出します。計算での出し方は前回の記事を参考にしてください。
【Unity勇者の奮闘記】Unityで物理の勉強4(初速を加えてから地面に落ちるまでの時間を計算する) - Unity勇者の冒険の書


地面に落ちる時間が分かったら次は座標を求めるのですが、求めるのが厄介なy座標に関しては既に決まっているので、あとはx,zを求めていきます。x,zは等速直線運動なので時間×速度で求める事が出来ます。

ソースコードはこんな感じです。

こんな感じになりました。
実際に予測した位置に落ちるのか、実験してみます。
いつものように、落ちる場所にダミーのボールを移動させています。
f:id:LightGive:20171028204104g:plain

うまく予測した位置に落ちていますね。
これを使えば、大砲が打たれた瞬間に、地面に危険マークを出したり、野球ゲームで誰かが打った時に落ちる位置に走りだしたりという演出が作れそうですね。

RigidBody任せなので完全に一致というわけにはいきませんが、うまく使ってゲーム作りに役立てて下さいね。

では。('_')

【Unity勇者の奮闘記】Unityで物理の勉強4(初速を加えた時、地面に落ちるまでの時間を計算する)

こんにちは。

今回は球に初速を加えた後、地面に落ちるまでの時間を計算で出していきたいと思います。

f:id:LightGive:20171028163330p:plain


まずは鉛直投げ上げの公式を使っていきたいと思います。


鉛直投げ上げの公式

  • 速度  v = v0 - gt
  • 変位  y = v0t -{\frac{1}{2}}gt^2
  • tを含まない式  v^2 - v0^2 = -2gy

今回知りたいのは時間なので、y座標からtが求められる式、二番目の変位の式を使います。式が分かりやすくなるように、それぞれ割り当てられている変数は何を表しているのか、書いてみます。式では投げる高さが0スタートになっているので、その高さ分を足します。

  • t(地面を通過する時間)
  • g(重力加速度)
  • v0(初速)
  • sp(投げ上げ始める高さ)
  • gp(地面の高さ)

これで変位の式を作ってみます。

 gp = v0t -{\frac{1}{2}}gt^2 + sp

この式のままだと、因数分解で解けないので片方の辺を0にしていきます。

 0 = -{\frac{1}{2}}gt^2 + v0t + sp - gp
 0 = {\frac{1}{2}}gt^2 - v0t - sp + gp

これで因数分解できる形の式になりましたが、普通の解き方はプログラムで書くのが難しいので、解の公式で解いていきます。

【解の公式はこれ】
 x = {\frac{-b±{\sqrt{b^2 - 4ac}}}{2a}}

それぞれa,b,cの値はこんな感じです。

  •  a = {\frac{1}{2}}gt^2
  •  b = {-v0}
  •  c = -sp + gp


この解の公式に当てはめていきます。
-sp + gpはまとめられるので、cとします。


 t = {\frac{-(-v0) ±{\sqrt{(-v0)^2 -4・{\frac{1}{2}}g・c}} }{2・{\frac{1}{2}}g}}

 t = {\frac{v0±{\sqrt{(-v0)^2-2gc}}}{g}}

±となっている所は、秒数が負の数なのはおかしいので、正の数になっている方を取ります。
これで式を簡略化できたので、これをC#に直して解くとこんな感じになると思います。

では作ったので、実験していきたいと思います。
今回も同じように、Debug.Break()で一定時間後に止めるようにしました。
f:id:LightGive:20171028163704g:plain

良い感じに地面の位置で止まってくれました。逆に、地面の位置を上にして、地面より下から投げ上げた時も実験してみます。
f:id:LightGive:20171028165040g:plain

t1,t2の値は下の様になってました。
t1…約2.0秒後
t2…約0.5秒後
このt2の値は何か調べていたら、地面の高さを下から上に通過した時の時間でした。下の図だと分かりやすいと思います。
f:id:LightGive:20171028171927p:plain


と、いう事で、今回は地面に落ちるまでの時間を計算してみました。意外と面倒でしたが敵のAI等を作る際に役立つと思います。

では(^^)/

【Unity勇者の奮闘記】Unityで物理の勉強3(弾道予測線を作成する)

こんにちは。

前々回は最高点に達した時間を求めて、
前回は最高点に達した時の座標を求めました。

リンクは下記参照
【Unity勇者の奮闘記】Unityで物理の勉強2(最高点に達した時の座標を求める) - Unity勇者の冒険の書
【Unity勇者の奮闘記】Unityで物理の勉強1(最高点に達するまでの時間を求める) - Unity勇者の冒険の書


今回は、アングリーバードを何気なくやっていた時に放物線の弾道予測線があったので、これをそれっぽく作ってみたいと思います。
f:id:LightGive:20171027194801j:plain

まず、点を表示する数とその間隔を決めます。

今回は20個の点で、0.1秒毎に表示します。
とりあえず表示する点の個数はdummyCountと変数にして、間隔の秒数はsecIntervalという変数にしておきます。

まず、x , z の座標は等速直線運動なので、単純に時間×速度で求める事が出来ます。

y座標に関してはいつものお馴染みの鉛直投げ上げの公式を使います。


y = v0t - {\frac{1}{2}}gt^2


あとは0.1秒後ずつのy座標を求めて、点を移動します。
これで試してみると、こんな感じになると思います。


いい感じになりました。
軌道もほぼ予測通りにうごいてくれたようです。
f:id:LightGive:20171027234602g:plain

更に、アングリーバードの場合、点が少しずつ前に動いているので、この演出を足すとこんな感じの実装になりました。

f:id:LightGive:20171027234536g:plain


という事で、今回はアングリーバードの弾道予測の演出を作ってみました。好きな演出を真似してみると意外と勉強になるので是非やってみてください。

では('_')

【Unity勇者の奮闘記】Unityで物理の勉強2(最高点に達した時の座標を求める)

こんちは。
前回は上に飛ばしたときに最高点に達した時の時間を求めました。

lightgive.hatenadiary.jp


今回は最高点に達したときの座標を求めてみます。
今回も物理の公式を使います。
というか毎回使います。

まず、Y座標については、鉛直投げ上げの公式の

y = v0t - {\frac{1}{2}}gt^2

を使います。

v0…初速
t …経過時間(秒)
g …重力加速度

として、初速と重力加速度については決まっているので、
頂点に達した時間を求めます。
これは前回の方法で求る事が出来ます。

時間 = 初速 / 重力加速度 ですね。

このtを鉛直投げ上げの公式に当てはめてみると、こんな感じになります。

var t = vec.y / (-Physics.gravity.y);

これでtを求める事が出来たので、続けて公式に当てはめてみます。

var y = transform.position.y+ (vec.y * t) - 0.5f * (-Physics.gravity.y) * Mathf.Pow(t, 2.0f);

最初にtransform.position.yを足しているのは、鉛直投げ上げの公式の初速を加えるyの位置が0スタートになっているからです。

これで、yの値は求められることが出来たので、あとは残りのx,zの座標を求めていきます。

xやzは、yと違って重力は関係無く等速直線運動なので、単純に時間×速度で求められます。ですので、

var x = transform.position.x + (vec.x * t);
var z = transform.position.z + (vec.z * t);

これで最高地点に達した時の座標が求められます。
では、前回の時の様に実験してみます。

前回、コルーチンでDebug.Break()で停止していましたが少し時間がずれていたようなので、今回はUpdateでカウントしてから停止するように変更して、誤差がないように試してみました。(ほんの若干の誤差だけど)

あと、ダミーの透明な球体を予測位置に配置し、誤差を求めるようにしました。
スフィアにアタッチしているスクリプトです。


結果、こんな感じになりました。

テスト
●一回目
f:id:LightGive:20171027163615g:plain
●二回目
f:id:LightGive:20171027163620g:plain
●三回目
f:id:LightGive:20171027163624g:plain


10回ほどテストしましたが、
一番大きい誤差で0.16554
一番小さい誤差で0.02354でした。

これは、一番大きい誤差の状態です。
f:id:LightGive:20171027163628g:plain
大きい誤差と言ってもこの程度なので、当たり判定がシビアでなければ結構ゲームで使えると思います。

では。('_')