意馬心猿 - いばしんえん

欲望のままにプログラミング

OpenGLでパ◯プロを作ってみた

最近、大学の授業でOpenGLを使ったプログラムを習っていて、最終の自由課題で僕が作ったパワプロ風のゲームを紹介します。

f:id:santamalia:20160807193214g:plain

こんな感じで、OpenGLのみで頑張ってパ◯プロっぽくしてみました。

最新のパワプロのように着弾点の円が着弾の数秒前から表示され、徐々に半径が小さくなっていく仕様です。

Blenderなど使えればもっとクオリティは上がるかと思います(その分重くなるかもしれないんですが)。

f:id:santamalia:20160807193707g:plain

 

◆ 使用した言語・ライブラリ等

C++

OpenGL / glut

・VisualStudio 2013 Professional (2015でも多分動きます)

 

◆実行方法

このソース一式は、以下のGithubにアップしているので、是非ともダウンロードしてプレイしてみてください。

github.com

実行方法は、

Windowsの人はVisualStudioでglutを導入して実行してください。

Macの人は標準でOpenGLが入っていますがglutのインクルードの仕方が違うので、main.hの

#include <GL/glut.h>  /* Windows */

コメントアウトし、

//#include <GLUT/glut.h> /* Mac */

のコメントを切ってください。

また、Macの方はコマンドからの実行になります。

コマンドは

cc -o exe Source.cpp -framework GLUT -framework OpenGL -Wno-deprecated

./exe

を叩いてください。

これでもしエラーが出た場合は、テクスチャの読み込みに問題があるので、source.cppの

textureID = loadTexture("grass.bmp");
textureID_score[0] = loadTexture("scoreboard0.bmp");
textureID_score[1] = loadTexture("scoreboard1.bmp");
textureID_score[2] = loadTexture("scoreboard2.bmp");

コメントアウトしてください。芝生などは表示されませんが一応実行できるはずです。その他、僕の場合はsprintf_sなどの"_s"を消したらmacでも実行できました。

 

 ◆ コマンド操作一覧

・投球

     s: ストレート
a: シュート<------ ○ ------> d: スライダー
       / |\
 z: シンカー /    |   \ c: カーブ
          x: SFF

w: Wボール("W"の字に変化する魔球)
q: ライジングキャノン(浮き上がる魔球)

 

・バッティング

- 方向キー(←↑→↓)でミートカーソル移動
- Enterキーでスイング

 

・視点移動

- U: 視点が上に移動
- D: 視点が下に移動
- R: 視点が右に移動
- L: 視点が左に移動
- S: 投手目線、打者目線の切り替え

 

f:id:santamalia:20160807194621p:plain

 

・マウス操作
-マウス操作でカメラの角度を調節可

 

・終了
- q または esc で終了

 

 ◆ プログラムの概要

1. アニメーションはglutIdleFunc()を使用

glutIdleFunc()はCPUが暇なときに画面を更新する関数です。はじめは、球を放物運動させる際に、

px += vx;

vy += 0.5;

py += vy;

のように、IdleFunc( )によって更新されるたび、差分を足していくことで軌道を計算していましたが、これだと、のちに着弾点の座標を計算すつ際に誤差が蓄積していくことがわかりました。

そこで、このプログラムではdisplay関数の中で、

tn++;

dt = 0.01;

t = dt * tn;

とすることで、double型の変数t が更新されるたび0.01ずつ増えていって、あたかも時間が経過しているかのように見せかけています。こうすることにより、軌道の計算が、実際の物理の方程式で記述することができ、誤差を極小にすることができました。

実際の軌道計算はソースコード633行目で

px = vx * (t - 2.0) + QX;
py = 0.5 * G * (t - 2.0) * (t - 2.0) + vy * (t - 2.0) + QY;
pz = 0.5 * az * (t - 2.0) * (t - 2.0) + vz * (t - 2.0) + QZ;

というふうになっています。高校物理で習う水平投射の公式です。

以下は変数の説明です。座標はカメラが向いているのがZ軸の方向で、カメラの左右がX軸の方向で、カメラの上下がY軸、となっています。

px, py, pz はそれぞれ球のX, Y, Z座標で、vx, vy, vz はX, Y, Z 軸成分の速度、Gは重力加速度、az はZ軸方向の加速度、tが投球からの経過時間です。

( t - 2.0 )となっているのは、投球のモーション分の2.0秒ずらしているためです。

 

2. 着弾点の座標の計算

 このプログラムは、ピッチャーが投げ込む球のコースは乱数によって決められています。具体的には、Y軸との角度で高め低め、X軸との角度で内角外角、そして初速度によって球速が決まります。これら3つは全て独立した乱数によって決まります。

プログラムでは

 

v = rand() % 20 + vo[n];

theta=(double)( (rand() % 45) * 0.05 + 0.0) / 180 * PI;

phi = (double)( (rand() % 40) * 0.2 - 1.5) / 180 * PI;

と記述しています。

横の角度は0.0 〜 2.25度の間になるようになっています。なぜ-2.25 〜 2.25でないかというと、この投手は右投げのため、投手から見て左に偏るように調節しています。

縦の角度もストライクゾーンの中心から投射されるわけではないので、横の角度と同様に角度に偏りが出るようにしています。

そして、本題の着弾点の座標計算です。着弾点の座標は、上の初速v、横の角度theta、縦の角度phiが決まれば計算することが可能です。

その手順は、

① 着弾する時間( t_achieve )を計算する。

② 着弾時間から、着弾点のX座標( px_achieve )、Y座標( py_achieve )を計算する。

という流れです。

プログラムでは639行目で、

/* 着弾点の x, y座標を計算 */
if (pz < 18.0){
t_achieve = (-vz + sqrt(vz * vz + 2 * az * 18.0)) / az;
px_achieve = vx * t_achieve + QX;
py_achieve = 0.5 * G * t_achieve * t_achieve + vy * t_achieve + QY;
}

としています。

t_achieveの計算は、解の公式を使っています。px は等速直線運動、py は自由落下の式です。また、pz < 18.0 としている理由は、変化球の対応するためです。変化球は、速度をちょっとずつ足す、という処理で実現しているので、球の軌道が変化するたびに、着弾点の座標を計算しなおす必要があるため、Z座標が18より少ない、すなわち、バッターの位置まで到達していない間は、常に計算するように指定しています。

 

3. バットとの衝突

今回、もっとも苦しんだのがこの衝突判定です。実はこのプログラム、審判もいて、画面上部にストライク、ボール、カウント、球種、飛距離を表示しています。ですが、僕の力量不足で、審判の判定は正しく動いていません。もし、改善案があればコメントにてご指南ください。

そして、バットとの衝突判定ですが、これは、変数たくさん用意しました。

まず、スイングしたかしていないかのswinged、衝突した時のみ1となるhitted、打球が飛んでいる間は1となるbatted、以上の3つです。

プログラムでは、

/* 衝突判定 */
if ( (fabs(mx - px_achieve) < 0.1250) * (fabs(my - py_achieve) < 0.1000) * (pz > 15.0) * (pz < 19.0) ){
hitted = 1;
batted = 1;
s_count = 0;
b_count = 0;
printf("hitted!");
tn_hitted = tn;
}

としています。以下変数の説明です。

mx, myはミートカーソルの中心のX, Y座標、px_achieve, py_achieveは先でいったとおり、着弾点の座標です。

つまり、fabs( )でミートカーソルとボールの距離を計算し、それが範囲内で、なおかつ、このif文はkeyboard関数内に記述しているので、スイングするボタン(Enter)が押された瞬間でのボールの位置が15.0 〜 19.0の間であれば、バットに当たったと判定するようになっています。このif文が真なら、hitted, batted に1が入れられ、display関数内で、

/* 衝突時の処理 */
if (hitted){
bat_angle_hitted = bat_angle + 400 * (2 * t_achieve - 2 * swinged_time + 2.15); // ボールと衝突時のバットの角度を計算
//th = 10 * PI / 180;
th = (2 * (bat_angle_hitted - 180) - phi) * PI / 180; /* 打球のY-Z平面となす角度の計算 */
//r = (200 * (py_achieve - my + 0.1) / 0.125) * PI / 180; /* 打球とX-Z平面となす角度の計算 */
r = 195 * PI / 180;

v = -0.3500 / (fabs(mx - px_achieve) + fabs(my - py_achieve) ) * 0.5 * 4;

px = px_achieve;
py = py_achieve;
pz = 18.0;
az = -0.1;

vx = v * sin(th) * cos(r);
vy = v * sin(r);
vz = v * cos(th) * cos(r);

hitted = 0;
printf("th = %f\n dt = %f\n",th,t_achieve - swinged_time + 2.0);
}

にヒットして、ボールの移動の向きを反対にします。また、このif文の最後にhitted = 0;としているため、この処理は必ず1度しか行われません。これが結構厄介でした。

飛距離はhypot( )関数で三平方の定理より計算しています。

以上がプログラムの概要です。

 

◆ 最後に

このプログラムは、審判の判定がおかしかったり、打球のバウンドがおかしかったりと、まだまだ課題が残っているプログラムですが、今回、初めてガチでプログラムを組んでみて、本当におもしろかったです。ゲームを作るのはこんなに大変なのかと、改めて感じたのと同時に、自分はまだまだ未熟だなと痛感しました。後期も頑張らないとな〜。

 

参考:C. ピッチングマシーンの実験

 

以下はsource.cpp全文です。

 

人工知能による画像合成”ostagram”の実行環境を整える【Mac】

 

1. Torch7のインストール

git clone https://github.com/torch/distro.git ~/torch --recursive
cd ~/torch; bash install-deps
./install.sh

この際、以前にhomebrew等でimagemagickwgetなどのパッケージをインストールしている場合、
Error: imagemagick-6.9.3-0_2 already installed
To install this version, first `brew unlink imagemagick`

のようなエラーになるかもしれませんが、その場合はエラーメッセージにしたがって

を実行してください。

また、上の作業が完了したら一度、

th

を実行し

______ __ | Torch7
/_ __/__ ________/ / | Scientific computing for Lua.
/ / / _ \/ __/ __/ _ \ | Type ? for help
/_/ \___/_/ \__/_//_/ | https://github.com/torch
| http://torch.ch
th> exit Do you really want to exit ([y]/n)? y

と表示されるかどうか確認してください。

※Torch7が正しくインストールされていない場合、2.のloadcaffeがインストールできません。

2. loadcaffeのインストール

brew install protobuf
luarocks install loadcaffe

この際
Error: No such keg: /usr/local/Cellar/gnuplotのようなエラーになるかもしれないですが、その場合は

brew install gnuplot

で僕は解決しました。

3. CUDAのインストール

※GeoForce等のGPU搭載PCの人以外は飛ばしてください。

以下からインストーラーをDLして実行。https://developer.nvidia.com/cuda-downloads

4. neural styleのインストール

git clone https://github.com/jcjohnson/neural-style
cd neural-style
sh models/download_models.sh

以上で環境構築の作業はおしまいです。

5. neural-styleの実行

いよいよ最後は実行です。実行は先ほど移動したneural-styleフォルダで行ってください。

 

f:id:santamalia:20160322011548j:plain

f:id:santamalia:20160322011554j:plain

f:id:santamalia:20160322013914p:plain

左がstyle-image01.jpg、右がcontent-image01.jpgです。

th neural_style.lua -style_image /style01.jpg -content_image /content01.jpg -gpu -1
を実行で、下のように合成されました。※CUDAをインストールした人は末尾の"-gpu -1"は入力する必要はありません。

今はGoogle人工知能囲碁で人間に勝利したり、人工知能が激アツです!みなさんもぜひおためしあれ!

【参考URL】

人工知能で画像合成させる「ostagram」実行環境の作り方 - Hacking My Way 〜 itogのhack日記

Torch | Getting started with Torch

https://www.facebook.com/ostagram/

https://research.preferred.jp/2015/09/chainer-gogh/

Rails アプリの日本語化まとめ

  1. https://github.com/svenfuchs/rails-i18n/blob/master/rails/locale/ja.yml > RAWで右クリック → リンク先を別名で保存( ja.ymlでOK )
  2. config > locales に保存
  3. config > application.rb のテキトーな場所にconfig.i18n.default_locale = :ja を追記
  4. rails serverを再起動
  5. localhost:3000で日本語化されてるか確認

Ruby on Rails 4 & Heroku でのアプリ作成手順まとめ

  1. コマンドを二つ開き、一つで
    postgres -D /usr/local/var/postgresを実行しPostgreSQLを起動
  2. rails new [name] -d postgresqlであらかじめpostgreSQLを使うようにアプリを作成
  3. cd [name]
  4. vim Gemfileでgem 'therubyracer', platforms: :rubyの前の#を削除
  5. scaffold等でテキトーに作成
  6. rake db:migrate
  7. rails sでサーバを起動、localhost:3000で動作確認
  8. git init
  9. git add .
  10. git commit -m "[comment]"
  11. vim Gemfileで末尾にgem 'rails_12factor',group: :production を追加
  12. vim Procfileで新規作成、web: bundle exec rails server -p $PORT を書きこむ 
  13. git add . & git commit -m "[comment]" で更新
  14. herokuでアプリを作成していない場合、heroku create [name] でheroku上にアプリを作成
  15. git push heroku masterでheroku上にプッシュ 
  16. heroku addons:add heroku-postgresql でデータベースを設定 
  17. heroku run rake db:migrate でマイグレート
  18. heroku apps:info でアプリの情報を確認
  19. 表示されたURLへアクセス、動作確認

<Ruby/>拡散のシミュレーション

今日は拡散のシミュレートをやってみました。

 

こいつはなかなか苦戦しましたが、なんとか描画できました。

 

 

f:id:santamalia:20160219192037p:plain

 

  • このプログラムは原点(width/2,height/2)にある粒子(半径1pxの円)を乱数でずらして行って、拡散をシミュレートしています。
  • はじめは白で塗りつぶすところの半径を1pxでやっていたので、点が判別できませんでしたが、半径を2pxにすることで解決されました。