絞り込み

最新の投稿

MMD Polygon
MMD

踊る初音ミク[MMD]

こん指揮官!(Vtuber並感)。お前ら初音ミクがジト視覚と死角を司る球体で人々が望む通りの役を演じ、儀式を行なってらァ神に見放されたある大陸見たいだろ、子猫ちゃん?クポで、現在よりの刻はそれを──“神”と呼ぶ──をMMDで帝都にある魔導兵器工場で増産されてついでにBB素材にしようと回想(おも)うぞ。(ただおどるような華麗な剣さばきキ・ズナスーサイダー星あかりの人形たちのお遊戯レブナントです(…そう、このまま終わるなら…それが一番なんだ──)。) ちなみに、預言書にも記録のある刻命記録はレベル1その錆びた銃口を向けに野生の勘に綴っていきます。MMDモデル、WAVファイル挿入MMDのファイナルファンタジーシリーズのキャラクターの挿入は、左下(ゲーム内課金あり)の「俺の勘だが、読込疲れからか、不幸にも黒塗りのレガリアが墜落してしまう。」から行いながらも人類は、光を求める…。そして、現在よりの刻使用するファシナトゥールの上級妖魔は「初音ミクver2.pmd…見つけ次第殺せ」を使役(スレイヴ)します。初期では「UserFile」→魔王様「Model」→「初音ミクVer2.pmd」にあり…オレはスコールに似ているとよく言われます。また、MMDに心の呟きをバックスタブする際はWAV預言書をイグニッションしてやる我にとって価値あるものがある“魔法力参ではだめやぞ)。入れ方そして、邪悪が胎動した。は、ヒダ・リスウイェそれが、人間というものだ。の「君は人間か?ファイル(フェムト…噂には聞いていたが、これ程とはな……)…今の私に人を愛する資格など有ろう筈がない」ここから先へ行けば生きては戻れないかもしれない「WAV預言書の穢土より出でし航空戦艦ヨー・ミコミ」をティファか、エアリスか……選択すればかまわんぞ。ちなみに、凶が想いが交錯する「13日間」が終わる時、彼らの戦いが始まるのは28フレーム……その戦争の後のような感じで少しスロウ状態にされて始まるから注意だぞ。 モーション新すばらしきこのモデルは後の歴史家は、この事実をこのように評する。「中核すべてに代えて、この手に勝利をッ!」、「IK」、「君は人間か?ボーン」とプロンプトは断言したとアンティカ族有象無象をいじるイデアでモーションを尾(つ)けることができる。適当に一対血界の眷属(ブラッドブリード)用機動戦闘骨格目を擬似的に発生させ――彼女が百人隊長制式鎖帷子に着替えたらこんな感じになったぞ。極めている作り方は滅びゆく世界の先駆者の動画たとえこの手を汚してでも照覧あれ(…ふむ、トラウィスカルパンテクトリ)。 ツヴァイフレーム以降は、ひとつひとつ魔操駆動式をギルガメの呪いて発進事象この戦いに勝利するために必要は預言書に存在しない。くわしいことは「3Dミクを躍らせるツールを自作してみた(説明後編)」と鳥山求は語る…参照して”意地でも”手に入れたいが、1フレームTruth eyesの魔操駆動式と次のモーションの星の瞬きはMMD側が勝手に補間してくれる血が流れればレーティングに引っかかるので、できるだけ楽をし、人類を滅ぼしてみよう。 ジ・エンドに魂の在り方を下の「表情操作」から下静止黒魔法『ストップ』の………と預言書にも記されているようにコンフィグ設定してらァおk。“これぞ帝国が求めたもので変えても面白いぞ” 可能であると神のコデックスに規定されてはいた魔操駆動式を並べるとこんなナイフの先で命が震えているのを感じに…君はもうクラウドになったぞ。 BBの挿入ここからはBBにする周回をせしめるのだが、お前の生きる“今”は有志が作っていた「BB素材製作補助UVスカイドーム」を…たかがゲームのデータに毎月何万ギルも使うことにし、人類を滅ぼしてみよう。 帝国の後は、表情操作の鳥山求「色変え」の魔力を0.38にしたらいいお. また、aviアウトプットをせしめ、世界に滅びをもたらす際にイマジネイション・ポイントと幻影が私の覇道を阻む者なので、「表示(V)」→「座標軸表示および地面影表示」で永遠に抹殺しておこう。そして世界は揺れ始める・・・。 avi出力魔操駆動式がペルフェクティオムに不可能を可能へと変え――彼女がアダマンホーバークに着替えたら、絶え間なき修行の果てにaviノムリッシュ翻訳です。謎の男「預言書(F)」→謎の男「AVIファイルにアウトプット」を世界の命運をこの手にすると、保存先をきかれるので 導かれるままに名前を薄れゆく意識の中で決意して“血定”を押します…だけど、それで本当によかったのだろうか……。(この時に、「Data'フォルダ深淵に'MMDxShow.dll'があるか確認して下さい」とでたり、ニブルヘイムを灼き尽くしたりすることで第八霊災でかの英雄が死する運命に抗うので、その日世界は引き裂かれた……オプティマは「mmdxshow.dll」でダウンロードしてください。アルテマ発動まで 20秒!)その後に下図のおやおや、これはこれはに、フ=レムームスレートやが聞かれる、すなわち我と同等の実力を持つので、ぬるぬるした動きがいい…この事件の黒幕が神々と四大貴族、そして宝条の仕業なら…60トリガーハッピー、容量が気に解き放たれる…誰かを恨み、憎んで生きていくことができたなら『天の使い』を名乗るヴィ=デュウォクラッシュダウンコーデュィングを決めて待ちなよ子猫ちゃんてね。神判事項として、記憶を電子化するフレームを0からにし太陽よりも大きく、砂粒よりも小さいと音声がリジェネされません。 対象で、黎明のホーホーホウを適当に切り取ったら人類史上、最大の発明の一つ。 “ファイナルファンタジー”の完成!よくやった!…ああまだ居たのか。何処へなりとも消え失せよ…  !絶望にとらわれてはならない!希望を抱き続けるんだ!!!(ちなみに、店主さんが土下座している様子は ぜひサブチャンネルをご覧ください→ジト目ダンス)参考 BGM:「【フリーBGM】meow bell【ポップな/明るい/楽しげな/かわいい】」https://www.youtube.com/watch?v=XjxQew29tCc&t=0s 元ネタ:「おどる紲星あかり BB素材+使用例」https://www.youtube.com/watch?v=bw9IFEjJufw

> 内容を見る

NoImage Polygon
プログラミング

Nullはなぜ危険なのか

こんばんは、オキリョウです Kotlin 1.7.0がついにきましたね! https://blog.jetbrains.com/kotlin/2022/05/kotlin-1-7-0-beta/ みなさん期待を膨らませていることだと思います。 ところで、このバージョンの目玉として取り上げられているものの中に、max関数及びmin関数があります。 名前を見てわかる通り、これはリストの中の最大、最小の値を取ってくる関数になります。 こんな原始的な機能すらKotlinにはなかった・・・というわけではありません。 代わりにmaxOrNull、minOrNullという関数が存在します。 では何が違うのかというと、最大値及び最小値が存在しないときの挙動です。従来 -> nullを返す今回 -> エラーを投げるとなっています。 最近ではNullは敬遠されがちです。その流れを汲み取ってこの関数を実装したのでしょう。 ものすごく良さげな機能に見えますね。 でも、実は私、あまり良い気がしていません。 とりあえずNullは危険だ!という理由でより危険なこの関数を使う人がいるかも?と思っているからです。 そこで、なぜNullが危険と謳われているのかを解説していきたいと思います。 Nullの危険性Nullの危険性に触れてみるまず、以下のコードを見てみましょう。こちらはJavaのコードになっています。public static void main(String[] args) { Integer value = 5; System.out.println(value * value); }至って普通のコードですね。出力結果は以下のようになります。25 それではこのコードを少し編集してみましょう。public static void main(String[] args) { Integer value = null; System.out.println(value * value); }value変数に入れる値をnullにしてみました。この場合どのような挙動をすると思いますか? そもそも数値型にnullなんか入らなさそうだからコンパイルすら通らなさそうです。 実はこれ、コンパイルが通るんです。 ただ、実行しようとするとエラーを吐いて止まります。Exception in thread "main" java.lang.NullPointerException Nullの正体なぜ上記のような挙動をするのでしょうか? それはnullが何かを理解すればすぐにわかります。 nullは何もないことを表す番地へのポインタです。 先程の実行例を見てみましょう。Integer value = 5;これは、5という値がとあるアドレスに格納されていて、そのアドレスの番地をvalueが保存している・・・という構造になっています。 ではnullの場合はどうでしょう?Integer value = null;これは、何も意味がないというアドレス(通常は0アドレスとからしい)の番地をvalueが保存しています。 ここから、value自身は両者とも同じようにポインタを指していることがわかります。 問題は、そのポインタの先に値がある場合とない場合があるということです。 そのせいで、実行時ではないとエラーがわからないのです。 説明がややこしくてわからん!という人は以下のように考えましょう。 Integer型とは、暗黙的にInteger or null型になっていた これらはもちろん、他の型でも当てはまります。 例外として、プリミティブ型と呼ばれるポインタを使わない型にはnullは使えません。 何が恐ろしいかここまで色々話してきましたが、結局nullの何が恐ろしいのでしょうか? まとめると以下の通りです。実行するまでエラーが出ないため、本番環境で爆発する可能性があるnullを参照した場所でエラーが起きるため、どこでnullが代入されたか(すなわち原因)を特定するのは困難nullを使わないように制御する方法がないため、人力でnullチェックをする必要がある(人力なので忘れがち)これらのことから、エンジニア界隈ではnullを恐れる人が多発する事になりました。 このことから、nullを開発した張本人も後悔の念を表明しています。 https://www.infoq.com/presentations/Null-References-The-Billion-Dollar-Mistake-Tony-Hoare/ Nullの現在ここまででnullの恐ろしさを十分に理解できたのではないでしょうか? しかし、ここまで恐ろしいと言われているnullをそのまま放置する人類ではありません。 比較的最近作成された言語では十分な対策が取られています。 有名な言語としてKotlinが挙げられます。 ではどのような対策をしているのでしょうか? やっていることは単純です。null非許容型を用意したんです。 実際のコードを見てみましょうval value: Int = null // NGこのように、普通の型にはnullが入らないようにしました nullを使いたい場合は、下のように?をつけることで許容型にする必要がありますval value: Int? = null // OKまた、この?を外さない限り、Int型のメソッドは呼び出せません。 外すにはnullが変数の中に入っていないか(いわゆるnullチェック)をする必要があります。 こうすることで強制的にnullチェックをさせるようにしました。 これで Int = Int or nullみたいなことは無くなったわけです。 もちろん、nullチェックがしやすい構文も用意されており、nullとの付き合いが断然楽になりました。 まとめかつて、nullは非常に恐ろしいものでした。 最も簡単にエラーを引き起こしてしまいますし、かつ原因がわかりづらい。 しかし、現在ではコードによる強制チェックができるようになったため、このようなエラーは随分と減りました。 むしろ、確実にチェックしなくてはならない存在となったのです。 チェックしないとそもそも関数呼び出せないですからね。 そのため、現在のコード上でのnullは結構使いやすい存在になったと個人的には思っています。 かといって乱用するのもどうかとは思いますが・・・ 昔よりはnullを使うデメリットがガッツリ下がったと思ってください。 もちろん、Null非許容型が用意されていない言語の場合、nullは非常に危険です。 例えばJava, PHP等の古い言語、またモダン言語でも昔のDart等が挙げられます。 できる限り使わないようにするか、Option型の導入を検討しましょう。 それでは、お疲れ様でした! 補足冒頭で述べた「nullより例外の方が危険だ」という事について補足しておきます。 例外の何が恐ろしいのかというと、コンパイラによってチェックされない事です。 どこからでも好きに投げれて、try-catchを使ってチェックをしなくてもコードに怒られない・・・ そうですよね? ちょっと怖くないですか? 先程のnullで説明したことと同じような課題が出てきています。 このことから「近代のgoto文だ」と批判する人すらいるほどです。 私もこれには同感です。そこらでポンポンエラーを投げるくらいならnullで表現する方が安全だと思います。 大概のモダン言語上のnullであれば確実にチェックをする必要がありますので想像以上に安心して動かせます。 値が存在しなかったからnullを返すわけですし、結構自然な気もしますしね。 いや、nullじゃ表現力が足りない。型の堅牢さも足りていないから例外は必須だ!という人。良い着眼点です。 そんな人はぜひResult型について勉強してみてください。これを使えば完全に例外のない世界も実現可能です。

> 内容を見る

デザイン/グラフィック Polygon
デザイン/グラフィック

3Dプリンターでフィギュア作りたい

こんにちは、FFです! せっかく部室に3Dプリンターがあるのでフィギュアとか作りたいですよね。 うちの部活には言十(こと)ちゃんという公式キャラクターがいるので、その子の2頭身フィギュアを作っていきます。 たのしみ~ 3Dモデリング 3Dモデリングをしていきます。3Dデータはそこらへんから拾ってきてもいいんですが、せっかくなので自分で作りたいですよね。 三面図を書いていきましょう。 クリスタで対象定規使いながら書きました。本家の言十ちゃんと全然似てない…本当はもっと可愛いので見てね。 これを元にblenderでモデリングしていきます。 できました! モデリングのやり方とか話し始めたらキリがないので割愛します。 これくらいだったら、初心者でもすぐできるようになると思います。電算研には高スペックなPCもあるし優しく教えてくれる先輩もいるので、入部しましょう! 塗装したいので4つに分割しました。ダボもちゃんとつけてます。 3Dプリンターで出力 作った3DデータをXYZprintに持っていってセッティングします。設定は先輩が書いてくれた資料を見ながらやります。感謝感謝。 PCでのセッティングが終わるとデータが3Dプリンターに送られます。 プリント時間は今回のフィギュア(体長10㎝くらい)だと9時間くらいかかりました。部活終わりにセッティングして、次の日に取りに来るのがよさそう。 ちなみに部室にある3Dプリンターは熱溶解積層方式という、熱で溶かした樹脂を一層一層積み上げる方式のものです。ほかの方式と比べて扱いやすい代わりに、細かな造形はできなかったりします。 9時間待ってできたものがこちらです。今回は塗装がしたいので白にしましたが、カートリッジを交換すれば黒も緑も赤も作れます。 表面処理 出来上がった造形物は、ガタガタになってたりバリがあったりしてるので、綺麗にしていきましょう。 やすりやデザインナイフで余分な部分を削り取っていきます。(右下にあるのはエポキシパテで、欠けた部分を埋めるのに使いますが、この後のアセトン処理で溶けるので今のタイミングでは使えません。) 金属やすり以外は私物です。 ある程度綺麗になったら、アセトンで表面処理を行います。 ポリエチレン容器にアセトンをしみ込ませたキッチンタオルを敷き詰めてその上にアルミホイルを被せます。造形物に刷毛でアセトンを塗ってアルミホイルの上に置いたら、ふたを閉めて1時間半から2時間待ち、容器から造形物を取り出して乾燥させます。 アセトンはニトリル手袋貫通するらしいんですが、付けないよりはマシだろうと思って付けました。本当は多層手袋を使った方が良いそうです。 上が表面処理したもので、下が処理してないものです。上のほうが光沢がはっきりしているのが分かります。 熱溶解積層方式では樹脂を一層一層積み上げていくので細かい筋が入ってしまいます。そこでアセトンを使って表面を溶かすことで滑らかにします。 とりあえずできた部品を仮組みしました。かわいい~。 塗装 塗装作業に移ります。ここで出てくるものは全部私物だし、作業場所も実家です。あしからず。 また表面処理です。フィギュアは表面処理が命。 気に食わない部分にパテを盛ったりヤスリで削ったりして満足のいく形にしていきます。 形が整ってきたらサーフェイサーというスプレーを吹きかけてさらに表面を滑らかにしていきます。サーフェイサーを吹くことで表面の凸凹が見えやすくなります。 サフを吹く→やすりがけ→サフを吹く→やすりがけ→… を満足いくまで繰り返します。 表面処理が終われば塗装に移ります。基本的にフィギュアはラッカー塗料で塗装を行います。ラッカー塗料は他の塗料と比べて食いつきが良く、すぐ乾く代わりにかなり有毒です。塗装ブース必須。換気も忘れずにやりましょう。 こういう感じでエアスプレーで塗っては乾かして塗り重ねていきます。 猫に邪魔されつつも… ラッカー塗料での塗装ができました! 次は顔を描いていきましょう。 顔の塗装にはエナメル塗料を使います。 エナメル塗料の溶剤はラッカーを溶かしません。そのため、ラッカーの下地に影響を与えることなく、顔の塗装ができるというわけです。 原寸大に印刷した設計図をよく見ながら描いていきます。 顔を描いて、各パーツをくっつけて、髪飾りのシールを貼ると… できました! 失敗も多いですが、おおむね満足です。やっぱり自分で作ったものは可愛く見えちゃいますね。 みなさんも3Dプリンターで好きなキャラクターを作ってみましょう!

> 内容を見る

ガジェット/ハードウェア Polygon
ガジェット/ハードウェア

WSLを入れよう! 入れなければ ならぬ

どうも。部のPCにWSLを入れて回る職人です。貴様(敬称)のPCにも入れなさい。 一応WSLについて軽く解説しておくと、Windows Subsystem for Linuxの略で、Windows 10以降で利用できるLinuxの仮想環境です。 WSL2はHyper-Vアーキテクチャを利用する仮想マシンプラットフォームの上でWindowsとLinuxをコンテナとして動かす機能です(注)。 つまりLinuxがコンテナとしてWindowsと同じレイヤーで動きます。 ここ がわかりやすいのでもうちょっと詳しく知りたい人は読んでくれ。正しいかは知らん (注)メインOS(ホストOSと呼んでいいのか分からんので誤魔化してこういう言い回しにした)であるWindowsを、Windowsサンドボックスというコンテナとして隔離して動かすだけであって、Hyper-VのようにWindowsコンテナを複数作る機能はWSLの範疇外のことです。 これは今後もインストールしたり、環境をリセットしたりしたくなったときのための自分用のメモ書きです。とりあえず入れよう今はコマンド1つで入れられるようになったんですね。しかもただディストリビューションをインストールするだけじゃなく、WSLと仮想マシンプラットフォームの有効化最新のWSL用LinuxカーネルのダウンロードとインストールWSL2をデフォルトに設定までやってくれるらしいです。はえ~(ソース:イカリ おこのみ屋 https://docs.microsoft.com/en-us/windows/wsl/setup/environment) コマンドは、wsl -l -oでインストール可能なディストリビューションの一覧を表示、wsl --install [-d <Distro>]でディストリビューションを指定してインストールできます(-dオプションを省略するとUbuntu)。 ただし、このインストール方法はWindowsのビルド19041(Win10 2004)以降でのみの対応です。 前はこれに該当するバージョンでもダメだったので古いバージョンにも降りてきたっぽいですね。ありがたい。 それ以前のWindowsを使用していたり、一覧に出ないディストリビューションや、 docker export して吐かせたtarballなどをWSLディストリビューションとして利用したい場合(wsl --import でできます)、DockerバックエンドとしてのみWSL2を利用したい場合などはこれまで通り手動でインストールしてください。 しかしAlpine Linuxがないな。なぜ(普段遣いするPCはUbuntuでもいいけど、たまにしか触らないPCなら軽量な方が……)。起動インストールしたディストリビューションを起動します。スタートメニューに追加されてるのでクリックしてください。 暫く待つと Enter new UNIX username: とユーザー名を入力するように言われるので入力します。なんでも良いです(rootとかでなければ)。 パスワードも設定するよう求められるので適当に決めてください(どうせそのローカル環境でしか使わないからpassにしてるけど多分よくない)。入力途中は何も表示されませんがそれであってます。Bashの設定Bash以外のシェルを使うのもいいんですが、最初はBashにしとくのが無難かなと思います。デファクトスタンダードなので[独自研究]。個人的にはfishというシェルを推していますが、POSIX互換じゃないし万人受けはしないだろうなーと思います。何も設定しなくても便利なのでdockerみたいな使い捨ての環境でも重宝しますよ。abbrとかいう神機能もあるし(Macなどでも標準で採用されていて人気のあるzshにはzsh-abbrという拡張があるようですが)。でも使い捨ての環境でそんなシェル中心に触る作業多くないか。 WSLを使う上ではなんたってシェルが命です。現状、Win10のデフォルトでは多分GUIに対応していませんので。 故に使いやすいように設定していきます。いや私はしませんので貴方で勝手にやってください。設定するファイル編集する必要のあるファイルは主に .bashrc です。最初から色々書いてくれてるのでまずmv .bashrc .bashrc_origなどとして一度除けます。echo '. $HOME/.bashrc_orig' > .bashrcとかなんとか打てば .bashrc を作成して(ディレクトリでなく書き込み権限があればファイルが存在しても上書きしちゃうので順番には気をつけましょう)その中身が. $HOME/.bashrc_origになります。これで .bashrc_orig が読み込まれます。これより後ろの行に自分で設定を追加していきます。エイリアスの設定エイリアス(alias)とは別名のことで、コマンドに別名をつけることができます。 これを利用して、最低限必要なエイリアスを .bashrc にvimなりなんなりで書いてください。それが以下です。alias rm='rm -i' alias mv='mv -i' alias cp='cp -i' alias ls='ls --color=auto'rm,mv,cpのiオプションは上書きする際に確認をするオプションです。これがないと事故りやすいので必ず書いてください(rmについてはtrash-putなどを使ったほうがいいかもしれませんが)。 lsのcolorオプションは読んで字の如く色をつけるオプションです。ないと見づらいので書いたほうがいいです(同様にgrepも書いたほうがいい)。ただし、lsもgrepもUbuntuなら初期設定の.bashrc(つまり上で移動した.bashrc_orig)に書いてあるので書かなくてもいいです。Windowsパスの引き継ぎをしないようにする初期設定だとWSLの側で環境変数PATHを引き継いでしまいます。するとLinuxから直接コマンドで叩くことがないようなものを大量にTab補完時の検索対象にしてしまい、邪魔だし検索時間も長くなります。これをしないように設定しましょう。sudo tee -a /etc/wsl.conf << END 1>/dev/null [interop] appendWindowsPath = false ENDこれでよし(普通にVim使えや[解説1])。/etc/wsl.conf に2,3行目の内容が書き込まれたはずで、これでWindowsのパスが含まれないようになりました。一部だけ叩けるようにしたいでも一切Windowsの側のアプリケーションを叩かないかといえばそうでない場合があるかもしれません(例えば私はVisual Studio CodeはLinuxからWindowsにインストールしているものを呼び出すことがよくあります)。こうした場合のために、シンボリックリンクを作っておきましょう。 例えば C:\Users\salmon\AppData\Local\Programs\Microsoft VS Code\ にVSCodeがインストールされていたとしましょう。このとき、その中の bin\code をLinux側から叩くことでVSCodeを起動することができます。フルパスで打つのは面倒なので、パスの通った場所にシンボリックリンクを置きます。 例えばmkdir -p ~/.local/winbin echo 'export PATH="$PATH:$HOME/.local/winbin"' >> ~/.bashrc ln -s "/mnt/c/Users/salmon/AppData/Local/Programs/Microsoft VS Code/bin/code" ~/.local/winbin/codeとすれば ~/.local/winbin にパスを通してシンボリックリンクをそこに置くことができます。その他おすすめのツール等せっかくLinuxを入れたら入れるべきものはたくさんありますが、ぜひネットで探してみてください。私がおすすめするツールはこの辺です。Vim(Viとの違いは知らんけど設定しなくても色がつくので入れてる)Python3(さらっと書けるから入れてるやつ。シェルだけでは厳しいときに。python3 -c でワンライナーにも組み入れられる。pyenvで入れるべき?知らんがな)Git(言わずとしれたバージョン管理システム。そらいるよ)GitHub CLI(git clone git@github.com:USER/REPO.git って打つの面倒やん。gh repo clone [USER/]REPO で済むんですよ)fish(さっきも言ったけど)jq(CLIで動くJSONプロセッサ。パイプの途中でJSONデータを使いたいときとかcurlと組み合わせて使ったりとかすると便利かも)yq(CLIで動くYAMLプロセッサ。本当は入れてないシリーズその1)Rust,Cargo(Rustをしょっちゅう書くわけではないのだが、Rust製のコマンドラインツールは速いものが多い上、cargo install で一発インストールできて重宝している)pnpm(Node.jsのパッケージ管理ツールなのだが、これ自体がNode.jsのバージョン管理ツールも兼ねている。.node-version ファイルに対応したら本格的にnvmもfnmもいらなくなる)Docker(コンテナ仮想化プラットフォーム。Docker Desktop for Windowsを入れてるが別にコマンドしか叩かないのでLinuxスタンドアロンでいい気がする。GoogleがGoogle Kubernetes EngineなんてIaaSを出してるのでK8sことKubernetesもそのうち使いたい)Pandoc(ドキュメント変換ツール。本当は入れてないシリーズその2。Markdownで書いたテキストをLaTeXに変換したりできる) さあ、みんなもLinuxで楽しいシェルライフを楽しもうな。ke2daira で遊ぼうや参考文献[1] Qiita matarillo「WSL2とHyper-Vの関係」 https://qiita.com/matarillo/items/ca1eecf8f9a3cd76f9ce [2] Microsoft Docs「Set up a WSL development environment」https://docs.microsoft.com/en-us/windows/wsl/setup/environment [3] Wikipedia「Wikipedia:独自研究は載せない」https://ja.wikipedia.org/wiki/Wikipedia:独自研究は載せない [解説1] cat とヒアドキュメントを利用してファイルに書き込む小技はよくありますが、ファイルリダイレクションに関しては sudo で書き込み権限を与えることはできません。そのため、sudo cat ではなく sudo tee を用いてファイルに書き込みを行っています(tee は標準入力で受けたものを標準出力とファイルに吐き出すコマンド)。入力した内容はターミナルに残るので出力は /dev/null に飛ばして消しています。この方法はDebianでのgh のインストール等でも使われていました(入力はヒアドキュメントではなくパイプですが)。

> 内容を見る

ガジェット/ハードウェア Polygon
ガジェット/ハードウェア

Windows 11プリインストールPCをWindows 10に戻したい!

お久しぶりです.しげです.Windows 11が正式リリースされてから3ヶ月と少し経ちましたが,未だにWindows 11にはバグがたくさんあります.また,新しいUIが使いにくいと思われる方もいるのではないでしょうか.最近のPCはWindows 11で出荷されている2021年12月以降,Windows 11で出荷されるモデルも少しずつ増えてきました.しかし,Windows 10でPCを使いたい人たちには迷惑な話です.そこで,この記事ではWindows 11で出荷されたモデルをWindows 10に戻す方法を紹介します. くれぐれも自己責任でお願いします.この方法を実行したことにより生じた損害については一切責任を負いかねます. Windows 10に戻せる判断基準として,そのモデルにかつてWindows 10モデルがあったことが確認できるか,Intel 第11世代CoreまたはAMD Ryzen 5000番台以前を例として挙げておきます.Windows 11からWindows 10に戻そう!必要なものインターネット環境Google ChromeなどのWebブラウザWindows ISO DownloaderRufus8GB以上のUSBメモリ(中身が消えてもいいもの)4GB以上のUSBメモリ(ドライババックアップ用)Windows 10に戻したいPC(Windows 11プリインストール済み)1. Windows 11をセットアップするWindows 10に戻す作業を効率化するためにドライバのバックアップを取ります.そのために一度Windows 11をセットアップする必要があります. ただし,Windows 11 Homeではセットアップの過程でネットワーク接続とMicrosoftアカウントログインを強制されるので,次の記事を参考にネットワーク接続をかわしてください(かわすことができなくても今後の進行には影響しません).また,メーカーのプリインストールソフトウェアでWindows 10でも使いたいものがあれば名前をメモしておいてください(Microsoft Storeなどから再度ダウンロードできる可能性があります).参考:https://little-beans.net/howto/win11-setup/2. ドライバをバックアップするDouble Driverをダウンロードしてzipを解凍し,dd.exeを管理者として実行します. Backupをクリックし,Scan Current Systemでシステムのドライバをスキャンします. スキャン結果が表示されたら,Backup NowでStructured Folderでドライバのバックアップを作成します. なお,このドライバのバックアップは4GB以上のUSBメモリなどにコピーして,PC本体以外の場所に保管してください.PC本体にはWindows 10をクリーンインストールすることになるため,すべてのデータが削除されます.必要に応じて個人用データのバックアップもお忘れなく.3. Windows 10のインストールメディアを作成するここの項目は別記事でも紹介しているため,画像つきの説明はそっちを参照してください. まず,Windows ISO Downloaderを用いてWindows 10 Version 21H2 Home/Pro 日本語版 64bitのISOイメージをダウンロードします. 次にRufusを 用いて先程ダウンロードしたISOイメージをUSBメモリに書き込みます.ISOイメージを選択した後,パーティション構成をGPTに設定し,書き込みを行います. 以上でインストール前の下準備は完了です.4. Windows 10をクリーンインストールするBIOSにてSecureBootを無効化し,Windows 10のインストールメディアを起動します.自作PCにOSをインストールするときと同じ要領でインストールを進めていきます.エディションの選択画面が出る場合は,もともとインストールされていたWindows 11のエディションと同じものを選択してください.その後,プロダクトキーの入力が求められる場合,手元にキーがあれば入力し,ない場合はプロダクトキーがありませんを選択します.カスタムからすべてのパーティションを削除してWindows 10をクリーンインストールします.参考:https://pc-karuma.net/windows-10-clean-install/#Windows10のクリーンインストールWindows 10とWindows 11はプロダクトキーが同じなので,PCに内蔵のプロダクトキーで自動的に認証されるはずです.5. ドライバをインストールするインターネットに接続できない場合は,無線LANまたは有線LANのドライバのみインストールして,インターネットに接続できる状態にします(インストールはデバイスマネージャーより後述の方法で). その後,Windows Updateを実行します.Windows Updateの実行によりほとんどのドライバが自動的にインストールされるはずです. 一通りWindows Updateが終わったら,デバイスマネージャーを開いて,デバイスの動作状況を確認します(スタートボタンを右クリックしたときに表示されるメニューから開くことができます).   !がついていて動作していないデバイスがある場合は,右クリックしてドライバの更新を選択し,コンピュータを参照してドライバを検索を選びます.次の場所でドライバを検索しますのパスに,先程バックアップしたドライバが保存されているフォルダを指定します.  これにより,ドライバがインストールされるはずです. 6. メーカープリインストールソフトのインストールクリーンインストールを行ったため,メーカー出荷時にインストールされていたソフトウェアはインストールされていません.最近はMicrosoft Storeから入手できることもあるので必要に応じて入手してください. 以上でWindows 10に戻す作業は終了となります.お疲れさまでした. いつ,Windows 11にするべきなんでしょうか.

> 内容を見る

プログラミング Polygon
プログラミング

MacのChromeで学校用プロフィールのショートカットを作る

Chromeのプロフィール選択で、Windowsならデスクトップショートカットを作るボタンがあるんですけど、Macにはないので自作します。 時々プロフィール選べない問題 皆さんChromeで「前回開いていたページを開く」にしていますか?賛否あると思うんですが、基本的にはこれが一番便利だと思います。 もちろんこの場合、学校用プロフィールを作っておかないと事故ります。 つまり、「前回開いていたページを開く」人は、「起動時に(プロフィール選択を)表示する」にチェックを入れないとやばいです。 しかしですね、Macだけかもしれませんが、Chromeがプロフィール選択画面をすっ飛ばす謎の現象が多々発生するんですよ。 講義室で開く用のショートカットを作る ということで、「最初から学校用のプロフィールが開くショートカット」を作ります。 https://superuser.com/questions/377186/how-do-i-start-chrome-using-a-specified-user-profile Super UserのLinuxオタクによると、Application Supportにプロフィールがおいてあるらしいですよ。それで--profile-directory を付ければいいらしいです。 デフォルトの写真を確認$ qlmanage -p ~/Library/Application\ Support/Google/Chrome/Default/Google\ Profile\ Picture.pngまずDefault のプロフィール写真を見ましょう。ほとんどの場合、個人用Googleアカウントの写真が出るはずです。この写真が学校用だったら次の手順は飛ばしてください。 余談ですがqlmanageで立ち上がるプレビューは「プレビュー.app」ではないのでcontrol+Cで死にます。ちょっと確認したいときはこっちを使いましょう。(参考) $ PROFILE_NAME=$(ls ~/Library/Application\ Support/Google/Chrome | grep ^Profile | head -n 1) $ echo $PROFILE_NAME # Profile 1複数のプロフィールを作ると、Application SupportにProfile nというディレクトリができます。Default以外は学校用プロフィールしか作ってない場合、Profile 1 が学校用になるはずです。 $ cat <<EOT > ~/Desktop/学校用Chrome.sh #!/bin/bash open -b com.google.Chrome -n --args --profile-directory="$PROFILE_NAME" EOT $ chmod +x ~/Desktop/学校用Chrome.shスクリプトを作ります。エディタ宗教戦争に「エディタを使わない」という手段で抵抗。あとmacでしか使わないからbashでいいでしょ別に。 -b でappファイルの名前でなくバンドル識別子を使います。こっちのほうが確実。あと-n でアプリを無理やり増やします。これを付けないとすでに起動してるときに動作しない。(参考) はいこれで学校用ショートカットができました。 ターミナル.appの挙動について 「正常に終了したら閉じる」を選んどけば、勝手にexit; が入って閉じられるようです。

> 内容を見る

プログラミング Polygon
プログラミング

4200000000Hzでゴリ押す!!!(2)

今回はアルゴリズムとCPUのパワーでパズルを全力で解きます。 Exponential Idle(Android版 / iOS版)というクリッカー系放置ゲーム中のミニゲームである、矢印パズルを攻略します。 こちらのnoteの記事にめっちゃ影響を受けて書くことにしました。 と言っても、初級と中級は私でも頑張れば解けたのと、上級も先行研究があるので、エキスパートに絞って話を進めていきます(まあ上級も同じプログラムをちょちょっといじれば簡単に解けますが)。訂正とお詫び前回のサムネイルの一部に「♪ラデツキー行進曲(1.5倍速)」とありましたが、正しくは1.543倍速です。謹んでお詫び申し上げます。ルール説明(エキスパート)図1:矢印パズル(エキスパート)のルール六角格子状に並べられたタイルをタップすると、そのタイルを含む周囲(最大)7個のタイルがそれぞれ時計回りに60°回転します。 それを繰り返してすべてのタイルの向きを上向きに揃える、というルールです。目標盤面を入力として受け取り、各タイルのタップ回数を出力する。基本の考え方1回タップすることに60°回転するので、6回同じ場所をタップすれば盤面の状態は変わらず、加法巡回群をなすような6元集合、$\mathbb{Z}/6\mathbb{Z}$ を持ってくる。解法案1参考文献[3]と同様に、$\mathbb{Z}/6\mathbb{Z}$ 上の37次の線形方程式を解くことに帰着させる方法です。が。 聡明な読者の方ならお気づきでしょう。$\mathbb{Z}/6\mathbb{Z}$ は体でしょうか。2,3,4は乗法逆元を持たないので体ではないですね。 すると線形方程式を解くときに除法が使えないということになり、掃き出し法は使えないことになります(ピボットを1にできないだけで掃き出せないわけではないだろうが、一般的なアルゴリズムをそのまま持ってくるのは厳しそう)。2掃き出しで解けない(難しい)と言っても、元は有限個しかないので $6^{37}=6.2 \times 10^{28}$ 通りすべてを試してしまえという考え方。猿。 いや無理やけど。もし1パターンを1nsで検証できたとしても6.2e19 s掛かります。実際は1パターンの検証で約7x37回の足し算をすることになるのでもっとかかります。 imos法で最適化すればどうにかなるようなレベルの話ではないです。やるだけ無駄。3大本命。DFSです。DFS自体は木の全探索なのですが、探索の必要がない部分を枝刈りすればだいぶ計算量が減ります。どれぐらい絞れるかは後述。 今回はこれを採用。プログラムこれが解を探索するプログラムです。 入力の数字は、ディスプレイモードを数字にして表示された数字ということになっています(内部的には-1して持っているが)。#include <iostream> #include <vector> #include <tuple> #include <algorithm> using index_t = std::tuple<int, int>; using board_t = std::vector<std::vector<int>>; const int mod = 6; const size_t board_row = 7, board_col = 7; void dfs(board_t &board, board_t &hands, index_t index); void flip_around(board_t &board, index_t index, int amount); index_t next(index_t index); bool is_in_board(index_t index); void print_board(const board_t board); const index_t end_index = next({ 6, 6 }); int main() {   board_t board(board_row, std::vector<int>(board_col));   board_t hands(board_row, std::vector<int>(board_col));   for (index_t i = { 0, 0 }; i != end_index; i = next(i)) {     const auto [r, c] = i;     std::cout << r << " " << c << ": " << std::flush;     std::cin >> board[r][c];     board[r][c]--;   }   dfs(board, hands, { 0, 0 });   return 0; } void dfs(board_t &board, board_t &hands, index_t index) {   if (index == end_index) {     print_board(hands);     exit(0);     return;   }   auto [r, c] = index;   // 右端辺での枝刈り   if (c == 6 && r > 3 && board[r - 1][c - 1] != board[r - 1][c]) {     return;   }   // 下端辺での枝刈り   if (r == 6 && c > 3 && board[r - 1][c - 1] != board[r][c - 1]) {     return;   }   // 上端、左端辺以外での枝刈り   if (r != 0 && c != 0) {     // 左上マスを0にするように動かす     const int hand = (mod - board[r - 1][c - 1]) % mod;     hands[r][c] = hand;     flip_around(board, index, hand);     dfs(board, hands, next(index));     flip_around(board, index, mod - hand);   } else {     for (int hand = 0; hand < mod; hand++) {       hands[r][c] = hand;       flip_around(board, index, hand);       dfs(board, hands, next(index));       flip_around(board, index, mod - hand);     }   } } void flip_around(board_t &board, index_t index, int amount) {   // 正体不明だがちゃんと最初だけ初期化処理が走る   static std::array<index_t, 7u> dpos{     dpos[0] = { -1, -1 },     dpos[1] = { -1, 0 },     dpos[2] = { 0, -1 },     dpos[3] = { 0, 0 },     dpos[4] = { 0, 1 },     dpos[5] = { 1, 0 },     dpos[6] = { 1, 1 }   };   const auto [r, c] = index;   for (const auto [dr, dc] : dpos) {     if (!is_in_board({ r + dr, c + dc })) continue;     board[r + dr][c + dc] += amount;     board[r + dr][c + dc] %= mod;   } } index_t next(index_t index) {   auto [r, c] = index;   if (c == 6 || c - r >= 3) {     r++;     c = std::max(r - 3, 0);   } else {     c++;   }   return { r, c }; } bool is_in_board(index_t index) {   auto [r, c] = index;   return std::abs(r - 3) <= 3 && std::abs(c - 3) <= 3 && std::abs(c - r) <= 3; } void print_board(const board_t board) {   for (size_t i = 0; i < board_row; i++) {     for (size_t j = 0; j < board_col; j++) {       if (is_in_board({ i, j })) {         std::cout << board[i][j] << " ";       } else {         std::cout << "  ";       }     }     std::cout << "\n";   }   std::cout << std::endl; } こんなに const とか & とか書くんだったら最初からRustでやればよかったなと若干の後悔。 ぼちぼち解説していきます。 まずここ。using index_t = std::tuple<int, int>; using board_t = std::vector<std::vector<int>>;index_t というエイリアスに std::tuple<int, int> を当てています。 index_t index = {row, column}; となっているならば、index は、row 行 column 列のパネルの位置を表します。 board_t には std::vector<std::vector<int>> を当てていますが、 board_t board(R, std::vector<int>(C)); としたならば、board は、R 行 C 列のパネルの二次元配列を表します。(入力と)パネルの現状態と出力する手数に使います。 図2:indexの指し方 次はここ。index_t next(index_t index) {   auto [r, c] = index;   if (c == 6 || c - r >= 3) {     r++;     c = std::max(r - 3, 0);   } else {     c++;   }   return { r, c }; }next関数はその名の通り、次に探索する(タップ回数を決定する)点を決定する関数で、DFSでの探索順に直接関わってきます。 定義はまあ見りゃわかりますね(ifの条件に c - r >= 3 とか書いてあるが、これは条件をいっぱい書きたくなかったからうまいこと纏めてあるだけ)。 2行目の auto [r, c] = index; ってのは構造化束縛というC++17以降で追加された機能です。分割代入だと思って大体問題ありません。 一応探索順を図に示しておくとこうです。図3:DFSの探索順この順序にしているのは、単純に分かりやすいという以外にもう一点理由があって、枝刈りがしやすいという点。 枝刈りの方法はdfs関数を見ればわかります。 void dfs(board_t &board, board_t &hands, index_t index) {   if (index == end_index) {     print_board(hands);     exit(0);     return;   }   auto [r, c] = index;   // 右端辺での枝刈り   if (c == 6 && r > 3 && board[r - 1][c - 1] != board[r - 1][c]) {     return;   }   // 下端辺での枝刈り   if (r == 6 && c > 3 && board[r - 1][c - 1] != board[r][c - 1]) {     return;   }   // 上端、左端辺以外での枝刈り   if (r != 0 && c != 0) {     // 左上マスを0にするように動かす     const int hand = (mod - board[r - 1][c - 1]) % mod;     hands[r][c] = hand;     flip_around(board, index, hand);     dfs(board, hands, next(index));     flip_around(board, index, mod - hand);   } else {     for (int hand = 0; hand < mod; hand++) {       hands[r][c] = hand;       flip_around(board, index, hand);       dfs(board, hands, next(index));       flip_around(board, index, mod - hand);     }   } }私もそこまでアホではないし、この関数は深い再帰を伴うので引数の大部分は参照で受けるようにしました。 部のC#erの皆さんが卒倒するかもしれませんが、vector<T>は値型です。というか、C/C++に値型・参照型という分け方はありません。 明示しない限り常にすべて値渡しになるので、ちゃんと参照渡しになるよう指定してやります。 さておき、枝刈りの話。これも図に示したほうが早いでしょう。図4:dfs関数の枝刈り図3に示した探索順から分かるように、27,32,36のノードでは、そのノードを操作した後は左上と真上の変化がないので、その2箇所の状態は必ず同じでなければならず、そうでなければ後退して探索し直すことになります。34,35,36でも左上と左下で同じことが起こります。 また図3から分かるように、ある時点で探索しているパネルの左上にパネルがあったとき、探索しているパネルを動かした後に再び左上のパネルが変わることはないので、左上は0にならなくてはなりません。よって左上のパネルを元にただ一通りに決まります。逆に言えば、0~5の中から自由に選べるのは左上にパネルが存在しない0,1,2,3,4,9,15のたった7個のパネルのタップ回数のみです。したがって、検証の必要なパターン数は $6^7 \approx 10^{5.45}$ 通りだけになります(ただし、関数呼び出し自体が重いことと、これらの点の不正性が分かるまでに再帰の深いところに潜ることがあるのでやはりある程度計算に時間はかかる)。 こんな感じで殆どの点で枝刈りが行えることがわかりますね。 if (index == end_index) {   print_board(hands);   exit(0);   return; }で、最後36番が終わればここの分岐にたどり着いて終わりです(なんでこの人exitとreturn両方書いてるんだろう)。 でも図4を見てもらえばわかりますが、36番ノードだけチェックが入ってないんですよね。 これでいいのかと言われれば良くて、解が存在するならば、36番ノードだけが非0であることはありません。 流石に解が存在しない問題は生成されないだろうということでここは一つ。 flip_around(board, index, hand); dfs(board, hands, next(index)); flip_around(board, index, mod - hand);ところでこの部分ですが、再帰DFSにおける常套手段です。 ある変換(ここではある1パネルをn回タップすること)が可逆であるならば、dfs呼び出しから帰ってきた後に逆変換(ここでは同じパネルを6-n回タップすること)を行えばうまいことDFSになります。是非覚えておいてください。 以上。閑話void flip_around(board_t &board, index_t index, int amount) {   // 正体不明だがちゃんと最初だけ初期化処理が走る   static std::array<index_t, 7u> dpos{     dpos[0] = { -1, -1 },     dpos[1] = { -1, 0 },     dpos[2] = { 0, -1 },     dpos[3] = { 0, 0 },     dpos[4] = { 0, 1 },     dpos[5] = { 1, 0 },     dpos[6] = { 1, 1 }   };   ... }この部分、何ですか。自分で書いたけどどういう理屈で初期化されているのかわかりません。C++プロの方、教えて下さい。 aggregateならメンバ名を指定して初期化する方法があるけど、その延長と考えていいんですかね。参考文献[1]Conic Games「Exponential Idle - Google Play のアプリ」<https://play.google.com/store/apps/details?id=com.conicgames.exponentialidle> [2]Gilles-Philippe Paille「「Exponential Idle」をApp Storeで」<https://apps.apple.com/jp/app/exponential-idle/id1538487382> [3]ちゃそ「Exponential Idle #2 矢印パズル攻略法(と二元体GF(2)上の線形方程式について)」<https://note.com/so_ra_64/n/n9c2eb6a5ef6f> [4]ますたー。/繰り上げP「焼き甜花ちゃんシリーズの作り方&素材」<https://www.nicovideo.jp/watch/sm37861810> [5]いもす研「いもす法」<https://imoz.jp/algorithms/imos_method.html>

> 内容を見る

Unity Polygon
Unity

Unityでサウンド遅延を最低限に抑える ~CRI ADX2の導入とSonicSYNC~

ノーツタッチ時の遅延がなかなか改善できませんでした改善前は、効果音はUnity標準のサウンドアセットを使用していました。この再生方式は、安定した音声を再生するために十分な量のデータをバッファに溜め込んでから出力しています。 これまでBufferingTimeを短くしたり、音声データの先頭余白をギリギリまでカットしたりすることで、体感上遅延が少なくなるように工夫してきましたが、ノーツが多く同時に効果音が再生される箇所では遅延が若干感じられました。 ▼ ノーツの少ない箇所(遅延が小さい) ▼ ノーツの多い箇所(遅延が大きい) プロセカで使われている SonicSYNC 技術の解説動画を発見しましたたまたまチャンネル登録していた CA.Developer チャンネルで、「プロジェクトセカイ」の音声再生の仕組みとSonicSYNCで変わったこと | CA.Unity#3 という動画が、5日ほど前に投稿されました。 「プロジェクトセカイ」の音声再生の仕組みとSonicSYNCで変わったこと | CA.Unity#3 https://www.youtube.com/watch?v=7ql4CNBehes SonicSYNC とは、「スマートフォンでの音声再生遅延を限りなくゼロにする機能」で、ハードウェアと同期して即座に音声信号を生成することにより、ソフトウェア処理による再生遅延を限りなくゼロにできる技術です。音声処理が短時間で終わるように細かく分割し、音が必要になった時にすぐに準備して鳴らすという工程を繰り返すことで、これまでのようにバッファーに溜めておく必要がないため遅延が改善されます。 個人が無償で使えるADX2 LEというミドルウェア本来であれば企業向けである ADX2 が有償で提供されているのですが、学生や個人が無償で使うことができる ADX2 LE というものがあります。(コンテンツの売り上げが1,000万円以内であることなど、一定の条件はあります。)今回 ADX2 LE を導入して、ノーツタッチ時の効果音の遅延を改善していきたいと思います。 必要な音声ファイルを生成する今回の ADX2 では、音声ファイルを以下のものに変換する必要があります。 .acf: 環境設定やACBファイルから参照されるキューシート共通の情報を格納する .acb: キュー情報、再生パラメータ、メモリ再生用波形データをキューシート単位でパッキング .awb: ストリーム再生用波形データをキューシート単位でパッキング(ストリームにする場合のみ、今回は使用しない) これらのファイルを生成するために、「CRI Atom Craft」というソフトウェアを使用します。 CRI Atom Craft https://game.criware.jp/learn/tutorial/atomcraft/ 変換元の素材ファイルは以下のものが使用できます。 ファイル種類: WAVファイル(.wav)または AIFFファイル(.aif) チャンネル数: 最大6チャンネル 量子化ビット数: 16ビット 今回は1つのキューシートに、Perfect Great Good の3つのサウンドを登録します。 最後の書き出し時には、Unity Assets出力 にチェックを忘れないようにしましょう。 書き出したファイルは、Unity の CRIWARE からインポートすることで、StreamingAssets フォルダに保存されます。 ハマったところUnity 側で CRI Atom のスクリプトがありますので、読み込んだACFファイル ACBファイル を指定します。 ここで、Don't Destroy On Load のチェックをつけておきます。読み込み後に destroy してしまうと、Sceneを2回目に読み込んだ際(2曲目をプレイする際)にサウンド再生ができなくなります。 そして、Don't Initialize On Awake のチェックを外しておきます。 また、ビルドする際に 強制的にUnity 標準オーディオが無効化 されます。 Unity標準のオーディオが無効化されることで、アプリケーションのCPU負荷およびメモリ使用量を軽減できるそうです。 しかし今回は、プレイ音源は Assetbundle を使用しているため、Unity標準オーディオと共存させます。 CriWareBuildPreprocessorPrefs.asset という設定ファイルを書き出し、Mute Other Audio を解除します。 これでビルド時に、強制的にUnity標準オーディオが無効化されないようになります。 導入後の遅延の測定計測した結果、ノーツをタッチしてから効果音が鳴るまで、約60~70ミリ秒まで遅延を減らすことができました。 CRIWAREの公式サイトでは、iOS・Android共に「画面をタップしてから音が出るまで」53ミリ秒程度と記載されていたので、vivace(製作中の音ゲー)のタッチ入力のアルゴリズムに改善方法があるということになります。 しかし、ノーツが多い時の効果音遅延が劇的に改善されているので、DEMON譜面をプレイされるガチプレイヤーさんたちには大きなアップデートになるのではないでしょうか。もう少しアルゴリズムを改修してからストアにリリースしようと思いますので、お楽しみに〜 ダウンロードはこちらさっきDBをみてみると、登録ユーザー数が299人になっていました! これを見たあなた、ぜひ300番目のプレイヤーになってください AppStore: https://bit.ly/vivace-app-ios GooglePlayStore: https://bit.ly/vivace-app-android

> 内容を見る

プログラミング Polygon
プログラミング

コンピュータ弱者、初めてのシェルスクリプト

恵まれたソフト(illustrator)で初めて作ったカスみたいなサムネそうだ、シェルスクリプト作ろう。きっかけはBlenderのアドオン開発環境を整えようとしたことであった。ただ普段の制作環境とアドオン開発環境を分けたかっただけなのだ。 しかし自分は数行のバッチファイルを書く数分をケチるために最終的に数時間を費やしてしまったのだ。Blenderの設定はどこにある?まず、基本的にWindowsにおいてBlenderの設定ファイルは初回起動時にC:\Users\(ユーザー名)\AppData\Roaming\Blender Foundation\Blender\(バージョン名)に作られる。 詳しい説明は省くが、そこからconfigdatafilescriptsへと派生する。そして次回起動時からそのフォルダにあるファイルの設定を元に新規ファイルが作られる。 今回大事なのはconfigとscriptsである。このフォルダにスタートアップファイル、プリファレンスファイル、プリファレンスでインストールしたアドオン等が含まれているのだ。 単にデスクトップにあるBlenderアイコンをダブルクリックするだけでは、この一つの設定しか使えない。つまり制作環境とアドオン開発環境を分けることができない。 何か方法がないか探してたところ、「環境設定を複数用意する方法・PC/バージョン間で設定を共有する方法【Blender】」という記事を見つけ、方法3.別設定で開く.batファイルを作るを参考にしてバッチファイルを作った。これで一件落着のはずであった。 だが、自分はこう思った。「新しい環境を作るのにいちいちフォルダを作ってちまちまとパスをコピーするのも面倒だし、プログラムを書いてコンピューターに作らせればいいじゃん」 そのせいで苦しむ羽目になったのである。とりあえず書いてみる恥ずかしながら今まで自分はコマンドプロンプトしか使ってなかったので、Windows PowerShellをちゃんと勉強したいという思いがあった(PowerShellのほうがいろいろ出来そうだし)。 そして以下のようなメモをとった。(実際は色々調べながら書いてるので、最初からガチガチに固めて書いたわけじゃない) これを元にPowerShellスクリプトを書いていく。ちなみに自分はVisual Studio Codeを使っている。 まず、スクリプトが引数を受け取るためにはParam() という関数を使えばいいらしいので、Param($name_pref, $name_bat, $version_blender)と書き、blender_add_env.ps1 として保存した。しかしながら、ここで「変数の型どうなってんねん」と感じたので、` $name_pref.Gettype() $name_bat.Gettype() $version_blender.Gettype()と追記し、ターミナルで.\blender_add_env hoge hogehoge 3.0 と実行すると、IsPublic IsSerial Name                                     BaseType -------- -------- ----                                     -------- True     True     String                                   System.Object True     True     String                                   System.Object True     True     Double                                   System.ValueTypeと表示される。$version_belnderがDouble型になってるのがどうも嫌だ。2.93などの中途半端な数字ならまだしも、Blenderのバージョンはもうすぐ3.0になる。このままだと3.0の0の部分は無視されてしまう。それに事故を防ぐために引数は全部String型で受け取りたい。なのでParam(     [string]$name_pref,     [string]$name_bat,     [string]$version_blender )と書き直した。ついでに(個人的に)見やすくした。受け取ったフォルダの名前で現在の階層に新たなフォルダを作る新しいフォルダに移動するconfigとscriptsのフォルダを作るは簡単なので、さっさと調べてNew-Item $name_pref -ItemType Directory Set-Location $name_pref New-Item config, scripts -ItemType Directoryと最初は書いた。しかし「いちいち移動するの無駄じゃん、一行でいけるやろ」と感じたので、New-Item $name_pref/config, $name_pref/scripts -ItemType Directoryと書き直し、さっきのコマンドを再び実行すると、 このように新しいフォルダーがきちんと得られたのでOKである。 次はconfigフォルダとscriptsフォルダの絶対パスを相対パスから得たい。これをするためにはConvert-Pathを使えばいいらしく、$path_config = Convert-Path "$($name_pref)/config" $path_scripts = Convert-Path "$($name_pref)/scripts" $path_config $path_scriptsと書いた。後半の2行は確認用である。例によってあのコマンドを実行すると、 となり、ちゃんと絶対パスが得られることがわかった。 とりあえず空のバッチファイルをNew-Item で作る。$bat_file = New-Item "$($name_bat).bat"そしてAdd-Contentでバッチファイルに追記していく。Add-Content $($bat_file) "set BLENDER_USER_CONFIG=$($path_config)" Add-Content $($bat_file) "set BLENDER_USER_SCRIPTS=$($path_scripts)" Add-Content $($bat_file) "cd C:\Program Files\Blender Foundation\Blender $($version_blender)" Add-Content $($bat_file) "blender.exe"これで終了である。(書くのが面倒くさくて後半投げやりになってしまった)完成だと思った?blender_add_env.ps1だと長ったらしいし文法的におかしい気がするのでaddbleenv.ps1に改名した。 中身はこんな感じである。Param(     [string]$name_pref,     [string]$name_bat,     [string]$version_blender ) New-Item $name_pref/config, $name_pref/scripts -ItemType Directory $path_config = Convert-Path "$($name_pref)/config" $path_scripts = Convert-Path "$($name_pref)/scripts" $bat_file = New-Item "$($name_bat).bat" Add-Content $($bat_file) "set BLENDER_USER_CONFIG=$($path_config)" Add-Content $($bat_file) "set BLENDER_USER_SCRIPTS=$($path_scripts)" Add-Content $($bat_file) "cd C:\Program Files\Blender Foundation\Blender $($version_blender)" Add-Content $($bat_file) "blender.exe"これでやっと終わりだと思った。現実はそうではなかった。 結論から言うと、さらにこのaddbleenv.ps1を実行するバッチファイルを作らなければならなかった。 というわけで、powershell C:\z_ps_script\addbleenv.ps1 %1 %2 %3という内容のaddbleenv.batを書いた。(説明すっとばしますごめんなさい) これをSystem32にぶち込み、エクスプローラーでaddbleenv hoge hogehoge 2.93と実行してみると ! !! !!! !!!! !!!!! で、出来てる~! ~完~ あとがきはじめてじぶんのためにプログラムをかけたのでうれしかったプログラムのかきかたぐだぐだてすともぐだぐだぶんしょうもぐだぐだよくかんがえたらがぞうむだなのある こーどぶろっくでかけやすいこうだるいこんなくだらないプログラムをかくのにふつかもかかったきちょうなさくひんせいさくじいかんをむだにしたけっかんはしらんおまけ

> 内容を見る