ゴミ箱
ゴミ
以下、過去の資料のストックで情報価値は低いです。中に間違っている情報もあります。読む価値はありません
微分積分と物理との関係
微分という計算をする事によって何が得られるか?
物理において微分を利用して値を求める事はイコール、グラフの接線=傾き=運動の速度を知る事になる
速度を手にいれれば、そこに時間を掛ければ距離が得られる。ある距離から時間が欲しければ距離を速度で割ればよい
また、漸化式として利用すれば科学的に必要な値、累乗根の実数値なども知ることが出来る
そのようにして計算で物理法則の様々な事柄が分析、予測できるようになる
<微分>
積分という計算をする事によって何が得られるか?
物理において積分を利用して値を求める事はイコール、面積や体積、(一次元である)距離や長さ等を知る事になる
これらを知る事により物の価値(例えば特定面積の土地の金額)やある体積を持った物の正確な重さ、科学的に必要な定数(円周率等)等を算出できるようになる
世間一般で扱われる測量と呼ばれる行為ができるようになる
<積分>
公式を使ったサンプル
コード内で
vf=vi+a*Δt
Δx=1/2*(vi+vf)*Δt
このふたつの公式を利用している。viが前フレーム、vfが現フレーム。Δxがその現フレームの移動量となる
<サンプルコード>
using UnityEngine;
using System.Collections;
public class VelocityTest1 : MonoBehaviour
{
float vi, vf, a; float deltaX; float roadLength; Transform _transform;
void Start () { _transform = transform; a = 450; //この場合の加速度はエンジンの駆動力に相当する //現在は45unit/s^2のアクセルベタ踏みの固定値だが //これはエンジン性能的なグラフで変位するものと想像する } void Update () { float deltaT = Time.deltaTime; if (Input.GetButton ("Jump")) { vf = vi + a * deltaT; } else { //ここはとりあえずアクセルを踏まなければ停まる感じを出す為に運動摩擦力を擬似的に追加している if (vf > 0) { vf -= (a/2) * deltaT; } if (vf < 0) { vf = 0; } }
deltaX = 0.5f * (vi + vf) * deltaT; if (deltaX < 0) { deltaX = 0; } roadLength += deltaX; vi = vf; _transform.rotation = Quaternion.Euler (0, 0, roadLength); }
}
- 放物線は相似形である
- 数学的に「正しい」放物線はこの世にたった一つ
放物線の方程式2
<資料>
放物線
y = a(x-h)^2 + k
x = a(y-k)^2 + h
頂点が(h,k)で対称軸x=h
yの場合、放物線はaが正数なら上に開き負数の時は下に開く
aが0に近づくほど開き具合は大きくなり遠ざかるほど小さくなる
aを0にすると直線になる
<パーティクルを利用して放物線のグラフを表示するコード>
インスペクタ内のパラメータを弄ると表示に反映されます
using UnityEngine;
using System.Collections;
public class Graph2 : MonoBehaviour {
[Range(10,1000)] public int resolution = 10; [Range(-3,3)] public float a =0.2f; [Range(0,5)] public float h = 2; [Range(0,5)] public float k = 3;
int currentResolution; float currentA; float currentH; float currentK;
ParticleSystem.Particle[] point; delegate float FunctionDelegate (float x);
void Awake () { ParticleSystem ps = gameObject.AddComponent<ParticleSystem>(); ps.Stop(); }
void Start () { CreatePoint(); } void Update () { if (currentResolution != resolution | | point == null | | currentA != a | | currentH != h | | currentK != k ) {CreatePoint ();}
FunctionDelegate f = Integral_Parabola;
for (int i = 0; i < resolution; i++) { Vector3 p = point [i].position; p.y = f (p.x); point [i].position = p; Color c = point [i].color; c.g = c.r; point [i].color = c; } particleSystem.SetParticles (point, point.Length); }
void CreatePoint () { currentResolution = resolution; currentA = a; currentH = h; currentK = k;
point = new ParticleSystem.Particle[resolution]; float increment = (h*2) / (resolution - 1); for (int i = 0; i < resolution; i++) { float x = i * increment; point [i].position = new Vector3 (x, 0, 0); point [i].color = new Color (x, 0, 0); point [i].size = 0.1f; } }
float Integral_Parabola(float t){ return ( a * Mathf.Pow( t - h , 2) ) + k; }
}
<旧コード>
using UnityEngine;
using System.Collections;
public class curv1 : MonoBehaviour
{
Transform _transform; float x = 0; float a = 3; float h = 2; float k = 0; float y = 0; void Start () { _transform = transform; } void OnGUI () { x = GUI.HorizontalSlider (new Rect (20, 20, 100, 30), x, -10f, 10f); a = GUI.HorizontalSlider (new Rect (20, 60, 100, 30), a, 0.1f, 5f); h = GUI.HorizontalSlider (new Rect (20, 100, 100, 30), h, -5f, 5f); k = GUI.HorizontalSlider (new Rect (20, 140, 100, 30), k, -5f, 5f);
y = (-1f / a) * Mathf.Pow (x - h, 2) + k;
GUI.Label (new Rect (150, 20, 200, 30), "x= " + x); GUI.Label (new Rect (150, 60, 200, 30), "a( 0.1f~5.0f)= " + a); GUI.Label (new Rect (150, 100, 200, 30), "h(-5.0f~5.0f)= " + h); GUI.Label (new Rect (150, 140, 200, 30), "k(-5.0f~5.0f)= " + k);
GUI.Label (new Rect (20, 200, 200, 30), "y= " + y); } void Update () { _transform.position = new Vector3 (x, 0.5f, y); }
}
Y = AX^2 + BX + C
放物線の公式
Y = AX^2 + BX + C
を利用してみる
放物線はaが正数なら上に開き負数の時は下に開く
aが0に近づくほど開き具合は大きくなり遠ざかるほど小さくなる
bは頂点からのフォーカスする位置のズレのような感じ
using UnityEngine;
using System.Collections;
public class curv2 : MonoBehaviour
{
float a, b, c, x, y; Transform _transform;
void Start () { _transform = transform; a = 0.1f; }
void OnGUI () { x = GUI.HorizontalSlider (new Rect (20, 20, 100, 30), x, -10f, 10f); a = GUI.HorizontalSlider (new Rect (20, 60, 100, 30), a, 0.1f, 1f); b = GUI.HorizontalSlider (new Rect (20, 100, 100, 30), b, -5f, 5f); c = GUI.HorizontalSlider (new Rect (20, 140, 100, 30), c, -5f, 5f);
GUI.Label (new Rect (130, 15, 200, 30), "x= " + x); GUI.Label (new Rect (130, 55, 200, 30), "a= " + a); GUI.Label (new Rect (130, 95, 200, 30), "b= " + b); GUI.Label (new Rect (130, 135, 200, 30), "c= " + c);
y = -a * Mathf.Pow (x, 2) + b * x + c; } void Update () { _transform.position = new Vector3 (x, 0, y); }
}
using UnityEngine;
using System.Collections;
using System.Numerics;
public class SqrtSolve1 : MonoBehaviour
{
public int root; //平方数 public int digit; //計算させる桁数 BigInteger sqrt; //答えとなる平方根 BigInteger restArea, deltaArea ; //残されたエリア、平方完成を利用して計算した膨張していく差分エリア //現時点、小数点表現を抜いた27桁まで表示できる void Start () { sqrt = 0; restArea = root; for (int i = 0; i < digit; i++) { sqrt *= 10; //平方根側の桁移動 for (int d = 1; d <= 10; d++) { BigInteger hit = restArea; //飛出し判定用の捨て変数 deltaArea = d * (sqrt * 2 + d); //ここで平方完成を利用している、L字の鎌型のエリア hit = hit - deltaArea; if (hit < 0) { //残されたエリアから差分エリアが飛び出したら //一歩戻して、残されたエリアから差分エリアを引算する deltaArea = (d - 1) * (sqrt * 2 + (d - 1)); restArea = restArea - deltaArea; sqrt += d - 1; break; } } //桁を移動させながら計算はより細かく密になっていく restArea *= 100; } print (sqrt); }
}
「良く似た値のふたつの数字の引算」で発生する誤差に対しての対処方法
A.計算を実数でなく有理数の状態で行う
B.値が根号の中に、ある場合などは「分母、分子の有理化」をうまく利用する
どちらの方法も有理数化をうまく利用して計算する事が重要になる。たとえばA.のケースを考えてみる
このような式があったとして答えを求めたい時、unityで、どのように数式をコード化すればよいか
using UnityEngine;
using System.Collections;
public class CalcTest2 : MonoBehaviour
{
float a, b; void Start () { a = 39f / 321f; //これがある"傾き"を表していた場合・・・ b = 81f / 667f; print (string.Format ("通常の計算方法 :{0}", a - b)); print (string.Format ("有理数として計算:{0}", ((39f * 667f - 81f * 321f) / (321f * 667f)))); }
}
出力
通常の計算方法 :5.605072E-05
有理数として計算:5.604674E-05
正常な値は、 0.121495327102804 - 0.12143928035982 = 0.00005604674298... なので 有理数として計算の方法が精度が高い事がわかる
この計算がしているのは式にすると単純に下記の計算をしているだけになっている
普段はあまり気にすることは無いが、この a や b がある傾きを表し、その計算精度を向上させたい時は、有理数として計算すると良いという事になる
using UnityEngine;
using System.Collections;
public class Line2LineCheck : MonoBehaviour
{
const float epsilon = 0.00001f;
public Vector3 vStart, vEnd; public Vector3 wStart, wEnd; public bool line2LineCheck;
void Update () { line2LineCheck = CheckLine2Line (vStart, vEnd, wStart, wEnd); }
float t1, t2; Vector3 hit1, hit2;
public bool CheckLine2Line (Vector3 vStart, Vector3 vEnd, Vector3 wStart, Vector3 wEnd) { Vector3 v = vEnd - vStart; Vector3 w = wEnd - wStart; Vector3 common = wEnd - vEnd; Vector3 common2 = vEnd - wEnd;
Vector3 cross_vw = Vector3.Cross (v, w); Vector3 cross_common = Vector3.Cross (common, v); Vector3 cross_common2 = Vector3.Cross (common2, w); bool crossNormalCheck = 0 == (Vector3.Cross (cross_vw, cross_common)).sqrMagnitude; bool crossDotCheck = 0 > Vector3.Dot (cross_vw, cross_common);
t1 = cross_common.magnitude / cross_vw.magnitude; t2 = cross_common2.magnitude / cross_vw.magnitude;
bool retCode = false; if (crossNormalCheck && crossDotCheck && t1 + epsilon > 0 && t1 - epsilon < 1 && t2 + epsilon > 0 && t2 - epsilon < 1) { retCode = true; }
//目視確認用 hit1 = w * -t1; hit2 = v * -t2; Vector3 drawShift = new Vector3 (0.02f, 0.02f, 0.02f); Debug.DrawLine (vEnd, vEnd + common, Color.grey); Debug.DrawLine (Vector3.zero, cross_vw, Color.white); Debug.DrawLine (Vector3.zero, cross_common, Color.grey); Debug.DrawLine (vStart + drawShift, vEnd + drawShift, Color.green); Debug.DrawLine (wStart + drawShift, wEnd + drawShift, Color.blue); Debug.DrawLine (wEnd, wEnd + hit1, Color.red); Debug.DrawLine (vEnd, vEnd + hit2, Color.cyan); return retCode; }
}
「面法線」はノーマライズ(正規化)された外積です
<サンプル>
using UnityEngine;
using System.Collections;
public class Cross1 : MonoBehaviour
{
Vector3 vecA, vecB, cross, unityCross, surfaceCross, unitySurfaceCross;
void Start () { vecA = new Vector3 (5, -2, 0); vecB = new Vector3 (1, 2, 3);
//外積を自前で計算 cross = new Vector3 ( vecA.y * vecB.z - vecA.z * vecB.y, vecA.z * vecB.x - vecA.x * vecB.z, vecA.x * vecB.y - vecA.y * vecB.x);
//unityの関数を利用 unityCross = Vector3.Cross (vecA, vecB);
//面法線の計算 float delta = Mathf.Sqrt (cross.x * cross.x + cross.y * cross.y + cross.z * cross.z); surfaceCross = new Vector3 (cross.x / delta, cross.y / delta, cross.z / delta);
//unityの関数を利用して面法線を計算 unitySurfaceCross = unityCross.normalized; }
}
ベクトルを考える
相対と絶対
- Translate()とpositionの違い
Translate(x) は position+=x と同じ。Translate()は相対座標。Positionは絶対座標。
- Rotate()とrotationの違い
Rotate(x) は rotation+=x と同じ。Rotate()は相対的回転。rotationは回転値。
引用と考察|ベクトル
ベクトルについて議論するとき、普通の数字(例えば浮動少数点の値)をスカラーとみなすことが通常です。この意味は、スカラーはひとつの "スケール" または大きさしかありませんが、ベクトルには大きさと向きがあります。
スカラー = スケール?
スカラー (scolar≠scale) | 方向性を持たない大きさ。時間、質量、長さ、エネルギー、電荷、温度などはスカラー量 |
ベクトル(Vector) | 大きさと向き(方向性)を持った量 |
float speed = 3.0f; Vector3 direction = new Vector3 (3, 0, 2);
void Update () { transform.Translate (direction.normalized * speed * Time.deltaTime); }
この場合、direction.normalized が正規化ベクトル。speed と Time.deltaTime がスカラー。正規化ベクトルは大きさ(長さ:magnitude)を「1」として「方向」を取り出したものと考えると良い
ベクトルにスカラーを乗算すると、元のベクトルと同じ方向を向くベクトルが得られます。しかし、新しいベクトルの大きさは元の大きさにスカラーの値を乗算した値となります。 同様に、スカラーの除算は元のベクトルの大きさをスカラーで割ります。 これらの演算はベクトルが動作のオフセットまたは力を表現するときに便利です。これによりベクトルの向きを変えることなくの大きさを変更できます。あるベクトルが自身の大きさにより除算されると結果は大きさが1のベクトルとなり、これは正規化ベクトルと呼ばれています。もし正規化ベクトルにスカラーを乗算すると、結果の大きさはスカラーの値と同じになります。これは力の向きが一定でありながら強さが制御できるときに便利です(例えば、車の車輪はつねに前に進みますが、強さはドライバーにより制御されます)。
上記のコードで例えればspeedがそれに該当する
内積
Vector3 vec1, vec2; float answer; void Start () { vec1 = new Vector3 (0, 0, 5); vec2 = new Vector3 (5, 0, 5); answer = Vector3.Dot (vec1.normalized, vec2.normalized); print (answer); print(vec2.magnitude); print(Mathf.Cos(45*Mathf.Deg2Rad)); }
0.7071068
7.071068
0.7071068
角度をもとにして、対応するコサインを計算機により見つけることは容易です。しかし、下図のようにいくつかの主なコサインの値を直感的に理解することが役に立ちます:
この事から内積は方向を調べる道具として使える事がわかる(垂直なら1、斜め45度なら0.7071。直交しているなら0。正反対方向なら-1になる)
(つまり「角度という値」を使わなくても、ベクトルのみで角度を扱う事が出来る事を意味する)
内積はとてもシンプルな演算であり、Mathf.Cos関数のまたはいくつかのケースではベクトルの magnitude 演算の代わりに使用できます。(まったく同じではありませんが、場合によっては得られる効果は同じです)。しかし、内積の関数を利用したほうが要するCPU時間が短く、このために価値の高い演算となります。
(これは距離を調べる際の事を言っているらしい。(誤差はあるが)sqrMagnitudeよりもさらに高速に処理できる?)
ベクトルを考える2
外積
この他の演算は 2D および 3D 、あるいはより高次元ベクトルのでも定義ができます。一方で対照的に、外積は、3Dベクトルでしか意味をなしません。二つのベクトルをもとに、別のベクトルを結果として出力します。
(Vector3×2 から Vector3 が返ってくる)
結果ベクトルは二つの入力ベクトルに対して垂直になります。"左手の法則" を覚えることで、入力ベクトルの順番による出力ベクトルの向きを判断することが出来ます。もし最初のパラメータが手の親指、二つめのパラメータが人差し指、そして結果は中指の方向を指します。もしパラメータの順番が逆になると結果のベクトルは正反対の向きで、ただし同じ大きさとなります。
Vector3 vec1, vec2, origin; Vector3 sideA, sideB; Vector3 answer; void Start () { origin = Vector3.zero; vec1 = new Vector3 (1, 0, 0); vec2 = new Vector3 (0, 1, 0); sideA = vec1 - origin; sideB = vec2 - origin; answer = Vector3.Cross (sideA, sideB);
}
void Update () { Debug.DrawLine (origin, vec1, Color.red); Debug.DrawLine (origin, vec2, Color.green); Debug.DrawLine (origin, answer, Color.blue); }
青線が外積によって導き出した線
「赤線」と「緑線」の2辺で構成する三角形の法線が「青線」という考えでよい
外積を使うと法線が求められる
結果の大きさは入力ベクトルの大きさを乗算して、さらにそれらがなす角のサインを乗算したものとなります。サイン関数で役立つ値を以下で示します。外積は、いくつかの便利な情報を返り値に合成するため複雑に感じられるかもしれません。しかし、内積と同様に数学的にとても効率的であり、これを使用しなければ遅い超越関数に依存するコードを最適化することが出来ます。
ベクトルを考える3
<サンプル1>
public GameObject player, enemy; Vector3 heading, normal, normal2,normal3; float distance, sqrDistance, dot; float range = 5.0f; void Start () { heading = enemy.transform.position - player.transform.position; heading.y = 0; distance = heading.magnitude; normal = heading / distance;
sqrDistance = heading.sqrMagnitude; dot = Vector3.Dot (heading, heading); normal2 = heading / Mathf.Sqrt (dot); normal3 = heading.normalized; if (sqrDistance <= range * range) { print ("range範囲内にplayer発見!"); } }
もし空間上の1点を別の点から引くと結果はひとつのオブジェクトからもうひとつを "指す" ベクトルとなります。
// プレイヤーの位置からターゲットの位置を指すベクトルを取得します。
heading = enemy.transform.position - player.transform.position;
ターゲットオブジェクトの方向を指すとともに、このベクトルの大きさは二つの位置の間の距離に等しくなります。一般的にはターゲットを向いた正規化されたベクトルが必要となります(発射物を向ける場合も同様)。オブジェクトの間の距離は向かっているベクトルと等しく、このベクトルを正規化するためには大きさで割ります
distance = heading.magnitude;
normal = heading / distance;
Vector3.magnitudeはベクトルの長さを求める平方根演算が内部で行われていて重い処理
このアプローチは大きさ(magnitude)と正規化されたプロパティの両方を別に使用する場合と比べ、両方ともにCPUをかなり消費するため(両方とも平方根計算を含むため)より望ましいです。 もし距離だけの比較が必要な場合(例えば近接度合いのチェック)であれば、大きさの計算そのものを回避できます。sqrMagnitude プロパティにより magnitude の値の累乗が得られ、 magnitudeのように計算されますが時間を消費する平方根計算をさけられます。大きさを既知の距離と比較するのではなく、大きさの2乗と距離の2乗を比較できます
sqrDistance = heading.sqrMagnitude;
dot = Vector3.Dot (heading, heading);
if (sqrDistance <= range * range) {
print ("range範囲内にplayer発見!");}
内積のdotはheading.sqrMagnitudeと同じ値になっていることに注目してください。これは処理的に同じことをしています。この例でみられるように内積(Dot関数)には距離を調べる使い方もあります。unityではVector3.sqrMagnitudeプロパティがあるので知識程度に知っておいた方が良いでしょう
上記のコードでnormal2とnormal3が同一の値になる事にも注目してください。Vector3.normalizedもまた平方根演算が内部で行われていて重い処理です
ここで言う「重い処理」はアクションゲームの関数内で多重的にループさせたりするとダメ的なニュアンス
(サンプルコードなどを見ると結構頻繁に使われている気がしないでもない。そういうコードは非力なマシンの場合重くなるのだと思います。たぶん)
内積計算のサンプル
public class Dot1 : MonoBehaviour
{
Vector3 vec1 = new Vector3 (5, 0, 3); Vector3 targetToVector; Transform _transform; float dot, dotUnity; public Transform target;
void Start () { _transform = transform;
targetToVector = target.position - _transform.position;
//vec1・dist(自前で内積の計算をするには以下のようにする) dot = vec1.x * targetToVector.x + vec1.y * targetToVector.y + vec1.z * targetToVector.z; //こちらはunityの関数 dotUnity = Vector3.Dot (vec1, targetToVector); }
void OnDrawGizmos () { //_transformは実行時にポインタがつくられるのでエディット時の為に、こちらにしておくと正常に処理される Gizmos.DrawLine (transform.position, vec1); }
}
Dotにより得られる値は24
ベクトルを利用した運動
位置の変位を求めるサンプルコード
二次元の変位では最終位置から最初の位置を引くことで求められる Δr =(rf - ri)
Vector2 rf,ri,deltaR; void Start () { rf = new Vector2(2,4); ri = new Vector2(6,2);
deltaR = rf-ri; print(deltaR); }
2,3次元の平均速度
平均速度の公式。
vv = Δr/t = rf-ri/t
Δrは変位ベクトル。tは時間
Vector2 rf,ri,averageVelocity; float time;
void Start () { rf = new Vector2(2,4); ri = new Vector2(6,2); time = 12;
averageVelocity = rf-ri/time; print(averageVelocity); }
運動を記述する式
一次元運動でも同じ公式を見たと思うが、これは2次元、3次元のベクトルに対しても同じように使える
vf=vi+a*t
Δx=1/2(vf+vi)t
Δx=vi*t+1/2*a*t^2
Δx=1/2(vf+vi)t
Δx=vi*t+1/2*a*t^2
a、vf、vi、Δxはベクトル量、tはスカラー
運動を記述する式の応用
Q.
ゲーム中で車が53°の向きに10ユニット/sの速さで走っています。30°の向きに5ユニット/sの割合で加速した場合、3秒後の速度を求めなさい。
初速度vi=10unit/sec@53°
加速度a=5unit/sec@30°
時間t=3sec
求める値:最終速度vf
これには vf=vi+a*t の公式を使うと良い
まず、極座標形式のviとaをデカルト座標に変換する
その後、公式の計算を行う
これをコーディングすると以下のようになる
using UnityEngine;
using System.Collections;
public class PhysicsTest1 : MonoBehaviour {
void Start () { Vector2 vi=PolarCoordinates2Descartes(53,10); Vector2 a =PolarCoordinates2Descartes(30,5); float t = 3.0f;
Vector2 vf = vi + a * t; print(vf); }
//極座標をデカルト座標へと変換する関数 public Vector2 PolarCoordinates2Descartes(float angle,float scalar){ return new Vector2(Mathf.Cos(angle*Mathf.Deg2Rad),Mathf.Sin(angle*Mathf.Deg2Rad))*scalar; }
}
出力
(19.0, 15.5)
運動を記述する式の応用(その2)
Q.
ゲーム内でオブジェクトが停止状態からベクトル(4,1,2)で表される加速度を得て動き出します。このオブジェクトは5秒後どれだけ移動していますか?
初速度vi=(0,0,0)
加速度a=(4,1,2)
時間t=5
求めるものΔx
これには公式 Δx=vi*t+1/2*a*t^2 を利用するよい
void Start () { Vector3 vi = Vector3.zero; Vector3 a = new Vector3(4,1,2); float t = 5;
Vector3 deltaX= vi * t + 0.5f * a * t * t; print(deltaX); }
出力
(50.0, 12.5, 25.0)
単純な重力表現
一旦、公式は忘れて単純に重力を表現してみるコード
落下速度の正確性の検証の為にRigedbodyを適用したゲームオブジェクトと、このスクリプトを適用したオブジェクトを並べておき
一定距離(結構高い位置において)自由落下させる。下にPlaneを置いて同時に着地すれば正しいと判断できる
using UnityEngine;
using System.Collections;
public class PhysicTest5 : MonoBehaviour {
float a = -9.8f; float speed; float time; float deltaY;
Transform _trasform;
void Start () { _trasform = transform; } void Update () { time += Time.deltaTime; speed = a*time; deltaY = speed * Time.deltaTime;
_trasform.Translate(new Vector3(0,deltaY,0)); }
}
もう一回、今度は公式を思い出して当てはめてみる
using UnityEngine;
using System.Collections;
public class PhysicTest5 : MonoBehaviour {
float a = -9.8f; float vi,vf,t; Transform _trasform;
void Start () { _trasform = transform; } void Update () { t = Time.deltaTime;
vf= vi + a * t; //最終速度=初速度+加速度×時間 float deltaX = 0.5f*(vi+vf)*t; //変位=1/2(初速度+最終速度)×時間 vi=vf;
_trasform.Translate(new Vector3(0,deltaX,0)); }
}
両者は計算方法が全く違うが同じ結果になる。また検証用のオブジェクトと同時に着地して重力計算が正しい事が確認できる
後者の公式を使ったコードは時間のカウンタ変数を用意しなくても動いている点が優れてる
又、加速度aが変位しても大丈夫。つまり車のアクセル可??(要確認)
投影
利用方法がちょっと分りにくい概念ではあるが、これは例えばベクトル反射の計算の途中で利用される
二つのベクトル vi、N' があったとしてN'が正規化された地面のベクトルだと考えた場合、正午の真昼の光がviに当って投影された影Pのベクトルは
P=(vi・N')×N' で求める事が出来る(「・」つまり計算には内積が利用されている)
using UnityEngine;
using System.Collections;
public class ProjectionTest : MonoBehaviour {
Vector3 vi,Ndush,P;
void Start () { Ndush = new Vector3(1,2).normalized; vi = new Vector3(5,3); P = Vector3.Dot(Ndush,vi) * Ndush;
} void Update () { Vector3 NdushVisivle = new Vector3(0.03f,0.03f,0.03f); Debug.DrawLine(Vector3.zero+NdushVisivle,Ndush+NdushVisivle,Color.green); Debug.DrawLine(Vector3.zero,vi); Debug.DrawLine(Vector3.zero,P,Color.red); }
}
using UnityEngine;
using System.Collections;
public class ProjectionTest2 : MonoBehaviour {
Vector3 vi,Ndush,P;
void Start () { Ndush = new Vector3(1,2); vi = new Vector3(5,3); P = Vector3.Project(vi,Ndush); //第二引数は渡す際ノーマライズしなくても良い } void Update () { Vector3 NdushVisivle = new Vector3(0.03f,0.03f,0.03f); Debug.DrawLine(Vector3.zero+NdushVisivle,Ndush+NdushVisivle,Color.green); Debug.DrawLine(Vector3.zero,vi); Debug.DrawLine(Vector3.zero,P,Color.red); }
}
公式
<Vector2の二頂点から得られる傾き>
<Vector2の傾きと垂直な線を求める公式>
<Vector3の二頂点から得られる単位ベクトル>
ベクトルの正規化
正規化とはベクトルの大きさを1にする操作のことを言う
(斜辺が1の直角三角形を作る感じ)
<基本的な考え方>
斜辺の長さを求めてデカルト座標の各スカラーをそれで割る
normalized関数を利用せずにベクトル(5,0,-12)を正規化してみるプログラム
vec2が自前で計算した結果
Vector3 vec1, vec2, unityNormalized; float normDeltaTest;
void Start () { vec1 = new Vector3 (5, 0, -12); vec2 = vec1;
float delta = Mathf.Sqrt (vec1.x * vec1.x + vec1.z * vec1.z); vec2.x = vec1.x / delta; vec2.z = vec1.z / delta;
normDeltaTest = Mathf.Sqrt (vec2.x * vec2.x + vec2.z * vec2.z); unityNormalized = vec1.normalized; }
二次方程式
未知数の「次数」が2である方程式を二次方程式と呼ぶ。何次方程式という式は最高次の次数に注目して名づけられる
例:未知数xの次数が2乗である方程式
この二次方程式の根は√2となる。根は解とも呼ぶ。平方根とは平方解の事。 + - は復号と呼ばれる
普段よく見かける二次方程式を例に考える
この方程式を因数分解すると
となる。この式は変形させると
となる。上記の事から二次方程式はその中に二つの一次方程式を隠し持っている事が分る
一番初めに√2の平方根を求めた際、根に + - の復号を付けたのは、導かれた二つの根を指す為である
要点:二次方程式は必ず二つの根を持つ。(x-1)^2の様に一つの根に見える時もあるが、それは重なっているだけで、基本二つの根になる
二次関数と放物線
二次関数は変形によってさまざまな式の形に出来る。そのグラフは必ず放物線(二次曲線)となる
式の変形は狙った機能と形を持った放物線を描く為に行うと考えると良い
ゲーム制作において重要な事は、この二次関数の放物線から得られる情報と微分した式から得られる結果が合致する点にある
一般的な二次関数として良くみられる式の形
|
<放物線>
弧状の突起の先端である頂点(頂上部)と放物線を二分する対称軸を持つ。対称軸が鉛直な上下型、対称軸が水平な左右型の4種がある
平方根の定義
<定義>
正の数 a の平方根のうち、正の方を
√a
と表す。「ルートa」と読む。また記号√を根号と呼ぶ。
√0 = 0 になる
定義でaが「正の数」になっている事に注意!同時に√aは「2乗するとaになる正の数」になっていることに留意
(ここに例えばルート内にマイナスがある√-2があった場合、√2×-1となり、ルート内部に虚数 i があることになる。この場合、式の意味が変わってくる)
だから定義では正の数としている(何気に重要。ちゃんと意識しておく事)
a の平方根は √a と -√a になる
平方根の特徴
- 平方数に対して、ふたつの平方根がある
- 一方は正、一方は負になる
- このふたつの平方根の絶対値(abs)は等しい
例:
a が 4 の場合
√4 = 2 -√4 = -2
2の平方根は±√2 (記号「±」 を「複号」と呼びます)
7の平方根は±√7
9の平方根は±√9 = ±3 (ふたつの平方根がある事を忘れないように!±が必要)
<簡単な定理>
指数計算
マイナス(ー)の符号が付いた指数計算時は注意すること
マイナスの指数がどのような計算の結果、有理数になるのか以下で確認できる
指数2は「5-3=2」であるから指数法則を用いて
となる。ここで両辺をaの5乗で割り算します
このように「aのマイナス3乗」は「aの3乗分の1」という意味になる
0乗が1になる様子。通常の指数は分子に、マイナスの指数は分母になると以下のようになる
方程式と恒等式
方程式と恒等式(こうとうしき。恒(つね)に等しい式)は違う
- 方程式 ある数をxに入れると、この式は成り立つと主張している。従って、この式を成り立たせる「ある数」を求めよという問題が出てくる
- 恒等式 どんな数をxに入れても、この式は成り立つと主張している。従ってこの式が「どんな数でも成り立つのは本当か?」という証明の問題となる
式を見ればすぐ方程式か恒等式か分る時もあれば判らない時もある。その等式を書いた人がどちらのつもりでその式を書いたのか読み取る必要がある
この式は恒等式でもあり方程式でもある事を主張している
全体としてx^2-2x-8という方程式を解く代わりに恒等式で変換した(x-4)(x+2)=0という方程式を解くという事を読み取ることができる
2点間(2D or 3D)の距離
3Dの場合、項が一つ増える
using UnityEngine;
using System.Collections;
public class Math2_distance : MonoBehaviour
{
Vector2 pointA, pointB; float distance;
//距離の算出 void Start () { pointA = new Vector2 (2, 2); pointB = new Vector2 (5, 5);
distance = Mathf.Sqrt (Mathf.Pow ((pointB.x - pointA.x), 2) + Mathf.Pow ((pointB.y - pointA.y), 2));
print (distance); print ((pointB - pointA).magnitude); print (Vector2.Distance (pointA, pointB)); }
}
サンプル1
using UnityEngine;
using System.Collections;
public class Math_Pythagoras : MonoBehaviour
{
Vector3 a, b, c; void Start () { a = new Vector3 (20f, 0f, 50f); b = new Vector3 (100f, 0f, 90f); c = new Vector3 (70f, 0f, 150f); float lineA = (a - b).magnitude; float lineB = (c - b).magnitude; float lineC = (a - c).magnitude; float lineA2 = Mathf.Sqrt (Mathf.Pow (a.x - b.x, 2) + Mathf.Pow (a.z - b.z, 2)); float lineB2 = Mathf.Sqrt (Mathf.Pow (c.x - b.x, 2) + Mathf.Pow (c.z - b.z, 2)); float lineC2 = Mathf.Sqrt (Mathf.Pow (a.x - c.x, 2) + Mathf.Pow (a.z - c.z, 2)); print (lineA + " / " + lineB + " / " + lineC); print (lineA2 + " / " + lineB2 + " / " + lineC2); print ("delta: " + Mathf.Sqrt (Mathf.Pow (lineA2, 2) + Mathf.Pow (lineB2, 2))); if ((lineA * lineA + lineB * lineB) == lineC * lineC) print ("Pythagoras!"); }
}
三角関数
- 三角関数とは直角三角形の3辺のうちの2辺間の関係を記述したもの
- 三角関数が有効なのは直角三角形の時だけ
- sin、cos、tanは2辺間の関係を示した単なる分数、比でしかない
(各関数は英語を筆記体で書いた際の筆順の関係で考えると憶えやすい)
<サンプル1>
既知の2辺の長さからピタゴラスの定理を使って未知の斜辺を求め
三角関数の各辺の関係に当てはめる
Vector2 endPoint; float c; float sinTheta, cosTheta, tanTheta;
void Start () { endPoint = new Vector2 (2, 2);
c = Mathf.Sqrt (endPoint.x * endPoint.x + endPoint.y * endPoint.y);
sinTheta = endPoint.y / c; cosTheta = endPoint.x / c; tanTheta = endPoint.x / endPoint.y; }
電卓などで45と入力してsinを押すと0.707106...と表示されます
これは角度θ°=45とした際の斜辺1の長さを持った、sinの2辺の関係の比を表示している事になります
Mathf.Sqrt関数はCPUパワーを莫大に消費するためsin関数やcos関数はこれらを利用せずに低コストで計算する為のものとも言えます
つまり上記の c = Mathf.Sqrt (endPoint.x * endPoint.x + endPoint.y * endPoint.y); の部分を実行させない為のものであり
コーディングする際はこれをよく理解した上で組んでいく必要がある
斜辺の角度へ変換するには
二次元の場合、XY2軸のデカルト座標を利用する事からアークタンジェントを利用する機会が多いだろう
計算機を使って確認してみると・・・例えばWindowsのアクセサリの電卓を起動して「1」を押して「Inv」、「tan-1」を押すと
45と表示される。これは(1,1)のアークタンジェント(単位:°)が出力された結果だ
これが例えば終辺上の点が(100,400)であった場合、「400」「/」「100」「=」「Inv」「tan-1」と計算する
返ってくる値は 75.963756532073521417107679840837 となり、おおよそ76°となる
<サンプル>
unityでアークタンジェントを使ったテスト
検証用にサインを使った斜辺の長さとピタゴラスの定理を使った斜辺の長さも求めている
デバッグで値を確認すると斜辺は 412.3106 になり同じになっている事が確認できる
using UnityEngine;
using System.Collections;
public class sankaku3 : MonoBehaviour
{
float x, y, angle; float c, test;
void Start () { x = 100; y = 400; angle = Mathf.Atan (y / x) * Mathf.Rad2Deg;
c = 1 / Mathf.Sin (Mathf.Deg2Rad * angle) * y; test = Mathf.Sqrt (x * x + y * y); }
}
正弦波(sinグラフ)の利用
正弦波を利用する事により周期的な波グラフを動きに流用できる
この波はパラメーターの与え方により水平方向に引き延ばしたり、押しつぶすこともできる
またsin関数で得られる値は-1~0~1であるが、これにスケール値を掛ける事で好みの値を得ることもできる
以下のサンプルを参照
<サンプル>
上下にふわふわとオブジェクトが浮かぶ動きをするサンプル
using UnityEngine;
using System.Collections;
public class sankaku4 : MonoBehaviour
{
float x, y; Transform _transform; Vector3 initPosition; public float scale = 0.5f; //単位:上下移動2unitとした際のスケール public float perSeconds = 3; //周期・3にすると3秒周期になる。0に近づくほど周期は速く、遠くなるほど遅くなる void Start () { x = 0; y = 0; _transform = transform; initPosition = _transform.position; } void Update () { x += Time.deltaTime * Mathf.PI * 2 / perSeconds; x = x % (Mathf.PI * 2); y = Mathf.Sin (x) * scale; _transform.position = initPosition + new Vector3 (0, y, 0); }
}
度とラジアンの変換
- ラジアン = 度 * (PI / 180)
- 度 = ラジアン * (180 / PI)
<変換サンプル1>
Mathfの既定の Deg2Rad や Rad2Deg 変数を利用すると便利です
float euler1, euler2; float radian1, radian2; void Start () { euler1 = 45.0f; radian1 = euler1 * Mathf.PI / 180; radian2 = euler1 * Mathf.Deg2Rad; euler2 = radian2 * Mathf.Rad2Deg; }
単位円
<公式>
x^2 + y^2 = 1
円の中で作成できる直角三角形の斜辺(半径)が1になっている点に注目
sinθ = y/1 = y
cosθ = x/1 = x
したがって
y = sinθ
x = cosθ
となる。これにより
cosθ^2 + sinθ^2 = 1
となる
一般的なワークフローは以下を参照すれば理解しやすい
unityの2D機能
大まかなワークフロー
テクスチャのスプライト化 → シーンに配置 → 必要であればコリダー追加 → レイヤー設定
AssetStore関連
「速さ」の意味
(ちょっと考えがまとまっていません)
割算をするという行為自体が「1単位当たりの量」を求める操作と考えると
速さの単位「m/h」メートル毎時は、すでにどこかで割算された値であり「1単位当たりの量」になっている
「リッター/毎時」や「リッター/km」、「入口/人」、「リッター/ポンプ」等は全て「割算がすでに行われた値」で単位化されている
そういう意味で、単純な一次元のkmや分、秒と、二次元のkm/sは別のものと考えた方が良い。2次元の単位は全て「速さ(仕事量)」と考えた方が計算はシンプルに考えれる?
速度になるという事は割算した瞬間、傾きもベクトル(方向)も生まれると考えた方が良い?
割算する → 有理数、1単位当たりの量、速度になる → 傾き、ベクトルになる ??
そう考えると掛算、割算する行為自体がベクトルを常に生んでいることになる・・・sin,cos,tan,i・・・正規化・・・
再帰処理に関する考察
メソッドを再帰処理にするメリット
- 漸化式のような何回ループ処理すれば良いか事前に判らないものを終了条件に照らし合わせながら計算する場合、利用価値がある
- ある前提から得られる組み合わせやパターンを検証する際に適している(その派生するパターン数やネストする数は事前に分かっていない)
<ゲームにおける応用>
- ゲームにおける敵の思考ルーチン、将棋や囲碁の場合、数手先のパターンの検証(勝率が一定値を超えるまで先の手を追いかけ続け検証する等)
- 先回りするような思考や検証
- パズルゲームの成立条件(たとえばコラムスやぷよぷよでは、そのパズルチップがルールを成立させて消えるかどうかを探す時などに使う)
<補足>
C#においては処理の高速化には必ずしも繋がらないらしい??
- 最終更新:2015-02-24 01:08:57