1日1本クソゲーを作ってみた
1日1本クソゲーを作りました。たまってきたのでまとめました。
はじめに
私はUnity(とC#)は全くの初心者でJava、Pythonをかじってます。最近、強化学習に興味があり「強化学習+ゲーム」で何か面白いことができそうなので、まず手始めにゲーム環境であるUnityを勉強しようと思いました。
1日1本クソゲー
「Game A Week」という毎週1本のゲームを作るというチャレンジが流行っている中、あえて1日1本のゲームを作るチャレンジをした方がより学習効率がいいのではないか という安直な考えで見切り発車しました。結果、めちゃんこ辛かったです。
1日目 カエルの卵が追いかけてくるゲーム
カエルの卵状のボールが追いかけてくるゲームです。チュートリアルの応用みたいなものです。
なぜかシーンを再読み込みするときに、なぜか全体が暗くなってしまう現象が発生。調べてみると、
すぐ解決。なぜこんな仕様になっているんだろうか・・・
Unity初心者が練習のためにクソゲー作りました。
— 日向亀 (@hinata_game3) 2017年12月11日
ゲームは鬼ごっこゲーです。カエルの卵みたいなのが敵です。
PC専用ブラウザゲームです。
操作:十字キー、リセット:Spacehttps://t.co/3N2AH74Yr6
感想やアドバイス待ってます!#クソゲー#Unity#鬼ごっこ pic.twitter.com/fUQPWqLmns
2日目 缶あて
2日目は缶あてゲームです。昔よくスマホでプレイしたゲームだったので作ってみたくて作りました。プレイしてみたらわかると思うんですけど、とても操作性が悪い。特に上段の缶がかなり当てにくいというクソゲー。操作性のいい世の中のゲームはすごいよ・・・
Unity初心者が練習のために1日でクソゲー作りました。
— 日向亀 (@hinata_game3) 2017年12月12日
ゲームは「缶あて」です。某先輩みたいに見える的に玉をぶつけるという、なんとも業の深いゲームです。 https://t.co/7IEvfBrl6n
PC専用ブラウザゲームです。感想やアドバイス待ってます! #クソゲー #Unity #野獣先輩 pic.twitter.com/ytYSMAFsyw
3日目 迷路
3日目は迷路。Unityと言えば「Unityちゃん」。Unityちゃんが迷路を駆け巡ります。後で気づいたのですが、止まっているのにUnityちゃんが回りだす現象が起きます。まさにクソゲー。速度のみを指定して動かしているので曲がったとき(?)などで加速度が与えられてしまって、何も操作していないと回りだしてしまうようです。この頃は「動かない+寝不足」だったので、夜中のテンションでよくわからない絵を描いてしまいました。
動かし方は以下のサイトを参考にしました。
【Unity講座】3D人型モデルの動かし方をユニティちゃんで学ぶ【Animator】 – Unity初心者向け講座<C#>
「Unity初心者が練習のために毎日クソゲー作る」3日目!!
— 日向亀 (@hinata_game3) 2017年12月13日
「迷路」ゲームです。
Unityちゃんが迷路をひたすら彷徨うクソゲーです。Unityちゃんが動いて感動!!
PC専用ブラウザゲームです。感想やアドバイス待ってます! https://t.co/9wZu4pATMt#クソゲー #Unity #Unityちゃん #迷路 pic.twitter.com/tB0IpOPex2
4日目 マ◯オ
4日目は横スクロールアクションです。マ◯オです。以前、Androidアプリ(ネイティブ)でマ◯オ系のゲームをチャレンジした時は当たり判定が難しく、ブロックにめり込んだり、瞬間移動したりとで開発をあきらめてしまいましたが、Unityを使えば何もせずともブロックにめり込むこともなければ、瞬間移動することもないのでさすがだなと思いました。
ただ、立った壁に触れた状態でジャンプすると昇り続けるというバグがあり、プレイした友達に「カタパルト」と称されるほどの出来のクソゲーです。
「Unity初心者が練習のために毎日クソゲー作る」4日目!!
— 日向亀 (@hinata_game3) 2017年12月15日
「横スクアクション」ゲームです。the定番!
もっとunityちゃんを滑らかに動かす方法ってありますかね?https://t.co/g65aaMYQJK#クソゲー #横スク #sao pic.twitter.com/TOuqiDodco
5日目 避けゲー
宝石の国風の避けゲーです。月人(Unityちゃん)が放ってくる矢(に見立てた赤い棒)を宝石(Unityちゃん)が避けるゲームです(困惑)。
宝石の国ってすごいですよね。けもふれは同じ3dcgアニメでしたが、CG感が強かったと思います(脚本は最高)が、宝石の国は3dcgがとても自然な感じがしませんか!? ストーリも謎が謎を呼ぶ展開で面白い。気になってマンが全部読んじゃいましたが・・・。金剛がまさか人g(殴
矢の向きは宝石がいる方向を向くはずなのですが、月人の反対側だと矢が宝石の方向に向かないバグがあります。
「Unity初心者が練習のために毎日クソゲー作る」5日目!!
— 日向亀 (@hinata_game3) 2017年12月16日
「宝石の国風」ゲーム」です。クオリティはもの凄く低いので、温かい目で見ていただけたら幸いです!https://t.co/dhIZy8IcsZ#クソゲー #宝石の国 #月人 #フォスフォフィライト pic.twitter.com/hzdjUp58DX
6日目 強制横スクロールゲー
強制横スクロールゲームです。落ちたり、赤い棒に当たるとゲームオーバー。
本当は「チャ◯走」のようなゲームを作ろうといましたが時間が足りなく作り方もよくわからないので、結果的にこのゲームで落ち着きました。
「Unity初心者が練習のために毎日クソゲー作る」6日目!!
— 日向亀 (@hinata_game3) 2017年12月19日
「強制横スクロール」ゲームです。クオリティはもの凄く低いので、温かい目で見ていただけたら幸いです!https://t.co/acfQ4F43G0#クソゲー #強制横スクロール pic.twitter.com/uv3CKn7jdc
7日目 ボタン早押しゲー
ボタンを時間内に早く押すだけのゲームです。UnityのButtonを使ったことがなかったので練習ついでで作ってみました。
日本語がTextで表示できなくて悩みました。Unityのエディターの方では普通に表示されていたのに出力したら何も書かれていない状態。
日本語の表示は以下のサイトを参考にしました。
qiita.com
フォントを変えるだけでOK。
「Unity初心者が練習のために毎日クソゲー作る」7日目!!
— 日向亀 (@hinata_game3) 2017年12月20日
「ボタン早押し」ゲームです。制限時間内でボタンの押した数を競うゲームです。
これをゲームというのかも怪しいので、温かい目で見ていただければ幸いです!https://t.co/AlvThao7Dw#クソゲー #早押し #高橋名人 pic.twitter.com/8zXrfv64DE
時間が足りない
帰宅後ゲームを作るのですが、帰宅時間が日によって異なるため、作業時間がまちまちになってしまいます。また、初心者なので何をするにもある程度時間がかかってしまい、基本毎日朝方近くまで作業を続けるという苦行になってしまいました。
まとめ
毎日1本のゲーム完成させるのは学習にはいいかもしれませんが、とてもしんどいです。開発期間が1日しかないので複雑なゲームやバグの修正ができずにアップロードするので、ゲームの完成度が著しく下がります。「Game A Week」のように一週間程度がちょうどいいのかもしれませんね。
ただ、自作ゲームをSNS上に公開することは「いいね」されただけでもモチベが上がりますし、続けていきたいなと思いました。
ポケモントレーナーのためにポケモン図鑑を作る②
二弾、ポケモン図鑑を作っていきます!
前回
ポケモン図鑑の音声を作る
アニメ版では、ポケモン図鑑はポケモンに向けると、その端末自体が自動で喋ってそのポケモンの情報を教えてくれます。なので、ポケモンの判別が終わったら音声でデータをユーザーに伝えられるようにします。
使用ソフト
- cevio体験版
音データ
初代のポケモンのゲームの図鑑の内容をしゃべらせたいと思います。
ポケモン | 台詞 |
---|---|
ヒトカゲ | トカゲポケモン 生まれたときから、尻尾に炎が灯っている。炎が消えたとき、その命は終わってしまう。 |
フシギダネ | 種ポケモン 生まれた時から背中に植物の種があって、少しずつ大きく育つ。 |
ゼニガメ | 亀の子ポケモン 長い首を甲羅の中に引っ込めるとき、勢いよく水鉄砲を発射する。 |
ピカチュウ | ねずみポケモン ほっぺたの両側に小さい電気袋を持つ。ピンチの時には放電する。 |
cevioを用いて、今回判別するポケモン(御三家+ピカチュウ)の図鑑データを音声ファイルにします。
cevio初めて使いましたけど本当にすごいわ。かなり滑らかに発音しますね。
図鑑プログラム
①で作った学習ファイルをKerasに読み込ませて判別します。判別できたら、cevioで作った音声ファイルをpyaudioを用いて再生します。
import keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras import backend as K import numpy as np import glob import cv2 import random import math import time import pyaudio import wave import threading import time import datetime import sys args = sys.argv batch_size = 1 num_classes = 2 epochs = 1 img_rows, img_cols = 224, 224 path = args[1] x_train = [] y_train = [] x_test = [] y_test = [] def sound(path): wf = wave.open(path, "r") p = pyaudio.PyAudio() stream = p.open(format=p.get_format_from_width(wf.getsampwidth()), channels=wf.getnchannels(), rate=wf.getframerate(), output=True) chunk = 1024 data = wf.readframes(chunk) while data != b"": stream.write(data) data = wf.readframes(chunk) stream.close() p.terminate() def makeData(path): img_src = cv2.imread(path) w,h,c = img_src.shape if w < h: img_src = img_src[:,(h-w)//2:(h+w)//2,:] elif w > h: img_src = img_src[(w-h)//2:(w+h)//2,:,:] dst = img_src[:,:,0] dst = cv2.resize(dst,(img_rows, img_cols)) dst2 = img_src[:,:,1] dst2 = cv2.resize(dst3,(img_rows, img_cols)) dst3 = img_src[:,:,2] dst3 = cv2.resize(dst4,(img_rows, img_cols)) x_train.append([ dst.tolist(), dst2.tolist(), dst3.tolist()] ) makeData(path) x_train = np.array(x_train) if K.image_data_format() == 'channels_first': x_train = x_train.reshape(x_train.shape[0], 3, img_rows, img_cols) input_shape = (3, img_rows, img_cols) else: x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 3) input_shape = (img_rows, img_cols, 3) x_train = x_train.astype('float32') x_train /= 255 print('x_train shape:', x_train.shape) print(x_train.shape[0], 'train samples') from keras.models import model_from_json model = model_from_json(open('cnn.json').read()) model.load_weights('cnn.h5') data = model.predict_classes( x_train , batch_size=1, verbose=0) if data[0] == 0: #ヒトカゲ print("ヒトカゲ") print("とかげポケモン") print("たかさ 0.6m") print("おもさ 8.5kg") print("うまれたときから しっぽに ほのおが") print("ともっている。ほのおが きえたとき") print("その いのちは おわって しまう。") sound("data/hitokage.wav") elif data[0] == 1: #フシギダネ print("フシギダネ") print("たねポケモン") print("たかさ 0.7m") print("おもさ 6.9kg") print("うまれたときから せなかに") print("しょくぶつの タネが あって") print("すこしずつ おおきく そだつ。") sound("data/hushigi.wav") elif data[0] == 2: #ピカチュウ print("ピカチュウ") print("ねずみポケモン") print("たかさ 0.4m") print("おもさ 6.0kg") print("ほっぺたの りょうがわに") print("ちいさい でんきぶくろを もつ。") print("ぴんちのときに ほうでんする") sound("data/pikatyu.wav") elif data[0] == 3: #ゼニガメ print("ゼニガメ") print("かめのこポケモン") print("たかさ 0.5m") print("おもさ 9.0kg") print("ながい くびを こうらのなかに") print("ひっこめるとき いきおいよく") print("みずでっぽうを はっしゃする。") sound("data/zenigame.wav")
図鑑実行
ピカチュウをイラストソフトで描いてみましたw
なんだか生気の薄いピカチュウになってしまいました。このピカチュウを判別にかけたいと思います。
実行結果
きちんと「ピカチュウ」と識別されました。動画が無いとわからないとおもいますが、図鑑の説明の音声も再生されました。
C:\Users\aaaaa\Documents\pokemon>python zukan.py C:\Users\aaaaa\Pictures\pikatyu.jpg Using TensorFlow backend. x_train shape: (1, 224, 224, 3) 1 train samples ピカチュウ ねずみポケモン たかさ 0.4m おもさ 6.0kg ほっぺたの りょうがわに ちいさい でんきぶくろを もつ。 ぴんちのときに ほうでんする
しかし、、
イラストはピカチュウでも、背景がオレンジ色(ヒトカゲの色)だとヒトカゲに認識。
ほかの色(青:ゼニガメ、緑:フシギダネ)の場合では、背景色に惑わされること無くピカチュウに判別してくれたのですが、オレンジ色だとうまく判別してくれませんでした。色の要素でもポケモンの種類を決めているような感じがする。
実行結果
C:\Users\aaaaa\Documents\pokemon>python zukan.py C:\Users\aaaaa\Pictures\b_pikatyu.jpg Using TensorFlow backend. x_train shape: (1, 224, 224, 3) 1 train samples ヒトカゲ とかげポケモン たかさ 0.6m おもさ 8.5kg うまれたときから しっぽに ほのうが ともっている。ほのうが きえたとき その いのちは おわって しまう。
実行動画
こんな感じに動作しました。PCのスペックが貧弱なのでtensorflowの読み込みがとにかく遅いです。
新米ポケモントレーナーのためにポケモン図鑑を作ってみましたhttps://t.co/emXaom962y#作ってみた#ポケモン#ピカチュウ #機械学習 pic.twitter.com/ucbL7ex0kG
— 日向亀 (@hinata_game3) 2017年10月29日
おわりに
今回もコードの解説が無くてすいません。これで一応、ポケモン図鑑の情報を伝えられるようになりました。
Deeplearning初心者なので、アドバイスをいただければ幸いです!
ポケモントレーナーのためにポケモン図鑑を作る①
ポケモン図鑑をこれから作っていきたいと思います。まずはポケモンの判別プログラム(CNN)から作っていきます。
次回
とりあえず初代の御三家から
いきなり数百種類のポケモンを判別するのは大変そうだし、何よりデータセットを作るのが大変。なので、御三家(ヒトカゲ、フシギダネ、ゼニガメ)+ピカチュウに絞ります。
ポケモントレーナーが、最初に渡されるポケモンを知らないかもしれませんしね。
使用した環境
今回は以下の環境で学習しました。
お気づきだろうか。
友達に言ったら「へー、Core2quad使ってるんだ(笑)」と冷笑されるほどの糞スペック。
「機械学習をやってみた系」のPC構成では見たことがないぐらいの前世代PC。でも、まだ戦える!!
データの準備
調べてみると、Deeplearningは数千枚オーダーの画像がそれぞれ必要なようです。ですが、数千枚はさすがに面倒なので、Google画像検索でヒットした画像各200,300枚程度をかさ増し(約6倍)して正方形の画像に加工し使っています。
学習
学習ライブラリはTensorflowベースのKerasを用いました。もちろんCPUモードでの学習になります。逆にこの構成のPCって最新のグラフィックボードを増設できるのだろうか。
学習モデルはCNNで、
Keras : 画像分類 : AlexNet – PyTorchのAlexNetを元に使用させていただきました。
AlexNetって本当に優秀ですね。自作のCNNモデルだと全然学習が進まないのに、AlexNetだとどんどん学習が進む。
コードが汚くてすいません。もっと短く書けそう。。
import keras from keras.datasets import mnist from keras.models import Sequential from keras.layers import Dense, Dropout, Flatten from keras.layers import Conv2D, MaxPooling2D from keras.layers import BatchNormalization from keras import backend as K import numpy as np import glob import cv2 import random import math batch_size = 100 num_classes = 4 epochs = 20 img_rows, img_cols = 224, 224 x_train = [] y_train = [] x_test = [] y_test = [] def makeData(path,ans): img_src = cv2.imread(path) dst = img_src[:,:,0] dst = cv2.resize(dst,(img_rows, img_cols)) dst2 = img_src[:,:,1] dst2 = cv2.resize(dst2,(img_rows, img_cols)) dst3 = img_src[:,:,2] dst3 = cv2.resize(dst3,(img_rows, img_cols)) x_train.append([ dst.tolist(), dst2.tolist(), dst3.tolist()] ) y_train.append(ans) label = -1 for i in glob.glob('train/*'): if 'not_use' in i: continue if '.' not in i: label += 1 print(i + "のラベル" + str(label)) for j in glob.glob(i + '/*'): if '.jpg' in j: makeData( j, label) for i in range(200): num = int(random.random() * len(x_train)) x_test.append(x_train.pop(num)) y_test.append(y_train.pop(num)) x_train = np.array(x_train) y_train = np.array(y_train) x_test = np.array(x_test) y_test = np.array(y_test) if K.image_data_format() == 'channels_first': x_train = x_train.reshape(x_train.shape[0],5,img_rows,img_cols) x_test = x_test.reshape(x_test.shape[0], 5, img_rows, img_cols) input_shape = (5, img_rows, img_cols) else: x_train = x_train.reshape(x_train.shape[0], img_rows, img_cols, 3) x_test = x_test.reshape(x_test.shape[0], img_rows, img_cols, 3) input_shape = (img_rows, img_cols, 3) x_train = x_train.astype('float32') x_test = x_test.astype('float32') x_train /= 255 x_test /= 255 y_train = keras.utils.to_categorical(y_train, num_classes) y_test = keras.utils.to_categorical(y_test, num_classes) model = Sequential() model.add(Conv2D(48, 11, strides=3,input_shape=input_shape, activation='relu', padding='same')) model.add(MaxPooling2D(3, strides=2)) model.add(BatchNormalization()) model.add(Conv2D(128, 5, strides=3, activation='relu', padding='same')) model.add(MaxPooling2D(3, strides=2)) model.add(BatchNormalization()) model.add(Conv2D(192, 3, strides=1, activation='relu', padding='same')) model.add(Conv2D(192, 3, strides=1, activation='relu', padding='same')) model.add(Conv2D(128, 3, strides=1, activation='relu', padding='same')) model.add(MaxPooling2D(3, strides=2)) model.add(BatchNormalization()) model.add(Flatten()) model.add(Dense(2048, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(2048, activation='relu')) model.add(Dropout(0.5)) model.add(Dense(num_classes, activation='softmax')) model.compile(loss=keras.losses.categorical_crossentropy,optimizer=keras.optimizers.Adadelta(),metrics=['accuracy']) model.fit(x_train, y_train,batch_size=batch_size,epochs=epochs,verbose=1,validation_data=(x_test, y_test)) model_json_str = model.to_json() open('cnn.json', 'w').write(model_json_str) model.save_weights('cnn.h5'); score = model.evaluate(x_test, y_test, verbose=0) print('Test loss:', score[0]) print('Test accuracy:', score[1])
結果
実行結果は以下のようになりました。
Using TensorFlow backend. train\hitokageのラベル0 train\hushigidaneのラベル1 train\pikatyuのラベル2 train\zenigameのラベル3 (224, 224, 3) (6556, 224, 224, 3) x_train shape: (6556, 224, 224, 3) 6556 train samples 200 test samples Train on 6556 samples, validate on 200 samples Epoch 1/25 6556/6556 [==============================] - 412s - loss: 1.0334 - acc: 0.5961 - val_loss: 1.4738 - val_acc: 0.3400 Epoch 2/25 6556/6556 [==============================] - 408s - loss: 0.6979 - acc: 0.7088 - val_loss: 1.1469 - val_acc: 0.4150 Epoch 3/25 6556/6556 [==============================] - 409s - loss: 0.6265 - acc: 0.7431 - val_loss: 0.9915 - val_acc: 0.5400 Epoch 4/25 6556/6556 [==============================] - 407s - loss: 0.5694 - acc: 0.7677 - val_loss: 0.7536 - val_acc: 0.6650 Epoch 5/25 6556/6556 [==============================] - 409s - loss: 0.5232 - acc: 0.7784 - val_loss: 0.7851 - val_acc: 0.6850 Epoch 6/25 6556/6556 [==============================] - 405s - loss: 0.4937 - acc: 0.7979 - val_loss: 1.2362 - val_acc: 0.5800 Epoch 7/25 6556/6556 [==============================] - 403s - loss: 0.4510 - acc: 0.8208 - val_loss: 0.6217 - val_acc: 0.7450 Epoch 8/25 6556/6556 [==============================] - 403s - loss: 0.4028 - acc: 0.8388 - val_loss: 0.5831 - val_acc: 0.7650 Epoch 9/25 6556/6556 [==============================] - 408s - loss: 0.3928 - acc: 0.8411 - val_loss: 0.4908 - val_acc: 0.8300 Epoch 10/25 6556/6556 [==============================] - 409s - loss: 0.3528 - acc: 0.8565 - val_loss: 1.6967 - val_acc: 0.5750 Epoch 11/25 6556/6556 [==============================] - 407s - loss: 0.3120 - acc: 0.8763 - val_loss: 0.5483 - val_acc: 0.8050 Epoch 12/25 6556/6556 [==============================] - 409s - loss: 0.2787 - acc: 0.8868 - val_loss: 1.0185 - val_acc: 0.7200 Epoch 13/25 6556/6556 [==============================] - 408s - loss: 0.2538 - acc: 0.9012 - val_loss: 2.0394 - val_acc: 0.6150 Epoch 14/25 6556/6556 [==============================] - 406s - loss: 0.2131 - acc: 0.9137 - val_loss: 0.6719 - val_acc: 0.7850 Epoch 15/25 6556/6556 [==============================] - 402s - loss: 0.2005 - acc: 0.9218 - val_loss: 1.2824 - val_acc: 0.6750 Epoch 16/25 6556/6556 [==============================] - 403s - loss: 0.1903 - acc: 0.9253 - val_loss: 1.2046 - val_acc: 0.7050 Epoch 17/25 6556/6556 [==============================] - 408s - loss: 0.1525 - acc: 0.9433 - val_loss: 0.7733 - val_acc: 0.7750 Epoch 18/25 6556/6556 [==============================] - 408s - loss: 0.1384 - acc: 0.9475 - val_loss: 1.4794 - val_acc: 0.6550 Epoch 19/25 6556/6556 [==============================] - 409s - loss: 0.1386 - acc: 0.9466 - val_loss: 0.8382 - val_acc: 0.8100 Epoch 20/25 6556/6556 [==============================] - 408s - loss: 0.1092 - acc: 0.9565 - val_loss: 2.3111 - val_acc: 0.5850 Epoch 21/25 6556/6556 [==============================] - 404s - loss: 0.0981 - acc: 0.9660 - val_loss: 0.9810 - val_acc: 0.7800 Epoch 22/25 6556/6556 [==============================] - 402s - loss: 0.1022 - acc: 0.9620 - val_loss: 1.4912 - val_acc: 0.7400 Epoch 23/25 6556/6556 [==============================] - 403s - loss: 0.0797 - acc: 0.9677 - val_loss: 1.0995 - val_acc: 0.8000 Epoch 24/25 6556/6556 [==============================] - 407s - loss: 0.0895 - acc: 0.9657 - val_loss: 0.7260 - val_acc: 0.8050 Epoch 25/25 6556/6556 [==============================] - 409s - loss: 0.0896 - acc: 0.9687 - val_loss: 1.0438 - val_acc: 0.7750 Test loss: 1.04382741451 Test accuracy: 0.775
少ないデータをかさ増ししたからなのか、精度は77.5%とあまりよくないですが、大体分別出来ているみたいです。よかった。
おわりに
コードを貼り付けただけで特に解説等なくてすいません。以上である程度の精度でポケモンを判別できるようになりました。
汚いコードですいません。Deeplearning初心者なので、アドバイスをいただければ幸いです!
ブログ初投稿
初投稿です。私の趣味(PC、電気工作、プログラミング)のブログです。
色々と書いていこうと思います。
よろしくお願いします。