ボタンとシーン遷移の実装【Processing】
ボタンを押して別シーンに遷移する処理を作ります!
実装の流れ
ボタンを作る
マウスがボタンに重なると色が変わり、またクリック後に別シーンにうつるようなものを作ります。
シーンを作り、ボタンを押すと別シーンにうつるようにする
シーン用に変数を用意してシーン用変数が特定の値の時に特定のシーンの処理を実行する、という感じです。
これは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; } }
Processingでモーショングラフィックスを作るまで その3
3回目です!
前回の記事はこちら
tofgame.hatenablog.com
今回作るもののイメージ
(1)文字が回転しながら中心に寄る
(2)全部の文字が中心に寄ったら文字が消える
(3)文字が消えた部分から丸が広がり文字が現れる
文字が回転しながら中心に寄る
文字用クラス「RotateText」を作成します。
中心点を基準に三角関数で位置を決めるイメージです。
半径と角度を変数定義しておき、それを変更することで回転、中心に寄るといった動作を実装します。
全部の文字が中心に寄ったら文字が消える
半径が0になったタイミングでArrayListのRemoveを使って消します。
文字が消えた部分から丸が広がり文字が現れる
circle関数を使って丸を書きます。
背景色を白、丸の色を黒にするので、文字色を背景色と同じにしてあたかも文字が自然に現れたような表現をします。
完成したもの
ソースコード
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); } } }
次回
近いうちに!
使ったフォント
こちらもどうぞ
映像作品でよく見るうごめくテキストを作る【Processing】
テキストがピントを合わせながら動いてる感じの表現を作りました。
できたもの
ポイント
filter関数を使って、ブラーを画面にかけます!
filter関数の引数として0~6をループする変数を使うことでくっきり⇔ぼやけの流れを作ります。
使いどころ
街の中に出現させたりします。
使った素材
マウスが重なっているUIオブジェクトを取得する【Unity】
やること
EventSystemを参照してマウスが指示している位置に重なっているUIオブジェクトを取得します!
取得するオブジェクトは、EventSystemのInspecterの下部にあるWindowのpointerEnterでも確認できます。
手順
まず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】
小一時間悩んだのでメモとして残します。
失敗例
以下のパターンだとずれました。
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の動画を再生する
はじめに
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」がおすすめです。
終わりに
再生までできたので、シークバー、検索機能、簡単MAD作成機能など、思い付きで機能追加していきます。
ファイル読み込みを自分で呼び出して認識させる【Unity】
起こった問題
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を書くとロードされます。
Processingで簡単な爆発エフェクトをつくる
爆発は正義なのでProcessingで爆発エフェクトを作りました!
イメージ
爆発の破片をrect関数を使って表現します
爆発時にフラッシュを炊いて迫力を付けます
circle関数で衝撃波も書きます
音も出します
制作フロー
爆発の破片クラスExRectクラスを作成する。
位置、速度、大きさ、回転速度を定義していじれるようにします。
爆発クラスExplosionを作成する。
この中で爆発の破片を生成して、さらに衝撃波とフラッシュ部分も書きます。
時間用の変数を定義して、衝撃波の大きさとフラッシュの間隔に使用します。
爆発をクリックした場所に出す
実際にインスタンス生成します。
Minimライブラリを読み込んで音も鳴らします。
できたもの
※デカイ音が鳴ります
※チカチカします
youtu.be
つかいどころ
ボールがぶつかったときに使う
※デカイ音が鳴ります
※チカチカします
youtu.be
GOTO文は使いよう【UnityC#】
そもそも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文のこともたまには思い出してあげてください。