2011-12-05
コミケットのジャンル別サークル数で判断することの無意味さ
myrmecoleon氏の「10月末に書かれた記事」がポインタとして広まってるけど、この記事が抱える問題点について誰も書いてないようなので適当に書きなぐっておく。
夏コミと冬コミでは様々な前提条件が異なる。たとえば「女性向けは大晦日を必ずはずす」「そもそも金土日の開催ではない」「年末である」などなど。
事実「夏コミには参加できるけど冬コミには参加できない」という人もいるので、夏コミ冬コミを同一レイヤーで考えるのは数値的には難しいのではないかと。比較するのであれば「去年の冬コミ」のように1年前、2年前と比較するほうが良いのではないかな。少なくとも1回2回の数字だけで判断することには意味が無いです。
・当落比率の無記載による数値の印象操作
この記事で要点となっているのは「タイバニによってサンライズ枠が大幅に増えた」「東方Project枠が減った」という2点だと思いますが、サークルスペースという有限な数に対して1ジャンルの割り当てが増えれば、その煽りを食らって減るジャンルも当然存在します。
そしてこの煽りを食らうジャンルというのは参加者が多いジャンルほど影響を受けるようになっているのがコミケの当落システムです。
(逆に応募者が極端に少ないジャンルでは100%に近い当選率になるように調整されているらしい)
仮にC80の東方Projectジャンルが当選率70%だとすると、応募総数は「2774/0.7」で3963サークルとなります。この数と同じだけ申し込みがあったと仮定すると、C81の当選率は「2617/3963*100」で66%となります。なお冬も夏と同様70%の当選率だったと仮定すると「2617/0.7」で3739サークル。
- この減った224サークルのうち、何割が別ジャンル(まどマギやタイバニ)に移動したんでしょうか。
- 「夏コミよりも冬コミは受かりづらい」というウワサ通り、実は当選率50%程度だったりするんですかね。
- そもそも4%〜5%の減少って誤差や外部要因って考えるほうが自然だと思うんですけどね。(俺は東方Projectが配置可能数的に上限に達してて、当選数キャップしてんじゃねーかなーと見てます)
・男性向けジャンルの数値増加
3日目に配置される男性向けというジャンル。このジャンルのみ非常に特殊で「ギャルゲー・アニメ系・東方・ゲーム系(スクエニやオンラインなど)・週刊月刊誌系・葉鍵・型月」などから18禁同人誌などの活動をするサークルが集合しています。つまりここが増えると関係ジャンルから全般的に削られるという形になるわけですが、C79に対してC81は250スペースほど増えてます。
C80と比較すると減ってますが、タイバニの影響で減らされたスペース数に対して、なんらかの要因による申し込みサークル数の増加があったと考えられます(まどマギやアイマスアニメ?)。
とまぁ適当に考えただけでもこんだけ推論が成り立つので、統計として記事を出すならもう少し条件や抽出情報を細かくしないと使えないんじゃないかなぁ。
2011-01-29
AIMSにおけるデザインパターン 構造の簡略化
あんまり思いつかなかったんですが、たとえば上下端でループするメニューとかですか。
-- メニュー情報を管理するテーブル _LIST = { index = 1; -- 選択中のインデックスを指定 next = next_index; -- 次のインデックスを取得する関数に接続 prev = prev_index; -- 前のインデックスを取得する関数に接続 }; function menu_OnStart() _LIST.index = 1; -- 必ず一番上にする(記憶したい場合はどっか保存しましょう) for i=1,3,1 do local actor = createActor(G.common.clear, 0, 0, 0); -- テキストアクターにすると管理が楽かも table.insert(_LIST, actor); end end function menu_OnStep() -- 使い方 if( getJoyPressCount(BUTTON_DOWN) == 1 )then _LIST.index = _LIST:next_index(); elseif( getJoyPressCount(BUTTON_UP) == 1 )then _LIST.index = _LIST:prev_index(); end end function menu_OnVanish() for i,v in pairs(_LIST)do if( type(i) == "number" )then vanish(v); _LIST[i] = nil; end end end function next_index(self) local i = self.index + 1; if( i > #self) i = 1; end return i; end function prev_index(self) local i = self.index -1; if( i < 1 ) i = #self; end return i; end
これ動作確認してないのでそのうち適当に動作確認してみます。
代入しなくてもself.indexを書き換えるだけで良かったかもしれない。
2011-01-27
AIMSにおけるデザインパターン スレッド操作
現在DNA-SではシーンのOnStepで複雑なコードを記述することを行っていません。
優先度の問題で、どうしてもOnStepでないといけない場合もありますが、基本的にはOnStepで行う処理というのはかなり簡略化しています。
DNA-Sではグローバル変数として「NEXT_THREAD」「NEXT_SCENE」という2つの変数を常用しています。
名前のとおり「次に動くもの」を定義するのですが、どのようにやっているかというと
function title_OnStart() NEXT_SCENE = nil; -- シーンは空にする NEXT_THREAD = "t_title"; -- 起動するスレッドを指定 startThread(NEXT_THREAD); end function title_OnStep() if( isLoaderThread() or isThreadRunning() )then return; end if( NEXT_SCENE ~= nil)then stopThread(); changeScene( NEXT_SCENE ); -- シーンが指定されている場合は切り替える end startThread( NEXT_THREAD ); NEXT_THREAD = nil; end function title_OnVanish() end function t_title() while( getJoyPressCount(BUTTON_TRIG1) == 0 )do wait(1); end NEXT_THREAD = "t_menu"; end function t_menu() while( getJoyPressCount(BUTTON_TRIG1) == 0 )do wait(1); end NEXT_SCENE = "game"; end
whileの内部をメインループ化してるわけです(全部が全部こういう処理でやってるわけではありませんが)。
これでNEXT_THREADに指定する文字列によって処理を入れ替えると、仕組みを考えるときに楽になります。
リアルタイム性を要求する部分では、この記述はオススメしませんが・・・。
2011-01-20
AIMSにおけるデザインパターン シーン制御
AIMSでゲームを制作する場合に、一番最初に立ちはだかるのはシーン管理でしょう。
シーン管理の方向として私が用いているのは、これから作るゲームの画面遷移を紙に書き出してみることです。
だいたいのゲームは
- ロゴ
- タイトル
- メニュー
という遷移があり、メニューから
- 最初から
- 続きから
- オプション
- 終了する
という4つの遷移に行くのが一般的なものでしょう。
このようにわけてみると
- ロゴ
- タイトル
- メニュー
- ゲーム
- オプション
というシーンに自然と分けられると考えられます(「最初から」と「続きから」は「ゲーム」のパラメータ違いと考えます)。
ですから、まずはこれらのシーンのスクリプトを用意して、画面の記述を完全に分離することからはじめます。
次に、シーンで主体となる入力をどこで判定するかを判断します。
タイトルはボタンを受け付けるだけなのでOnStepで判断しても問題はありません。メニューシーンでも同様に、入力をシビアに取る必要がない場面ではあまり深く考えずに組んでも問題はおきません。
しかしゲーム中でキャラクターが動くための入力は、OnStepで処理を行うと1フレーム遅延を起こします。これは「アクターのOnStepがシーンのOnStepよりも先に処理される」ことに起因しますので、フレーム処理を正しく行う必要があるシーンではアクタークラスの中で入力を受け付けるようにしましょう。
また、処理順をさらに厳密に取るのであれば、レイヤーの順番も考慮する必要があります。シューティングのように当たり判定を正しく取る必要がある場合は、プレイヤーアクターと当たり判定用アクターを別レイヤーに配置するなどの工夫が必要になるでしょう。
シーンの開始時に処理されるOnStartでは、そのシーンでしか使わない画像のロードや変数の代入を行います。逆にOnCloseではOnStartで新たに定義された画像や変数をクリアするのを目的としています。
OnStartであまりに大量のファイルロードを行うと、AIMSが応答なしとなってしまうので、大量のファイルロードを行う場合はLoaderThreadを使用してOnStepで処理を待つようにしましょう。
function title_OnStart() G.title = {}; A.title = {}; startLoaderThread("title_load"); end function title_OnStep() if(isLoaderThread())then -- ロードスレッドが動いている場合は処理を抜ける return; end if(getJoyPressCount(BUTTON_TRIG1) == 1 or getJoyPressCount(BUTTON_TRIG2) == 1 )then changeScene("menu"); end end function title_OnClose() deleteAllGraphic(G.title); -- タイトルで使用した画像を全部破棄 end function title_load() --画像ロードしたりする G.title.base = loadGraphic("gfx/title/base.png"); G.title.title = cutGraphic(G.title.base, 0, 0, 240, 200); G.title.arrow = cutGraphic(G.title.base, 0, 0, 64, 64); G.title.text = cutGraphic(G.title.base, 0, 200, 120, 64); startThread("title_create"); end function title_create() local _G = G.title; -- テーブルは参照コピーされる local _A = A.title; -- テーブルは参照コピーされる _A.title = createActor(_G.create, 320, 120, 1); _A.arrow = createActor(_G.arrow, 120, 400, 2); _A.text= createActor(_G.text, 150, 400); end
こんな感じで。これは全く適当に書いたコードですが、30分ほどで記述しています(多分エラー出ます・・・)。
2010-11-16
いい年した大人が見ず知らずの人にいきなりキレて説教を始める時代
Date: 2010年11月15日3時3分
Twitterでいきなり絡んできた挙句逆ギレした人が20前後の社会人前じゃなくて40前のおっさんだったという事実は笑い話では済まないかもなぁ。
ゲームを作りたいと思って行動することに年齢は関係ないし、作ろうとして動いていることには一定の評価はできるけど、他人を否定するにはあまりに足りない「本人の経験と知識と結果と度量」に怒るというよりは呆れているわけですが、まぁブロックなんてしませんよ。別に間違ったこと言ったつもりもないし。
ただ140文字じゃ相手の考えもわからないし、こちらからの説明には文字数足りないし、結局はTwitterというシステムの前提上、討論の形になってしまうんだよなぁ。そこがもどかしい。
ま、どういう結果を出すかは遠くから見ておきますかね。