2015年9月29日火曜日

HSPでスプライトの試作

昨日書いて、今日もかくという異例の更新頻度を達成したぞ!ヽ( ´¬`)ノ ワ~イ !!
動機などは昨日のブログに書いたが、便利な hspdx を使わずにスプライトを動かしてみたいと思う。
今日はスプライトの細かな仕様を決める前に、実際に処理速度が十分出るかを検証してみた。

基本的には、昨日のプログラムと同じで、パーツ画像をバッファに読み込み、それを重ね合わせてコピーしていく。
昨日は、パーツ画像全体を重ねたが、今回は、キャラ1個分の 24x32 ピクセルだけを重ね合わせて、1個のスプライトを描画する。
そして、そのようなプロセスを繰り返して、スプライトを縦横に画面いっぱいに並べて配置した。


1フレームは 330 ミリ秒だ。
フレームごとに、一度、白い四角で塗りつぶしてからキャラを1つずつ書いている。
それだけだと、画面がちらつくので、ダブルバッファリングをしている。
具体的には一度バッファ(id=9)に書いてからそれをウィンドウ(id=0)にコピー転送した。

おおまかなプログラムはこんな感じだ。
この処理の前に昨日のパーツ画像の読み込みとバッファ(id=9)の初期化処理をしている。

*ScSpTest1
dim frameIdx, 4
frameIdx.0 = 0
frameIdx.1 = 1
frameIdx.2 = 2
frameIdx.3 = 1
for animeFrame, 0, 4, 1
gsel 9 // バッファにかく
color 255, 255, 255
boxf 24 * 0, 32 * 2, 320, 480
for spX, 24 * 0, 320 - 24, 24
for spY, 32 * 2, 480, 32
for partsIdx, 1, 9, 1
pos spX, spY
gmode 2, 24, 32
// 下向きの歩行アニメーション
gcopy partsIdx, frameIdx(animeFrame) * 24 + 0, 0
next
next
next
gsel 0 // バッファからウィンドウに転送
pos 0, 32 * 2
gcopy 9, 0, 32 * 2, 320, 480 - 32 * 2
wait 33
next
goto *SCSpTest1

frameIdx という配列では、 0, 1, 2, 1 をいれている。
これは、アニメーションのフレームの表示の順番だ。
歩行のアニメーションは3フレームだが、
一度真ん中のフレームに戻ったほうが自然にアニメーションするので、
このようにフレームの順番を指定している。

ループが一杯あるが、 animeFrame 変数のループが今いったアニメーションのフレーム番号のループだ。
このループの終わりのところで 330 ミリ秒待って繰り返している。

spX, spY 変数のループは、スプライトを縦横に並べるための描画位置を表すループだ。
24 とか 32 というマジックナンバーはスプライトの幅だ。

partsIdx 変数のループは昨日書いた、パーツ画像の組み合わせのループだ。

テクニックも何もなく素直に書いて実行してみたが、十分速度は足りているようだ。
これから音楽やキャラのAIや判定などを足していったら足りないかもしれないが、ひとまず安心した。

と思ったが、処理時間を計測していなかった。
見た感じはスムーズに見えるので問題なさそうだが、1フレームの描画にかかる時間を計測してみた。
上に出ているのが、1フレームの描画にかかった経過時間だ。単位はミリ秒だ。
330ミリ秒に1回の更新を目標にしているので 13x13=169個のスプライトを15ミリ秒で処理しているのだから、これでいいかもしれない。
30FPSとか60FPSを出すようなアクションゲームを作るならば描画で15ミリ秒はぎりぎりな感じかもしれないが、今回は十分な速さだ。

速度計測には GetTickCount を使った。

// GetTickCount を使うための準備
#uselib "Kernel32.dll"
#cfunc GetTickCount "GetTickCount"

tBegin = GetTickCount() // 計測開始

///// ここに計測する処理をかく。今回は gsel 9の前 ~ wait 33の前まで /////

// 経過時間の表示
tElapsed = GetTickCount() - tBegin
pos 0, 0
color 255, 255, 255
boxf 0, 0, 96, 16
color 0, 0, 0
mes(tElapsed)

tBegin で計測を始めたときの時刻(ミリ秒)を保持する。
処理が終わった時刻から tBegin を差し引いて経過時間 tElapsed を計算する。
timeGetTime のほうが精度は高まるが、手軽なほうを選んだ。

2015年9月28日月曜日

HSPで画像の重ね合わせをしてみた

あいにくの曇り空だったが、スーパームーンを少し見ることができた。
なんとなくだが、月明かりがいつもよりも強い気がする。
中秋の名月とほぼ同時に月が地球に
接近するのは稀らしいので何かありがたい(-人-)

先週ブログを書いていたときに、
ハロウィンにちなんだゲームを作りたいなー
と思っていて、ふわっとした企画を考えて、少し作り始めた。
まだできるかどうかわからないけど、初めてHSPで絵を出せたのがうれしいのでブログを書いてみる。

HSP自体はだいぶ前から知っていて、ちょっとしたGUIのツールを作ったりしていた。
GUIアプリケーションをここまで短く実装できる言語は自分の中ではこれが一番だと思う。
もっと短くできるかもしれないが、ビギナーな私でもこのくらい短くかける。

screen 0, 160, 64 // ウィンドウ作成
button "greet", *OnGreet // ボタン作成&イベント関連付け
stop
*OnGreet // イベント
dialog "Hello!"
stop

バージョンアップして今では WebGL  iOS Android でも実行できる。
そのときは HSP3Dish という環境を使うために #include "hsp3dish.as" でスクリプトを読み込む。
 参照:HSP3Dish プログラミングマニュアル・基本仕様ガイド
制限として、拡張プラグインやCOM/Variant型や外部DLL呼び出しやモジュール変数については未サポートのようだ。

ゲームでスプライトを用いるため es_set などのスプライト用の関数を使いたかったが、これは hspdx という拡張プラグインなので HSP3Dish には対応していないと思う。
そんな理由から、スプライト系の処理を自作しようと思う。
先週ちまちまとドット絵を描いたので、それを HSP のウィンドウに描画してみた。


なんかドット絵を作っている最中は、わりと良く思えたのに、
ウィンドウに出してみると何か微妙(´・ω・`)
ちなみに、キャラは4コマにも描いている大砲ゲーム「お団子キャノン」に出てくるキャラクターだ。
キャラの並び方は、Wolf RPG Editor に同梱されているグラフィック合成器のフォーマットにあわせている。
寄らば大樹の陰。仕様に悩むこともなく説明もしやすい。助かります。


キャラの描画手順

さきほどの画像は1枚の絵ではなく、帽子、頭、肌、服上、服下、靴のパーツにわけて作成している。
作成は有名なドット絵のエディタ Edge を使っている。
これもレイヤーごとに描画ができて、重ね合わせ・アニメーションの確認も簡単にできてとても便利だ。
作ったパーツ画像を複数のバッファに読み込み、それを重ねて表示するプログラムをHSPで作成した。

パーツ画像の読み込み

まずは、パーツ画像をバッファに読み込む。
for i, 0, 8, 1
buffer i + 1
picload imagePath(i)
next

今回は8つのパーツ画像を読み込むため8回処理を繰り返している。
buffer 命令で直後の picload の画像読込先をスクリーンではなくバッファに切り替えている。
引数の番号がバッファの番号だ。

パーツ画像の重ね合わせ描画

読み込んだパーツ画像を、バッファからスクリーンに繰り返しコピー転送する。
gsel 0
for i, 0, 8, 1
pos 0, 0
gmode 2, 320, 480//24, 32
gcopy i + 1, 0, 0
next

gsel 命令で 0 番目のバッファを gcopy の転送先に指定している。
0 番目は screen 命令で作られたウィンドウのバッファだ。
buffer 命令だとウィンドウのないメモリ上だけのバッファになるが、 screen ではウィンドウに表示する画像データを扱うバッファになる。
gmode でコピー転送の方法を指定する。
今回は重ね合わせの際に黒(0, 0, 0)の色を透明色として扱って転送するために、第1引数に 2 を指定した。
これは黒から変えられないようで、 edge で最初白を透明色にしていたのを黒に変更して対応した。
edge のイメージのメニューに特定の色を置き換える項目があってすごく助かった。

今後の予定

単純な処理かもしれないが、右往左往したのできちんと重ねて表示されたときはすごいうれしかった。
作りたいゲームのイメージでは、もっとたくさんのキャラを動かす予定だ。
しかし、1つのキャラでさえも8つのパーツを組み合わせて作り、それを333ミリ秒ごとにアニメーションさせるとなると、実際に処理時間が足りなくなるかもしれない。
次は、アニメーションを手動でやってみる。

こんな出だしで、ハロウィンのころまで間に合うのかわからないが、だめもとでやってみよう。
あと、口調を意図的に変えてみたのだが、これもいつまで続くかわからない。



2015年9月22日火曜日

Unityの出力ファイルの容量を減らす&万歩計4コマ

Unity の出力ファイルの容量を減らす


Unity の出力ファイルの容量を減らしました
というか、今までのファイルの容量が異常に大きかったので普通にしました。

対象は WebPlayer と Google Play 用の apk です。
特に apk については容量がシビアなスマホやタブレットで利用している人が多いので、
こちらをメインにしてやりました。

Google Play で公開している3作品についてサイズを減らしました。
特に最初にリリースした「お団子キャノン」などは
ミニゲームらしからぬけしからんサイズを半分以下にすることができました。

apk      : 39MB → 16MB
WebPlayer : 21MB → 7MB

これでローディングの待ち時間も少しは短くなったと思います。

ダウンロードして、インストールしている方は世界で10人くらいのようなので
かなり需要の少ない対応だけどがんばったよ
世界のインストールしてくれてる10人くらいの人ありがとう!

やったことは主に3つです。
apk の容量のうちわけは apk を作成した直後に、
Unity のコンソール画面右上のメニューから Open Editor Log を開くと見ることができます。
見て驚くのは画像ファイルが無圧縮なのか1ファイルで3MBもくっていたことです。

・使わないフォントファイルの削除

フリーのフォントをまとめていれていたのですが、結局1つしか使っていなかったので他のフォントファイルは削除しました。これで4MB減らすことができました。

・Texture の Max Size の変更

Texture の Max Size プロパティは既定では 1024 でしたが、これを段階的に下げていきます。
下げると画像が荒くなるのですが、わりと 512 くらいまでは問題ありませんでした。
これで1つの画像ファイルが3MBから200KBくらいまでぐぐっとさがりました。
これを複数ファイルに適用するとかなりサイズが減ります。
ここまでサイズが減るとやっていて楽しいです。

・Android の Device Filter の変更

これは apk だけに影響しますが、 10MBくらい軽くなりました。
Player Settings>Android>Other Settings>Device Filter の項目が ARMv7 + x86 となっている値を ARMv7 に変更します。
x86 を外すので対応できなくなる環境があるらしいのですが、かなりレアらしいのでやりました。
その環境の人がいたら残念なのですが、かなりレアらしいので、10MB軽くなる恩恵を選びました。
こないとは思いますが、もしもクレームがきたら android の x86 版を対応するか、 WebPlayer 版などで我慢してもらう形になると思います。

久々に4コマ漫画を描いてみた

4コマ漫画をひさびさに描きました。
最近、活動量計という万歩計に似た商品をプレゼントしたのですが、
そのときに4コマの展開が思い浮かんで夜中までかけて描きました。
Drawing タブでも見ることができます。

最近、迷走気味なのですが4コマ漫画を久々に描いたらちょっとリフレッシュできた気がします。
時期的にハロウィンネタのゲームを作りたいなー。それではまた(´・ω・`)ノシ

2015年9月13日日曜日

CodeIQ のコンテストやったよ。あとQTableViewの続き書いたよ。

有言実行はできないとかっこ悪いので、なるべく無言実行(事後報告)の SakuraCrowd です。
こんばんわ(´#)・ω・`)

今日は CodeIQ の第1回コンテストの締め切りなので、朝からやってました。

どうせやるなら100位以内に自分の名前をいれたいです!(`・ω・´)シャキーン
そこで昨日、挑戦前の説明を呼んだり、ランキングを見て作戦をたてました。

今回のランキングのソート順は、達成率>挑戦回数>作業時間になっています。
処理時間は、遅すぎれば失敗になるかもしれませんが、ランキングには影響しないようです。
あまり時間は意識せず失敗回数を抑えることが重要です。

設問は4つあります。
ランキングを見ると達成率は ALL 100 が当たり前でした。
そして挑戦回数も、ノーミスの4回から1回ミスした5回までの人たちでしめられています。

絶対にミスれない戦いがそこにある!!!(; ・`д・´)…ゴクリ…(`・д´・ ;)
そんな感じです。

ミスれない戦いの結果(; ・`д・´)

結果として19時ころの暫定ランクは


あなたのランキング/得点
ランキング: 現在150位( 628人中)
総合得点: 400
総挑戦回数: 6
合計解答時間: 4:16:58

こんな感じになりました。(´#)・ω・`)2カイミスッタゼ

犬ときじはノーミスでいけたのですが、サルと鬼の問題で1回ずつミスりました。

サルの問題では、複数のネストなのに break を1回しかしていないせいで、ループを脱出失敗してました。
break じゃなくて goto でどーんと exit: ラベルまで飛んでればこのミスは防げたと思います。
はからずも、最近ツイッターで goto もいいよねという話に混ぜさせてもらったばかりだったことを思い出しました。

鬼の問題では、最大値をきちんと確認しないせいで失敗しました。a と b の2変数のループをそれぞれ大きな値からやっていったら最初に見つかった値が最大値だろうと思っていましたが、必ずしもそうはなりません。
例題2個がOKだったので、大丈夫かもと思ってしまいました。(´・ω・`)

このどちらも、例題ではクリアしていて、テストケースの後半で失敗という形でした。
#もっと例題がほしかったよ。゚(゚´Д`゚)゚。

サルの問題については自分で例題を作れるので、
テストケースを増やせばよかったと思います。

鬼の問題は、テストケースを作る方法がわからないのですが、処理速度を気にしすぎ、演算回数を減らしすぎたのがいけなかったと思います。
せっかく CodeIQ 実行くんという実行環境が提供されているので、
遅くてもより確実なものから試していけばよいと思いました。

ランキングには入れませんでしたが
Python 中級と C++ 上級のスキルピースというものがもらえたのでよかったと思います。(´・ω・`)v
なんか、こういう認定証ってうれしいですよね。

競技プログラミングは難しそうなので敬遠していましたが、もっと上達してランキングに入れるようになりたいです。

QTableView の列と行の追加と削除


先週ブログで QTableView の使い方の続きをかくと言ってしまったので、ちょろっと書いておきます。
QTableView の見た目は、
セットしたモデルのデータを変更しただけでは更新されません。

モデルのデータを追加・削除し、
QTableView の beginResetModel() と endResetModel() 
を呼び出すことで行と列の表示が更新されます。

よし、これで有言実行したぞ╭( ・ㅂ・)و ̑̑ グッ


2015年9月6日日曜日

QTableView で表を表示してみる

タイトルは駄洒落を狙っているわけではありません。
こんばんわ SakuraCrowd です。

今回は今作っているソフトの経過報告です。
最近のブログのパターンは、
「XXX作ったよ。これがスクリーンショットね。あとこんなこと思ったよ。」
という流れですが、
今日はできていないソフトの部分的な話なので、
いつもよりもプログラムちっくな話になると思います。(´Ծ_Ծ´)メガネノトキハマジメブッテル

QTableView GUIライブラリ Qt のクラスです。
それを Python で使うための PySide というライブラリを使っています。
某表計算ソフト っぽい表の GUI です。

このデータの日付が 09/01 なのでおそらくその日に
  QTableView 使うぞ!(`・ω・´)
とサンプルデータを作ったのでしょう。

Qt Designer という GUI エディタを使うとポトペタでウィンドウを設計できます。
選択できる GUI の中に QTableView QTableWidget があります。
どちらも上のような表の GUI です。

QTableWidget簡単に値をいれたりできます。
Qt Designer 上で直接編集することができるので、
サンプルの表を簡単に作ることができます。
それに対して、 QTableView は Qt Designer 上では値を編集できません。たぶん。

QTableView の強みは MVC の構造を使えることです。
名前のとおり QTableView は View です。
これにデータを管理している Model オブジェクトを設定して使います。
Model クラスを作る手間がかかりますが、 GUI の細かな操作をしなくても Model に応じた表を表示してくれます。

Model は QAbstractTableModel継承して作ります。
コンストラクタで基底クラスの処理を呼び出し、いくつかの純粋仮想関数をオーバーライドします。

def __init__(self, parent=None, *args):
    QtCore.QAbstractTableModel.__init__(self, parent, *args)
    #自前の処理

def rowCount(self, parent):
    return 行数

def columnCount(self, parent):
    return 列数

def data(self, index, role):
    return index の指すデータの値

rowCount と columnCount はすぐにわかると思いますが、表の行数と列数を返します。

data 関数はちょっと面倒でした。
index はただの要素番号ではなく QModelIndex というクラスのオブジェクトです。
2 次元の座標を表したり、いろいろとメソッドがあります。
行番号と列番号を得るだけならば index.row() と index.column() で取得できます。
role はまだ QtCore.Qt.DisplayRole しか使っていないのですが、要するに表示用のデータとか編集用のデータとか同じセルでもほしいデータの種類がいくつかあるみたいです。
それに応じてそのセルの値を返します。


さらに data メンバ関数は返す値も少しとまどいました。
古い資料などでは QVariant オブジェクトを返すようになっていますが、
そんなクラスはないとエラーになります。
現在は廃止されたようです。
#なんか封印されし古(いにしえ)のクラスってかっこいいかも。
現在は python のプリミティブ型を返したい型に応じて返します。
文字列が返したければ文字列型、無効ならば None を返します。

こんな感じでデータを管理するモデルを作って、 QTableView の setModel でモデルを設定すればデータを上の画像のように表示してくれます。

本当は行の追加と削除も書きたかったのですが、説明が下手で長くなってきたのでとりあえずここまでにします。
ちょっとしたツールを作るつもりが、それで1ヶ月経過とか普通にあるよね?普通だよね?それじゃーまた(´;ω;`)ノシ