ぱふの自由帳

週3更新(火・木・日)を目指すUnityブログ。良ければフォローお願いします(`・ω・´)

【unity2017】Animationで設定した値を相対的な値として扱う方法

はじめに

Animtionの使い方は ”unity Animation 使い方” で素晴らしいサイトが沢山ありますのでそちらを参考にしてください!今回はAnimationの小技(?)の話です。

「前置きはいいから、解決方法だ早く教えて!」という方は《解決したいこと》を読み飛ばしてください(`・ω・´)b

解決したいこと

では早速ですが、下の写真の赤枠の箇所をみてくださいー。

f:id:PafuOfDuck:20171001143152p:plain

枠内の値を変更することでアニメーションを作成していきますよね。この値がアタッチされているオブジェクトのTransformを書き換えてくれることでアニメーションとなっているのですが...

f:id:PafuOfDuck:20171001144730p:plain

このままでは大変不便に感じますね。
例えば、原点で(親を持たない)Cubeがジャンプするようなアニメーションを作ったとします。その後、「このCubeを隣にもう一つ並べたい!」と思った時にこのままでは使えません。(Animationで設定した値でTransformを書き換えているので、配置時にずらしていても同じ場所で同じ動きをします。)

問題点
同じAnimation Clipを設定すると同じ場所で同じ動きをするオブジェクトになってしまう

さて、これを解決するためには配置時のTransformを基準としてAnimationで設定した値を適用してくれればOKですね。その方法について説明します。

解決方法

"Root Motion"という機能を使用して問題を解決していきます。

まず、適用したいAnimation Clipを選択してください。選択するとInspectorが以下の画像のようになるので、赤線の箇所をクリックしてください。

f:id:PafuOfDuck:20171001150635p:plain

次に、アニメーション予定のオブジェクトにアタッチされているAnimatorの設定を変更します。赤線の箇所にチェックを入れてください。(画像はチェック後です。)

f:id:PafuOfDuck:20171001151020p:plain

はい。たったこれだけで完了です!
実際にGIFを用意すればちゃんと意図通りに動いてるのをお見せできるのですが、GIFを用意するのが面倒くさ.. 今回は用意できていません(´・ω・`。)

小噺

この機能を知るまで私がどのように実装していたかという話に興味があれば読んでみてください。(笑)

「Transformが書き換わるのかー。ってことは親を設定してあげるとLocalPositionが書き換わるってことでしょ?じゃあ親を設定してあげれば(無駄なオブジェクトにはなるけど)実装できるじゃん。」ってな訳で...ほいっとな。

f:id:PafuOfDuck:20171001152325p:plain

こうすることで"JumpCubeSet"の位置を動かせばその位置を基準としてアニメーションしてくれます。めでたしめでたし...?

・・・。

はい。これはAnimationの遷移に対応するのが面倒でした。(おかげで"Root Motion"に辿り着けたのですがネ。)

おわり

"Root Motion"を上手く使えば、Animationだけで移動させることも簡単です!是非お試しをば(`・ω・´)b

ブログの書き方を試行錯誤して、分かりやすい構成を模索中です...。今回も迷走している気がしますが優しく見守ってください(´・ω・`。)

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています...(`・ω・´) ヨロシク!

Actionを使おうとして「あれ?エラー...?」となった話

はじめに

現環境でもActionを使うのが良いのか分かりませんが、Action使ってみたい!と思った時に少しだけ時間を無駄にしたのでメモ。

メイン

「コルーチンとAction組み合わせてみよう!Qiitaに良き記事があるね。良きかな良きかな。」カキカキ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Hoge : MonoBehaviour {

    void Start(){
        //n秒後に実行する
        StartCoroutine (DelayMethod (n, () => {
            Debug.Log ("Delay call");
        }));
    }

    private IEnumerator DelayMethod(float waitTime, Action action){
        yield return new WaitForSeconds (waitTime);
        action ();
    }
}

「さて実行...あれ?Actionが存在しないですよエラーが出てる(´・ω・`)

解決方法

using System;

これを忘れずにつけときましょう。
ちなみにFuncを使用する際にも必要だそうです。

おわり

usingが足りないんだろうなぁと思っていたにもかかわらず【unity Action 使い方】とググってしまいました。(笑)
今調べてみると【unity Action using】で1発で出ますね...(´・ω・`。)ググり力が足りないZE...

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています...(`・ω・´) ヨロシク!

Admobの動画広告を連続で表示したい

はじめに

「Admobの動画広告を何度も表示させたい!でも、公式サイトのサンプルのままじゃ1回しか読み込めない…」ということがあったのでメモついでの記事になります。
もっとスマートな方法があると思いますが…ネ

実装

細かいAdmobの使用方法は以下のサイトを参考にしてください。

さて、色々説明する前にコードを貼っときます!

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using GoogleMobileAds.Api;


public class Admob : MonoBehaviour {

    private RewardBasedVideoAd rewardBasedVideo;

    private string videoId;

    void Start(){
        RequestRewardBasedVideo ();
    }

    // 動画広告リクエスト(最初に呼びます)
    private void RequestRewardBasedVideo() {

        #if UNITY_ANDROID
        videoId = "ca-app-pub-3940256099942544/5224354917";
        #elif UNITY_IPHONE
        videoId = "ca-app-pub-3940256099942544/1712485313";
        #else
        videoId = "unexpected_platform";
        #endif

        rewardBasedVideo = RewardBasedVideoAd.Instance;
        AdRequest request = new AdRequest.Builder().Build();
        rewardBasedVideo.LoadAd(request, videoId);
        rewardBasedVideo.OnAdRewarded += HandleRewardBasedVideoRewarded;
    }

    // 動画広告が最後まで視聴されたら呼び出される
    void HandleRewardBasedVideoRewarded(object sender, System.EventArgs e){
        // 動画広告再取得
        StartCoroutine ("RequestRewardBasedVideoAgain");
    }

    // 動画広告再取得関数
    IEnumerator RequestRewardBasedVideoAgain(){
        while (true){
            // 適当な時間を設定します(失敗時の再取得時間)
            yield return new WaitForSeconds(10.0f);
    
            // 通信ができない場合は、リロードしない
            if (Application.internetReachability != NetworkReachability.NotReachable){
                rewardBasedVideo = RewardBasedVideoAd.Instance;
                AdRequest request = new AdRequest.Builder().Build();
                rewardBasedVideo.LoadAd(request, videoId);
                break;
            }
        }
    }
}

コードを読むとある程度分かると思うので、軽く流れを説明しておきます。

まず動画広告を流すことが出来るように用意をします。私も詳しくは分からないのですが、この処理をリクエスト処理と呼ぶらしいです。そして、動画広告を最後まで視聴すると動画広告を再取得する関数が呼び出されるようになっています。
工夫点(?)はRequestRewardBasedVideoAgain関数内で動画広告が取得出来なかった際に再取得する処理を行っている点です。なぜか視聴後すぐにリクエスト処理を行うとすると失敗するんですよね…。しかも、リクエスト処理が成功する間隔が一定じゃない気もする…。(原因が分かる方はコメントに書いていただけると助かります!)

!使用する際の注意!
上記のコードは動画が最後まで視聴された場合しか想定していませんが、実際は途中で視聴をキャンセルした場合等も考慮してコードを完成させる必要があります。上記のサイトを参考にイベントハンドラを追加してください。

おわり

同じ処理してるコードまとめないの?恥ずかしくないの?という声が聞こえてきそうなコードですが…そこは目を瞑ってください(´・ω・`。) 良い関数名が思いつかなかったんです…(笑)

なお、「こういう手軽な方法があるよ」や「間違ったこと書いているよ」等ありましたらコメントやtwitterのDMで教えて下さると助かります( ´ ▽ ` )

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

DateTimeを文字列から生成する

はじめに

文字列のセーブは出来るけどDateTime型をセーブでが出来ない!そんな場面に出くわしたのでDateTime→文字列や文字列→DateTimeに変換する方法をメモして置きます。

変換方法

DateTime→文字列

ToString()を使用するだけですー。下のような感じで…。

DateTime.Now.ToString ();

文字列→DateTime

DateTime.Parse(String s)という便利な関数があります。文字列を渡すとDateTime型に変換してくれます。なお、適当な文字列を渡すとどうなるかは調べていません><。

String time = DateTime.Now.ToString ();
Debug.Log(DateTime.Parse(time).GetType());

//出力結果
System.DateTime

おわり

DateTime.Parse(String s)という便利な関数の存在を知らずに少し手間取りました。ちゃんと、「変換関数ぐらいあるのでは?」とリファレンスを読むべきでした(´・ω・`。)

あ。DateTime使うときにusing Systemを忘れずに!では!

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

型変換メソッドのConvert.ToXXX() を使用する

はじめに

型変換であるConvert.ToInt32()Convert.ToBoolean()を使用する時に少しエラーを出してしまったので使い方メモです

使い方

using System;

を追加してからConvert.ToXXX()を実装しましょう。

おわり

ググればすぐ見つかりますが、とりあえずメモしときました。

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

Prefab変更したのにPrefabから生成したオブジェクトが変更されない件

はじめに

「Prefabを変更することで一括で値を変えて…っと。あれ?反映されない…(´・ω・`)」という実話を元に作成しました(笑
理解してしまえば当たり前のことだったので、内容は入門者向けです。

注意:D&Dという用語を多用しますが、ドラッグ&ドロップのつもりです

メイン

現象説明

冒頭だけではいまいちどういう話をしているか分からないと思うので、簡単に説明します。

  1. Prefabを元にD&DでオブジェクトAを生成
  2. オブジェクトAをもぞもぞ弄る
  3. オブジェクトAをコピーしてオブジェクトB~Zを作成
  4. オブジェクトA~Zの値を一括変更したいのでPrefabを変更
  5. 「あれ。値変わらないんだけど」

という感じの現象です。2のもぞもぞ弄る際にどれだけもぞもぞ弄るかで結果が変わってきます。

Let's確認

文字よりも実際に画像で見た方がわかりやすと思うので画像を交えて説明します。

BoxというPrefabを用意しました。BoxのTransformは以下の図のようになっています。

f:id:PafuOfDuck:20170908204705p:plain

例1

D&Dでもして、このBoxを元に適当な場所にAという(インスタンス)オブジェクトを生成します。AのTransformは以下の図のようになっています。

f:id:PafuOfDuck:20170908205135p:plain

AからコピーであるBを作成しました。BのTransformは以下の図のようになっています。

f:id:PafuOfDuck:20170908205154p:plain

実際のオブジェクトを見ると下図のように見えます。

f:id:PafuOfDuck:20170908205426p:plain

ここでPrefabのPositionとRotation、Scaleを変更するとどうなると思いますか?
実は変更されるのはScaleのみなのです。

〈適当に弄った後の図〉 f:id:PafuOfDuck:20170908205826p:plain

ここで、一つ知識の紹介です。
《Prefabの値から一度でも修正した値はPrefabを変更しても自動で適用されない》
当たり前と言えば当たり前ですね。Prefabに従い続けるのであれば位置の修正も出来ませんよね。というわけで、D&Dした瞬間に修正されるPositionは変更が適用されなかったのです。
逆に修正を加えていないScaleは変更が適用されました。

あれ?Rotationは?と思った方。そうです。
《RotationもD&Dの際に修正されたものとして扱われる》
これ知らなかった方もいるんじゃないでしょうか?(私のように..orz

例2

例1と同様にD&Dでもして、Boxを元に適当な場所にAという(インスタンス)オブジェクトを生成します。そして、Scaleを弄ります。AのTransformは以下の図のようになっています。

f:id:PafuOfDuck:20170908211106p:plain

Scaleを弄った後のAからコピーであるBを作成しました。
実際のオブジェクトを見ると下図のように見えます。

f:id:PafuOfDuck:20170908211048p:plain

ここでPrefabのScaleを変更するとどうなると思いますか?AはScaleを修正してしまっているので変更は適用されませんよね?Bはどうでしょうか?
以下の図で見てみましょう。

f:id:PafuOfDuck:20170908211852p:plain

はい。何も変わりませんね。そもそも変更前、PrefabのScaleが(1, 1, 1)だったのにBは(1, 0.1, 1)というように既に修正された値になってます。なので、変更はAと同じく適用されません。

まとめる

例1&2から分かったことは以下の事柄です!

  • Prefabの値から一度でも修正した値はPrefabを変更しても自動で適用されない
  • RotationはD&Dで生成の際に(値が同じでも)値を修正したものとして扱われる
  • そもそもPositionとRotationは常にPrefabから変更できない
  • 値を修正したオブジェクトから複製されるオブジェクトも値を修正したオブジェクトとして扱われる
  • よく見ると、修正した値の変数名が太字になっている

例1&2では挙げていませんが、Inspectorで設定したpublic変数の値も同様の扱いを受けるので気にかけておきましょう。

解決方法は?

私が考えているのは"オブジェクトからオブジェクトを複製する前に必ずApply押そうね!じゃないとPrefabで一括変更しようとした時に痛い目見るよ!”というぐらいです。Apply押せばPrefabが更新されるので基本的にうまくいくはずです…きっと…。
他に良い方法があれば是非教えてください。

おしまい

長くてすいません。私も長い文章書くの面倒なので分かりやすいサイトになるようにもっと簡単に説明できるようになりたいものです。

なお、「こういう手軽な方法があるよ」や「間違ったこと書いているよ」等ありましたらコメントやtwitterのDMで教えて下さると助かります( ´ ▽ ` )

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

DrowCall数について分かっていることまとめ【随時更新】

はじめに

DrowCallがどの様な時に増加or減少するかを分かり次第追記していきます。

分かっていること

増加編

基本

  • 1オブジェクト1DrowCall…のはず?

  • 影の描写で一気にDrowCallが増える

  • カメラに写っているものが影響する

3D Object

※光源があると影の描写によってdrowCallが増えてしまうので光源はないものとして考えています

  • Cube/Plane/Quad/Cylinder
    オブジェクト増やしてもMaterialが異ならない限り1のまま。変形してもなんのその。もちろんCubeとPlaneを同時に使用すれば2になります。

  • Sphere/Capsule
    オブジェクト増やすごとに1つずつ増えてしまう。丸が難しいのではと個人的に解釈。

UI(初期値)

  • Text
    1:文字数増やしても変わらない

  • Image
    1:特になし

  • Raw Image
    1:特になし

  • Button
    2:Button内部のTextを消すと1になる

  • Toggle
    3:内部パーツを消せば1ずつ減る

  • Slider
    3:内部パーツを消せば…(ry

  • Scrollbar
    2:内部パーツを消せ…(ry ただし、使い物になるかは微妙

  • DropDown 2:一覧を出すとさらに増加する(初期だと6程度)

  • InputField
    1:入力の時のチカチカするやつが出ると+1、1文字以上入力でさらに+1

  • Canvas
    0:描画されないので安定の0

  • Panel
    1:特になし

  • Scroll View
    4~5:茶目っ気を出してきて値がブレました

  • EventSystem
    0:仲間外れにするのもアレなので一応入れました

減少編

  • StaticなオブジェクトにすればMaterialが同じである限り、形が異なっていてもまとめて1となる。
    例1)CubeとSphereをStaticにすると同一のMaterialを使用であれば1
    例2)CubeとSphereをStaticにすると異なるMaterialを使用であれば2

    ちなみに、同じMaterialを設定したのちに色を変えてMaterialを節約しようとすると逆に増加するので注意!(別のMaterial作って設定した方が良い)理由はわかりません。((内部でMaterialコピーしてコネコネするから…?

おわり

減少させる方法少なくてすいません…orz
ただ、ググって見つかる全ての方法を書こうとするとめんどくさい時間かかるので私自身が実践したものだけ書いていこうと思います!

なお、「こういう手軽な方法があるよ」や「間違ったこと書いているよ」等ありましたらコメントやtwitterのDMで教えて下さると助かります( ´ ▽ ` )

最終更新:2017/9/7

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

【unity2017】コライダーを1つだけ持ったオブジェクト同士の反射をスクリプトで実装する《2D》

はじめに

今回はタイトルに書いてあることを実装していきます。様々な方の意見を頂きつつ実装に至りました。私一人では解決しなかったと思うので未来の誰かの参考になれば幸いです。

実装

実装内容確認

今回使用する2つのオブジェクトを以下に示します。

  • Player
    • Sprite Renderer
    • Rigidbody 2D
    • Circle Collider 2D
  • Obstacle
    • Sprite Renderer
    • Box Collider 2D

Playerオブジェクトを操作し、Obstacleオブジェクトに衝突すると反射するという実装を行っていきます。その前に少しだけ話を....

なぜunityの物理演算を使用しないのか?

ここで、そもそもなぜ自前で実装する必要があるのかという事について少し説明しておきます。

unityの物理演算を用いて反射を実装する(Physics Material 2Dを用いる)と、反発係数を1にしていても以下の様な現象が起こります。(弱い方の力ベクトルが消えてしまう)

f:id:PafuOfDuck:20170902214033p:plain

私は今回、mi●iのモン●トの様な動きをするPlayerを実装したかったので自前で実装する必要がありました。

まず最初にどこで詰まったか?

以下の様な場合、どの辺に衝突したのかということをどの様に見分けるのかで詰まっていました。

f:id:PafuOfDuck:20170902215014p:plain

場合によっては、Box Collider 2Dを持つ子オブジェクトをObstacleに4つほど(4方向分)持たせる方法も出来なくはありませんが、今回ObstacleはMap Editorの様なアセットを使用してタイルとして配置する予定でした。そのアセットでは子オブジェクトをタイルに持たせることが出来ない(出来るかもしれないが私がその方法を理解出来ない)ので断念しました。

最終的に取った方法

様々なアドバイスを頂いた中で、今回はCollision2Dから法線を取得してvelocityと共にVector2.Refrectに渡すという 青木とと(ˊᗜˋ*)さん (@lycoris102)の案で実装していきたいと思います。

他の方々からPhysics2D.Raycastを実装してPlayerからRayを飛ばして当たった向きを調べるという方法も提案して頂きましたが、「Obstacleの角に当たった時どうするの?」と考えていく内にRayの数や太さを考慮するのが大変なのではないかと考え断念しました。(((せっかく頂いたのに...実装すら諦めてしまい申し訳ありません...orz

実装開始

さて、早速Bounce.csを実装していきます。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bounce : MonoBehaviour
{
    private Vector2 lastVelocity;
    private Rigidbody2D rb;

    void Start()
    {
        this.rb = this.GetComponent<Rigidbody2D>();
    }

    void FixedUpdate()
    {
        this.lastVelocity = this.rb.velocity;
    }

    void OnCollisionEnter2D(Collision2D coll)
    {
        Vector2 refrectVec = Vector2.Reflect(this.lastVelocity, coll.contacts[0].normal);
        this.rb.velocity = refrectVec;
    }
}

Vector2.Reflectについてですが、私は今回初めて知る機能でした。以下に公式のリファレンスに書いてあった機能をそのまま貼っておきます。

法線を基準にしてベクトルの反射したベクトルを取得します。

このスクリプトをPlayerにアタッチすることでPhysics Material 2Dを用いることなく反射を実装できます。(変な所で反射している様に見えますが、FPSが低いだけです...笑)

f:id:PafuOfDuck:20170902231811g:plain

実は初め失敗した話

興味のない方は飛ばしてしてください。

先ほどのBounce.csでは

void FixedUpdate()
    {
        this.lastVelocity = this.rb.velocity;
    }

というようにFixedUpdate関数内で速度ベクトルをコピーしてOnCollisionEnter2D関数で使用していました。しかし、初めの方はOnCollisionEnter2D関数で速度ベクトルを取得して失敗しました。

こちら(Unityイベント関数の実行順)のサイトで確認できますがイベント関数の実行順は以下の様になってます。

f:id:PafuOfDuck:20170902230257p:plain

InternalPhysicsUpdateが速度の更新箇所になっているため、OnCollisionEnter2Dが実行される時に速度ベクトルをコピーしても既に速度ベクトルは0になってしまっており、全く跳ね返らないという状況になってしまっていました。

ここでも 青木とと(ˊᗜˋ*)さん がご指摘くださって解決しました。ありがとうございました!

おわりに

すごく苦労して沢山の人の意見を参考にさせていただきました。皆様、本当にありがとうございました!また何かありましたらその時はよろしくお願いいたします...(´・ω・`。)

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています...(`・ω・´) ヨロシクデス!

Buttonはクリック以外でも押せることに気づいた話

はじめに

タイトルそのままです。知ってる方も多いのかな?

ポチッとせずとも…?

さて、下のGIFにもあるようにボタンをクリックした後に”Space”と”Enter”を押せばButtonが押されたのと同じ動作をします。ボタンをクリックした後にボタン以外の場所をクリックすれば、"Space"と”Enter”を押しても何も反応しません。

f:id:PafuOfDuck:20170824170128g:plain

一応、コードも載せておきますー。

CountUp()はButtonが押されると呼び出される関数です。

using UnityEngine;
using UnityEngine.UI;

public class Test : MonoBehaviour {

    private int count = 0;
    public Text testTx;

    public void CountUp(){
        count++;
        testTx.text = count + "回押されたよー";
    }
}

おわり

Unity1weekの作品作ってる時に躓いたので誰かの役に立つかもと思い書きましたー。また見つけたら適当に書きます!短く書けそうなネタ限定で(笑)

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!

for(~; ~; i=j++) という表現について【C#】

for(~; ~; i=j++)という書き方を初めて見たのでメモしておきます。
以下、unityでのコードとなります。

int i = 0, j = 0;
for(i=5, j=0; j<10; i=j++){
    Debug.Log (i.ToString() +"&"+ j.ToString());
}

出力結果は

5&0
0&1
1&2
2&3
...
7&8
8&9

となります。
代入->インクリメントの順に実行されているようです。

(……使う場面が思いつきませんが便利なのですかね…?)

初心者の方や熟練者の方を問わずTwitterのフォローお待ちしています…(`・ω・´) ヨロシク!