- 投稿日:2020-02-24T23:38:04+09:00
『Unity C# ガチ初心者用』 for文
自分やガチ始めたばかりの初心者用
テンプレ
//iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); }
int i = 0;
int型のiを作り0を代入
i < 5;
iの値が5より未満の時に中身を起動する
i++
iに1を足すint型のiが5未満の時に中身を起動し、その後iに1を足す
という処理になる
この時iが0の時も処理が行われ、5未満なので4になるまで繰り替えされるので
0,1,2,3,4の5回処理が行われる
数値の種類は変えられる
//INTで使用 //iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); } //floatで使用 //iが10未満の時に起動し、iに2.5fを足す for (float i = 0; i < 10; i += 2.5f) { //iの値をコンソールに表示する Debug.Log(i); }一定の回数などならint型を使用し、
座標などならfloat型を使用する2重で使用
//xが5以下の時に起動し、xに1を足す for (int x = 0; x <= 5; x++) { //yが10以下の時に起動し、yに2.5fを足す for (float y = 0; y <= 10; y += 2.5f) { //iの値をコンソールに表示する Debug.Log(x); //iの値をコンソールに表示する Debug.Log(y); } }処理の流れは
1.一つ目のfor文のxの値が0の時
2.二つ目のfor文の処理がすべて行われる
3.その後一つ目のfor文のxにx++で1が足され
4.再び二つ目のfor文の処理がすべて行われる
5.その後一つ目の(以下略このような流れになる
- 投稿日:2020-02-24T23:38:04+09:00
『Unity C# 個人用メモ』 for文
自分やガチ始めたばかりの初心者用
テンプレ
//iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); }
int i = 0;
int型のiを作り0を代入
i < 5;
iの値が5より未満の時に中身を起動する
i++
iに1を足すint型のiが5未満の時に中身を起動し、その後iに1を足す
という処理になる
この時iが0の時も処理が行われ、5未満なので4になるまで繰り替えされるので
0,1,2,3,4の5回処理が行われる
数値の種類は変えられる
//INTで使用 //iが5未満の時に起動し、iに1を足す for (int i = 0; i < 5; i++) { //iの値をコンソールに表示する Debug.Log(i); } //floatで使用 //iが10未満の時に起動し、iに2.5fを足す for (float i = 0; i < 10; i += 2.5f) { //iの値をコンソールに表示する Debug.Log(i); }一定の回数などならint型を使用し、
座標などならfloat型を使用する2重で使用
//xが5以下の時に起動し、xに1を足す for (int x = 0; x <= 5; x++) { //yが10以下の時に起動し、yに2.5fを足す for (float y = 0; y <= 10; y += 2.5f) { //iの値をコンソールに表示する Debug.Log(x); //iの値をコンソールに表示する Debug.Log(y); } }処理の流れは
1.一つ目のfor文のxの値が0の時
2.二つ目のfor文の処理がすべて行われる
3.その後一つ目のfor文のxにx++で1が足され
4.再び二つ目のfor文の処理がすべて行われる
5.その後一つ目の(以下略このような流れになる
- 投稿日:2020-02-24T15:58:22+09:00
【Unity】RenderTextureを使ってオブジェクトにアウトラインを付ける
こんにちはっ?八ツ橋まろんです
今回はUnityのアウトラインシェーダー&マテリアルの話です。
Unityで3Dモデルにアウトラインを付ける方法はいくつかありますが、以下の条件をお求めの人におすすめです。
・アウトラインを付けたい対象のオブジェクトのシェーダーは何でもOK
(アウトライン機能のないスタンダードシェーダーとかでもOK)
・アウトラインを極太にしてもヘンな描画にならない(ただし描画コストが上がる)アウトライン描画の概要
こんな感じで、破綻しづらく太いアウトラインができました。
RenderTextureに対してシェーダーで処理をするので、対象の3Dモデルはどんなシェーダーでも良いというのがgoodです✨
(よくあるアウトライン描画の方法では、Stencilを仕込んだり、メッシュを拡張するためににシェーダーをいじる必要があるのですよね)また、Render Textureを後ろに重ねるだけなので、二重や三重にすることもできるので、上の画像の一番右のように3Dモデル→白枠→細い灰色枠→影と言う風に3枚のRender Textureでより豪華な表現ができます。
シェーダーコード
以下、シェーダーコードです。
MaronOutlineShader.csShader "Custom/MaronOutlineShader" { Properties { [NoScaleOffset]_MainTex ("Render Texture", 2D) = "white" {} _SubTex ("Pattern Texture", 2D) = "white" {} _Color ("Main Color", Color) = (1,1,1,1) _Outline ("Outline thickness", Range (0,1)) = 0.1 _DrawNum ("Drawing times", Range (2,32)) = 2 } SubShader { Tags { "RenderType"="Transparent" "Queue" = "Transparent" } LOD 100 ZWrite off Blend srcAlpha OneMinusSrcAlpha Pass { CGPROGRAM #pragma vertex vert #pragma fragment frag // make fog work #pragma multi_compile_fog #include "UnityCG.cginc" struct appdata { float4 vertex : POSITION; float2 uv : TEXCOORD0; }; struct v2f { float2 uv : TEXCOORD0; UNITY_FOG_COORDS(1) float4 vertex : SV_POSITION; }; sampler2D _MainTex; float4 _MainTex_ST; float4 _MainTex_TexelSize; sampler2D _SubTex; float4 _SubTex_ST; float4 _SubTex_TexelSize; float4 _Color; float _Outline; int _DrawNum; v2f vert (appdata v) { v2f o; o.vertex = UnityObjectToClipPos(v.vertex); o.uv = TRANSFORM_TEX(v.uv, _MainTex); UNITY_TRANSFER_FOG(o,o.vertex); return o; } float2 PolarToCartesian(float r, float theta) { float x = r * cos(theta); float y = r * sin(theta); return float2(x, y); } fixed4 frag (v2f i) : SV_Target { fixed4 col = tex2D(_MainTex, i.uv); float2 tiling_offset_uv = float2(i.uv.xy * _SubTex_ST *float2(1, _MainTex_TexelSize.x/_MainTex_TexelSize.y) + _SubTex_ST.zw); fixed4 subcol = tex2D(_SubTex, tiling_offset_uv); float2 outline = _Outline*0.1; float PI = 3.14159265358979323; int n = _DrawNum; float theta; for(int j = 0; j < 2 * n; j++) { theta = PI * j / n; col += tex2D(_MainTex, i.uv + PolarToCartesian(outline, theta)*float2(_MainTex_TexelSize.x/_MainTex_TexelSize.y, 1)); } //境界をなめらかに col = smoothstep(0.5, 0.51, col); // apply fog UNITY_APPLY_FOG(i.fogCoord, col); //一定以下のalphaはゼロにする _Color.a *= step(1-col.a, 0.01); return subcol*_Color; } ENDCG } } }このシェーダーのキモは、以下のfor文の部分と、PolarToCartesianと定義した関数部分です。
for文for(int j = 0; j < 2 * n; j++) { theta = PI * j / n; col += tex2D(_MainTex, i.uv + PolarToCartesian(outline, theta)*float2(_MainTex_TexelSize.x/_MainTex_TexelSize.y, 1)); }PolarToCartesian関数float2 PolarToCartesian(float r, float theta) { float x = r * cos(theta); float y = r * sin(theta); return float2(x, y); }for文は2n回だけループしますが、ループのたびにthetaがπ/nだけ変化し、PolarToCartesian関数に代入されます。
そして、PolarToCartesian関数は極座標→直交座標の変換をする関数です。( (r, θ)表記を(x, y)表記に直しています)
rはアウトラインの太さ(ずれの大きさ)です。
つまり、例えばn = 2のとき、π/2 = 90度なので、90度ずつrだけずれた描画が2n = 4回なされて、RenderTextureと同じものが上下左右に描画されます。n = 2なのでX軸Y軸の2軸分ずらしたものを描画しているわけです(下画像:左)
(下方向の影は見づらいけど、脚が下方向に伸びていますね)
n = 2では、ごく短い距離にしないとアウトラインとして成立しないですが、これを、n = 4で影が8つ、n = 8で影が16という風に増やしていくと、太くてもきれいなアウトラインになります。シェーダの核の部分はこんな感じです。
あとは、シェーダーをTransparentにしたり、模様を付けたいとき用にサブのテクスチャを設定できるようにしてtiling, offsetに対応したり、境界部分をsmoothで多少滑らかにしたりという感じです?以上、アウトラインシェーダーの話でしたっ?
もし良ければイイネ押していってくださいね?八ツ橋まろん
https://twitter.com/Maron_Vtuber
Booth(シェーダーも売ってる)
https://maronvtuber.booth.pm/
- 投稿日:2020-02-24T14:18:06+09:00
FinalIKでキャラクターに思いのままにインタラクションさせる方法
使用アセット
Final IK
Final IK
https://assetstore.unity.com/packages/tools/animation/final-ik-14290背景
Fantasy Forest Environment
https://assetstore.unity.com/packages/3d/environments/fantasy/fantasy-forest-environment-34245モデル
京狐
https://booth.pm/ja/items/1826902アニメーション
Magician-Motion Pack
https://assetstore.unity.com/packages/3d/animations/magician-motion-pack-133515インタラクションの参考
Final IKインタラクションのチュートリアル
https://www.youtube.com/watch?v=r5jiZnsDH3M足を接地
Sceneに配置したモデルにFull Body Biped IKを設定
※基本的には自動で設定されるがモノによっては設定が上手くいかないものがある。
床用のレイヤーを追加し、接地させるようのメッシュにレイヤーを設定する。
今回はGroundレイヤーを追加し、地面に設定した。
Grounder Full Body Bipedをモデルにアタッチする。
Grounder Full Body BipedのIKにFull Body Biped IKをアタッチする。
Solverを開き、Layersに接地用レイヤーを設定する。今回はGroundを追加した。
IKだけでオブジェクトに片手を触れて放す
アイドルモーション用に使っていたAnimatorコンポーネントをオフにする
Weight Curvesを追加しTypeはPosition Weightを選ぶ
Curvesは(Time,Value)でKeyを(0, 0) > (0.5, 1) > (1, 0)と設定した。
※0秒から0.5秒にかけて目標座標に到達し、0.5秒から1秒にかけて元に戻るようなカーブです。
インタラクションさせるモデルにInteractionSystemをアタッチする
Full Bodyにはモデルに設定したFull Body Biped IKを設定する
InteractionSystemTestGUIをアタッチする
※ゲームビューにInteractionSystemを実行するためのボタンが追加されます。FinalIKの動作テスト用に同梱されたスクリプトであり、任意のタイミングでインタラクションするにはスクリプトの修正が必要です。
Interaction ObjectにInteraciton Objectをアタッチしたのを設定する
Effectorsを増やしRight Handを選択する
※これで右手にInteractionを適用することができる
IKだけでオブジェクトに両手を触れて放す
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
IKだけでオブジェクトに片手を触れ続ける
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
Interaction ObjectのEventsを追加し
Timeを0.5
Pauseにチェックを入れる
動作としてはWeightCurvに従って0.5秒かけてオブジェクトに手を近づけ、
Evetntsで設定されたPauseが呼ばれる動作です
Curvを伸ばし、Timeを調整することでタイミングを調整できます。IKだけでオブジェクトに両手で触れ続ける
上の章(IKだけでオブジェクトに片手を触れ続ける)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
IKだけでオブジェクトを片手で掴む
上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
動かすとこんな感じ
掴むオブジェクトの回転をモデルのボーンに適用する
Interaction ObjectのMultipliersを追加し以下のように設定する
Curve:Position Weight
Multiplier:1
Result:Rotate Bone Weight
動かすとこんな感じ
MultiPliersのCurveはWeight Curveで指定したCurveから選ぶオブジェクトに手の位置を合わせる
モデルを複製し、複製したモデルの手(Hand)をオブジェクトの置きたい座標に移動させる
※この時手が伸びてるように見えるが問題はない
複製した手を置きたいオブジェクトの子階層に配置する
コピーしたモデルは削除してよい
オブジェクト直下に配置したHandにInteractionTargetをアタッチし以下のように設定
EffectType:RightHand
※合わせるのに使用した部位を選ぶ
動かすとこんな感じ
ポジションはあってるが回転があってないのでWeight Curveを追加し、Rotation Weightを追加する
動かすとこんな感じ
オブジェクトに指の位置を合わせる
モデルの合わせたい指がある手(Hand.R)にHandPoserをアタッチ
調整するときはゲームを再生し、IK適用後にオブジェクト直下に配置してるHandの指を動かす
調整したHandオブジェクトをコピーして再生を終了する
コピーしたオブジェクトに置き換える位置によって手首がねじれるのでオブジェクトのInteractionTargetに
Pivot:インタラクションするオブジェクト
※再生中にTwistAxisを調整してコピペして角度合わせるのも有効
Rotate Once:チェックをつけると常に角度が修正されるようになる
手を動かすときのY座標のオフセットを指定する
オブジェクトのInteractionObjectのWeightCurvを追加する
Y座標のオフセットを修正する際にはPosition OffsetYにする
Curvesは(Time,Value)でKeyを(0, 0) > (0.25, 1) > (0.5, 0)と設定した。
※0秒から0.25秒にかけてオフセットのYに到達し、0.25秒から0.5秒にかけて目的座標に戻るようなカーブです。
手を動かすときの腕の伸ばし具合のオフセットを指定する
オブジェクトのInteractionObjectのWeightCurvを追加する
腕の伸ばし具合のオフセットを修正する際にはReachにする
沢山伸ばしたい場合はValueを0に近づける
調整するときは再生してコピペでCurveを設定する
Pauseを0.5としてるので
Curvesは(Time,Value)でKeyを(0, 0) (0.5, 0.08)と設定した。
動かすとこんな感じ
- 投稿日:2020-02-24T13:55:55+09:00
ゼルダの伝説BotWのカメラ研究 with Unity 其の一「基本の動き」
はじめに
- ゼルダの伝説BotWはここでは「ゼルダの伝説 ブレスオブザワイルド」を指します。
- 場合によっては誤りがある場合があります。十分に気を付けてはおりますが、筆者は特に数学が苦手ですのでその部分で誤りがあるやしれません。
- 解説を分かりやすくするために一部初級者向けの内容が含まれています。
- Unityで動きを演出する部分があります。
- 何回かに分けて考察を書いていきます。今回は基本的な動きに目を付けたいと思います。
現在、この項に関して考察は完了していますがそれを実現するための処理は完成していません。予めご了承ください。- →(2020年2月25日追記)処理が完成しました。
Unityの動作のテスト環境
Windows10 Home
Unity 2019.3.0f6
VisualStudio 2019 Professional基本の動き
基本の映し方としては一般的な三人称視点のカメラと変わりはありません。
注目していただきたいのはカメラの回転速度(左右の軸周り)です。
動画を確認された方は「何を言ってるんだ、普通の動きじゃないか」などと思われるかもしれませんが、例えばUnityのRotateAround関数の処理をC#で書き表すとこんな感じになります。
ソースコード
RotateAround.csusing UnityEngine; public class RotateAround : MonoBehaviour { [SerializeField] GameObject parent; float speed; // Start is called before the first frame update void Awake() { speed = 50.0f; } // Update is called once per frame void Update() { float x = Input.GetAxis("RightHorizontal"); float y = Input.GetAxis("RightVirtical"); //初期値 float rad = 2 * Mathf.PI; //RotateAroundと同等の動きをする Vector3 radSpeed = x * transform.right * rad; transform.position += radSpeed * Time.deltaTime; //振動を防ぐためにMathf.Clampは利用していない //振動を防ぐ方法として一定以上もしくは以下になったらそれ以上動きを足さない処理を利用する float eulerAnglesX = transform.eulerAngles.x; if (!((eulerAnglesX < 180 && eulerAnglesX > 89.0f && y > 0) || (eulerAnglesX > 180 && eulerAnglesX < 271.0f && y < 0))) transform.RotateAround(parent.transform.position, transform.right, y * speed * Time.deltaTime); transform.LookAt(parent.transform); } }簡単なコード解説
- 使っているのはXboxController for Windows
- 上下の軸周りはそのままUnityのRotateAround関数を利用しています。
- 動きを分かりやすくするためにLookAt関数を利用しています。
- カメラの角度制限を設けた際の振動を抑える処理を追加しています。
動かすとこうなる
y軸と、基準点とカメラ位置のなす直線がなす角が狭まれば狭まるほど高速に回転を始めることがわかると思います。
(下記の画像と上記の動画で言いたいことがわかっていただけると幸いです。)
なお、下から覗くような形でも同様の現象が確認できます。
考察
まず必要な条件として、カメラが基準となる位置を周回するときに回転数が半径に関わらず常に一定であるということが挙げられるかと思います。
周回する際の半径に関わらず回転数が一定である条件
まず、Unityで動かした方は半径に関わらず速度が一定という条件での動作でした。
これでは半径が短くなることで円周の長さも短くなった場合に一周する速さが変わり、半径によって回転数が変化してしまします。そこで必要になるのが角速度を利用するという考え方です。
そもそも角速度とは
角速度は回転運動の速度を単位時間当たりの角度で表したものになります。
回転運動(円運動)をしている物体の角速度が一定である場合、その物体は等速円運動を行っていると定義できます。結論:ゼルダBotWのカメラを再現するための条件
角速度が一定である運動 = 等速円運動を行わせることでゼルダのカメラの基礎的な部分は再現できる!!
と、条件を定めました。結論が出たうえでの再現処理
RotateAround.cs[SerializeField] GameObject parent; float speed; float distance; //追加 基準オブジェクトとの距離。半径の役割を持つ Vector3 distancePosition; //追加 半径設定用のベクトル // Start is called before the first frame update void Awake() { speed = 50.0f; //半径設定 高さをカメラと同一にすることで距離をそのまま半径として利用できる distancePosition = new Vector3(parent.transform.position.x, transform.position.y, parent.transform.position.z); distance = Vector3.Distance(distancePosition, transform.position); } // Update is called once per frame void Update() { float x = Input.GetAxis("RightHorizontal"); float y = Input.GetAxis("RightVirtical"); //等速円運動をする物体の角速度(rad/s)の公式は 角速度(ベクトル) = 速度 / 半径 もしくは 角速度(ベクトル) = 角度 / 時間 (2π * 半径 / 時間) //今回は角速度(ベクトル) = 2π * 半径 / 時間 を利用する //今回の場合、秒間72°移動する処理である float radSpeedValue = 2 * Mathf.PI * distance / 5.0f; //変更 //RotateAroundと同等の動きをする Vector3 radSpeed = x * transform.right * radSpeedValue; transform.position += radSpeed * Time.deltaTime; //振動を防ぐためにMathf.Clampは利用していない //振動を防ぐ方法として一定以上もしくは以下になったらそれ以上動きを足さない処理を利用する float eulerAnglesX = transform.eulerAngles.x; if (!((eulerAnglesX < 180 && eulerAnglesX > 89.0f && y > 0) || (eulerAnglesX > 180 && eulerAnglesX < 271.0f && y < 0))) transform.RotateAround(parent.transform.position, transform.right, y * speed * Time.deltaTime); transform.LookAt(parent.transform); //半径設定 高さをカメラと同一にすることで距離をそのまま半径として利用できる distancePosition = new Vector3(parent.transform.position.x, transform.position.y, parent.transform.position.z); //y軸となす角度によって距離が変動するため、毎フレーム更新 distance = Vector3.Distance(distancePosition, transform.position); }簡単なコード解説
- Update関数の一部を変更し、半径を設定することで角速度の計算を行えるように変更しました。
- 距離を取得するための変数を追加しました。
関連する記事
まだ書いていません。