=============================================================================== 技術資料「ブロッキング」 ===============================================================================  どういう原理でブロッキングを実装しているかを解説します。ソースの公開は諸事情 により、まだ出来ませんので、文章による説明のみとさせていただきます。  なお、WORKマクロとstatus.hが必要です。注意してください。 ------------------------------------------------------------------------------- 1.  ブロッキングの基本は、システムによる当たり判定が行われる前に、自前で当たり判 定のチェックが出来なければなりません。  調査の結果、1サイクル内の処理順序は以下のようになっていると思われます。 OPT_サイクル SHOT移動 SHOT表示 ACT_??? コマンド解析 SHOT当たり判定 OPT_サイクル2 身体当たり判定  このことからSHOTの当たり判定チェックは、コマンド解析ルーチン内で行うのがいい と思われます。身体当たり判定チェックも同じ箇所で行うのがいいのでしょうが、 「Ryu_R」ではOPT_サイクル2で判定しています。 ------------------------------------------------------------------------------- 2.  接触判定は、直接攻撃の場合は簡単に、自分の赤黄色矩形と相手の青矩形が重なって いるかどうかを調べれば済みます。現サイクルで攻撃判定が発生したかどうかは、 if( 攻撃取得_最多ヒット数( p2 ) > 0 && 攻撃取得_ヒット情報( p2 ) ) ; 以上の条件が真であるかどうかで判定出来ます。  SHOTの場合は、相手のSHOTモジュール全てについて調べなければなりません。これは ポインタと双方向リスト構造の知識がないと少し難しいかもしれません。  相手のSHOTモジュール全てを検索するプログラムのスケルトンを以下に示します。 SHOT *sht; if( ( sht = (SHOT *)WORK( p2 , 0x0f08 , SHOT * ) ) != 0 ) { do { /* 各SHOTモジュールに対する処理を書く */ } while( ( sht = sht->ptr_next ) != 0 ); }  上のプログラムで全てのSHOTモジュールを検索出来るので、それぞれについて接触判 定を行います。Ryu_Rの場合、1回のブロッキングで打ち消せる飛び道具は一つだ けなので、最初の接触しているモジュールを発見した時点で検索を打ち切るようにして います。  存在するモジュール全てについて接触判定をする必要はありません。なぜなら、 「SHOT_TYPE_虚像」といった攻撃判定を持たないモジュールや、無効状態にあるモジュ ールに対して接触判定をする必要はないからです。  接触判定をする条件は sht->shot_type != SHOT_TYPE_虚像 && sht->shot_state != SHOT_STATE_無効 以上に該当するものになります。本来ならSHOT構造体のメンバ friend についても調べ る必要があります。しかし、飛び道具反射の原理がはっきり分かっていないので、これ については無視しています。  次にSHOTの攻撃矩形を取得する方法です。SHOTが右向きの時と左向きの時で計算式が 違うので注意が必要です。 右向き x1 = sht->pos_x + sht->rect_x; x2 = sht->pos_x + sht->rect_x + sht->rect_w - 1; y1 = sht->pos_y + sht->rect_y; y2 = sht->pos_y + sht->rect_y + sht->rect_h - 1; 左向き x1 = sht->pos_x + sht->len_x * 16 - sht->rect_x - sht->rect_w; x2 = sht->pos_x + sht->len_x * 16 - sht->rect_x + 1; y1 = sht->pos_y + sht->rect_y; y2 = sht->pos_y + sht->rect_y + sht->rect_h - 1; 以上の計算式で得られた座標と、自分の赤黄色矩形を比較する事でSHOTモジュールとの 接触を調べる事が出来ます。 ------------------------------------------------------------------------------- 3.  ブロッキング成功時に行うべき処理ですが、これは直接攻撃とSHOTで異なります。 直接攻撃の場合 ・最多ヒット数を−1 ・キャンセル許可が1だったらキャンセル受付を1にする ・VCT_与ダメージ量調整処理を呼び出す ・硬直時間を設定する ・自分を無敵状態にする SHOTの場合 ・SHOT_CMD_対身体衝突を送信する ・VCT_与ダメージ量調整処理を呼び出す ・硬直時間を設定する ・自分を無敵状態にする 以上の処理が必要になります。いくつかは、説明の必要があると思います。 ・VCT_与ダメージ量調整処理を呼び出す 内部的には、攻撃のヒット・ガード時には必ず、このベクタが呼び出されます。ですか ら、相手から見て、攻撃がガードされたと思わせるには、このベクタ呼出が必須です。 ・自分を無敵状態にする 最多ヒット数が2以上の攻撃をブロッキングした場合、1回分の攻撃を打ち消しても2 回目の攻撃判定が残っているので、システムによる当たり判定が行われてしまいます。 これを防ぐには、自分を無敵状態にするか、ヒット情報の登録を取り消さなければなり ません。SHOTのブロッキングでは、自分を無敵状態にするしかないので、それに合わせ ています。  それぞれの実現方法を以下に示します。 ・最多ヒット数を−1 WORK( p2 , SYS_最多ヒット数 , short ) --; ・キャンセル許可が1だったらキャンセル受付を1にする if( WORK( p2 , SYS_キャンセル許可 , short ) ) WORK( p2 , SYS_キャンセル受付 , short ) = ON; ・VCT_与ダメージ量調整処理を呼び出す short dm = 0 , dft = ON; void (*vct)( int , int , short * , short * ); vct = (void *)MMP取得_動作関数( p2 , VCT_与ダメージ量調整処理 ); if( vct ) vct( p2 , p1 , &dm ,&dft ); ・硬直時間を設定する 状態設定_硬直時間( p1 , 10 ); 状態設定_硬直時間( p2 , 10 ); ・自分を無敵状態にする 状態設定_無敵( p1 , IVC_無敵 , 10 + 1 ); ・SHOT_CMD_対身体衝突を送信する Shot送信_コマンド( sht , SHOT_CMD_対身体衝突 , 0 , 0 ); ------------------------------------------------------------------------------- 4.  相手を行動不能にさせる処理ですが、これはかなり不安定なので注意が必要です。  現在はどういった処理で実現しているかというと、 ・動作IDとカウンタの値を取得する ・相手の動作IDを、用意しておいた、何もしない動作IDに変更する ・行動不能が終了する瞬間に、保存しておいた動作IDに変更し、カウンタを設定する  これだけでは、ブロッキングと同時にキャンセルが行われていた場合に対応出来ない ので細工が必要です。キャンセルが行われていたかどうかと、キャンセル後の動作ID を取得しなければなりません。  結論から言うと WORK( p2 , SYS_動作初期化フラグ , short ) == ON ならキャンセルが 行われた事になります。  また、キャンセル後の動作IDは WORK( p2 , SYS_地上複合動作ID , short ) で得 られます。  よって、ブロッキングにより発生したヒットストップの最後のサイクルで行うべき処 理は以下のようになります。 if( WORK( p2 , SYS_動作初期化フラグ , short ) ) { カウンタ保存 = -1; 動作ID保存 = WORK( p2 , SYS_地上複合動作ID , short ); } else { カウンタ保存 = WORK( p2 , SYS_カウンタ , short ); 動作ID保存 = WORK( p2 , SYS_動作ID , short ); } Cmd実行_動作( p2 , ACT_行動不能 ); 行動不能時間 = 10;  そして「ACT_行動不能」は以下のように記述します。 void Act_行動不能( int p1 , int p2 ) { short c = カウンタ設定_初期値( p1 , 行動不能時間 ); if( c >=2 ) ; else if( c >= 1 ) { if( カウンタ保存 < 0 ) { Cmd実行_動作( p1 , 動作ID保存 ); } else { 状態設定_動作( p1 , 動作ID保存 ); WORK( p1 , SYS_カウンタ , short ) = カウンタ保存; } } }  カウンタ保存、動作ID保存、行動不能時間はグローバル変数として宣言された変数 です。 ------------------------------------------------------------------------------ 5.  ブロッキングコマンド解析ですが、原作ではレバガチャによる安易なブロッキング入 力が出来ないようにするため、さまざまな対策が施されています。  ポイントは ・コマンドが入力されたにもかかわらず、ブロッキングが発生しなかった時、再入力無 効時間が発生する。 ・コマンド入力から、受付時間終了までに、不要なレバー入力がされた場合、再入力無 効時間が発生する。 この2点です。これらを踏まえてコマンド解析プログラムを作成すると以下のようにな ります。 void Cmd_上段ブロッキング( int p1 , int p2 ) { if( ブロッキング無効時間 > 0 ) return; if( ブロッキング受付時間 > 0 ) { if( !Stc取得_直接状態( p1 , STICK_前 ) && !Stc取得_直接状態( p1 , STICK_N ) ) { ブロッキング無効時間 = 20; return; } } if( ブロッキング受付時間 == 0 && Stc取得_直接状態( p1 , STICK_前 ) ) { Stc取得_初期化( p1 , 2 ); if( Stc取得_存在( p1 , STICK_N ) ) { ブロッキング受付時間 = 8; ブロッキング方向 = DRCT_上; } } }  下段ブロッキングや空中ブロッキングも同様のコーディングになります。  また、OPT_サイクルもしくはOPT_サイクル2で各カウンタ変数のデクリメントを行う 必要があります。どちらで行っても大差はないのですが、OPT_サイクル2でやることに します。 void Opt_サイクル( int p1 , int p2 ) { switch( ブロッキング無効時間 ) { case 0: break; case 1: ブロッキング受付時間 = 0; break; default: ブロッキング無効時間--; } switch( ブロッキング受付時間 ) { case 0: break; case 1: ブロッキング無効時間 = 10; break; default: ブロッキング受付時間--; } } *原作ではブロッキングコマンド入力後、レバーをニュートラルに戻すと、ブロッキン グ受付時間が延長されるというフィーチャーが存在します。この要素を取り入れるのは いくつかのフラグを用意すればさほど難しくないでしょう。 ------------------------------------------------------------------------------ 6.  上記の処理で、ブロッキングコマンドの判定とブロッキング受付時間の設定、当たり 判定のチェックが出来るのですが、これだけでは不十分な点があります。  ブロッキング受付時間ならいつでもブロッキング可能というわけではありませんから ブロッキング実行可能な条件が必要になってきます。  なぜ、このような二段構えになっているかというと、ブロッキングの先行入力を実現 するためです。  この条件とは、地上なら地上基本技、空中なら空中基本技が出せるかどうか。また、 現在ヒットストップ中であるかどうかの2点です。  これを踏まえて、ブロッキングの判定ルーチンのスケルトンを示します。 void Fnc_地上直接攻撃ブロッキング( int p1 , int p2 ) { HIT_INFO *hit; if( ブロッキング無効時間 > 0 ) return; if( ブロッキング受付時間 == 0 ) return; if( 攻撃取得_最多ヒット数( p2 ) == 0 ) return; if( ( hit = 攻撃取得_ヒット情報( p2 ) ) == 0 ) return; if( 状態取得_硬直時間( p1 ) > 1 || 状態取得_硬直時間( p2 ) > 1 ) return; if( !Cmd条件_地上基本技( p1 , OFF , OFF ) ) return; if( hit->pt == PTGA_上段 && ブロッキング種類 == DRCT_下 || hit->pt == PTGA_下段 && ブロッキング種類 == DRCT_上 ) return; if( !矩形接触判定( p1 , p2 ) ) return; { void (*vct)( int , int , *short , *short ); short dm = 0 , dft = ON; vct = (void *)MMP取得_動作関数( p2 , VCT_与ダメージ量調整処理 ); if( vct ) vct( p2 , p1 , &dm ,&dft ); WORK( p2 , SYS_最多ヒット数 , short ) --; if( WORK( p2 , SYS_キャンセル受付 , short ) ) WORK( p2 , SYS_キャンセル許可 , short ) = ON; 状態設定_硬直時間( p1 , 10 ); 状態設定_硬直時間( p2 , 10 ); 状態設定_無敵( p1 , IVC_無敵 , 10 + 1 ); } } 矩形接触判定()は自分で用意してください。特に難しい処理は必要ないはずです。 void Fnc_地上飛び道具ブロッキング( int p1 , int p2 ) { SHOT *sht; if( ブロッキング無効時間 > 0 ) return; if( ブロッキング受付時間 == 0 ) return; if( 状態取得_硬直時間( p1 ) > 1 || 状態取得_硬直時間( p2 ) > 1 ) return; if( !Cmd条件_地上基本技( p1 , OFF , OFF ) ) return; if( ( sht = SHOT接触判定( p1 , p2 ) ) == 0 ) return; { void (*vct)( int , int , *short , *short ); short dm = 0 , dft = ON; vct = (void *)MMP取得_動作関数( p2 , VCT_与ダメージ量調整処理 ); if( vct ) vct( p2 , p1 , &dm ,&dft ); Shot送信_コマンド( sht , SHOT_CMD_対身体衝突 , 0 , 0 ); 状態設定_硬直時間( p1 , 10 ); 状態設定_硬直時間( p2 , 10 ); 状態設定_無敵( p1 , IVC_無敵 , 10 + 1 ); } } ------------------------------------------------------------------------------  以上で、原作とほぼ同等のブロッキングが実現出来るはずです。しかし、これが最良 とは思えないので、もっと良い方法があったらぜひ教えてください。 ============================================================================== (eof)