トップページ > 記事閲覧
斜めの壁の実装方法
名前:PP 日時: 2020/10/24 22:56

ゲームにおいて壁を設置したいのですが、真っ直ぐの壁は簡単に実装できるのですが、 斜めの壁の実装方法が分かりません。 これまでに色々なサイトを巡ってきましたが、全て失敗に終わりました。 斜めの壁の実装方法を教えてください。 #include "DxLib.h" int pX = 320, pY = 240; int pW = 16, pH = 16; int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow) { ChangeWindowMode(TRUE); if (DxLib_Init() == -1) return -1; int player = LoadGraph("player.png"); int wall = LoadGraph("wall.png"); while (!ProcessMessage()) { SetDrawScreen(DX_SCREEN_BACK); //描画先をMakeScreen用の画面から裏画面に変更 ClearDrawScreen(); //裏画面のデータを全て削除 if (CheckHitKey(KEY_INPUT_LEFT))pX -= 2; if (CheckHitKey(KEY_INPUT_RIGHT))pX += 2; if (CheckHitKey(KEY_INPUT_UP))pY -= 2; if (CheckHitKey(KEY_INPUT_DOWN))pY += 2; if (pX >= 400)pX = 400; //真っ直ぐの壁の実装 DrawModiGraph(400 + pH, 0, 640, 0, 640, 480, 400+pH, 480, wall, TRUE); //真っ直ぐの壁の描画 DrawModiGraph(0, 0, 200, 0, 70, 480, 0, 480, wall, TRUE); //斜めの壁の描画 DrawGraph(pX, pY, player, TRUE); ScreenFlip(); //裏画面データを表画面へ反映 } DxLib_End(); // DXライブラリ使用の終了処理 return 0; // ソフトの終了 }
メンテ

Page: 1 |

Re: 斜めの壁の実装方法 ( No.1 )
名前:管理人 日時:2020/10/26 00:33

斜めの壁は、斜めの壁の無いゲームのシステムを容易に作れるようなプログラミング力を 持たれた方でも実装の難易度や作業量の増大といった面から早々手を出さない難易度の高い要素ですので、 まずは斜めの壁が無いゲームを作ってみてください
メンテ
Re: 斜めの壁の実装方法 ( No.2 )
名前:kanamaru 日時:2020/10/26 17:35

斜めじゃなければできてるみたいなので大丈夫でしょう。 斜めの壁ってそんなに大変ですかね?実際に作ったことはないですが。 僕が思うに斜めの壁に関する情報をきちんと配列かなんかに入れておけばいくらでもできると思うんですが。 必要な情報は斜めの壁の始点と終点、斜めの壁の式があればできるでしょう。 必要なのは、衝突の検知と衝突のキャンセルです。 衝突の検知は、始点と終点で値の範囲に気をつけつつ、のめりこみを考えると、正方形の向かい合ってる二点が壁となる直線に対して反対側にあればいい。 それは壁となる直線の式をy=ax+bとすると、y-ax-bの符号が逆かどうかで調べられます。 うまく式変形すれば始点と終点だけでもできますが、正直計算が面倒なのでどうせ始点と終点がわかってるなら式の計算をしておきましょう。 で、後は、これで壁との衝突は大丈夫です。 次は衝突のキャンセルですが、ちょっと大変です。 まず、壁の内側にある点を特定します。これに関しては、上記で衝突を調べるときに計算したやつの符号からわかるので簡単です。 正方形の頂点の中で一つだけ符号が違うはずなのでそれを見つけます。(複数の頂点が壁の内側にある場合は考えなくていいでしょう。斜めだとまずないですし) 次に、壁の内側にある点と壁となる直線の距離を特定します。これは有名な点と直線の距離の公式で求まります。 (といっても絶対値にしなくていいです。その方が後で楽です。) で、その距離をcとすると、後はぶつかった方向に応じて処理します。 下記でのcosθとsinθは、aが正の数の時はa=tanθとして計算して、aが負の数の時は-a=tanθとして計算します。 (その方が下で行うときに場合分けが楽になる。どうせ固定値なんで壁の式と同じように保存しておいてもいいかもしれません) 上下でぶつかったときは、c/cosθを引いて、左右でぶつかったときは、c/sinθを引けば衝突がキャンセルできます。 ちなみに斜めでぶつかったときはちょっと面倒で、普通に考えると上記の長さを縦横両方引けばいいんですが、 斜めの場合は上下はc/cosθではなくccosθ、左右はc/sinθではなくcsinθを引きます。 これで衝突のキャンセルができました。 以上で斜めの壁との衝突が実装できると思います。 最初に言いましたが実際に作ったときが無いので自信はありません。 とはいっても上記を書くときに図を書いて計算したのは事実です。 試しに図に書いてみるとイメージがしやすいと思います。
メンテ
Re: 斜めの壁の実装方法 ( No.3 )
名前:NNN 日時:2020/10/27 08:12

>kanamaruさん アクションゲームにおける当たり判定は1:1判定はおっしゃるとおりなのですが、 大体はプレイヤーキャラに対して1:Nに当たり判定の対象があった場合に複雑性が跳ね上がります。 最初に判定すべきオブジェクトを何かしらの計算で求める必要がありますが 矩形のみである場合と多角形が含まれる場合では難易度が大分変わります。 斜め判定がなくてもプレイヤーの移動速度やゲーム性的に処理結果の正解が異なったりしますので アクションゲームの1:Nの時の判定は複雑です 管理人さんもこういった意味を含めてまずは斜め壁なしでの実装を推奨しているのだと思います。
メンテ
Re: 斜めの壁の実装方法 ( No.4 )
名前:kanamaru 日時:2020/10/27 09:12

この手のゲームに多角形はなかなか出てこないでしょう。 wall.pngがどんなのかわかりませんが、質問者さんが書いたプログラムを読む限り 多角形が出てくるようなゲームに思えません。回転もしなさそうですし。 というか多角形で当たり判定が必要があるゲームはあるんでしょうか? たまにシューティングでバリアが多角形なぐらいしか心当たりがありません。 よっぽどのことが無い限り矩形にして判定してると思います。(これは僕がゲームをあんまりやらないから知らないだけかもしれませんが。) だから矩形と直線と楕円ぐらいを考えとけばいいでしょう。(もしかしたら楕円いらないかも。) 直線との衝突は壁の時ぐらいですし、まっすぐの時と斜めの時で処理すればいい。 楕円と直線も中心と直線の距離でいくらでも。 楕円と矩形ですけど、上下と左右で処理を分ければ難しくありません。 矩形どうしは言うまでもありませんよね。 問題は判定すべきオブジェクトですけど、 この手のゲームって画面上のオブジェクトは数が限られているでしょうし 挿入ソートで座標基準でソートされている状態にすれば二分探索で十分できます。 なんなら総当たりでもいうほど速度に問題はないでしょう。 質問者さんの回答次第でしょうか。 とはいってもまずは斜めなしというのもわからなくはないんですが。
メンテ
Re: 斜めの壁の実装方法 ( No.5 )
名前:NNN 日時:2020/10/27 09:44

>kanamaruさん 多角形の所に反応してしまっているのですが、私が難しいと言いたいのは1:N判定の方です 例えば以下のような三角形(斜め部分)と矩形部分の中間あたりで上から下に向かって2つのオブジェクトに接触した場合 凵 三角形から当たり判定補正をしてしまえば斜面に隣接し 矩形から当たり判定補正をしてしまえば矩形の上辺に補正されます どちらが正しいかはゲームの仕様次第ですが矩形のみの構成にすれば考えるケースが少なくなるのは確かなのです 私は質問者様の前の質問からマリオやロックマンみたいなアクションを想定していると思ったので 自身がロックマン風ゲームを作った経験から思わぬ接触判定の難しさを感じたので管理人様の意見に同調した次第です
メンテ
Re: 斜めの壁の実装方法 ( No.6 )
名前:kanamaru 日時:2020/10/27 12:32

まず、質問者さんに質問なのですが、 斜面にプレイヤーが言ったときの挙動が気になります。 僕は、なんていうんでしょうか、地面と平行に移動すると考えていました。 (マリオとかそうですよね?) NNNさんの言う通り斜面に隣接するのであれば、処理がだいぶ変わります。 なので斜面の挙動をどう考えているのか教えてください。 で、NNNさんの話ですが、僕だったらどちらにも補正します。 いい感じに比率を調整して。 たぶんプレイヤーが長方形ならちょうど境目の頂点?がプレイヤーの下辺のどの位置にあるかを用いるかな? そうすればいい感じにプレイヤーが進んでくれるはず。 どっちかに補正するとたぶん挙動に違和感がある気がします。 最後にNNNさんに提案です。質問者さんが回答してないのにこのままは話を進めるのどうかと思うので、 続きは質問者さんが回答してくれてからにしましょう。
メンテ
Re: 斜めの壁の実装方法 ( No.7 )
名前:PP 日時:2020/10/27 17:59

始めは僕もプレイヤーが壁に接触したときに地面と平行に移動する処理をしたかったのですが、 それも含めるとかなり処理がごちゃごちゃしそうなので、 今は取り敢えず壁に接触すれば、壁と平行移動するとかは考えずにプレイヤーの動きが止まるようにすればいいです。
メンテ
Re: 斜めの壁の実装方法 ( No.8 )
名前:usao 日時:2020/10/27 18:33

とりあえず止まればいいなら, > pX += 2; みたいな座標更新処理を, 「ある単位時間の間,等速直線運動した結果として(何とも衝突しないならば)pXが2だけ増える結果となる」 (すなわち,速度2で移動している) と捉えたときに, この単位時間内に最初に衝突する対象を求めれば良いでしょう. (その「衝突時刻」での位置に留めればよい)
メンテ
Re: 斜めの壁の実装方法 ( No.9 )
名前:usao 日時:2020/10/27 19:04

上記の話は,壁の向き(まっすぐだとか斜めだとかいう事柄)に依存しない点に注意されたい. 接触箇所で止めずに「接触した壁に沿う方向に滑らせたい」のであれば, 上記の処理結果で終了せずに, 上記処理で棄却された移動量のうち接触した壁の法線方向成分を除去した上で その場所から「残りの時間」を処理すれば良いだろう. --- …とだけ書くと意味不明だろうから,簡単に「移動する質点と垂直な壁」を例にして説明: ある点が位置(x,y)にいて,ここから(単位時間=1 だけ時間をかけて) (X+10, Y+20) に移動したいとしよう (質問のコード的に言えば, pX+=10; pY+=20; ) ただし,位置x+4にそびえ立つ垂直な壁があるとしよう.(点はx+4よりもx正方向には行けない). この場合,時間が0.4だけ進んだとき,点は壁に接する. 接した時刻での点の位置は ( x+10*0.4, y+20*0.4 ) = ( x+4, y+8 ) である. ここで止めるならば,処理は終了であり,残りの x方向 10*0.6 = 6 y方向 20*0.6 = 12 だけの移動量は棄却されることになる. 「壁にそって滑らせる」ならば,この移動量のうち壁にめり込む方向の成分だけを棄却し,壁に沿う方向の成分は採用すれば良い. この例では壁は垂直なので,x方向の成分 6 は捨てて,y方向の成分 12 だけを採用すればよいということだ. もちろん,今の位置 ( x+4, y+8 ) から問答無用でy方向に12だけ移動させてしまうと他の何かにめり込むかもしれないから, 「点が位置( x'(=x+4), y'(=y+8) )にいて,ここから残りの時間0.6だけ時間をかけて,(x'+0, y'+12)に移動したい」 という話として同様に処理する.
メンテ
Re: 斜めの壁の実装方法 ( No.10 )
名前:PP(解決) 日時:2020/10/28 19:46

取り敢えずusaoが挙げた前者の方法で何とかいけました。 というわけでこれで解決とさせて頂きます。 皆さん本当にありがとうございました。
メンテ
Re: 斜めの壁の実装方法 ( No.11 )
名前:usao 日時:2020/10/28 20:22

念のため注意点を述べておくと, 動く壁みたいなのがある場合にはそれを想定した何かしらの工夫を入れやる必要がある. 例えば, オブジェクトが左右2つの壁にぴったり挟まれている状態で さらに壁がオブジェクトを押しつぶさんと迫ってくる状態では, (オブジェクトの幅よりも空間が狭くなるのだから)「めり込み」を解消しきれない. (「滑る」側の処理では何も考えないとこういう場面で無限ループとかになり得る.)
メンテ

Page: 1 |

題名
名前
コメント
パスワード (記事メンテ時に使用)

   クッキー保存