黒い画面(コンソール)はじめの一歩

阪医python会の投稿用に書いています。

対象は計算科学始めたてでlsコマンドやcdコマンドはわかる人向けです。

黒い画面のススメ

黒い画面=コンソールで作業ができるようになると、キーボードからマウスへ手を動かす回数が減り仕事が捗ります。

ただ、初心者には難しいかもしれないので、最初のとっかかりの部分だけ説明したいと思います。

コンソールはOSX(mac)の場合はターミナル、Windowsの場合はGit bash(gitをインストールした時についているbash)やbash on windows(WindowsLinuxサブシステム)を想定しています(前者がオススメ)。

はじめの一歩

Tabボタンで入力補完!

コンソール画面で、コマンドを途中まで打って「tab」ボタンを押すとそれ以降の文字が補完されます。

cleまで打ってtabを押すと f:id:kyusque:20190127183954p:plain

最後まで補完されます f:id:kyusque:20190127184011p:plain

補完候補が複数あると画面上に表示されます(clまで打ってtabを押した場合) f:id:kyusque:20190127184025p:plain

open(explorer)コマンドでファイル、ディレクトリ(フォルダ)を開く!

今回は「コンソール画面だけ」で作業するのではなく「コンソール画面でより多く」作業をしてマウスでの操作を減らすことを目的としています。 黒い画面のライトユーザーです。

初めはコンソールでファイルやフォルダを開けるようになるだけで、大幅に時間が短縮できるのでまずはこれを覚えましょう。

OSX(Mac)の場合

ファイルやフォルダを開くコマンドはopenになります。

open .

と入力するとファインダーが開きます。

open hoge.docx

と入力すると、hoge.docxがワード(拡張子に対してデフォルトのアプリ)で開きます。

Windows(Git bash)の場合

ファイルやフォルダを開くコマンドはexplorerになります。

explorer .

と入力するとエクスプローラーが開きます。

explorer hoge.docx

と入力すると、hoge.docxがワード(拡張子に対してデフォルトのアプリ)で開きます。

もう一歩

aliasでオレオレコマンド

alias 別名='コマンド'と打ち込むと特定のコマンドを別名のコマンドで実行できるようになります。

経験的にtabボタンに左手の小指が乗ってる状態で補完をかけられるまで打ち込めるような名前にしておくと良いです。

例えば、windows の場合、先のexplorerのexが打ちにくいので、openにエイリアスしておくと使いやすくなります。

alias open='explorer'

ただ、このままだと再起動すると設定がリセットされます。 .bash_profileという名前のファイルをホームフォルダに作って(隠しファイルと拡張子が見えるように設定しておく必要)、置いておくと毎回打ち込む必要が無くなります。

.bash_profileファイル

alias open='explorer'

この他にも、OSX(mac)だと alias アプリ名='open -a アプリ名'にしておくとアプリを直接起動できるので便利だったりします。

alias rstudio='open -a RStudio'

色々やりやすいように改造してみてください

ユニティちゃんトリプルアクセル(6回転してしまっている)

こちらはUnity #2 Advent Calender 2018 24日目の記事です。

筆者は2018全日本フィギュアスケート最終日に観戦に行きます。

最終日は男子のフリーですが、現役復帰した高橋大輔選手が演技するのが楽しみです。

最近は女子でもトリプルアクセルを余裕で降りる選手が増え、年々レベルが上がってるのを実感します。

というわけで、前回は非実装だったトリプルアクセルを今回なんとか実装してみようかと思います。

概要

  • Unityちゃんにトリプルアクセルをやってもらう。
  • アニメーションを作り込んで行くのはしんどいので、Final IKと組み合わせて少ない変数で制御する
  • モーキャプという手もあるが、陸地で足元をやるのは限界があるし、そもそも筆者は氷上でもトリプルアクセルを跳べない
  • 最終的にはプロシージャルアニメーションみたいな感じでやりたい(願望)
  • 上半身の動きは今回考えない(モーキャプでブレンドすることを想定)

Final IKで足の曲がりをいい感じにしてもらう

膝の曲げなどの設定をしていくのを出来るだけ減らしたいのでFinal IKのLeg IKを設定します。

左右の足にIKをきかせたいので、UnityちゃんのモデルにふたつLeg IKをアタッチします。

f:id:kyusque:20181224132541p:plain

で、Targetにつま先、Bend Goalに膝のIKのターゲットをとりたいのでUnityちゃんと同じ階層に左右の足のターゲットと、そのターゲットの子オブジェクト(膝用)をつま先の前方におきます

f:id:kyusque:20181224132246p:plain

トリプルアクセルの途中動作を書いていく

で、下のようなスクリプトを書いてUnityちゃんとIKターゲットの親オブジェクトにアタッチします。

基本的に3つのオブジェクト(Unityちゃん、左右のIKターゲット)の向きと高さで制御できるようになっています。

using System.Collections;
using System.Collections.Generic;
using UnityEditor;
using UnityEngine;
using UnityEngine.UI;

public class FootManager : MonoBehaviour
{
    [SerializeField] private Transform character;
    [SerializeField] private Transform rightTarget;
    [SerializeField] private Transform leftTarget;

    private void OnGUI()
    {
        if (GUILayout.Button("Preparation"))
        {
            character.localEulerAngles = new Vector3(0f, 90f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, 180f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, 90f, 0f);

            character.localPosition = new Vector3(0f, -0.01f, 0f);
            rightTarget.localPosition = new Vector3(0.1f, 0f, -0.05f);
            leftTarget.localPosition = new Vector3(0.1f, 0.1f, 0.1f);
        }

        if (GUILayout.Button("Steping"))
        {
            character.localEulerAngles = new Vector3(0f, 45f, 0f);
            rightTarget.localEulerAngles = new Vector3(45f, 90f, 90f);
            leftTarget.localEulerAngles = new Vector3(0f, 0f, 0f);

            character.localPosition = new Vector3(0f, -0.09f, 0f);
            rightTarget.localPosition = new Vector3(0.5f, 0.5f, -0.5f);
            leftTarget.localPosition = new Vector3(0.1f, 0f, 0.1f);
        }

        if (GUILayout.Button("TakingOff1"))
        {
            character.localEulerAngles = new Vector3(0f, 0f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, 0f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, 0f, 0f);

            character.localPosition = new Vector3(0f, -0.09f, 0f);
            rightTarget.localPosition = new Vector3(0.3f, 0.1f, 0.2f);
            leftTarget.localPosition = new Vector3(0.1f, 0f, 0.1f);
        }

        if (GUILayout.Button("TakingOff2"))
        {
            character.localEulerAngles = new Vector3(0f, -45f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -45f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -45f, 0f);

            character.localPosition = new Vector3(0f, -0.02f, 0f);
            rightTarget.localPosition = new Vector3(0.0f, 0.3f, 0.4f);
            leftTarget.localPosition = new Vector3(0.1f, 0.0f, 0.0f);
        }

        if (GUILayout.Button("TakingOff3"))
        {
            character.localEulerAngles = new Vector3(0f, -90f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -90f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -90f, 0f);

            character.localPosition = new Vector3(0f, 0.1f, 0f);
            rightTarget.localPosition = new Vector3(0.0f, 0.3f, 0.4f);
            leftTarget.localPosition = new Vector3(0.1f, 0.12f, 0.0f);
        }

        if (GUILayout.Button("TakingOff4"))
        {
            character.localEulerAngles = new Vector3(0f, -180f, 0f);
            rightTarget.localEulerAngles = new Vector3(0f, -190f, 0f);
            leftTarget.localEulerAngles = new Vector3(0f, -190f, 0f);

            character.localPosition = new Vector3(0f, 0.27f, 0f);
            rightTarget.localPosition = new Vector3(0.05f, 0.3f, 0.1f);
            leftTarget.localPosition = new Vector3(0.0f, 0.33f, 0.0f);
        }


        if (GUILayout.Button("AirPosition1"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.28f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.3f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.33f, 0.1f);
        }

        if (GUILayout.Button("AirPosition2"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, 0.3f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.32f, 0.1f);
            leftTarget.localPosition = new Vector3(-0.05f, 0.35f, 0.0f);
        }

        if (GUILayout.Button("AirPosition3"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.25f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.3f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.33f, 0.1f);
        }

        if (GUILayout.Button("AirPosition4"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, 0.2f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.25f, 0.1f);
            leftTarget.localPosition = new Vector3(-0.05f, 0.28f, 0.0f);
        }

        if (GUILayout.Button("AirPosition5"))
        {
            character.localEulerAngles = new Vector3(0, 0, 0);
            rightTarget.localEulerAngles = new Vector3(0, -20, 0);
            leftTarget.localEulerAngles = new Vector3(0, -20, 0);

            character.localPosition = new Vector3(0, 0.15f, 0);
            rightTarget.localPosition = new Vector3(-0.05f, 0.2f, 0.0f);
            leftTarget.localPosition = new Vector3(0.05f, 0.23f, 0.3f);
        }

        if (GUILayout.Button("Landing"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -200, 0);
            leftTarget.localEulerAngles = new Vector3(0, -200, 0);

            character.localPosition = new Vector3(0, -0.07f, 0);
            rightTarget.localPosition = new Vector3(0.05f, 0.0f, 0.1f);
            leftTarget.localPosition = new Vector3(0.45f, 0.03f, -0.2f);
        }

        if (GUILayout.Button("Check"))
        {
            character.localEulerAngles = new Vector3(0, -180, 0);
            rightTarget.localEulerAngles = new Vector3(0, -180, 0);
            leftTarget.localEulerAngles = new Vector3(0, -240, -90);

            character.localPosition = new Vector3(0, -0.17f, 0);
            rightTarget.localPosition = new Vector3(0.1f, 0.0f, 0.2f);
            leftTarget.localPosition = new Vector3(0.46f, 0.4f, 0.7f);
        }
    }
}

f:id:kyusque:20181224133536p:plainf:id:kyusque:20181224133541p:plainf:id:kyusque:20181224133548p:plainf:id:kyusque:20181224133553p:plainf:id:kyusque:20181224133559p:plainf:id:kyusque:20181224133605p:plainf:id:kyusque:20181224133609p:plainf:id:kyusque:20181224133615p:plainf:id:kyusque:20181224133621p:plainf:id:kyusque:20181224133626p:plainf:id:kyusque:20181224133631p:plainf:id:kyusque:20181224133638p:plainf:id:kyusque:20181224133644p:plain

コルーチンでLerpを使いながら実行

では上の変数を参考に補完も加えながら、コルーチン化してみたいと思います。

以下がコードです。

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

public class FootManager2 : MonoBehaviour {

    [SerializeField] private Transform character;
    [SerializeField] private Transform rightTarget;
    [SerializeField] private Transform leftTarget;
    
    
    // Use this for initialization
    void Start () {
    }
    
    // Update is called once per frame
    void Update () {
        
    }

    private void OnGUI()
    {
        if (GUILayout.Button("Triple Axel"))
        {
            StartCoroutine(TripleAxel());
        }       
    }

    IEnumerator TripleAxel()
    {
        Vector3[] char_tmp;
        Vector3[] right_tmp;
        Vector3[] left_tmp;
        int span;
        
        //Preparation
        character.localEulerAngles = new Vector3(0, 90, 0);
        rightTarget.localEulerAngles = new Vector3(0, 180, 0);
        leftTarget.localEulerAngles = new Vector3(0, 90, 0);

        character.localPosition = new Vector3(0, -0.01f, 0);
        rightTarget.localPosition = new Vector3(0.1f, 0, -0.05f);
        leftTarget.localPosition = new Vector3(0.1f, 0.1f, 0.1f);
        
        for (int i = 0; i <= 10; i++)
        {
            yield return 0;
        }
                
        //Steping

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 45, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(45, 90, 90), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, 0, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.09f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.5f, 0.5f, -0.5f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0, 0.1f), (float)i / span);
            yield return 0;
        }

        
        //TakingOff1

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, 0, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.09f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.3f, 0.1f, 0.2f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0, 0.1f), (float)i / span);
            yield return 0;
        }

        //TakingOff2

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -45f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -45f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -45, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, -0.02f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.0f, 0.3f, 0.4f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0.0f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //TakingOff3

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -90f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -90f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -90, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, 0.1f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.0f, 0.3f, 0.4f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.1f, 0.12f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //TakingOff4

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0f, -180f, 0f), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0f, -190f, 0f), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0f, -190f, 0f), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0f, 0.27f, 0f), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.3f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.0f, 0.33f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition1

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.28f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.3f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.33f, 0.1f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition2

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.3f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.32f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(-0.05f, 0.35f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition3

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.25f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.3f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.33f, 0.1f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition4

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i < span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.2f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.25f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(-0.05f, 0.28f, 0.0f), (float)i / span);
            yield return 0;
        }
        
        //AirPosition5

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, 0, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -20, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -20, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, 0.15f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(-0.05f, 0.2f, 0.0f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.05f, 0.23f, 0.3f), (float)i / span);
            yield return 0;
        }
        
        //Landing

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -200, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -200, 0), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.07f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.05f, 0.0f, 0.1f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.45f, 0.03f, -0.2f), (float)i / span);
            yield return 0;
        }
        
        //Check

        span = 3;
        char_tmp = new[] {character.localEulerAngles, character.localPosition};
        right_tmp = new[] {rightTarget.localEulerAngles, rightTarget.localPosition};
        left_tmp = new[] {leftTarget.localEulerAngles, leftTarget.localPosition};
        
        for (int i = 0; i <= span; i++)
        {
            character.localEulerAngles = Vector3.Lerp(char_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            rightTarget.localEulerAngles = Vector3.Lerp(right_tmp[0], new Vector3(0, -180, 0), (float)i / span);
            leftTarget.localEulerAngles = Vector3.Lerp(left_tmp[0], new Vector3(0, -240, -90), (float)i / span);

            character.localPosition = Vector3.Lerp(char_tmp[1], new Vector3(0, -0.17f, 0), (float)i / span);
            rightTarget.localPosition = Vector3.Lerp(right_tmp[1], new Vector3(0.1f, 0.0f, 0.2f), (float)i / span);
            leftTarget.localPosition = Vector3.Lerp(left_tmp[1], new Vector3(0.46f, 0.4f, 0.7f), (float)i / span);
            yield return 0;
        }
        
    }
    
}

動画

あれ!!!6回転以上してる!!!???

多分、Lerpで回りすぎているみたいです。この辺りを調査しないと…誰か……

プロシージャルアニメーションにしたいんだったらここを詰めないといけないかもです。

ただ、もうそろそろ男子フリーが始まるので今回はここまで!!

シングルセル解析ソフトScanpyを試してみる

この記事は創薬 Advent Calendar 2018 17日目の記事です。

シングルセル解析ソフトScanpyを試してみる

PythonのシングルセルRNA-seq解析ツールであるところのScanpy阪大医学部Python会@yyoshiakiさんに教えてもらったので、試してみました。

RだとSeuratというパッケージがいいらしいですが、Pythonの方をプッシュされたのでそちらを行いました。

筆者の知識レベル

  • in vitroの実験はやったことない。
  • FACSは同期がやってるので概要はなんとなく知ってる
  • RNA-seqはほぼ知識ゼロ

チュートリアル

最初のチュートリアルZheng 2017をやりました。

論文の概要

ドロップ型のscRNA-seqシステムの開発

サンプル中の細胞をひとつひとつ高い確率でキャプチャでき、高速にmRNAを数えることができる。

  • droplet内に一つずつ細胞を入れ、その細胞のmRNAをエマルジョン(a Gel bead in EMulsion, GEM)内でまるっと数えることができる(一細胞RNA-seq)
  • cDNAへの逆転写はGEM内でバルクで進行する
  • GEMごとのプライマーにバーコード(14bp)がついてる
    • どのRNAがどの細胞由来かわかる
  • GEMごとのバーコードに加え、さらにプライマーごとにランダムなUMI(unique molecular identifier, 10 bp)がついてる
    • UMIの数を数えることで、その遺伝子の数がわかる。
    • UMIにはサンプルに対応するIDも入ってるので、複数サンプルを平行に処理することが可能
  • タグ(バーコード、UMI)がついてるので、PCR増幅はGEMを壊してまとめて行える

元文献では色々なサンプルを使って測定したデータを使って解析してましたが、今回はperipheral blood mononuclear cells (PBMCs) のクラスタリングチュートリアルで行っていました。

チュートリアルのコード(改変あり)

元データ

1細胞データ

遺伝子発現ラベル(バルク)

全ファイル同じフォルダに入れてください

本家(scanpy == 1.0.0)とscanpyのバージョンが違い(scanpy == 1.3.6)、できないものがあったので、できたやつだけ書きました。

各種パッケージのインポート

import numpy as np
import pandas as pd
import matplotlib.pyplot as pl
import scanpy.api as sc

sc.settings.verbosity = 1  # verbosity: errors (0), warnings (1), info (2), hints (3)
sc.settings.set_figure_params(dpi=80)  # low dpi (dots per inch) yields small inline figures
sc.logging.print_versions()
scanpy==1.3.6 anndata==0.6.16 numpy==1.14.2 scipy==1.2.0 pandas==0.23.4 scikit-learn==0.20.2 statsmodels==0.9.0 

データの読み込み

%%time
adata = sc.read('matrix.mtx', cache=True).T  # transpose the data
adata.var_names = pd.read_csv('genes.tsv', header=None, sep='\t')[1]
adata.obs_names = pd.read_csv('barcodes.tsv', header=None)[0]
Variable names are not unique. To make them unique, call `.var_names_make_unique`.


CPU times: user 1.16 s, sys: 386 ms, total: 1.54 s
Wall time: 1.65 s
adata
AnnData object with n_obs × n_vars = 68579 × 32738 

重複した名前をユニークにする

adata.var_names_make_unique() #最初からユニークでは?
adata
AnnData object with n_obs × n_vars = 68579 × 32738 

バルク(1細胞じゃない)での遺伝子発現ラベルの読み込み

adata.obs['bulk_labels'] = pd.read_csv('./zheng17_bulk_lables.txt', header=None)[0].values

変数を減らして、正規化後、Cell Rangerでフィルタリング

記事筆者はCell Rangerについて何も知らない(力尽きた)

%%time
sc.pp.filter_genes(adata, min_counts=1)  # only consider genes with more than 1 count
sc.pp.normalize_per_cell(adata)          # normalize with total UMI count per cell
filter_result = sc.pp.filter_genes_dispersion(
    adata.X, flavor='cell_ranger', n_top_genes=1000, log=False)
CPU times: user 2.07 s, sys: 979 ms, total: 3.04 s
Wall time: 3.08 s

プロット

対数プロットになるらしいが……

sc.pl.filter_genes_dispersion(filter_result, log=True)

f:id:kyusque:20181220163745p:plain

PCAによる次元圧縮

%%time
adata = adata[:, filter_result.gene_subset]  # filter genes
sc.pp.normalize_per_cell(adata)  # need to redo normalization after filtering
sc.pp.log1p(adata)  # log transform: X = log(X + 1)
sc.pp.scale(adata)
# the PCA is *not* contained in the recipe sc.pp.recipe_zheng17(adata)
sc.tl.pca(adata, n_comps=50)
CPU times: user 5.54 s, sys: 668 ms, total: 6.21 s
Wall time: 3.18 s

PCAのloadingのプロット

便利。

記事筆者が知らない遺伝子ばっかりだ…

sc.pl.pca_loadings(adata)

f:id:kyusque:20181220163837p:plain

K meansによるクラスタリング

1細胞のmRNAからの情報のみによる分類でも、バルクのグループに分かれてますね。

%%time
sc.pp.neighbors(adata)
CPU times: user 20.5 s, sys: 4.07 s, total: 24.6 s
Wall time: 21.7 s
%%time
sc.tl.umap(adata)
CPU times: user 56 s, sys: 615 ms, total: 56.6 s
Wall time: 49.4 s
sc.pl.umap(adata, color='bulk_labels')
... storing 'bulk_labels' as categorical

f:id:kyusque:20181220163901p:plain

メモ

  • 他にもマーカー遺伝子を取り出す手法などありましたが、現バージョンでそのままできなかったので今回は諦めました。
  • 移植後の患者からの細胞群の解析(こういう実験はin vivoに入るのでしょうか?in vitro?)など元データの文献自体も面白そうだったので、輪読に使えるのかなーと思います。
  • 元文献を読んでると知らない知識が多く、質問事項がえらいことになったので、おいおい勉強していこうと思います。

各種手袋によるホロレンズのジェスチャー認識の検証

この記事はHoloLens Advent Calendar 2018 5日目の記事になります。

 

先日Hololensの開発をしてて思ったのですが、手袋着けててもジェスチャー操作できるのが結構便利なことに気づきました。

ただ、モノによってできたりできなかったりがあるので、今回、どんな手袋のジェスチャーがHololensで認識できるのか否かを検討してみることにしました。

HoloLensのジェスチャー認識機能

基本のジェスチャーはタップとブルームになります。

これらを各種手袋で認識するか試します。

普通の手袋

よく見る普通の手袋

f:id:kyusque:20181209165810j:plain

f:id:kyusque:20181209164005g:plain

 

タップ、ブルームともに問題なくできます。

 

グローブ

バイクとか乗るときに使うあったかいグローブ

 

f:id:kyusque:20181209170204j:plain

 

f:id:kyusque:20181209164232g:plain

 

タップは問題なくできますが、ブルームが認識しづらいです。

 

ゴム手袋

掃除用の洗剤を使うにつけるゴム手袋

f:id:kyusque:20181209170214j:plain

 

f:id:kyusque:20181209164127g:plain

 

タップは問題なくできますが、ブルームが少し認識しづらいです。

 

ビニール手袋

毛染めとかによくついてる安っぽい手袋

f:id:kyusque:20181209170137j:plain

 

f:id:kyusque:20181209164413g:plain

 

タップ、ブルームともに問題なくできます。

 

実験用手袋

実験用の手袋

f:id:kyusque:20181209170149j:plain

 

f:id:kyusque:20181209164758g:plain

 

タップ、ブルームともに問題なくできます。

 

汚い軍手

汚い軍手

 

f:id:kyusque:20181209170224j:plain

 

f:id:kyusque:20181209164926g:plain

 

タップ、ブルームともに問題なくできます。

 

野球のグローブ

内野用グローブ。ワンチャンでブルームはいけるのでは?

f:id:kyusque:20181209170234j:plain

f:id:kyusque:20181209171128g:plain

無理でした!!

薬学関連で計算科学をやっていると良く聞かれる質問集

  • 2018/12/06 宇宙海賊コブラ さん @TGrlmP3I4TRy023 からいただいた質問を追加しました

こちらは創薬 Advent Calendar 2018 3日目の記事です。

一応、薬学系の研究室で計算化学を使って研究している者です。

応用的な理論研究をやっていると良く実験系の人とコラボすることが多いですが、分野の違う人の前で発表するとプリミティプな質問や答えに窮する質問が飛んできます。

対応できるものやそもそも現状無理なものまでたくさんあります。

筆者が一番心に残っているものは、学部時代に実験の先生から言われた「絵に描いた餅は食えない(理論)」という言葉です。 すぐに「昔はそうやったが、最近は食べられるようになってきたのかなと思う」というフォローが入りましたが、含蓄のある言葉です。 実際、計算しただけでは意味が無く、計算した結果を実験や実世界にどう生かすのかも考えるのが重要に思います。 そのためには、自分の分野以外の人の研究も把握して、自分の研究がどういう立ち位置にあるかを理解しておくのが重要です。 餅の絵が欲しいから計算をやっているわけではなく、餅が食べたいから計算をやってるという意識で研究していきたいですね。 実験系にフィードバックするまでが計算科学です。

今回は、自分の分野や他の分野の人から受けた質問や聞いたことのある質問を思い出せる限り書いていきます。 対策とかはないです。

もし、コメントで他の質問をいただけたら、名前も含めて追記していきます。

薬学関連の計算科学の研究をしていると良く聞かれる質問集

普通の質問

この研究は意味ある?

良くある質問ですがこの質問をされる場合、発表の対象設定が間違えてるかもしれません。

この計算意味ある?

「無」という結果を発表した場合、このような質問が飛び出るかもしれません。

この計算研究として成立してる?

一つだけ計算やって、コントロール的な対象がなければこうなるかもしれません。

どういう風に論文にするんですか

多分本当に分野違いの人の純粋な疑問か、あるいは、目的が不明瞭な場合

新規性は何?

前のやつでもできるのでは?違いを教えて

研究をやって行く中で一番難しかった所はどこですか?

優しい質問。筆者も学部生に対して良くやる。

前任者はこう言ってましたが〜?

卒業した先輩のテーマでやってるとこんな質問が飛んできます。 はやく先輩を超えましょう。

その結果からはそういう結論にはならないと思いますが。

その先生の専門分野で地雷を踏んだかもしれません。 ただ詳しく聞けば、いい感じに直してくれるかもしれません。

オーバートークでは?

検討できていない条件があるっぽいです。

倫理上問題ないの?

臨床データの扱いには注意しましょう

ビジネスにはしづらいな。

お金にならない

これはアカデミアでやらなくてもいいのでは?

もっと基礎研究に寄せよう。

発表についての質問コメント

ストーリーがわかりづらい

事実の羅列だけで科学になるから、ストーリーなどいらない。そう考えていた時期もありました。

スライドが重い。

字が多すぎる

字が小さい。

字が小さい

私は分かったけど大多数はわからなかったと思う。

チェアマンの背景知識に合わせた発表にするのがいいとは言いますよね。

実験系の質問

実験的な裏付けはないんですか?

あるといいですね。

実験における先行研究の結果との整合性は?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

整合性が取れてないと実験系の前で発表するのは厳しいかもです。 実験系で信じられていることを計算のみで覆して説得するのはとても難しい。

なぜ実験ではなく計算なのか?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

根源的な質問で、状況によって答えが様々に変わる質問。

その人によってどこまで言えば納得してくれるかも分からないことが多く、ヒアリングが必要になってくるなため、『あとで話しましょう』になりがちな質問。 あとで話しましょう。

ただ、発表側からすると質問してくれるだけありがたいので、本当に気になった場合は積極的に使っていましょう。

返答例いろいろ

  • 実際の現象をシミュレーションで直接みることができる。
    • 生物系の人に対しては良く通じる言い方
    • 「何をみてるか」が明らか
    • その現象をみてると証明するためにポジコンとったりネガコンとったり苦労しているところは価値を分かってくれる
  • 測定に伴う撹乱要因を排除できる。
    • 測定機器や試薬による変化を取り除くことができる。
    • 実験では(科学的議論においては)それらをコントロール実験で排除するが、繊細すぎて取り除けないものが存在。
    • シミュレーションではその現象のみに絞って結果をだせる。
    • 物性系の人は機器分析でその現象そのものが見れてると思いがち(ToDo:適切な文章に)
  • 意思決定に役出つ
    • 科学的な議論はできなくとも、結果から今後の実験の計画を立てることができる。

他の例はないですか?

無かったら無いっていうしかない。 あるのであれば、最初からいうべきであった。

私はそうは思いません。

バトルの始まりです。 ただ、大抵の場合、計算条件の説明が足りなかったり、結論がデカすぎるだけなので、うまく条件を絞っていきましょう。

実験でやってみないん?

時間があれば。卒業する人にこれを言ってもという感じではある。

うちにはお金も人でもないからそれはできない

なんか言い方が悪かった部分あった可能性があります。 実験系は大変です。手伝ってもらってることに感謝しながら発表しましょう。

後付けの理論のように思いますが?

実験で部分的しにか検証できないことを、計算で補完または示唆できる例をあげましょう。

実験で分かっている(とされている)ことの紹介の仕方が悪かったのかもしれません。

何作ればいい?

何か提案しましょう。

これはすぐには作れない。

合成可能性も考えましょう。

ドクターには進むの?

「ドクター発表で最終結果を聞くから首を洗って待ってろ」の言い換え

計算科学系でも微妙に背景の違う人からの質問

これが分かって何かいいことはあるの?

目的と実験設定が乖離している?

計算科学で同じ分野の人

ターゲット選定間違えてない?

なぜこのターゲットを選んだか説明しましょう。 チャレンジングだから、でも良かったりします。

このパラメータおかしくない?

説明しましょう

なぜこの基底関数選んだの?

(from 宇宙海賊コブラ さん @TGrlmP3I4TRy023)

量子化学計算をやっていると聞かれる質問。 ガウス基底や平面波基底などがあって、系によって最適なものが異なる。

そもそもこの計算手法ではその現象は説明できないのでは?

やばい

別の手法でも試した?

できればやる。できなければ、できない理由を説明する。

ここを説明できないと、メジャーリビジョン以上ですよ。

リジェクト

追加募集中!

他に色々ある人はコメントしていただければ、名前も含めて追加します。

真夏のアイススケート!!! in Unity

真夏のアイススケート!!! in Unity

この記事は「Unity アセット真夏のアドベントカレンダー 2018 Summer! 」 の18日目の記事になります。

暑いならスケートリンクにいこう

今年は記録的な猛暑で、暑さにやられてしまった人も多いかと思います。 そんな時、冷房がガンガンに効いたアイススケート場にいくのもありかと思います。 今回のアドカレではUnityちゃんこと大鳥こはくちゃんにテーマソングのUnite In The Skyでフィギュアスケートを滑ってもらいました。

f:id:kyusque:20180818114937p:plain

シーン上にIceHockeyStadiumとUnityちゃんを配置

アイススケートリンクのアセットはいくつかありましたが、その中で一番手ごろなものを選びました。

観客席や天井が作りこまれていていい感じです

f:id:kyusque:20180817122923p:plain

とりあえず、制服ユニティちゃんに氷の上に立ってもらいました。ここから最初の動画を作るための設定をしていきます。

f:id:kyusque:20180817123311p:plain

StadiumとScoreboardの影を消す

天井の柱の影が落ちてしまっているので、StadiumとScoreboardの[Mesh Senderer]-[Ligeting Cast Shadows]をOffにします。

f:id:kyusque:20180817123630p:plain

いい感じにライトを設定します

そのままだと、顔が暗くなってしまうので適切にライトを設定します。 とりあえず、天井から真上と横4方向からのDirectional Lightを合計5個作りました。

Rotationは 真上(91,1,1) 横1(20,0,0) 横2(20,90,0) 横3(20,180,0) 横4(20,270,0) と設定しました。 Intensityは0.25にしました。

このあたりもっとうまい方法があるような気がするのですが、アドバイスありましたらよろしくお願いします。

設定前

f:id:kyusque:20180817130754p:plain

設定後

f:id:kyusque:20180817130818p:plain

だいぶ、顔が見えるようになりました、氷上だともっと化粧が濃い方がいいのですが、私にはUnityちゃんに化粧してもらう技術がないためそのまま進みます。

ProBuilderで関係ない器材を消す

今回はフィギュアスケートなのでホッケー関係の器材を消していきます。 もともとホッケースタジアムということもあり、メッシュにそのままついていますが、今回は関係ないので消していきます。消すのはゴールと氷に書かれたライン、保護用の透明な壁です。

Unity 2018からUnityに統合されたアセットのProBuilderをつかってメッシュを改変していきます。

f:id:kyusque:20180817131106p:plain

ProBuilderのインストール

[Window]-[Package Mnager]を開いてProBuilderをインストール

f:id:kyusque:20180817122632p:plain

実際に消していく

IceHockeyStadiumのPitchを選択した状態で[Tools]-[ProBuilder]-[Object]-[Pro Builderize]すると、Pb_Objectがアタッチされます。

f:id:kyusque:20180817131635p:plain

Open ProBuilderを押してエディットモードを開きます。

開いた後、画面上の四角のシンボルが書かれたボタン(Face Selection)を押します。

f:id:kyusque:20180817132022p:plain

適当な面を選んでProBuilderウインドウのSelect Face Ring やSelect Face Loopやを押すと選択範囲が広がります。全体が選ばれた状態になったらDelete Facesを押して消していきます。

消去前

f:id:kyusque:20180817132451p:plain

消去後

f:id:kyusque:20180817132544p:plain

綺麗になりました。

f:id:kyusque:20180817135830p:plain

靴を履かせる

氷上に普通の靴で歩くのは滑って危ないので、スケート靴を履いてもらいます。

アッセトストアにはホッケー用の靴しかなかったので、とりあえずこれを履いてもらいました。

モデリングはまだよくわからないので、Unityちゃんのfoot下にプレハブをアタッチしました。

f:id:kyusque:20180817174741p:plain

アニメーションの編集をする

Perception Neuronを触らしてもらう機会があったのですが、そのときにモーションを取っていたのでこれを使います。

f:id:kyusque:20180817175417p:plain

Perception Neuronからエクスポートした直後だとfbxファイルのRigがGeneralなのでHumanoidに変更後Applyします。

f:id:kyusque:20180817175428p:plain

開始時間は終了時刻はfbxファイルのインスペクターのAnimationタブで編集できます。

f:id:kyusque:20180817175438p:plain

新しくanimatorを作ってStateにモーションを付けたあと、Unityちゃんにアタッチします。

途中結果

Curvy Splineでスケートっぽく平行移動させる

アイススケートは踊りながら移動していく競技なので、普通に陸上でモーションキャプチャしたものでは移動量が足りないです。

移動経路の設定は今回は手動でやるのですが、これを簡単に設定できるソフトのCurvy Splinesを利用しました。

アセットをインポートすると画面上にボタンが表示されます(Preferenceで消せる)

f:id:kyusque:20180817194908p:plain

Draw Splineを押した後、Ctl+左クリックで制御点を作っていきます。 するとオブジェクトとしてCurvy Splineが生成されます。 これを適宜動かしてカーブを作ります。

f:id:kyusque:20180817195350p:plain

作り終えたら、UnityちゃんにSpline Controllerをアタッチし、[General]-[Spline]に先ほどのCurvy Splineをドラッグ&ドロップします。 とりあえず、動かしたければMoveのところを設定すれば、このカーブに沿って動いてくれます。

今回はこれだけですが、Eventなどでも制御できそうで使いでは良さそうです。

f:id:kyusque:20180817195537p:plain

音楽を付けて完成!

Audio Clipをアタッチして曲を付けたものがこちらです。

今後

今回、出来合いのモーションを使ってそれらしい動画を作ることが出来ました。

今後は、IKなども組み合わせて、Unityちゃんの目線や腕を調整してもっとプレゼンスを高められたと思います。 本来は観客アピールなんかも考えるべきなので、そのあたりモーションキャプチャの後でも調整できるようにしたいと思います。

また、ステップの種類が少なかったりエッジの向きと移動が微妙にあっていなかったりで、Unityちゃんが下手に見えてしまっているので、何とかしたいと思います。

あと、衣装なんかの揺れものはスケート自体の移動量が大きい分良く映えると思います。 現実にはありえないエフェクトなんかも入れられそうです。 この記事を見て何かを作るのに特に制限はないので、これはというのがあればじゃんじゃん作っていってください。 大好物です。