【アニメ】Re:ステージ! ドリームデイズ♪ 第一話感想【ネタバレあり!】
初めに
はじめまして!ぴーたーです!
記事を見ていただきありがとうございます。このブログではアニメ「Re:ステージ! ドリームデイズ♪」の各話ごとの感想を書いていきます。
特に深い話はせずに、単純に自分の感想を書いていくので、Reステが好きな皆さんと共感しあえたらなーと思ってます!
この記事はオンエアーから1週間後に更新していきますが、
ネタバレをバンバン含むので気になる方はご注意ください> <
この日をずっとまっていた!
この作品とは1年以上前に出会いました。
とある方が作っているアニソンメドレーを聴いていたら、心にぶっ刺さる曲があり、その曲を調べて...
それ以来Reステの曲に魅了され、iTunesで曲を聴き漁るようになりました。そのうちReステの音ゲー「プリズムステップ」にも手を出し、
ガチャで出てくるキャラを見ていると"推し"が決まり、どんどん作品にはまっていきました。
古参とまでは言えませんが、結構前から応援してきた作品がアニメ化されると決まった時はとてもうれしかったです。
ぼくはこの第一話放送日をずっと楽しみに待ってました!!
開幕のStellamaris(ステラマリス)
いよいよアニメが放送される!0時になった瞬間、お世話になっているdアニメストアで再生を開始!
すると開幕でStellamarisの「Brilliant Wings」!この曲が電光掲示板で流れるという少し独特な入り!やばい!良い!もう最高!!
この、Stellamarisというユニットが、ぼくがはじめて出会ったReステのユニットであり”推し”ユニットです!好きなユニットの曲がずっと楽しみにしていたアニメの開幕でかかることの喜び!伝われーーー!!!
正直、開幕は主題歌でもある「Don’t think,スマイル!!」だと思ってました笑(それはそれで飛んで喜んでいたと思いますがぼくの期待を大きく上回ってきました!)
推しキャラの式宮舞菜ちゃんの紹介
ここではぼくの"推し"キャラを紹介します!プリズムステップで初めて引いた最高レアリティキャラということもあり特別な存在のキャラで、普通にぼくの好みの女の子です!めちゃ可愛い!好き!大好き!
まず見た目ですね。可愛い。髪型、スタイル、胸のサイズどこをとっても最高。文句なし!
アニメで動く舞菜ちゃんを見れてぼくは幸せです...泣ける。感動。
しぐさとかやばいですよね。
こことかこことかあとアホげ可愛いアニメ化してくれてありがとう。
ちなみに原作のイラストと比較するとアニメの方がほわーっとしてますね。どっちも可愛いけど原作版の方がちょっとえtt...また、ゲームでも一応キャラボイスは聴けていましたが、アニメでは舞菜ちゃんの声をたくさん聴けます。やばい。声かわいすぎる。生きがい。あー可愛い好き最高。
こんなに好き好き言ってますが、アニメ見るまでは可愛いー好きだー程度だったのですが、アニメ第一話のこのシーンで完全に落ちました。こういうスペックあるのに何かコンプレックスがあって…的な子に弱いんです。一瞬で落ちました。好きもう無理!
感想まとめ
ここまで読んでくれてありがとうございました。アニメの感想というより推しの話をしただけですね。
こいつキモいなーばかだなーって思いながら読んでもらえると嬉しいです。2話以降はもう少しまともな感想が書けるように頑張ります。
正直な話、アニメ化されるのはうれしいけど他のアイドルアニメのn番煎じにならないか心配な部分がありました。
しかし実際にアニメをみてみると、不満に感じる部分は一切ないし、推しは可愛いしで良い意味で期待を大きく裏切られました。今後が楽しみです!
P.S. なぁ副会長、キリっとしてるけどゲームだとみぃみぃ言ってたよな...
おまけ:好きな曲紹介
ここでは好きな曲を紹介します!今回は、KiRaReの4thシングル「宣誓センセーション」です!
PV第2弾でテーマソングになってましたね。ぼくは「いつも大大大 声を大にして叫ぶ 「WE CAN TRY!!」できるよ」という部分がめちゃ好きですね。
KiRaReらしい元気な曲ですね!カラオケでも良く歌います!ポップな感じなのでワンチャン一般勢とカラオケ行ったときにも歌えます!
youtu.be
Siv3D Advent Calendar 2018 Siv3Dと物理エンジンBullet Physicsで3Dゲームを作る
Siv3DとBulletでねねっちのゲームを作る
この記事はSiv3D Advent Calendar 2018 13日目の記事です.
qiita.com
今回は描画やゲームを作る際に便利な関数がそろっているSiv3Dと,物理エンジンのBullet Physicsを組み合わせて簡単なゲームを作成してみました.
作るゲームは,アニメ「NEW GAME!!」でねねっちが劇中で作成していた迷路ゲームです!
劇中では上司であるうみこさんから課題を与えられ,3か月足らずで迷路ゲームを作っていました.またこの際ねねっちはゲームエンジンに頼っていないと発言していたのでUnityUE等は使いません.そのため物理演算にはBullet Physicsを使うことにしました.
Siv3DとBulletについては昨年のAdCにも記事を書いてくださった方がいるので良ければ見てください!!
qiita.com
この記事で伝えたいことは
・描画や細かい関数はSiv3Dが便利で楽!
・Bulletの雰囲気
・憧れのねねっちと同じゲームを作れた
・ぼくがC++を勉強し始め,Siv3Dに出会えたのはねねっちのおかげ
・Siv3DはC++入門に最適
です!他の方ほど濃い内容の記事ではありませんが,
暇つぶし程度に読んでもらえると嬉しいです!!
Bullet Physicsについて
Bullet - Wikipedia代表的な物理演算ライブラリの一つです.
3Dのゲーム制作やシミュレーションを行う上では,
物理演算ライブラリを利用することで作業がかなり楽になるので使い得かと思います.
細かい説明はWikiや昨年のAdCをご覧ください.
いくつか有名なライブラリがあるなかでBulletを選んだ理由は名前がかっこいいからです.
またソースコードが理解しやすかったのも多少理由に含まれます.
作成するゲームの説明
迷路を傾けることでボールをゴールまで運ぶゲームです.作り方・立方体を組み合わせて迷路を作る
・立方体同士は2点で点球結合をすることで締結する
・ユニバーサルジョイントを用いて迷路を十字方向に傾けられるようにする
・ボールは普通の球
シンプルなコードでレンダリングができ,ゲームを作る際に必要な関数がそろっているSiv3Dと物理エンジンBulletを組み合わせて作りました.迷路の壁の座標を決める際にはOpenSiv3Dのscript機能がとても役に立ちました.
・ユニバーサルジョイントについて
簡単にいうと2軸方向に回転することができる継手です.
原点となる物体と迷路をユニバーサルジョイントで繋ぐことで前後左右に迷路を動かせるようになります.
各軸をモータで動かせるように設定することでキー入力などで自在に動かせるようになります.
ソース,ライブラリの説明
ゲームのソースコードはこんな感じです.今後自分の研究でも利用すると思い自分用のヘッダを作成し簡単にBulletを扱えるようにしました.# include "BulletSiv3D.hpp" void Main() { Window::Resize(640 * 2, 480 * 2); Graphics::SetBackground(Color(172, 69, 124)); s3dbt::btBox originBox(Vec3::Zero, Vec3::One, 0, Color(0, 0, 0, 0), false); s3dbt::btBox boardBox(Vec3(0, 3, 0), Vec3(10, 0.25, 10), 100000.0, Color(107, 150, 228)); s3dbt::btDynamicWorld world; world.addRigidBody(originBox); world.addRigidBody(boardBox); s3dbt::btUniversalJoint uniJoint(originBox, boardBox, Vec3::Zero, Vec3::UnitZ, Vec3::UnitX); double range = 0.1; uniJoint.setAngularLowerLimit(Vec3(0, -Math::PiF * range, -Math::PiF * range)); uniJoint.setAngularUpperLimit(Vec3(0, Math::PiF * range, Math::PiF * range)); world.addJoint(uniJoint); uniJoint.setRotationalLimitMotor(s3dbt::btUniversalJoint::AXIS::Z); uniJoint.setRotationalLimitMotor(s3dbt::btUniversalJoint::AXIS::Y); s3dbt::btSphere ball(Vec3(8.5, 5, 8.5), 0.25, 1, Color(141, 144, 179)); world.addRigidBody(ball); Array<s3dbt::btBox> walls; Array<s3dbt::btPoint2PointJoint> wallJoints; { CSVReader csv(L"../FieldEdit/App/resource_realscale.csv"); auto size = static_cast<int>(csv.rows); const double blocksCenterY = 3. + 0.25 + 0.5; auto wallSize = size; walls.reserve(size); wallJoints.reserve(size * 2); for (auto i : step(size)) { auto sizex = csv.get<double>(i, 0); auto sizey = csv.get<double>(i, 1); auto centerx = csv.get<double>(i, 2); auto centery = csv.get<double>(i, 3); walls.emplace_back(Vec3(centerx - 10., blocksCenterY, -centery + 10.), Vec3(sizex, 1, sizey) * 0.5, 1); } for (auto&& wall : walls) { world.addRigidBody(wall); auto boxSize = wall.getSizeSiv3d() * 2; auto boxCenter = wall.getPosSiv3d(); btVector3 btHalfSize(boxSize.x * 0.5, boxSize.y * 0.5, boxSize.z * 0.5); const Vec3 plateSize = boardBox.getSizeSiv3d(); const Vec3 plateCenter = boardBox.getPosSiv3d(); // ベースから左下手前 Vec3 pivotInA(boxCenter.x - btHalfSize.x(), plateSize.y, boxCenter.z - btHalfSize.z()); // 壁の中心から左下手前 Vec3 pivotInB(-btHalfSize.x(), -btHalfSize.y(), -btHalfSize.z()); wallJoints.emplace_back(boardBox, wall, pivotInA, pivotInB); // ベースから右下奥 Vec3 pivotInC(boxCenter.x + btHalfSize.x(), plateSize.y, boxCenter.z + btHalfSize.z()); // 壁の中心から右下奥 Vec3 pivotInD(btHalfSize.x(), -btHalfSize.y(), btHalfSize.z()); wallJoints.emplace_back(boardBox, wall, pivotInC, pivotInD); } for (auto&& joint : wallJoints) { world.addJoint(joint); } } while (System::Update()) { { double force = 100000.; if (Input::KeyG.pressed) { uniJoint.setMotorForce(2, -10, force); } else if (Input::KeyH.pressed) { uniJoint.setMotorForce(2, 10, force); } else { uniJoint.setMotorForce(2, 0, force); } if (Input::KeyY.pressed) { uniJoint.setMotorForce(1, -10, force); } else if (Input::KeyB.pressed) { uniJoint.setMotorForce(1, 10, force); } else { uniJoint.setMotorForce(1, 0, force); } } world.update(); world.draw(); } }
ここではBullet(今回扱う部分のみ)の簡単な説明をします.
・btDynamicWorld
物理シミュレーション空間のクラスです.
このクラスに定義した物体を登録,更新することで衝突判定や物体の座標と角度を計算してくれます.
・btBox
普通の立方体です.できるだけSiv3Dと同じような感覚で使えるようにしましたが実力不足と時間の都合で完璧な再現はできませんでした.
・btSphere
普通の球です
・btPoint2PointJoint
2つの物体を点でつなぎます.それぞれの物体のローカル座標を指定し,その点同士で固定します.
一定の加重がかかったら破断させるように設定できます.
・btUniversalJoint
上でも説明したユニバーサルジョイントです.
拘束する2つの物体,回転軸の座標(anchor),回転軸(axis)を2つ指定します.
回転軸は必ず直交しなければいけないようです.
この拘束は3つの回転軸がそれぞれ何度まで回るのかを指定できます.
回転させる際は,回転軸に対してモータを設定し,角度を指定し力を加えることで回転させることができます.
下のリンクからCloneしていただければゲームを動かせると思います.
github.com
最後に
ここまで見てくださった人ありがとうございます.ぼくがC++を始めたきっかけはNEW GAME!のねねっちです.おそらく一定数ねねっちに憧れてプログラミング始めた人がいるのではと思っています.そんな憧れのプログラマが作っていたゲームを自分も作れるようになったのはC++の入門には最適なSiv3Dのおかげです.Ryo Suzukiさんや開発にかかわっている方々には本当に感謝しております.
また昨年のAdCの記事を見ると,OpenSiv3Dで3次元描画やBulletを使えるようになる日がそのうち来るのかなーと思います!そうなればもっと手軽に3Dゲームを作れるようになるので楽しみですね!!
C++ バイト先で学んだこと-2
今回も学んだことまとめます。
・生ポインタ使わない
複数人でコードを書くときは、生ポインタではなくシェアードポインタを使う。あたりまえかもですが一応...
・ちょっとした関数を作るときは無名名前空間を使う。
あるクラスとかでちょっとした関数を作ったときに、ヘッダーに宣言を書くといろいろ問題になる可能性があるらしい。そういう時は無名名前空間を使います。これはそのファイル内でしか見ることができないネームスペースだから、他のところで再定義されたりすることがなくなります。
//unnamed namespace namespace { void Test() { } }
・constポインタ
const int a = 0; //値がconst const int* p = nullptr; //ポインタの値がconst int* const q = nullptr; //ポインタの場所がconst const int* const c = nullptr; //値もポインタもconst int b = 0; a = 2; //ダメ p = &b; //良い *p = b; //ダメ q = &b; //ダメ *q = b; //良い c = &b; //ダメ *c = b; //ダメ
ここで疑問に思ったのがポインタのconstなんていつ使うのかということです。値がconstは、const関数内とかでたまにconstじゃないときがあるから。
class A { public: A() :test(0) {} int test; }; class B { public: B() : a(), p(std::make_shared<A>()) {} A a; std::shared_ptr<A> p; void set()const { a.test = 1;//ダメ p->test = 1;//できちゃう } };
ポインタの場所がconstの必要性について
調べたけどあんまり解説がなかったので先輩に聞きました。
結論から言うとそれほど必要!って感じではないけど、インターフェースとしてある方が良いという感じです。
ここでいうインターフェースとは、そのクラスや関数を使う人からどう見えるかという意味です。
関数の引数がconst参照だった場合は中で変更がないことを意味していて、非constなら変更があるかもしれないという意味になります。
ちなみに参照じゃない場合はinputにしか使わないということになるのでconstはそもそも必要ありません。使う側からしたらどちらも同じです。
//この二つはconstだろうとそうじゃなかろうとvalは変更されない void func(int val){} /*1*/ void func(const int val){} /*2*/ //これは値が変わるかも void func(MyClass& ins){} /*3*/ //これは値が変わらないことを意味している void func(const MyClass& ins){} /*4*/
※ここで4番の関数と1と2は一緒の意味だから全部1でいいじゃんって思うかもしれません。上でも説明したように1と2は同じですが、4は違います。4は参照を使っていますが、これは無駄なコピーコストをなくすためです。
あとはconstということは確実に何らかの値が入っていることが保証されています。初期化忘れがないのは大事です。
そんなわけでポインタの場所がconstにズバリこれ!といった意味はありません。(もしかしたらあるかもしれないけどぼくの周りにはわかる人いません)
どっか間違ってたら教えてください>
C++ バイト先で学んだこと-1
今回からバイト先で学んだことを自分用兼見てくれる誰か用に書き起こしていきます。
バイトは中小企業でコーディングをしていて、現代的で実用的なC++を少しずつ教わったり勉強したりしてます。そんなわけで学んだことを箇条書きしていきます。
・変数を受けるときは auto を使う!
autoは受ける変数の型を勝手にコピーしてくれるものです。初期化が保証されるという利点があるためなるべくautoで受けたほうがいいらしいです。
MyClass obj; auto tmp = obj; auto mcp = std::make_shared<MyClass>();
・キャスト
C言語チックな(int)みたいな書き方しないでstatic_castを使う!
std::vector<MyClass> mvec; double d = static_cast<double>(mvec.size()); //ほんとはここもautoの方がいい? auto s = static_cast<double>(mvec.size());
・i++ なのか ++i なのか問題
結論から言うと++i。
これはiがただのintなら関係ないけどイテレータとかだとiter++より++iterの方が速い。そんなわけで統一感出すため?に++i
for(int i = 0; i < 5; ++i){ }
・スコープは短く!
一つの関数の中でも、できるだけ変数のスコープは短くする。
例えば単純な例だと
int Func(){ int tmp = 0; int i = 1; tmp += i; return tmp; }
よりも
int Func(){ int tmp = 0; { int i = 1; tmp += i; } return tmp; }
の方がint iが無駄に居座り続けない。
・#define NUMBER 5 みたいな書き方しない!
いろいろ問題があるので定数ならconst int 、0から順番ならenumを使う。#defineだと良くない挙動が起きたりする。マクロとかも上手く使えばいいけど下手に書くと意図しない挙動をするかも...
#define MAX(x, y) ((x) > (y) ? (x) : (y)) int a = 1; int b = 2; MAX(++a, --b);//->((++a) > (--b) ? (++a) : (--b))になっちゃう
こんな感じで箇条書き形式で学んだことをメモしていきます。
LuaとSolをC++から使う(導入、入門)
LuaとSolをc++で使う方法を紹介します。(OSはWindows)
細かいことはいいからとにかく使いたいって人向けです。一応c言語をざっくり理解してる体で書きます。
【導入方法】
・Lua
とりあえず公式サイトに行ってください
Lua Binaries Download
そうしたら、Windows64bitの人は"lua-5.3.4_Win64_dllw4_lib.zip"をダウンロードしてください。2018/1/25現在では5.3.4が最新でしたが、これより新しいのがあったらそちらを落としてください。
てきとうな場所で展開したあと、いい感じの場所に移動させておきます。ぼくの場合は"C:\Program Files (x86)\lua"にincludeフォルダ、liblua53.a、lua53.dllを入れておきました。(C:\Program Files (x86)にluaというフォルダを自分で作りました。)
それではテストプログラムを動かしてみます。
まずは普通のVisual C++のWindowsコンソールアプリケーションのプロジェクトを作成します。
ここからがちょっとだけ重要です。
やることは4つあります。
1つ目
上のほうにあるプロジェクト(P)からプロパティを開きます。
C/C++の全般の追加のインクルードディレクトリに、さっき展開した中身のincludeフォルダのパスを追加します。ぼくの場合はC:\Program Files (x86)\lua\includeです。
2つ目
リンカーの追加のライブラリディレクトリに、libを入れてあるフォルダのパスを追加します。ぼくの場合はC:\Program Files (x86)\luaです。
3つ目
リンカーの入力に、liblua53.aを追加します。
4つ目
このプロジェクトのexeとかが置かれている場所かcppファイルが置かれている場所(ワークスペース?)にlua53.dllを置いておきます。自身がなかったらとりあえずいろんなところに置いておきましょう笑
これで動くようになったと思います。こんな感じの作業はluaに限らずライブラリとかを使う時にはよくやるのでなんとなく覚えておくといいかもです。
それではサンプルを動かしてみましょう。
#include "stdafx.h" #include <lua.hpp> int _tmain(int argc, _TCHAR* argv[]) { lua_State *L = luaL_newstate(); lua_close(L); return 0; }
警告 LNK4272 ライブラリのコンピューターの種類 'x64' がターゲットのコンピューターの種類' x86' と競合しています
こんな感じのエラーがでたら、たぶんダウンロードするときに64と32を間違えたか、ソリューションプラットホームをx86のまま動かしていると思うので、前者ならダウンロードし直し、後者ならx64に変更して再トライしましょう。この記事書いてて思いましたがx64用なのにProgram Files (x86)にフォルダ作ったのおかしいですね笑
エラーが出なければオッケーです。
・Sol
ここにいってsol.hppをダウンロードしましょう。
Releases · ThePhD/sol2 · GitHub
そしたら、sol.hppをプロジェクトのcppとかがある場所にコピペして、既存の項目の追加で追加しましょう。
これで準備ができました。サンプルを動かしてみます。
#include "stdafx.h" #include <lua.hpp> #include "sol.hpp" int _tmain(int argc, _TCHAR* argv[]) { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package); lua.script("print('Hello Work')"); lua_State *L = luaL_newstate(); lua_close(L); return 0; }
たぶんうまくいくと思います。
【サンプル】
最後にサンプルと簡単な解説をしていきます。
まずはcppの方から
#include "stdafx.h" #include <lua.hpp> #include "sol.hpp" #include<iostream> int main() { sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package); lua.script("print('Hello Work')"); lua.script_file("test.lua"); std::cout << lua["a"].get<int>(); std::cout << lua["b"].get<int>(); std::cout << lua["c"].get<int>() << std::endl; std::cout << lua["not_exist"].get_or(999) << std::endl; ret = lua["f"](20); std::cout << ret << std::endl; int ret = lua["f2"](100,200); std::cout << ret << std::endl; ret = lua["f3"](); std::cout << ret << std::endl; auto it = lua["f4"](); }
次はlua。ちなみに.luaファイルは普通に.txtファイルの拡張子を.luaに変えればできると思います。
test.lua
a = 114 b = 514 c = 1919810 local classA = { value = 0, get = function( self ) return self.value end, set = function( self, value ) self.value = value--コメントアウト end, update=function(self,val) self.value=val+self.value return self.value end, } function f(x) return x * 3 end function f2(x,y) return x+y end function f3() return a end function f4() print("PeterPan") print( classA:get() ) classA.set( classA, 1 ) print( classA.get( classA ) ) print(classA.set(classA,0)) print("---------------------") for v = 0, 100 do if v%10==0 then print( classA.update( classA,v ) ) end end print("---------------------") classA.set(classA,1000) print( classA.update( classA,555 ) ) return end
軽い解説します。ほんとに軽いです。
まずはcpp側のこれ。
sol::state lua; lua.open_libraries(sol::lib::base, sol::lib::package);
おまじないです。ぼくもとりあえず動けばいいというスタンスなので現時点では何か知りません。そのうちしっかり理解しようと思います。
lua.script("print('Hello Work')");
これはたぶんprint('Hello Work')と書かれている.luaファイルを読み込んで実行しているのと同じ状況になるんだと思います。lua.script("print(10+100+1)");にしたら111って表示されます。
ここから.luaが関わってきます。
cpp
lua.script_file("test.lua"); std::cout << lua["a"].get<int>(); std::cout << lua["b"].get<int>(); std::cout << lua["c"].get<int>() << std::endl; std::cout << lua["not_exist"].get_or(999) << std::endl;
ここではtest.luaを読み込んで、lua側に書かれている変数a,b,cの値をint型として取得しています。最後の行はもしlua側にnot_existという変数がなかったら999ということにしておくって感じです。
a = 114 b = 514 c = 1919810
lua側は上からプログラムが動くというわけではないので、構文だけ守っててきとうに書きなぐればオッケーです。
cpp
int ret = lua["f2"](100,200); std::cout << ret << std::endl; ret = lua["f3"](); std::cout << ret << std::endl;
function f(x) return x * 3 end function f2(x,y) return x+y end function f3() return a end
簡単な関数を用意して動かしています。こんな感じで書くんですね。最後のreturn aのaはさっき定義した114が入っているやつです。あれグローバル変数なんですね。
最後にいろいろ詰め込んだ関数です。
cpp
auto it = lua["f4"]();
local classA = { value = 0, get = function( self ) return self.value end, set = function( self, value ) self.value = value--コメントアウト end, update=function(self,val) self.value=val+self.value return self.value end, } function f4() print("PeterPan") print( classA:get() ) classA.set( classA, 1 ) print( classA.get( classA ) ) print(classA.set(classA,0)) print("---------------------") for v = 0, 100 do if v%10==0 then print( classA.update( classA,v ) ) end end print("---------------------") classA.set(classA,1000) print( classA.update( classA,555 ) ) return end
ここではclassAというクラスを作ってみました。
メソッドはこんな感じで書けばいいんですね。luaではコメントアウトは--らしいです。
for文は
for v = 0, 100 do --なんか処理 end
こんな感じで書くと0から100まで1ずつ増えていくやつができます。注意することは、0から99ではなく、100まで進むこと、カウンタを+1以外にするにはfor i = 0, 100, 2って書くってことです。
if文は
if v%10==0 then --なんか処理 end
こんな感じで書きます。他の制御文もc言語がわかればなんとなく察することができそうです。あと、たぶんですけどi=i+1を意味する、i++とかi+=1は使えないと思います。
【最後に】
最後まで見てくれてありがとうございます。ここではほんとに触りの触り程度にしか説明をしていません。さらに踏み込んだ内容は自力で頑張って見てください。また、この記事は
qiita.com
qiita.com
これらの記事を参考に書きました。リンク先の記事の方はもっと詳しいことまで書いてあるので良ければ参考にしてみるといいかもです。
luaを始めてから2日くらいですが、正直何に使えばいいのかまだ見えてきません。現段階では、細かいパラメータをluaで管理すれば、ちょっと変更を加えたとしてもコンパイルしなおす手間がない、Pythonでも使えるらしいので移植が楽ってところが良いと思っています。何か質問があれば@peterpanpppまでよろしくです。(答えられるかわかりません笑)
2017年”個人的”アニメランキング
今更感ありますが、みなさん2017年のアニメどうでしたか?ここ数年は心から面白いと思える作品が少なかったのですが,2017年のアニメは良作が多かった気がします.そんなわけで2017年個人的アニメランキングを発表します!(ランクに入る可能性があるアニメは最後まで見たもののみでリタイヤしたものは入れません)
第5位
賭ケグルイ
©河本ほむら・尚村透/SQUARE ENIX・「賭ケグルイ」製作委員会
ギャンブルの強さで地位が決まる高校が舞台のアニメです.私自身ギャンブルが好きということもあり,1話見ただけで引き込まれました.ヒロインの蛇喰夢子がギャンブル強者の生徒会に対戦を挑み,勝ったり負けたりします.特にギャンブルシーンでみられる相手を挑発するシーンは痛快です.ギャンブル特有のいやらしい感じが良く表現されていると思いました.推しは田中海美さんが演じる早乙女芽亜里です.一度はどん底に落とされますが何だかんだ這い上がるところがギャンブラーって感じで好きです.2期決まったので楽しみです。
第4位
Wake Up, Girls! 新章
©Green Leaves / Wake Up,Girls!3製作委員会
現実みがあるアイドルアニメです.2期になって成長したwugちゃんたちが,新たな壁にぶつかり,またその壁を乗り越えて成長していく感じが最高です.推しはもちろん田中海美さんが演じる片山実波さんですが,今期新たに結成した後輩ユニットRun Girls, Run!のメンバーである,林鼓子さんが演じる速志歩ちゃんも推し認定しました.速志歩ちゃんのひたむきな姿勢は見習わないとですね.中の人の演技も新人だけど演技のレベル高くてファンになりました.アニメ興味ないって人は曲だけでも聴いてみてください!
第3位
プリンセス・プリンシパル
© Princess Principal Project
スパイの女の子がいろいろやるアニメです.とにかくプリンセス様がかわいい!アンジェかっこいい!と思っていたアニメですが所々に描かれる描写に細かい対比などが盛り込まれていて見直すごとに新たな発見があります.単純にストーリーも面白く,衝撃を受ける場面がいくつもありました.全体的に丁寧に作られていて安心して楽しめるアニメだと思います.
第2位
この素晴らしい世界に祝福を!2
© 2017 暁なつめ・三嶋くろね/KADOKAWA/このすば2製作委員会
コメディーアニメです.1期に引き続き何も考えずに見て,笑うことができます.主人公のカズマを中心とした日常のような非日常がとにかく面白いです.魔王幹部との対戦シーンも見どころです.もう全部見どころです.気分が乗らないときにこのすばを見ると元気になるので何度も見てしまいます.
ここで見たけど惜しくもランキングに入らなかったアニメを紹介します.
・クジラの子は砂上に歌う
SF系ですがやや鬱っぽいアニメです.見ていると何とも言えない感情になりますが,ストーリーは面白いです.
・地獄少女 宵伽
好きなシリーズの第4期なんですがちょっと短かったなー.面白いには面白いので,見たことない人は1期から見ることをお勧めします.
・魔法使いの嫁
世界観が素晴らしいです.2クールアニメで13話以降を現在放送中です.
・ようこそ実力至上主義の教室へ
ゴリゴリの頭脳バトル系だとおもったのですが、ちょっと萌え方向に寄せてしまったのが残念です。でも最終回のあのシーンは良かったですね。最後まで見てよかったと思いました。まあ見方を変えれば頭脳派バトルと萌えを両方楽しめるってことなのでそういうのが好みの人には良いかもです。
2017年”個人的”アニメランキング第一位を発表します.
第一位
NEW GAME!!
© 得能正太郎・芳文社/NEW GAME!!製作委員会
2017年アニメの中では圧倒的だと思います。2期になって青葉がデザイナーとして成長し、新しいゲームの制作では青葉もメインキャラのデザインをするようになりました。1期と比較してゲーム制作についてより深く描かれており、辛い話もありました。それでも現実を受け入れて前に進もうとする青葉には感動します。
中盤からはインターンに来た新キャラもでてきます。キャラ班のももちゃんこと望月紅葉ちゃんはキャラデザでは負けたくないという結構気が強いタイプの女の子です。ライバル関係になった青葉とももちゃんの絡みは共感できる部分が多いです。最終回とか泣けますよ?
プログラマ班にはツバk...なるっちこと鳴海ツバメさんという家事料理コーディングなんでもできる女の子が加入します。ねねっちのことをこね入社と罵倒しギスギスする場面もありますが、なるっちが背負っているものを知ると応援したくなります。NEW GAME!独特の生々しい人間関係の絡みや背景が最高に面白いです。
ぼくが2期で一番気に入っているのは5話です。新しくキャラリーダーになったひふみんに、進捗報告で鯖を読む青葉とゆん。青葉はそのあとすぐに訂正したけどゆんは言いそびれてしまいました。そんなわけでどうにかして提出期限に間に合わせようとするゆんですが…という前半パート。まさにNEW GAME!って感じです。青葉が八神さんに報告するときの癖で鯖を読むってのがなんか面白かったです。中盤パートでは企画がまとまらずに悩むはじめにゆんと青葉が相談に乗ってあげます。ゆんとはじめのあの距離感がいいですよね笑。そして、後半ではぼくの一押しキャラのほたるんが登場します!あおっちとねねっちとほたるんの3人で温泉に行くのですが、もう最高ですよね。ほたるんはまだサブキャラ感がありますが、もし3期があったら3人にスポットを当てた過去編とか期待しちゃいます。3期来ないかなー
© 得能正太郎・芳文社/NEW GAME!!製作委員会
Siv3D 「Kinectを使ってブロック崩し」をしてみた
研究室にKinectがあったので使ってみました.また,Siv3Dでおなじみのブロック崩しに応用してみました.
・Kinectとは
Kinectとは,マイクロソフトから発売されたジェスチャー・音声認識によって操作ができるデバイスです.(Wikiより引用) Xboxなどに使われています.
1. インストール
簡単にインストール方法を説明します.
Download Kinect for Windows SDK 2.0 from Official Microsoft Download Center
上のurlに行ってもらい,緑の背景に白文字でContinueと書いてあるボタンをクリックします.そうするとexeをダウンロードできるので,exeを実行すればインストールされます.
2. サンプルを動かしてみる
とりあえずSiv3Dのリファレンスに行ってサンプルを動かしてみます.
Kinect v2 の,「デプスとボディを取得する」というサンプルを動かし,特にエラーなどが出なければインストール成功だと思います.
3. 機能紹介
Kinectの機能を紹介し,簡単なサンプルを載せておきます.
・カラー
たぶん普通のカメラです.以下にカラー画像を表示するプログラムを示します.
DynamicTexture colorTexture; std::array<Optional<KinectV2Body>, 6> bodies; while (System::Update()) { if (KinectV2::HasNewColorFrame()) { KinectV2::GetColorFrame(colorTexture); } if (KinectV2::HasNewBodyFrame()) { KinectV2::GetBodyFrame(bodies); } colorTexture.draw(); }
・デプス
リファレンスにあったサンプルで使用していた機能です.深度センサで距離を取得することができます.
DynamicTexture depthTexture; std::array<Optional<KinectV2Body>, 6> bodies; while (System::Update()) { if (KinectV2::HasNewDepthFrame()) { KinectV2::GetDepthFrame(depthTexture); } if (KinectV2::HasNewBodyFrame()) { KinectV2::GetBodyFrame(bodies); } depthTexture.draw(); }
・関節検出
人間の関節を検出することができます.関節の種類は全部で25箇所あり,列挙型で定義がされています.KinectV2.hppファイルにそれぞれの部位の説明が書いてあります.
具体的な関節の座標の取得方法を説明します.下の例では左手のカラー画像上とデプス画像上の座標を取得してみます.
std::array<Optional<KinectV2Body>, 6> bodies; Vec2 ColorLeftHand; Vec2 DepthLeftHand; KinectV2::GetBodyFrame(bodies);//ここでbodiesにデータが入る for (const auto& body : bodies) { if (!body) { continue; } ColorLeftHand = body->joints[V2JointType::HandLeft].colorSpacePos; //左手のカラー座標を取得 DepthLeftHand = body->joints[V2JointType::HandLeft].depthSpacePos; //左手のデプス座標を取得 }
ここで少し注意することがあります.例えばプログラム上で,左手がある場所に〇を書きたいときのことを考えます.もしカラー画像を出力しているときに,デプス画像上の左手の座標に〇を書いても思った場所に〇は書かれないので注意しましょう.カラー画像を出力しているときは,カラー画像上の座標を扱うようにしましょう.
ここではブロック崩しに使う機能をざっくり説明しましたが,ほかにも手の状態を検出したり,音声認識の機能があります.
4. ブロック崩しに応用
Kinectで両手の座標を取得し,バーを動かすことでブロック崩しに応用しました.基本的にサンプルプログラムを組み合わせただけです.
LeftHand = body->joints[V2JointType::HandLeft].colorSpacePos; RightHand = body->joints[V2JointType::HandRight].colorSpacePos;
この2行で両手の座標を取得し,バーの座標としています.起動直後はまだKinectが動いていないことがあるので,スペースキーを押すまで待機するようにしています.
# include <Siv3D.hpp> //ブロックがいい感じに消える struct Spark : IEffect{ Array<std::pair<Vec2, Vec2>> m_particles; Spark(const Point& pos, const Point& size) : m_particles(50) { for (auto& particle : m_particles) { particle.second = Circular(Random(40.0), Random(TwoPi)); particle.first = pos.movedBy(Random(-size.x / 2, size.x / 2), Random(-size.y / 2, size.y / 2)); } } bool update(double t) override { for (const auto& particle : m_particles) { const Vec2 pos = particle.first + particle.second * t + 0.5* t*t * Vec2(0, 120); Triangle(pos, 8.0, particle.second.x).draw(HSV(pos.y - 40).toColorF(1.0 - t)); } return t < 1.0; } }; //数字のエフェクト struct TextEffect : IEffect{ const Font m_font; const int32 m_value; const Vec2 m_from; const double N = 1.0; const bool flg; TextEffect(const Font& font, const int32 value, const Vec2& from, bool f) : m_font(font) , m_value(value) , m_from(from) , flg(f) {} bool update(const double t) override { // N 秒で消える if (t >= N) { return false; } const double alpha = N - t; if (flg) { m_font(L"+", m_value).drawCenter(m_from + Vec2(0, -40 * t), HSV(60 - m_value).toColorF(alpha)); } else { m_font(L"-", m_value).drawCenter(m_from + Vec2(0, -40 * t), HSV(160 - m_value).toColorF(alpha)); } return true; } }; void Main(){ //カラー画像の大きさに合わせる Window::Resize(1920, 1080); //Kinect用 if (!KinectV2::IsAvailable()) { exit(1); } if (!KinectV2::Start()) { exit(1); } DynamicTexture colorTexture; std::array<Optional<KinectV2Body>, 6> bodies; Vec2 RightHand = Vec2(1920 / 2, 1080 / 2); Vec2 LeftHand = Vec2(1920 / 2, 1080 / 2); const double HandRadius = 50; Effect effect; const Font font(40, Typeface::Heavy); int Score = 0; int Combo = 0; int ComboM = 0; const Sound sound(Wave(0.1s, [](double t) { return Fraction(t * 440)*0.5 - 0.25; })); const Size blockSize(40, 20); const double speed = 8.0; Rect bar(180, 20); Rect bar2(180, 20);//このブロック崩しは両手を使うのでバーが二つ必要 Circle ball(960, 540, 8); Vec2 ballSpeed(0, -speed); Array<Rect> blocks; bool start = false; for (auto p : step({ Window::Width() / blockSize.x , 5 })) { blocks.emplace_back((p*blockSize).moveBy(0, 60), blockSize); } while (System::Update()&&!start) { if (KinectV2::HasNewColorFrame()) { KinectV2::GetColorFrame(colorTexture); } if (KinectV2::HasNewBodyFrame()) { KinectV2::GetBodyFrame(bodies); } colorTexture.draw(); if (Input::KeySpace.clicked) { start = true; } } while (System::Update()) { if (KinectV2::HasNewColorFrame()) { KinectV2::GetColorFrame(colorTexture); } if (KinectV2::HasNewBodyFrame()) { KinectV2::GetBodyFrame(bodies); } colorTexture.draw(); if (Input::KeySpace.clicked) { ball.center = Vec2(960, 540); ballSpeed = Vec2(0, -speed); } ball.moveBy(ballSpeed); for (const auto& body : bodies) { if (!body) { continue; } LeftHand = body->joints[V2JointType::HandLeft].colorSpacePos; RightHand = body->joints[V2JointType::HandRight].colorSpacePos; } Circle(LeftHand, HandRadius).draw(Palette::Green); Circle(RightHand, HandRadius).draw(Palette::Green); bar.setCenter((int)LeftHand.x,(int)LeftHand.y); bar2.setCenter((int)RightHand.x, (int)RightHand.y); for (auto it = blocks.begin(); it != blocks.end(); ++it) { if (it->intersects(ball)) { Combo++;//2コンボ以上でエフェクト if (Combo >= 2) { effect.add<TextEffect>(font, Combo, it->pos, true); } Score += Combo; (it->bottom.intersects(ball) || it->top.intersects(ball) ? ballSpeed.y : ballSpeed.x) *= -1; effect.add<Spark>(it->center, blockSize); blocks.erase(it); sound.playMulti(); break; } } for (auto const& block : blocks) { block.stretched(-1).draw(HSV(block.y - 40)); } if (ball.y < 0 && ballSpeed.y < 0) { ballSpeed.y *= -1; } if ((ball.x < 0 && ballSpeed.x < 0) || (Window::Width() < ball.x && ballSpeed.x > 0)) { ballSpeed.x *= -1; } if (ballSpeed.y > 0 && bar.intersects(ball)) { Combo = 0; ballSpeed = Vec2((ball.x - bar.center.x) / 8, -ballSpeed.y).setLength(speed); } if (ballSpeed.y > 0 && bar2.intersects(ball)) { Combo = 0; ballSpeed = Vec2((ball.x - bar2.center.x) / 8, -ballSpeed.y).setLength(speed); } ball.draw(); bar.draw(); bar2.draw(); effect.update();//エフェクト表示 font(L"score : ", Score).draw(Vec2(0, 0), Palette::Black);//スコア表示 } }
このブロック崩しが意外と好評で,大学の文化祭の展示の一部になりました.最終的なプログラムは少し長いのでgithubに置いておきます.プログラムの中でいろいろと画像を使用していますが,権利的な問題が気になったので,ご自身で用意してもらえると助かります.
最後まで見ていただき,ありがとうございました.質問があればいつでも受け付けています.明日は@ongaeshi さんの記事です.よろしくお願いします.