トーフメモ

主にゲーム制作

【CUBASE】楽譜の音部記号や調号を変更する

楽譜作成時に結構調べたので備忘録として残します。Windowsでの編集なのでMacだと少し違うかもしれません。
また、Cubase AI 9.5での編集になります。

楽譜はデフォルトでト音記号、Cスケールに設定されているので、「ヘ音記号など別の音部記号に変更したりするとき」「楽譜に調号を付けたいとき」どこを触れればいいのかを説明します!

スコアエディタを開く

f:id:tofgame:20190726172053p:plain
スコアエディタを開き何かしら打ち込みます。

譜表の設定を開く

f:id:tofgame:20190726172401p:plain
スコアエディタをアクティブにした状態(白枠が付きます)で画面上部の「MIDI/スコア/譜表の設定」を開きます。

f:id:tofgame:20190726172518p:plain
すると、このような画面が開きます。ここで音部記号や調号の編集ができます。
ト音記号が表示されている左右のUIで変更することができます。
編集したら、画面下部の「適用」をクリックします。

f:id:tofgame:20190726172530p:plain
スコアエディタを確認し、変更されていればOKです。

ボタンとシーン遷移の実装【Processing】

f:id:tofgame:20190718174908p:plain
ボタンを押して別シーンに遷移する処理を作ります!

実装の流れ

ボタンを作る

マウスがボタンに重なると色が変わり、またクリック後に別シーンにうつるようなものを作ります。

シーンを作り、ボタンを押すと別シーンにうつるようにする

シーン用に変数を用意してシーン用変数が特定の値の時に特定のシーンの処理を実行する、という感じです。
これはswitch文などを使うとわりと簡単に実装できます。
ボタンに用意した関数を使ってシーンを切り替えます。

ボタンを作る

class Button {
  float x, y;
  float sizeX, sizeY;
  int state;

  color baseCol;
  float nb;
  float sb;
  float pb;

  String str;

  Button(float x, float y, float sizeX, float sizeY, color baseCol, String str) {
    this.x=x;
    this.y=y;
    this.sizeX=sizeX;
    this.sizeY=sizeY;
    this.baseCol=baseCol;
    this.str=str;

    nb=1; //normalBrightness
    sb=0.8; //selectBrightness
    pb=0.6; //pushBrightness
  }

  void run() {
    rogic();
    display();
  }

  void display() {
    noStroke();
    changeColor();
    rect(x, y, sizeX, sizeY);

    fill(0, 0, 100);
    textSize(30);
    text(str, x, y);
  }

  void rogic() {
    state=checkState();
  }

  //===================================================================
  boolean isPush() {
    if (checkState()==2) return true;
    return false;
  }

  int checkState() {
    if (!checkInMouse()) return 0;
    if (!mousePressed) return 1;
    return 2;
  }

  boolean checkInMouse() {
    if (mouseX>x-sizeX/2&&mouseX<x+sizeX/2) {
      if (mouseY>y-sizeY/2&&mouseY<y+sizeY/2) {
        return true;
      }
    }
    return false;
  }

  void changeColor() {
    switch(state) {
    case 0:
      fill(hue(baseCol), saturation(baseCol), brightness(baseCol)*nb);
      break;

    case 1:
      fill(hue(baseCol), saturation(baseCol), brightness(baseCol)*sb);
      break;

    case 2:
      fill(hue(baseCol), saturation(baseCol), brightness(baseCol)*pb);
      break;

    default:
      fill(0, 0, 0);
      break;
    }
  }
}

シーンを作る

Button button;

int scene=0;

void setup() {
  size(600, 600);
  rectMode(CENTER);
  colorMode(HSB, 360, 100, 100);
  textAlign(CENTER, CENTER);

  button=new Button(width/2, height/2, 200, 100, color(190, 100, 70), "PUSH!");
}

void draw() {
  background(0, 0, 100);
  fill(0, 0, 0);
  text("scene "+scene, width/2, height/4);

  switch(scene) {
  case 0:
    button.run();

    if (button.isPush()) {
      scene=1;
    }
    break;
  case 1:

    break;
  }
}

実行すると・・・

youtu.be

こんな感じです!

Processingでモーショングラフィックスを作るまで その3

f:id:tofgame:20190609024601p:plain
3回目です!

前回の記事はこちら
tofgame.hatenablog.com

今回作るもののイメージ

(1)文字が回転しながら中心に寄る
(2)全部の文字が中心に寄ったら文字が消える
(3)文字が消えた部分から丸が広がり文字が現れる

文字が回転しながら中心に寄る

文字用クラス「RotateText」を作成します。
中心点を基準に三角関数で位置を決めるイメージです。
半径と角度を変数定義しておき、それを変更することで回転、中心に寄るといった動作を実装します。

f:id:tofgame:20190609012614g:plain

全部の文字が中心に寄ったら文字が消える

半径が0になったタイミングでArrayListのRemoveを使って消します。
f:id:tofgame:20190609014937g:plain

文字が消えた部分から丸が広がり文字が現れる

circle関数を使って丸を書きます。
背景色を白、丸の色を黒にするので、文字色を背景色と同じにしてあたかも文字が自然に現れたような表現をします。

f:id:tofgame:20190609023249g:plain

完成したもの

f:id:tofgame:20190609023344g:plain

ソースコード

PFont font;
ArrayList<RotateText> movingTexts=new ArrayList<RotateText>();
GrowingCircle growingCircle;
Blur blur;
String displayText="MOTIONTHREE";

int scene;

void setup() {
  size(600, 600);
  textAlign(CENTER, CENTER);
  colorMode(HSB, 360, 100, 100, 100);

  font=createFont("Voynich_MonoSpace.ttf", 60);
  textFont(font, 60);

  for (int i=0; i<displayText.length(); i++) {
    movingTexts.add(new RotateText(String.valueOf(displayText.charAt(i)), width*1.1, i/(float)displayText.length()*360, new PVector(width/2, height/2)));
  }

  growingCircle=new GrowingCircle(new PVector(width/2, height/2), 0, 100);
  blur=new Blur();
}

void draw() {
  background(0, 0, 100);

  switch(scene) {
  case 0:
    if (keyPressed) {
      scene=1;
    }
    break;

  case 1:
    for (int i=movingTexts.size()-1; i>=0; i--) {
      movingTexts.get(i).run();

      if (movingTexts.get(i).removeFlag) {
        movingTexts.remove(i);
      }

      if (movingTexts.size()<=0) {
        scene=2;
      }
    }
    break;

  case 2:
    growingCircle.run();

    fill(0, 0, 100);
    text(displayText, width/2, height/2);

    blur.run();

    break;
  }
}

class RotateText {
  String str; //文字
  float radius; //半径
  float angle; //角度
  PVector midPos; //中心位置

  PVector pos; //位置

  boolean removeFlag;

  RotateText(String str, float radius, float angle, PVector midPos) {
    this.str=str;
    this.radius=radius;
    this.angle=angle;
    this.midPos=midPos;
  }

  void run() {
    rogic();
    display();
  }

  void rogic() {
    angle+=6;
    radius-=3;
    if (radius<=0) removeFlag=true;
    pos=RoundPos(midPos, radius, angle);
  }

  void display() {
    fill(0, 0, 0);
    text(str, pos.x, pos.y);
  }

  PVector RoundPos(PVector midPos, float radius, float angle) {
    return new PVector(midPos.x+sin(radians(angle+180))*radius, midPos.y+cos(radians(angle+180))*radius);
  }
}

class GrowingCircle {
  PVector pos;
  float size;
  float spd;

  GrowingCircle(PVector pos, float size, float spd) {
    this.pos=pos;
    this.size=size;
    this.spd=spd;
  }

  void run() {
    rogic();
    display();
  }

  void rogic() {
    size+=spd;
  }

  void display() {
    noStroke();
    fill(0, 0, 0);
    circle(pos.x, pos.y, size);
  }
}

class Blur {
  float angle;
  float time;

  void run() {
    time+=1;
    if (time>=60) {
      angle+=random(1, 10);
      filter(BLUR, abs(sin(radians(angle)))*4);
    }
  }
}

次回

近いうちに!

使ったフォント

aramugi.com

こちらもどうぞ

tofgame.hatenablog.com

映像作品でよく見るうごめくテキストを作る【Processing】

テキストがピントを合わせながら動いてる感じの表現を作りました。

できたもの

f:id:tofgame:20190608233747g:plain

ポイント

filter関数を使って、ブラーを画面にかけます!
filter関数の引数として0~6をループする変数を使うことでくっきり⇔ぼやけの流れを作ります。

使いどころ

街の中に出現させたりします。
f:id:tofgame:20190608234710g:plain

使った素材

ymnk-design.com

マウスが重なっているUIオブジェクトを取得する【Unity】

f:id:tofgame:20190428235106j:plain

やること

EventSystemを参照してマウスが指示している位置に重なっているUIオブジェクトを取得します!
取得するオブジェクトは、EventSystemのInspecterの下部にあるWindowのpointerEnterでも確認できます。

f:id:tofgame:20190603015447p:plain

手順

まずUnityEngine.EventSystemsをusingします。

using UnityEngine.EventSystems;

EventSystemを参照します。(アタッチするなりスクリプトで参照するなり)

private EventSystem eventSystem;
eventSystem=FindObjectOfType<EventSystem>();

currentSelectedGameObjectでUIオブジェクトを取得できます。

private gameObject mouse=eventSystem.currentSelectedGameObject;

使い道

シーン遷移時にフェード画像を一番上に重ねてクリックできないようにする

とか

ButtonじゃないUIをボタンみたいに動作させる

とか

いろんなシチュエーションで使えそうです!

Image(Canvas)の座標変更でハマった話【Unity】

f:id:tofgame:20190428235503j:plain

小一時間悩んだのでメモとして残します。

やりたいこと

スクリプトからCanvas下に作ったImageの座標を任意の位置に変更する。

失敗例

以下のパターンだとずれました。

transform.position= new Vector2((100,100));

transform.localPosition= new Vector2((100,100));

GetComponent<RectTransform>().position= new Vector2((100,100));

GetComponent<RectTransform>().localPosition= new Vector2((100,100));

成功例

GetComponent<RectTransform>().anchoredPosition= new Vector2((100,100));

なぜうまくいったのか

Imageのアンカーを変更していたので座標の基準点が変わっていました。

つまり、アンカーのことも考えないと座標が合わないということです。

なお、特にアンカーを変更せずにそのままの座標で合わせると、localPositionでもうまくいく時があります。

ですが、この場合でもlocalPositionとanchoredPositionの位置が同じなので、anchoredPositionでやると無難だと思います。

UnityでYoutubeの動画を再生する

f:id:tofgame:20190428235106j:plain

はじめに

Youtube、言わずと知れた動画サイトで、あらゆる動画コンテンツがあります。

そして、UnityでYoutubeの動画を再生できれば、自分で色々な機能を追加できると思われます。

データとして取れれば、Unity内で編集してMAD動画をつくれたり、ゲーム内で使えたり、バーチャル空間で再生なんかもできます!

夢がひろがりますね!

もちろん著作権など諸々の話はあるので注意が必要です。

※自己責任でお願いします!

注意点

Youtubeは動画のダウンロードを禁止しています。注意を払って実装しなければいけません。
www.youtube.com

なので、今回は「いったんデータを取得して、Unityの実行を終了したらそのデータを消す」という実装にしています。
また、UnityEditor上でしか動作しない実装になっています。

動作環境

OS : Windows10
Unityバージョン : 2018.3.7f1
VideoLibrary : 2.0.3

制作フロー

VideoLibraryをUnityに組み込む

Youtubeの動画を再生するためには、とにかくデータを取得する手段が必要です。
「VideoLibrary」というYoutubeの動画を取得できるNuGetパッケージがあるので、それを使用します。
www.nuget.org

UnityでNugetパッケージを使用する方法として以下のページを参考にしました。
qiita.com

VideoLibraryを使って動画データの取得

各種パッケージ
using System; //NonSerializedなど
using System.IO; //入出力
using VideoLibrary; //Youtubeデータ取得
using System.Threading.Tasks; //非同期処理
using UnityEngine.Video; //動画再生
using UnityEditor; //アセット操作
動画情報取得からの動画取得
 private string uri = "Youtubeの動画のURI";

[NonSerialized]
public string movieTitle;

[NonSerialized]
public string moviePath;

private async void Start () {
    var v = await GetVideoInforationAsync(uri);
    if(v == null) return;

    if(!(IsFormatSupport(v.FileExtension))) return;

    movieTitle = v.FullName;
    moviePath = Application.dataPath + @"/Movies/" + movieTitle;
    if(IsFileExist(moviePath)) return;

    var t = await DownLoadMovieFromYoutubeAsync(v);
    if(t == null) return;

    File.WriteAllBytes(moviePath,t);
    AssetDatabase.Refresh();
}

それぞれのフェーズでデータが取れているかをチェックしていきます。どこかで不具合があれば動画を取得しないようにします。

(1)URIから動画の情報を取得。

private async Task<YouTubeVideo> GetVideoInforationAsync (string uri) {
    try {
        var youTube = YouTube.Default;
        var video = await youTube.GetVideoAsync(uri);
        Debug.Log("動画情報を取得しました。");
        return video;
    } catch(Exception e) {
        Debug.Log("動画情報取得時にエラーが発生しました。:" + e);
        return null;
    }
}

(2)Unityに対応している動画のフォーマットはあらかじめ決まっているので、チェック。

private bool IsFormatSupport (string fileExtention) {
    string[] supportFormat = { ".asf",".avi",".dv",".m4v",".mov",".mp4",".mpg",".mpeg",".ogv",".vp8",".webm",".wmv" };

    for(int i = 0;i < supportFormat.Length;i++) {
        if(fileExtention.Equals(supportFormat[i])) {
            return true;
        }
    }

    Debug.Log("対応していない動画フォーマットです:" + fileExtention);
    return false;
}

対応フォーマットは以下のページに書いてあります。OSごとに対応するファイルが違うので注意です。
docs.unity3d.com

(3)Unity内に取得済動画があるかどうかチェック。

private bool IsFileExist (string path) {
    if(File.Exists(path)) {
        Debug.Log("ファイルが存在します。");
        return true;
    }

    return false;
}

(4)動画のデータを取得。

private async Task<byte[]> DownLoadMovieFromYoutubeAsync (YouTubeVideo y) {
    try {
        if(!y.IsEncrypted) {
            Debug.Log("動画の再生準備中です。少しお待ちください。");
            byte[] bytes = await y.GetBytesAsync();

            Debug.Log("完了しました!");
            return bytes;
        } else {
            Debug.Log("再生できない動画です。");
            return null;
        }
    } catch(Exception e) {
        Debug.Log("動画再生準備時にエラーが発生しました。:" + e);
        return null;
    }
}

IsEncryptedで暗号化されて取得できない動画をはじきます。

(5)動画データを一時的に保存。
File.WriteAllBytes()を使用します。

(6)動画データをUnityにアセットとして認識させる。
AssetDatabase.Reflesh()を使用します。
tofgame.hatenablog.com

ここまでで動画のデータの準備ができました!
あとは画面に映すだけです。

動画の再生

private VideoPlayer videoPlayer;
private VideoClip videoClip;
private bool moviePlayFlag;

private void Awake(){
    videoPlayer = GetComponent<VideoPlayer>();
}

private void Update () {
    if(!moviePlayFlag) {
        videoClip = AssetDatabase.LoadAssetAtPath(@"Assets/Movies/" + movieTitle,typeof(VideoClip)) as VideoClip;

        if(!IsFileExist(moviePath) || videoClip == null) return;
        videoPlayer.clip = videoClip;

        Debug.Log(videoPlayer.clip);
        videoPlayer.Play();

        moviePlayFlag = true;
    }
}

VideoPlayerを使用して動画を表示します。
docs.unity3d.com

動画データが取得でき次第VideoClipにして再生します。(雑な実装)

動画データの後始末

private void OnApplicationQuit () {
    videoPlayer.clip = null;

    if(IsFileExist(moviePath)) {
        File.Delete(moviePath);
        Debug.Log("動画ファイルを削除しました。");
    }

    if(IsFileExist(moviePath + ".meta")) {
        File.Delete(moviePath + ".meta");
        Debug.Log("メタファイルを削除しました。");
    }

    AssetDatabase.Refresh();
}

Unityの実行を止めるタイミングで残っている動画データを削除します。
metaファイルも生成されているので、それも削除。

ゲームオブジェクトの設定

作成したスクリプトとVideoPlayerをアタッチします。

スクリプトはMovieManagerなり、お好きな名前にしてください。

VideoPlayerのAspect Ratioは「Fit Inside」がおすすめです。

f:id:tofgame:20190531232724p:plain

実行すると

www.youtube.com

再生されます!やったー!

終わりに

再生までできたので、シークバー、検索機能、簡単MAD作成機能など、思い付きで機能追加していきます。

ファイル読み込みを自分で呼び出して認識させる【Unity】

f:id:tofgame:20190428235628j:plain

起こった問題

Unity実行中に、System.IO.Fileクラスで作成したファイル、フォルダや、その他方法で作成したデータをAssetフォルダ上に置いて使用するとします。

ですが、Unityエディタ上ではすぐ認識されず、Unity側の認識を待たなければResources.Loadなどではロードできない状況になります。

ProjectViewで確認してもわかりますが、実行中にファイルをAssetフォルダに含めても、更新がかかるまで少し待つことになります。

解決策

AssetDatabase.Refresh()を使用します!
docs.unity3d.com

なお、AssetDatabase.Refresh()を使用する時はUnityEditorをusingする必要があるので、この動作はエディタ上限定になります。

実際に確かめてみる

Refresh有り無しでファイル生成処理を比較します。

動かしてみるとわかりますが、Refreshを書かない場合、Resources.Loadで取得しようとすると認識されません。
Refreshを書くとロードされます。
f:id:tofgame:20190530234002p:plain

Processingで簡単な爆発エフェクトをつくる

f:id:tofgame:20190521215955p:plain
爆発は正義なのでProcessingで爆発エフェクトを作りました!

イメージ

爆発の破片をrect関数を使って表現します
爆発時にフラッシュを炊いて迫力を付けます
circle関数で衝撃波も書きます
音も出します

制作フロー

爆発の破片クラスExRectクラスを作成する。

位置、速度、大きさ、回転速度を定義していじれるようにします。

爆発クラスExplosionを作成する。

この中で爆発の破片を生成して、さらに衝撃波とフラッシュ部分も書きます。
時間用の変数を定義して、衝撃波の大きさとフラッシュの間隔に使用します。

爆発をクリックした場所に出す

実際にインスタンス生成します。
Minimライブラリを読み込んで音も鳴らします。


できたもの

※デカイ音が鳴ります
※チカチカします
youtu.be

つかいどころ

ボールがぶつかったときに使う

※デカイ音が鳴ります
※チカチカします
youtu.be

GOTO文は使いよう【UnityC#】

f:id:tofgame:20190528160203p:plain

そもそもGOTO文とは

ラベルを記述した場所に処理を飛ばすことができる記法です。

private IEnumerator Start () {
    A:
    Debug.Log("ループするよ");
    yield return new WaitForEndOfFrame();
    goto A;
}

上みたいなのだとwhile(true)と同じような動作をします。
これだとどこに飛ぶかがわかりにくい・・・
きれいに記述されたコードが光属性なら、GOTO文をたくさん使ったコードは闇属性だと思います(?)。
なんとなく嫌なイメージが先行しているGOTO文ですが、GOTO文について僕は何もしらなかったので、詳しく調べてみました。

なぜ忌嫌われるのか

スパゲッティコードを量産できてしまう」点にあると思います。

簡単☆スパゲッティソース☆

private IEnumerator Start () {
    while(true) {
        START:
        int num = Random.Range(0,4);

        switch(num) {
            case 0:
            Debug.Log("run 0 to 2");
            yield return new WaitForEndOfFrame();
            goto case 2;

            case 1:
            Debug.Log("run 1 to 4");
            yield return new WaitForEndOfFrame();
            goto case 4;

            case 2:
            Debug.Log("run 2 to 1");
            yield return new WaitForEndOfFrame();
            goto case 1;

            case 3:
            Debug.Log("run 3 to START");
            yield return new WaitForEndOfFrame();
            goto START;

            case 4:
            Debug.Log("run 4 to end");
            yield return new WaitForEndOfFrame();
            break;
        }

        Debug.Log("end");
        break;
    }
}

何を書いているのか僕にもわかりません。
スパゲッティがかなり的を射た比喩なのが実感できます。

Go To Statement Considered Harmful

「Go To Statement Considered Harmful」という名前の論文も出るほどにgotoについては議論されています。
この論文に、「The unbridled use of the go to statement has an immediate consequence that it becomes
terribly hard to find a meaningful set of coordinates in which to describe the process progress.
Usually, people take into account as well the values of some well chosen variables, but this is out of the question because it is relative to the progress that the meaning of these values is to be understood!」と書かれています。
意味の通るようなプログラムの進行にしにくくなるので、goto文の乱用はよくないそうです。
(論文リンク)http://www.gdv.informatik.uni-frankfurt.de/lehre/ws2005/PRG1/Readings/W4-Go-To-Statement-Considered-Harmful.pdf


どこで使うといいのか

多重ループ(ネストループ)から抜ける

forループなどで条件に応じてループを抜ける、といった書き方はbreakを使えばできますが、ループが2重3重に重なると、breakも2重3重に重ねなければいけません。
しかし、goto文を使いループ外にジャンプすることでループから抜けられます。(関数化してreturnしても同じようにできますが・・・)

private void Start () {
    for(int i = 0;i < 100;i++) {
        for(int j = 0;j < 100;j++) {
            for(int k = 0;k < 100;k++) {
                if(i * j * k >= 10000) {
                    goto END;
                }
            }
        }
    }

    END:;
}

おわりに

goto文を使わなくてもいい場面が多いので、めったに使うことはありませんが、goto文のこともたまには思い出してあげてください。

参考にしました

ufcpp.net
marycore.jp
marycore.jp