TAKOYAKING’s blog 一覧

TAKOYAKING’s blog

たこ焼き系

Unity: Animatorのアニメーションをリセットしたい

AnimatorのアニメーションのSetTriggerで発火したものをリセットしたいことがあったのですが、ResetTriggerを呼べば解除できるみたいなのがあったので、試してみたのですが、どうもうまくいきませんでした。

やりたいことはidleに戻せればよかったので、idle(Default State)に戻す方法を探しました。

やり方

how to restart mecanim state animation or play it from random position [SOLVED] - Unity Answers

ここに書いてありました。
オプションなどもありましたが、簡単に戻すだけなら、

var animator = GetComponent<Animator>();
animator.Play("idle");

だけでいけました。

感想

簡単にできてよかった!

Unity: ボタンやトグルはフォーカスがある時にスペースキーやエンターキーでクリックイベントが発火してしまう

ボタンやトグルはフォーカスがあると、スペースキーやエンターキーでクリックイベントが発火してしまうので、意図しないクリックイベントが発火してしまい、少々面倒でした。

やり方

調べてみるとありました。
Button is being triggered by spacebar after clicked once - Unity Answers

NavigationをNoneにすれば良いみたいです。

解説

ナビゲーション操作 - Unity マニュアル

Navigation Options は UI 要素のナビゲーションがプレイモードでどのように管理されるかを参照します。

【uGUI】Buttonの使い方 - Qiita
例えばNavigationをNoneにするとフォーカスしなくなるので、
Selected Colorの色は使われることはありません。

感想

プロトタイプ作って、面白いか検証している時に、scene移動のボタンとかのクリックイベントが発火してしまい、煩わしかったのですが、これで解決しそうです!

Visual Studioで保存時にUnityスクリプトのコンパイルをさせる

Unity 2019.3になってからコンパイル時間が長くなったような気がするので、とても面倒でした。
今まで回ることのなかったレイボーまで回り始めました。

Enter Play Mode Optionsを設定して、スクリプトを一切編集していないときはめちゃくちゃ速くなったのですが、コンパイル時間の解決にはなっていませんでした。

そこで出来るだけ簡単にコンパイル時間の長さの体感だけ下げようと思い、保存したらスクリプトコンパイルをするようにVisual Studioに設定しました。

環境

  • Mac 2019 (Macにしか対応していません)
  • Unity Hub

やり方

【Unity】ソースコード編集->エディタプレイの手間を減らす(Mac) - Flat Leon Works
こちらの記事に書いてありました。

スクリプトコンパイルさせる方法は

AssetDatabase.Refresh();

を行えば良いみたいです。

これをVisual Studioへ入力していきます。
f:id:TAKOYAKING:20200305170741p:plain
※パスはUnity2019.3.3を利用しているので3.3となります。(変更必要)

タイトル: Refresh
コマンド: open
引数(A): /Applications/Unity/Hub/Editor/2019.3.3f1/Unity.app -g --args -executeMethod AssetDatabase.Refresh
現在のファイルを保存: On

解説

コマンドラインからUnityへコマンド実行させるコマンドは以下になります。
(保存後以下のコマンドを手動で実行しても同じことになります)

open /Applications/Unity/Hub/Editor/2019.3.3f1/Unity.app -g --args -executeMethod AssetDatabase.Refresh

openはアプリを開くコマンドで色々optionがあります。
-g: 背面にあるアプリを前面にしない
これをつけていないと保存のたびにUnityの画面に切り替わってしまいます。
--args: 引数 (-executeMethod AssetDatabase.Refresh)

Unityのコマンドラインのオプション
-executeMethod: メソッドを実行できる(ここにAssetDatabase.Refreshを指定)

懸念事項

Visual Studioの設定にすでにある?

設定にこれと同機能があるように見えるのですが、なぜか動作していません。もしかするとUnityHubを使っているせいかもしれません。UnityHubとUnity単体のパスは違うのでそのせいかもしれません。(想像です。)

Unityのアップグレードのたびにパスを変更する必要がある

今の所Unityまでのパスの取得方法は直接書いてしまっているので、もし2019.4とかになるといちいち書き直す必要があります。

Visual Studioで今開いているUnityのパスが取得できたら良いのですが、全く調べていないので、わかりません。

コンパイル速度を速くするなら別の手段の方が良い

この方法はコンパイル速度を改善したわけではないので、根本的解決にはなっていません。ただ手軽に体感を改善できるので僕は気に入っています。

感想

保存した直後にすぐに切り替えると、やっぱり待たされる感はありますが、一呼吸置いて、切り替えた時は、設定していなかった時と比べ、イライラ度はかなり下がりました!

手軽に試せるので、コスパは良いです!

Rust: 型推論で型(タイプヒント)の部分省略

イテレーターの特定メソッドについて調べていたら、型を部分省略する方法があったのでメモします。

イテレータ

Rust 公式チュートリアルより抜粋

もしあなたが覚えているなら、 ::<> 構文で型ヒント(type hint)を与え、整数型のベクタが欲しいと伝えることができます。かといって常に型をまるごとを書く必要はありません。 _ を用いることで部分的に推論してくれます。

let one_to_one_hundred = (1..101).collect::<Vec<i32>>();
let one_to_one_hundred = (1..101).collect::<Vec<_>>();

Rustでは型は一部分だけ記述する記法も用意されていて、

Vecと書かずに
Vec<_>と書いて
「i32(T)」の部分は「_」で省略できるみたいです。

一応左辺verも用意

let one_to_one_hundred: Vec<_> = (1..101).collect();

感想

忘れそうなのでメモ。

Rust: enumのコンストラクターは関数のようにふるまえる

stringの限界 | Rust by Example
Rust Examplesより

map_errの引数は関数であるのにenumを直接ドーンと代入している箇所があって、どういうことか疑問に思ったので調べてみました。

現象

map_errの引数はFnOnce(E) -> Fであるのに関数の代わりにenumを渡してもきちんと処理されていた。

use std::num::ParseIntError;
use std::fmt;

type Result<T> = std::result::Result<T, DoubleError>;

enum DoubleError {
    // このエラーに関しては、エラーの詳細を説明するのに追加の情報は必要ない。
    EmptyVec,
    // パースに失敗した場合はParseIntErrorの挙動に追従する。追加の情報を持たせたい
    // 場合は、この型に、より多くのデータを追加しなくてはならない。
    Parse(ParseIntError),
}

fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first()
       // エラーを新しい型のものに変換
       .ok_or(DoubleError::EmptyVec)
       .and_then(|s| s.parse::<i32>()
            // ここでも新しいエラー型へとアップデートさせる。
            .map_err(DoubleError::Parse) // <=================ここ
            .map(|i| 2 * i))
}

検証

ドキュメントの型通りにmap_errを実装するなら

fn double_first(vec: Vec<&str>) -> Result<i32> {
    vec.first()
        .ok_or(DoubleError::EmptyVec)
        .and_then(
            |s| s.parse::<i32>()
            .map_err(|e|DoubleError::Parse(e))
            .map(|i| 2 * i)
        )
}

になると思ったし、これで問題なく動きます。
enumのタプル構造体を渡してもどちらでも動くのでもしかしてmap_errだけでなく、他でも動作するのではと思い、以下サンプルを試してみました。

#[derive(Debug)]
enum Osaka {
    Takoyaki(i32),
}

fn takoyaki<F>(f: F) where
    F: FnOnce(i32) -> Osaka
{
    println!("{:?}", f(7));
}

fn main() {
    takoyaki(Osaka::Takoyaki);
}

// 出力:Takoyaki(7)

できてしまいました・・・

検索すると出てきました
列挙型
以下抜粋

列挙型のコンストラクターは関数のようにも使うことができます

試しに意図的にエラーを起こして、エラーメッセージを読んでみます。

fn main() {
    println!("{}", Osaka::Takoyaki); // エラー
}

// fn(i32) -> Osaka {Osaka::Takoyaki}` cannot be formatted with the default formatter

すると型が判明し、fn(i32) -> Osakaだということがわかりました。

なんかこんなのもありました。
Rust 1.40.0でtuple構造体とenum variantのコンストラクタがconst fnになる - Qiita


感想

すっ飛ばそうと思ったけど、ちゃんと調べてよかった!スッキリ!

Rust: エラー処理のための「Try!」と「?」

try!入門 | Rust by Example
こちらのサンプルコードで

fn setup() -> std::io::Result<()> {
    let mut a = try!(File::create("a"));
    try!(a.write_all(b"grape"));

    let mut b = try!(File::create("b"));
    b.write_all(b"fruit")
}

を実行すると

expected expression, found reserved keyword `try`

が出てしまいました。

環境

rustc 1.40.0

原因

サンプルコードは古いRustなのですが、今現在、新しい記法になっているためです。

対処方法

1. try!の代わりに「?」をつける
fn setup() -> std::io::Result<()> {
    let mut a = File::create("a")?;
    a.write_all(b"grape")?;

    let mut b = File::create("b")?;
    b.write_all(b"fruit")
}

try!の代わりに「?」が導入されているので手軽にエラー処理を行うことができます。

2. try!の代わりに「r#try!」をつける
fn setup() -> std::io::Result<()> {
    let mut a = r#try!(File::create("a"));
    try!(a.write_all(b"grape"));

    let mut b = r#try!(File::create("b"));
    b.write_all(b"fruit")
}

r#をつけることでtry!マクロをギリギリまだ使用できるみたいです。

感想

「?」便利だけどなんと呼べばいいのかわからないので「はてな」と呼べば良いのでしょうか?

tryが予約語になっていた理由ですが、
rfcs/2388-try-expr.md at master · rust-lang/rfcs · GitHub
ここに書いてあって、色々議論された故の予約語だったようです。

Rust: map VS and_then

Rustのmapとand_thenが最初見たときにどちらも加工するという処理なのに、どういう時に使いわけるかイメージできなかったので、まとめてみました。
(Option型にも使用できますが、例はResultにしています。)

map

returnの型: Result
functionの型: FnOnce(T) -> U (Resultではない)
functionはErrなら呼ばれず、Okなら呼ばれる

and_then

returnの型: Result
functionの型: F: FnOnce(T) -> Result (Result!)
functionはErrなら呼ばれず、Okなら呼ばれる

and_thenとmapのサンプル

コンビネータを組み合わせる | Rust by Example

// ファイルからデータを取り出し、値を`Result`として返す。
fn get_data(path: &str) -> Result<String> {
    File::open(path)
        .map_err(|err| err.to_string())
        .and_then(|mut file| {
            let mut contents = String::new();

            // データを`contents`に読み込む。
            file.read_to_string(&mut contents)
                .map_err(|err| err.to_string())
                // `read_to_string`の返り値は無視し、`contents`を返す。
                .map(|_| contents)
        })
}

// 二つのファイルの中身を結合し、新しい`Result`にして返す。
fn concat(filename_a: &str, filename_b: &str) -> Result<String> {
    let (data_a, data_b) = (get_data(filename_a), get_data(filename_b));

    data_a.and_then(|a|
        // `a`と`b`が両方とも`Ok`ならば`Ok`を返し、そうでなければ
        // 先に`Err`を挙げた方の`Err`を返す。
        data_b.and_then(|b| Ok(a + &b))
    )
}

上の例を見るとand_thenはfnの中身がエラーを起こる可能性がある時に使われ、mapはエラーの可能性がない時に使われています。

https://users.rust-lang.org/t/what-is-the-difference-between-map-and-and-then/29108/2
ここにもand_thenは失敗する可能性のある一連の処理の時に使用すると書いてありました。

まとめ

mapはエラーがない時に使う
and_thenはエラーが発生する可能性がある時に使う