質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

Q&A

解決済

3回答

6350閲覧

ベクトルを使った当たり判定の実装方法が知りたい

退会済みユーザー

退会済みユーザー

総合スコア0

C++

C++はC言語をもとにしてつくられた最もよく使われるマルチパラダイムプログラミング言語の1つです。オブジェクト指向、ジェネリック、命令型など広く対応しており、多目的に使用されています。

0グッド

1クリップ

投稿2019/08/25 12:43

編集2019/08/27 18:23

イメージ説明コードが長いので断片的な部分だけです、コメント部の記入すべきコードです。

2Dアクションゲームでベクトルを使って移動した時にブロックや他の画像と重なってしまういわゆる"めり込み"の修正方が知りたいです。自分はベクトルを使い方向の情報を使ってぶつかってきた方向に追い返す処理を考えました。
図の場合は右と下に進んでいるのでその逆の左上に追い返す(座標をずらす)ということをしたいです。
また水平に並んだブロックに上から下と右に移動しながら進んできた時に上に座標をずらすといった処理を書きたいのですがどうしたらいいのか実装が思いつかないのですが教えてくれますでしょうか?

画像:画像の青がプレイやーで端が紫の青いブロックは移動してる時つまりプレイヤーに移動速度してる情報が追加されたときの画像で赤がブロックです。

環境:dxlibです右がx++で下がy++の座標系です。2Dです。
player_col()部です。

追記ですが下にブロックがあり右の真横にもブロックがる場合上に上がってしましますこれはどうすればいいのでしょうか?
画像の場所です。

イメージ説明
イメージ説明

Position Map::Collision::isMapCell(const Position pos) { Position p; for (int y = 0; y < MAP_HEIGHT; y++) { for (int x = 0; x < MAP_WIDTH; x++) { } } if (get_mapCell(pos.get_x() / CELL, pos.get_y() / CELL) == (int)mapChip::eBrick || get_mapCell(pos.get_x() / CELL, pos.get_y() / CELL) == (int)mapChip::eQuestion) { p.new_pos_x((pos.get_x() / CELL)); p.new_pos_y((pos.get_y() / CELL)); ofs << "p.get_x(): " << p.get_x() << std::endl; ofs << "p.get_y(): " << p.get_y() << std::endl; return p; } return Position(-1,-1); } //座標が引数 std::optional<Position> Map::Collision::col(const Position p) { Position c_pos(Map::Collision::isMapCell(p)); if (c_pos.get_x() > -1 && c_pos.get_y() > -1) { if (c_pos.get_x() * CELL <= p.get_x() && (c_pos.get_x() * CELL) + CELL >= p.get_x() ) { } } return std::nullopt; } /*全方位の当たり判定 return セル番号を返す*/ std::optional<Position> Map::Collision::player_col(const Position *pos,const Position *move) { Position p; p.new_pos_x( pos->get_x() + move->get_x() + CELL); p.new_pos_y( pos->get_y() + move->get_y() + CELL); static bool isground = false; int px = pos->get_x(); int py = pos->get_y(); int add_x = move->get_x(); int add_y = (int)move->get_y(); // ofs << "move->get_x(): " << move->get_x()<<"\n"; // ofs << "move->get_y(): " << move->get_y() << "\n"; int corr_x = 0; int corr_y = 0; Position pp = Map::Collision::isMapCell(p); if (pp.get_x() > -1) { if (move->get_x() > 0) { //ofs << "x補正" << std::endl; //px = pos->get_x() -CELL; } //ofs << "playe_col isground: " << isground << std::endl; if ((move->get_y() < 0)) { ofs << "ground " << std::endl; isground = true; //py -= ((pos->get_y() + (-move->get_y()) + CELL) - (pp.get_y() * CELL)); //ofs << "pp.get_y(): " << ((pos->get_y() + (-move->get_y()) + CELL) - (pp.get_y() * CELL)); int yy = ((pos->get_y() + (-move->get_y()) + CELL) - ((pp.get_y() * CELL))); py -= yy; } std::optional<Position> p = Position(px, py); return p.value();//セルを返す } return std::nullopt; }
#include <iostream> #include <fstream> #include "Input.h" #include "Player.h" #include "DxLib.h" #include "Map.h" #include "Animation.h" #define MOVE_SPD 5 #define JUMP_FORCE 4.0624 * 3 static std::ofstream ofs("Log.txt"); /*コンストラクタ*/ Player::Player(const char* str, int xx, int yy) { //LoadDivGraph(str,7,7,1,64,64,g_handle); AnimeClip = new Animation(anime_s::ewait,str,7,7,1); pos = new Position(); prev = new Position(); move = new Position(); AnimeClip->setAnime(anime_s::ewalk,1,3,3); AnimeClip->setAnime(anime_s::ewait, 0, 0, 1); AnimeClip->setAnime(anime_s::ejump, 5, 5, 1); } /*計算更新*/ void Player::Update() { gravity(); input_key(); jump_up(); //side_move();// AnimeClip->Update(); DrawFormatString(200, 280, GetColor(255, 255, 255), "y %d", pos->get_y()); DrawFormatString(200, 360, GetColor(255, 255, 255), "x %d", pos->get_x()); // ofs << (double)std::atan2((double)pos->get_x(),(double)pos->get_y()) << std::endl; DrawFormatString(100, 200, GetColor(255, 255, 255), "isGround %d", isGround); DrawFormatString(100, 280, GetColor(255, 255, 255), "isJump %d", isJump); ofs << "pos.x: " << pos->get_x() << std::endl; ofs << "pos.y: " << pos->get_y() << std::endl; *prev = *pos; } /*描画更新*/ void Player::Draw_Update() { if ( key == -1) { //DrawTurnGraph(get_x(), get_y(), g_handle[0], true); /////// DrawTurnGraph(pos->get_x(), pos->get_y(), AnimeClip->draw_setClip(), true); } /*右→*/ if (key == 1) { /// DrawGraph(pos->get_x(), pos->get_y(), AnimeClip->draw_setClip(), true); } AnimeClip->Draw_Update(); } /*キー入力*/ void Player::input_key() { if (keybord(KEY_INPUT_LEFT) > 0)//← { key = -1; AnimeClip->Update_changeAnime(anime_s::ewalk,key); pos->set_x(-MOVE_SPD); move->new_pos_x(-MOVE_SPD); }else if (keybord(KEY_INPUT_RIGHT) > 0)//→ { key = 1; AnimeClip->Update_changeAnime(anime_s::ewalk,key); move->new_pos_x(MOVE_SPD); pos->set_x(+MOVE_SPD); }else if (keybord(KEY_INPUT_UP) > 0)//上 { //pos->set_y(+MOVE_SPD); }else if (keybord(KEY_INPUT_DOWN) > 0)//下 { // pos->set_y(-MOVE_SPD); } else if (keybord(KEY_INPUT_SPACE) > 0)//ジャンプ { //AnimeClip->Update_changeAnime(anime_s::ejump); if (isGround == true && jf == 0) { //isJump = true; isGround = false; jf = JUMP_FORCE; } AnimeClip->Update_changeAnime(anime_s::ejump); } else//何も押していない時 { move->new_pos_x(0); AnimeClip->Update_changeAnime(anime_s::ewait); } } /************************ジャンプ ***********************/ void Player::jump_up() { if (Map::Collision::player_col(pos, move) == std::nullopt) { if (isGround == false) { move->new_pos_y((int)jf); pos->set_y((int)jf); //if (Fps::gframe() % 20 == 0) { jf = ((jf - 0.126953125f * 20.0f)); if (jf < -0.126953125f * 3.0f * 10.0f) { jf = -0.126953125f * 3.0f * 10.0f; //pos->set_y(); } //} } } } /*********************************************************/ /***********************重力*******************************/ void Player::gravity() { /*地面着地*/ if (Map::Collision::player_col(pos, move) != std::nullopt) { // ofs << "test\n"; std::optional<Position> p = Map::Collision::player_col(pos, move); pos->new_pos_x(p->get_x()); pos->new_pos_y(p->get_y()); //pos->set_y(p->get_y()); //pos->set_x(-p->get_x()); // ofs << "under_col true\n"; } else { //ofs << "palyer_col nullopt" <<"\n"; } //落下 if (Map::Collision::player_col(pos,move) == std::nullopt) //if (Map::Collision::under_col(pos, prev) == std::nullopt && isGround == false) { //DrawFormatString(200, 200, GetColor(255, 255, 255), "x %d", pos->get_x()); //if (Map::Collision::player_col(pos, move) == std::nullopt) { if (isGround == false) { move->new_pos_y((int)jf); pos->set_y((int)jf); jf = ((jf - 0.126953125f * 20.0f)); if (jf < -0.126953125f * 3.0f * 10.0f) { jf = -0.126953125f * 3.0f * 10.0f; //pos->set_y(); } } else { } // } } } /**********************************************************/

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

bsk

2019/08/25 15:03

プログラミングというより数学的なアドバイスがほしいと思うんですが全く理解できません。 方向関係の定義を説明してください。 真横の壁とは何から見て真横なのですか。左に補正されるとは何が左に補正されるのですか。上とはZ座標のこと?空間は2Dですか3Dですか。ソースコード内の各変数は何を示しているのでしょうか。 できれば図形込みで説明してくれたら理解が進みます。
退会済みユーザー

退会済みユーザー

2019/08/25 15:08

わかりました図を作ります。
退会済みユーザー

退会済みユーザー

2019/08/25 15:33

文章と図とを追加し質問内容を最適化しました。
guest

回答3

0

他の質問に同じ話を書いた気がしますが,それを絵にしてみました.

図(1):
キャラクタの移動前の位置が赤実線矩形で,移動したい先が赤破線矩形です.
この絵の例では矩形の四隅を用いて衝突判定していますが,実際は必要十分な方法で判定してください.
黒矢印は各頂点の移動したい量です.
この例では,このまま進むと真っ先に障害物とぶつかるのは右下の頂点です(オレンジ色で衝突マークを描いています)

図(2):
右下の頂点がぶつかる直前までは移動して良いので,キャラクタを表す赤矩形をそこまで移動させました.
青破線矢印が残りの所望移動量ですが,当然,そのままこの方向に進むわけにはいきません.
この例では障害物の左辺に衝突しているので,残りの所望移動量のうち,X方向成分(衝突した辺の法線方向成分)を0にします.
すなわち残りの所望移動量を,図内の下向きの黒矢印に修正します.

図(3):
最初と同じように,残りの移動が可能かどうかを判定していきます.
この例では障害物にぶつかりませんから,黒矢印の分だけ移動してOKです.

図(4):
この例での最終結果です.

イメージ説明

上記の例とは違う当たり方,例えば,右下頂点が障害物の上辺にぶつかっていた場合を考えれば,そのときは残り移動量のY方向成分を捨てる形になりますから,ブロックの上にキャラクタが乗る形になります.

投稿2019/08/26 02:47

fana

総合スコア11654

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

fana

2019/08/26 03:13

実際上は,4頂点それぞれに衝突判定とかせずとも障害物の判定領域側を拡大することで1点の判定で済むとかそういう工夫はあるだろうけども, ここでは解りやすく概念を説明することを優先し,そういう話は入れませんでした.
fana

2019/08/26 09:10 編集

不要な補足かもだけど, キャラクタが衝突する対象が「ブロック」という単位で設けられているとしたら, ここで言う「障害物」とは「1つ以上のブロックの集合」である点に注意されたい. つまり,所望の移動経路が複数のブロックと衝突する場合には,「最初に当たるブロック」を特定する必要があるということ.
退会済みユーザー

退会済みユーザー

2020/02/22 03:10

今更ですがY軸にプラス(ジャンプして上昇)している時でX軸方向に動いているときに天井のブロックに 当たるとx軸が補正されてしまうので左に動いているとき一番右に移動してしまうというバグが発生します。 これはどうすればいいのでしょうか? 逆に落下中に地面と当たった時も同じような反応をしてしますのですが...
fana

2020/02/24 01:52

その「バグ」自体はあなたの今現在の(おそらくこの質問時とは異なる)コード特有の話だと思いますので,何とも言えません. 多分,「X方向に補正するか否か」みたいな判断を,何かしらの場合分けの果てに行う的な処理実装になっているのではないかと勝手に推測しますが,もしそうであれば,そのような分岐が存在する形自体が既にこの回答とは異なっているわけです. 現状のバグをどうにかするには, ・そもそもその判定処理の内容が妥当なのか?(アルゴリズム自体の問題) ・妥当だとして,その通りに実装できているのか?(実装バグ) を順に疑うしかないと思いますし,その点で他者の助けを借りようとするならば,あなたのアルゴリズムの全容(と,必要に応じて,実装コードとの対比関係等)を相応に説明する作業が発生すると思います. 「ジャンプ」や「落下」という状況は,この回答内の絵を90度回転して見た状態にあたると思います.この回答の話というのは,どっちがXだとかYだとかいう区別の概念すらない(区別しなくても済む)処理の話になっています.
guest

0

ベストアンサー

例えば、プレイヤーの足元のy軸とマップの上のy軸を引き算すればy軸の差分が出ます。
これが今回貴方が補正してあげなければならいない補正量Yです。

投稿2019/08/26 07:20

stdio

総合スコア3307

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/08/27 17:58

それはわかるのですが右のブロックにのめり込む時にyが下がってxが右に両方のめり込む時にxは上手く修正できますがyがその右のブロックの上の座標に修正されてしまい困っています。
stdio

2019/08/28 01:57

私の感で申し訳ございませんが、問題になっているのは、衝突判定を行う順番です。 きっと、地面の衝突判定を先に行ってしまっていることが原因ではないかと考えられます。 順番としましては、まず、天井、左右の壁、最後に地面という順番に変更してみて下さい。 それとプレイヤーの衝突判定の大きさは、画像に対して少し小さめに判定を行うのが良いかと思います。 左右の壁は、プレイヤーを真ん中起点に双方3/4ぐらいにするのが、ベストだと思います。
退会済みユーザー

退会済みユーザー

2019/08/30 00:30

四隅で判定するという方法でかいけつしました。
stdio

2019/08/30 01:52

おお、おめでとう!!
退会済みユーザー

退会済みユーザー

2019/09/01 02:14

なぜすこし小さめにしたほうがいいのでしょうか?w
stdio

2019/09/02 00:51

単純に元の画像サイズよりもキャラクターが描かれた大きさの方が狭いから...かな...? それとしっかりとしたサイズで衝突判定をしているとプレイヤーが理不尽と感じてしまうから。
guest

0

アクションゲームの判定でしたらこちらが参考になるかもしれません

https://dxlib.xsrv.jp/program/ActionSample.cpp

投稿2019/08/25 14:20

nicoyou

総合スコア129

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

退会済みユーザー

退会済みユーザー

2019/08/25 14:29

ベクトルを使った判定の仕方が知りたいです。
nicoyou

2019/08/25 16:04

URLのサイトにはベクトルを使った判定のサンプルがのっているはずですが...
退会済みユーザー

退会済みユーザー

2019/08/25 16:11

正直イライラしてたので見落としてました。見てみます...
退会済みユーザー

退会済みユーザー

2019/08/25 16:43

確認したのですが。真横の場合はどうすればいいのでしょうか?真横のブロックの上に上がってしまいます。
nicoyou

2019/08/26 01:34

もしコードをコピペしたのでしたらそのようなことは起きないと思いますが.... 横と縦を別々に考える方法なので真横でも斜めでも同じ処理方法で移動できます 理解できれば自分の書き方に書き換えることは簡単に出来ると思います
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問