20200224のUnityに関する記事は5件です。

『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.その後一つ目の(以下略

このような流れになる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『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.その後一つ目の(以下略

このような流れになる

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

【Unity】RenderTextureを使ってオブジェクトにアウトラインを付ける

こんにちはっ?八ツ橋まろんです

今回はUnityのアウトラインシェーダー&マテリアルの話です。

Unityで3Dモデルにアウトラインを付ける方法はいくつかありますが、以下の条件をお求めの人におすすめです。

・アウトラインを付けたい対象のオブジェクトのシェーダーは何でもOK
 (アウトライン機能のないスタンダードシェーダーとかでもOK)
・アウトラインを極太にしてもヘンな描画にならない(ただし描画コストが上がる)

アウトライン描画の概要

本記事でのアウトラインの描画の流れを以下の画像に示します。
図1.png

こんな感じで、破綻しづらく太いアウトラインができました。
RenderTextureに対してシェーダーで処理をするので、対象の3Dモデルはどんなシェーダーでも良いというのがgoodです✨
(よくあるアウトライン描画の方法では、Stencilを仕込んだり、メッシュを拡張するためににシェーダーをいじる必要があるのですよね)

また、Render Textureを後ろに重ねるだけなので、二重や三重にすることもできるので、上の画像の一番右のように3Dモデル→白枠→細い灰色枠→影と言う風に3枚のRender Textureでより豪華な表現ができます。

シェーダーコード

以下、シェーダーコードです。

MaronOutlineShader.cs
Shader "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軸分ずらしたものを描画しているわけです(下画像:左)
qiita1.png

(下方向の影は見づらいけど、脚が下方向に伸びていますね)
n = 2では、ごく短い距離にしないとアウトラインとして成立しないですが、これを、n = 4で影が8つ、n = 8で影が16という風に増やしていくと、太くてもきれいなアウトラインになります。

シェーダの核の部分はこんな感じです。
あとは、シェーダーをTransparentにしたり、模様を付けたいとき用にサブのテクスチャを設定できるようにしてtiling, offsetに対応したり、境界部分をsmoothで多少滑らかにしたりという感じです?

以上、アウトラインシェーダーの話でしたっ?
もし良ければイイネ押していってくださいね?

八ツ橋まろん

Twitter
https://twitter.com/Maron_Vtuber
Booth(シェーダーも売ってる)
https://maronvtuber.booth.pm/

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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を設定
※基本的には自動で設定されるがモノによっては設定が上手くいかないものがある。
01.png

上手く設定できるとこんな感じになる
02.png

床用のレイヤーを追加し、接地させるようのメッシュにレイヤーを設定する。
今回はGroundレイヤーを追加し、地面に設定した。
03.png

Grounder Full Body Bipedをモデルにアタッチする。
Grounder Full Body BipedのIKにFull Body Biped IKをアタッチする。
Solverを開き、Layersに接地用レイヤーを設定する。今回はGroundを追加した。
04.png

設定するとこんな感じになる。
01.gif

IKだけでオブジェクトに片手を触れて放す

アイドルモーション用に使っていたAnimatorコンポーネントをオフにする
05.png

オブジェクトにInteractionObjectをアタッチ
06.png

Weight Curvesを追加しTypeはPosition Weightを選ぶ
Curvesは(Time,Value)でKeyを(0, 0) > (0.5, 1) > (1, 0)と設定した。
※0秒から0.5秒にかけて目標座標に到達し、0.5秒から1秒にかけて元に戻るようなカーブです。
07.png

インタラクションさせるモデルにInteractionSystemをアタッチする
Full Bodyにはモデルに設定したFull Body Biped IKを設定する
08.png

InteractionSystemTestGUIをアタッチする
※ゲームビューにInteractionSystemを実行するためのボタンが追加されます。

FinalIKの動作テスト用に同梱されたスクリプトであり、任意のタイミングでインタラクションするにはスクリプトの修正が必要です。
Interaction ObjectにInteraciton Objectをアタッチしたのを設定する
Effectorsを増やしRight Handを選択する
※これで右手にInteractionを適用することができる
09.png

動かすとこんな感じ
4.gif

IKだけでオブジェクトに両手を触れて放す

上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
11.png

動かすとこんな感じ
7.gif

IKだけでオブジェクトに片手を触れ続ける

上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
Interaction ObjectのEventsを追加し
Timeを0.5
Pauseにチェックを入れる
10.png

動作としてはWeightCurvに従って0.5秒かけてオブジェクトに手を近づけ、
Evetntsで設定されたPauseが呼ばれる動作です
Curvを伸ばし、Timeを調整することでタイミングを調整できます。

動かすとこんな感じ
5.gif

IKだけでオブジェクトに両手で触れ続ける

上の章(IKだけでオブジェクトに片手を触れ続ける)で紹介した工程をしたあとに
InteractionSystemTestGUIのEffectorsを増やし
RightHandとLeftHandを設定するだけです。
11.png

動かすとこんな感じ
6.gif

IKだけでオブジェクトを片手で掴む

上の章(IKだけでオブジェクトに片手を触れて放す)で紹介した工程をしたあとに
12.png
動かすとこんな感じ
8.gif

掴むオブジェクトの回転をモデルのボーンに適用する

Interaction ObjectのMultipliersを追加し以下のように設定する
Curve:Position Weight
Multiplier:1
Result:Rotate Bone Weight
13.png
動かすとこんな感じ
9.gif
MultiPliersのCurveはWeight Curveで指定したCurveから選ぶ

オブジェクトに手の位置を合わせる

モデルを複製し、複製したモデルの手(Hand)をオブジェクトの置きたい座標に移動させる
※この時手が伸びてるように見えるが問題はない
14.png
複製した手を置きたいオブジェクトの子階層に配置する
15.png
コピーしたモデルは削除してよい
オブジェクト直下に配置したHandにInteractionTargetをアタッチし以下のように設定
EffectType:RightHand
※合わせるのに使用した部位を選ぶ
16.png
動かすとこんな感じ
10.gif

ポジションはあってるが回転があってないのでWeight Curveを追加し、Rotation Weightを追加する
17.png
動かすとこんな感じ
11.gif

オブジェクトに指の位置を合わせる

モデルの合わせたい指がある手(Hand.R)にHandPoserをアタッチ
18.png

調整するときはゲームを再生し、IK適用後にオブジェクト直下に配置してるHandの指を動かす
12.gif

調整したHandオブジェクトをコピーして再生を終了する
コピーしたオブジェクトに置き換える

位置によって手首がねじれるのでオブジェクトのInteractionTargetに
Pivot:インタラクションするオブジェクト
※再生中にTwistAxisを調整してコピペして角度合わせるのも有効
Rotate Once:チェックをつけると常に角度が修正されるようになる
19.png

動かすとこんな感じ
13.gif

手を動かすときの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秒にかけて目的座標に戻るようなカーブです。
20.png

動かすとこんな感じ
14.gif

手を動かすときの腕の伸ばし具合のオフセットを指定する

オブジェクトのInteractionObjectのWeightCurvを追加する
腕の伸ばし具合のオフセットを修正する際にはReachにする
沢山伸ばしたい場合はValueを0に近づける
21.png
調整するときは再生してコピペでCurveを設定する
16.gif
Pauseを0.5としてるので
Curvesは(Time,Value)でKeyを(0, 0) (0.5, 0.08)と設定した。
動かすとこんな感じ
15.gif

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

ゼルダの伝説BotWのカメラ研究 with Unity 其の一「基本の動き」

はじめに

  • ゼルダの伝説BotWはここでは「ゼルダの伝説 ブレスオブザワイルド」を指します。
  • 場合によっては誤りがある場合があります。十分に気を付けてはおりますが、筆者は特に数学が苦手ですのでその部分で誤りがあるやしれません。
  • 解説を分かりやすくするために一部初級者向けの内容が含まれています。
  • Unityで動きを演出する部分があります。
  • 何回かに分けて考察を書いていきます。今回は基本的な動きに目を付けたいと思います。
  • 現在、この項に関して考察は完了していますがそれを実現するための処理は完成していません。予めご了承ください。
  • (2020年2月25日追記)処理が完成しました。

Unityの動作のテスト環境

Windows10 Home
Unity 2019.3.0f6
VisualStudio 2019 Professional

基本の動き

基本の映し方としては一般的な三人称視点のカメラと変わりはありません。

20200224Zelda-image.jpg

注目していただきたいのはカメラの回転速度(左右の軸周り)です。

動画はこちらを確認してください

動画を確認された方は「何を言ってるんだ、普通の動きじゃないか」などと思われるかもしれませんが、例えばUnityのRotateAround関数の処理をC#で書き表すとこんな感じになります。

ソースコード

RotateAround.cs
using 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軸と、基準点とカメラ位置のなす直線がなす角が狭まれば狭まるほど高速に回転を始めることがわかると思います。
(下記の画像と上記の動画で言いたいことがわかっていただけると幸いです。)
nasukaku.png

なお、下から覗くような形でも同様の現象が確認できます。

考察

まず必要な条件として、カメラが基準となる位置を周回するときに回転数が半径に関わらず常に一定であるということが挙げられるかと思います。

周回する際の半径に関わらず回転数が一定である条件

まず、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関数の一部を変更し、半径を設定することで角速度の計算を行えるように変更しました。
  • 距離を取得するための変数を追加しました。

関連する記事

まだ書いていません。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む