2018振り返りと来年の話
今年振り返り
— ai (@ai_9684_dct) 2018年12月30日
1月 記憶にない
2月 ぷちコン初参加
3月 UE4Fest行った
4月 Niagaraやった
5月 ハッカソン参加した
6月 某逆求人いった
7月 インターンの面接した
8月 インターン行った
9月 ぷちコン参加した
10月 某ゲームの企画練ってた
11月 死んでた
12月 Pythonばっか書いてたら
本当はしっかりした記事を書きたかったのだが年末時間が取れなかったので振り返りのツイートで。年明け時間があればちゃんとした記事を書きたい。
来年は就活に卒研色々忙しくなりそうで、ブログ更新頻度も下がると思われ。それでも最低月1ブログ更新、そして大学卒業までにゲームリリースはしたいなあ。
Python 連番テキストファイルを結合する
test_0.txt, test_1.txt, test_2.txt, test_3.txt...と連番で用意したテキストファイルを順番通りに結合して、test.txtという一つのファイルとして書き出すプログラム。
import glob import os.path # ファイルの結合 def join_file(filePath): fileList = create_filelist(filePath) with open(filePath, 'wb') as saveFile: for f in fileList: data = open(f, "rb").read() saveFile.write(data) saveFile.flush() # 連番ファイルのリスト作成 def create_filelist(filePath): pathList = [] for index in range(100000): filename = file_indexed(filePath, index) # ファイルが存在しなければ終了 if not os.path.exists(filename): break else: pathList.append(filename) return pathList # ファイル名に指定のindex値をふる def file_indexed(filePath, index): name, ext = os.path.splitext(filePath) return "{0}_{1}{2}".format(name, index, ext) if __name__ == "__main__": join_file("testData/test.txt") # 相対パス
まとめ
catでいいやん。
Ubuntu + VSCodeでAsciiDocを書く環境を整える
以前の記事ではWindows上でAsciidocを書く環境を整えたが、Ubuntuでも同じことがしたくなったので導入してみた。今回はその作業メモ。
検証環境
Rubyの導入
Asciidoctorパッケージをインストール
Rubyを導入できればgemコマンドが叩ける。というわけで以下実行。
$ gem install asciidoctor
$ gem install --pre asciidoctor-pdf
$ gem install asciidoctor-pdf-cjk
$ gem install asciidoctor-diagram
VSCodeで.adocのリアルタイムプレビューを有効にする
拡張機能からAsciiDocと検索して最初に表示されるAsciiDoc pluginをインストール。
続いて、Settingを開き(ファイル→基本設定→設定)、検索欄にasciidocと入力。
Use_asciidoctor_jsのチェックを外す。(最近のAsciiDoc pluginの更新でこの設定の名前が変更になった。以前の設定のままでプレビューが効かなくなったらまずここを疑うべき。)
VSCodeを再起動する。
Ctrl
+ Shift
+ P
→ AsciiDoc Open Preview to the side
を実行すればプレビューが出来るようになる。
AsciiDoc + VSCodeでかっこいい文書作る
某方に布教されてAsciiDocにめっちゃハマった。
AsciiDocって
markdownより優れている点
- markdownより高機能。
- 困ったらHTMLタグ直書きとかしなくていい。
- 表セルを結合出来る。
- ソースコード、PlantUMLなどをincludeできる。
- 目次が自動で作られる。
- 使用可能なアイコンが大量にある。
- PDF書き出しした時の見た目が安定&綺麗。
(plantUMLについては過去記事参照)
markdownに劣る点
- 環境構築がダルい
- 普及してない
- はてなブログがasciidocに対応していない
環境構築
- ruby installerをDLし、実行環境を整える
- asciidoctorとエクステンションを追加する
- 以下をコマンドプロンプトで実行
- asciidoctor-diagramはplantUML等をasciidocで利用するためのエクステンション
- asciidoctor-pdfはPDF変換に必要。
gem install asciidoctor
gem install asciidoctor-diagram
gem install --pre asciidoctor-pdf
"AsciiDoc.use_asciidoctor_js": false, "AsciiDoc.asciidoctor_command": "asciidoctor -a outdir=tmp -a imagesdir=tmp -a imagesoutdir=tmp -r asciidoctor-diagram -o-",
これで環境構築完了。.adocフォルダを作って編集していく。
ctrl + K + V
もしくは Ctrl + Shift + P → AsciiDoc: Open preview to the side
でサイドパネルにリアルタイムプレビューが表示される。
HTML&PDF書き出し
- HTMLに書き出す時
asciidoctor [ファイル名].adoc
- PDFに書き出す時
asciidoctor-pdf [ファイル名].adoc
- asciidoctor-pdf等を使う時
asciidoctor -r asciidoctor-diagram [ファイル名].adoc asciidoctor-pdf -r asciidoctor-diagram [ファイル名].adoc
文法
ここで説明するより既にあるリファレンスを見てもらうほうが確実。
- 超基本的な機能に絞って紹介されている記事
- 公式リファレンス
iconについて
- 冒頭に
:icons: font
を追加すればアイコンが使用できる。
NOTE: NOTE TIP: TIP IMPORTANT: IMPORTANT WARNING: WARNING CAUTION: CAUTION
レンダリング結果
- 他にも色々なアイコンが使用できる。有名企業のアイコンもある。
icon:font[] icon:fire[] icon:hand-stop-o[] icon:amazon[] [aqua]#icon:twitter[]#
レンダリング結果
- 使えるフォントはFont-Awesomeに依存。以下のサイトで調べることが出来る。
Icons from Font Awesome, Bootstrap and Google
HTML/PDF書き出しのテーマの変更
- 書き出し時、ヘッダーフッターを設定したり、文字のフォーマットを指定したり、色々な事ができる。
- PDF書き出しのフォーマットを変更したければ、デフォルト設定なら以下のテーマを書き換えることで実現。
C:\Ruby25-x64\lib\ruby\gems\2.5.0\gems\asciidoctor-pdf-1.5.0.alpha.16\data\themes
- 参考サイト
- PDF書き出しのフォーマットを変更したければ、デフォルト設定なら以下のテーマを書き換えることで実現。
OpenSiv3D C++ 反射ベクトルを計算して玉の反射を実現する1
OpenSiv3Dのリファレンスにはブロック崩しのサンプルがある。
https://scrapbox.io/Siv3D/%E3%83%96%E3%83%AD%E3%83%83%E3%82%AF%E3%81%8F%E3%81%9A%E3%81%97
このサンプルでは玉が壁 or ブロックに衝突した時、玉の速度ベクトルのX成分もしくはY成分を反転させることで反射を実現している。
しかし、この方法では斜め壁に対応できていない。
というわけで壁に衝突した時に反射ベクトルを計算して玉を反射させるプログラムを書いた。
コード
# include <Siv3D.hpp> // OpenSiv3D v0.2.5 Vec2 calcReflectVec(const Vec2& direction, const Vec2& normal) { auto L = -direction.normalized(); auto LdotNx2 = 2.0 * L.dot(normal); return (LdotNx2 * normal - L).normalized(); } Vec2 calcLineNormal(const Line& line) { auto dirVec = Vec2(line.begin.x - line.end.x, line.begin.y - line.end.y); auto normal = Vec2(-dirVec.y, dirVec.x); return normal.normalize(); } class Ball { public: Ball() = default; Ball(Vec2 pos, Vec2 v) :circle(pos, 8) ,vec(v.normalize()) {} ~Ball() = default; void update() { circle.moveBy(SPEED * vec.normalize()); } void draw() { circle.draw(); } Circle getCircle() const { return circle; } void bound(const Vec2& normal) { auto n = (vec.dot(normal) >= 0) ? normal : -normal; vec = calcReflectVec(vec, n); } private: Circle circle; const double SPEED = 8.0; Vec2 vec; }; void Main() { Array<Line> lines; lines.push_back(Line(0, 0, 0, Window::Height())); lines.push_back(Line(Window::Width(), 0, Window::Width(), Window::Height())); lines.push_back(Line(0, 0, Window::Width(), 0)); lines.push_back(Line(0, Window::Height(), Window::Width(), Window::Height())); lines.push_back(Line(550, 250, 420, 420)); lines.push_back(Line(0, 150, 220, 200)); Ball ball(Window::Center(), Vec2(-0.7, 0.5)); while (System::Update()) { ball.update(); for (const auto& line : lines) { if (line.intersects(ball.getCircle())) { ball.bound(calcLineNormal(line)); } } ball.draw(); for (const auto& line : lines) { line.draw(); } } }
方針
-L: 玉の速度ベクトル
N : 壁の法線ベクトル
R : 壁に反射した時の玉の反射ベクトル
-LとNがわかれば以下の式でRを求めることができる。
玉のクラスを作成する
class Ball { public: Ball() = default; Ball(Vec2 pos, Vec2 v) :circle(pos, 8) ,vec(v.normalize()) {} ~Ball() = default; void update() { circle.moveBy(SPEED * vec.normalize()); } void draw() { circle.draw(); } Circle getCircle() const { return circle; } void bound(const Vec2& normal) { } private: Circle circle; const double SPEED = 8.0; Vec2 vec; };
bound()は後で実装する。
壁の法線ベクトルを求める
今回、壁はSiv3DのLineクラスを利用する。
壁の端点の座標を減算し、壁の方向ベクトルtを求めることができる。
tと壁の法線ベクトルnは内積を取ると0になる。
ここからnは二通りの解が得られる。
なぜ法線が二種類得られるかというと、壁には表裏があるから。
Vec2 calcLineNormal(const Line& line) { auto dirVec = Vec2(line.begin.x - line.end.x, line.begin.y - line.end.y); auto normal = Vec2(-dirVec.y, dirVec.x); return normal.normalize(); }
ここで、図をもう一度見返してみる。
正しい法線nは、玉の方向ベクトルと鈍角をなす。鈍角をなす2つのベクトルは内積を取ると負の値を取るので、以下の処理を挟んで正しい法線ベクトルを得る必要がある。
auto n = (vec.dot(normal) >= 0) ? normal : -normal;
反射ベクトルを計算する
-L: 玉の速度ベクトル
N : 壁の法線ベクトル
R : 壁に反射した時の玉の反射ベクトル
とした時、
なので、以下の関数で反射ベクトルを求めることができる。
Vec2 calcReflectVec(const Vec2& direction, const Vec2& normal) { auto L = -direction.normalized(); auto LdotNx2 = 2.0 * L.dot(normal); return (LdotNx2 * normal - L).normalized(); }
ここで求めた反射ベクトルをbound()関数で使用する。
void bound(const Vec2& normal) { auto n = (vec.dot(normal) >= 0) ? normal : -normal; vec = calcReflectVec(vec, n); }
壁を作成する
Siv3Dの動的配列Array<T>
を利用してLineを格納する。
Array<Line> lines; //ここからウィンドウ四隅の壁 lines.push_back(Line(0, 0, 0, Window::Height())); lines.push_back(Line(Window::Width(), 0, Window::Width(), Window::Height())); lines.push_back(Line(0, 0, Window::Width(), 0)); lines.push_back(Line(0, Window::Height(), Window::Width(), Window::Height())); //ここまでウィンドウ四隅の壁 lines.push_back(Line(550, 250, 420, 420)); lines.push_back(Line(0, 150, 220, 200));
玉と壁の当たり判定を取る
今回、壁と玉の当たり判定はintersects()
関数を利用する。
壁をRange-based forでArray<Line>
に格納した壁を取り出し、総当たりで当たり判定を取り、衝突していればbound()
を呼び出す。
ball.update(); for (const auto& line : lines) { if (line.intersects(ball.getCircle())) { ball.bound(calcLineNormal(line)); } }
描画する
draw()
を呼び出して描画する。
ball.draw(); for (const auto& line : lines) { line.draw(); }
結果
未解決の問題
壁には厚みがあるので壁の横から衝突した時におかしな動きになってしまう。
まとめ
実装を始めた当初は数式を見てギョッとしたが、冷静に一つ一つ見ていくとそこまで難しいことはやっていない。未解決の問題だけはまだどうしようも出来ていないが。どなたか解決方法があったら教えてください。
参考サイト
C++で浮動小数点の誤差を考慮して等価比較する
<cfloat>
ヘッダ中にあるDBL_EPSILON
を使えば誤差も考慮して2つのdouble値の等価比較ができる。
float型ならFLT_EPSILON
を使えばよい。
#include <cmath> #include <cfloat> //double型のaとbを誤差考慮して比較する bool NearlyEqual(double a, double b) { return abs(a - b) < DBL_EPSILON; } int main() { double a,b; if(NearlyEqual(a,b)) { return 0; } return 1; }
追記(11/07) 故あってUE4の数学ライブラリ読んでたら似たような関数があったので、それに習って書き換えてみる。
struct Math { static constexpr bool NearlyEqual(const float a, const float b, const float err = FLT_EPSILON) { return Abs<float>(a - b) <= err; } static constexpr bool NearlyEqual(const double a, const double b, const float err = DBL_EPSILON) { return Abs<double>(a - b) <= err; } template<typename T> static constexpr inline T Abs(const T a) { return (a >= (T)0) ? a : -a; } };
Unity2018でTestRunnerを使うための参考リンク
現在Unityでゲームを作ってる。
PlantUMLで設計書から作り始めてるんだし、折角ならテストコードも書いてみたいよなー、とTest Runnerを試してみたのだが、Unity2018でTestRunnerの導入方法が大幅に変わっていて、困ったので参考になったサイトをメモ。
(plantUMLについては以前書いた)
Unity2018以降でのTest Runnerの導入方法
大体このサイト読めば解決すると思う。
Test Runnerの使い方
www.slideshare.net
- そもそもテストってなんやねんってところから解説している。若干古い(2015年)情報であることは注意。
- 初めてテストを書く時に参考になる。TestRunnerを使うメリットが分かる。
- Unityテスト完全に理解した勉強会のスライド集。2018年の情報で(記事執筆時点では)新しい情報なのが嬉しい。
実際使ってみて
一々MonoBehaviorでnewしてAttachさせてDebug.Log()させる手間が省けるのは良い。
設計がカッチリ決めてからの開発には向いているが、場当たり的な開発には向いてなそう。(テスト自体そう)