🎄teratailクリスマスプレゼントキャンペーン2024🎄』開催中!

\teratail特別グッズやAmazonギフトカード最大2,000円分が当たる!/

詳細はこちら
C++

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

Q&A

解決済

4回答

733閲覧

当たり判定で当たっていないときはNULL当たった時はクラスを返すプログラムはどのように戻り値を取るべきか?

退会済みユーザー

退会済みユーザー

総合スコア0

C++

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

0グッド

0クリップ

投稿2019/12/30 04:53

提示コード最上部のcrosss_pos()関数部の戻り値ですがreturn new Position();をしてしまっていてこのコードは悪いコードだと思います。
しかしながら当たっているときは衝突座標を返して当たっていないときはNULLや無効な値を返したいと思うのですがなにかいい方法はあるのでしょうか? 0だと0に補正するという場合に困るので避けたいです。

/*-----------------------------------衝突座標を返す関数-----------------------------------------*/ //std::optional<Position> cross_pos(Position a, Position b, Position c, Position d) Position* cross_pos(Position a, Position b, Position c, Position d) { /*線分の交差か平行か判定*/ //tc = (x1 - x2) * (y3 - y1) + (y1 - y2)* (x1 - x3); float tc = (a.x - b.x) * (c.y - a.y) + (a.y - b.y) * (a.x - c.x); //td = (x1 - x2) * (y4 - y1) + (y1 - y2)* (x1 - x4); float td = (a.x - b.x) * (d.y - a.y) + (a.y - b.y) * (a.x - d.x); //ta = (x3 - x4) * (y1 - y3) + (y3 - y4) * (x3 - x1) float ta = (c.x - d.x) * (a.y - c.y) + (c.y - d.y) * (c.x - a.x); //tb = (x3 - x4) * (y2 - y3) + (y3 - y4) * (x3 - x2) float tb = (c.x - d.x) * (b.y - c.y) + (c.y - d.y) * (c.x - b.x); Position ab, ac, bc, bd, ad, cd, cb, db, ca; /*線分を取得*/ ab = Position(b.x - a.x, b.y - a.y); ad = Position(d.x - a.x, d.y - a.y); ac = Position(c.x - a.x, c.y - a.y); bc = Position(c.x - b.x, c.y - b.y); cb = Position(b.x - c.x, b.y - c.y); bd = Position(d.x - b.x, d.y - b.y); cd = Position(d.x - c.x, d.y - c.y); db = Position(b.x - d.x, b.y - d.y); ca = Position(a.x - c.x, a.y - c.y); /*外積を使ってスカラーを取得*/ float BD_AB = (bd.x * ab.y) - (ab.x * bd.y); float CB_DB = (cb.x * bd.y) - (bd.x * cb.y); float BD_BC = (bd.x * bc.y) - (bc.x * bd.y); float AD_CD = (ad.x * cd.y) - (cd.x * ad.y); float BC_BD = (bd.x * bc.y) - (bc.x * bd.y); float BD_AD = (ad.x * bd.y) - (bd.x * ad.y); float AB_AC = (ac.x * ab.y) - (ac.x * ab.y); float AB_AD = (ad.x * ab.y) - (ad.x * ab.y); printf("ta: %f\n", ta); printf("tb: %f\n", tb); printf("tc: %f\n", tc); printf("td: %f\n", td); /*線分の交差判定 平行はfalse*/ if (tc * td < 0 && ta * tb < 0) { /*比率*/ float k = AD_CD / (AD_CD + CB_DB); float x = a.x + k * (b.x - a.x); float y = a.y + k * (b.y - a.y); // printf("交差している。\n"); printf("x: %f\n", x); printf("y: %f\n", y); //return 交差座標 //return Position(x, y); return new Position(x, y); } else { Position acV(c.x - a.x, c.y - a.y);//線分の始点Aと判定するCのベクトルを設定 Position abV(b.x - a.x, b.y - a.y);//線分の始点Aと判定するDのベクトルを設定 Position caV(a.x - c.x, a.y - c.y);//線分の始点Cと判定するAのベクトルを設定 Position cbV(b.x - c.x, b.y - c.y);//線分の始点Cと判定するBのベクトルを設定 //(x1-x0)*(x2-x0) + (y1-y0)*(y2-y0) //x1終点 x0 始点 2 点 float CV = (b.x - a.x) * (c.x - a.x) + (b.y - a.y) * (c.y - a.y); float DV = (b.x - a.x) * (d.x - a.x) + (b.y - a.y) * (d.y - a.y); float AV = (d.x - c.x) * (a.x - c.x) + (d.y - c.y) * (a.y - c.y); float BV = (d.x - c.x) * (b.x - c.x) + (d.y - c.y) * (b.y - c.y); float rangeAB = sqrt((ab.y * ab.y) + (ab.x * ab.x));//A -> Bの長さ float rangeAC = sqrt((ac.y * ac.y) + (ac.x * ac.x));//ACの距離 float rangeAD = sqrt((ad.y * ad.y) + (ad.x * ad.x));//ADの距離 float rangeCD = sqrt((cd.y * cd.y) + (cd.x * cd.x));//C -> Dの長さ float rangeCA = sqrt((ca.y * ca.y) + (ca.x * ca.x));//CAの距離 float rangeCB = sqrt((cb.y * cb.y) + (cb.x * cb.x));//CDの距離 printf("CV: %f\n", CV); printf("DV: %f\n", DV); //C D の線分がABの線分の上に居る。 if ((CV == (rangeAB * rangeAC) && rangeAB >= rangeAC) && (DV == (rangeAB * rangeAD) && rangeAB >= rangeAD)) { if (CV < DV) { // return Position(c.x, c.y); return new Position(c.x, c.y); } else { return new Position(d.x, d.y); } printf("C D が線分の上にある。\n"); } else if (CV == (rangeAB * rangeAC) && rangeAB >= rangeAC) { printf("C が線分上にある。\n"); return new Position(c.x, c.y); } else if ((DV == (rangeAB * rangeAD) && rangeAB >= rangeAD)) { printf("D が線分上にある。\n"); return new Position(d.x, d.y); } else { /*-----------------------------------CD線分上にA,Bがあるかどうかを判定-----------------------------------------*/ printf("ABがCD線分上に居るか判定\n"); // if ((CV == (rangeAB * rangeAC) && rangeAB >= rangeAC) && (DV == (rangeAB * rangeAD) && rangeAB >= rangeAD)) if ((AV == (rangeCD * rangeCA) && rangeCD >= rangeCA) && (BV == (rangeCD * rangeCB) && rangeCD >= rangeCB)) { if (AV < BV) { return new Position(a.x, a.y); } else { return new Position(b.x, b.y); } printf("A B が線分の上にある。\n"); } else if (AV == (rangeCD * rangeCA) && rangeCD >= rangeCA) { printf("A が線分上にある。\n"); return new Position(a.x, a.y); } else if (BV == (rangeCD * rangeCB) && rangeCD >= rangeCB) { printf("b が線分上にある。\n"); return new Position(b.x, b.y); } } } // return std::nullopt; return NULL;//衝突してない時 } /* a の 向きを取得 プラスが← マイナスが右 */ int cross_direction(Position a, Position b, Position c, Position d) { /*線分の交差か平行か判定*/ //tc = (x1 - x2) * (y3 - y1) + (y1 - y2)* (x1 - x3); float tc = (a.x - b.x) * (c.y - a.y) + (a.y - b.y) * (a.x - c.x); //td = (x1 - x2) * (y4 - y1) + (y1 - y2)* (x1 - x4); float td = (a.x - b.x) * (d.y - a.y) + (a.y - b.y) * (a.x - d.x); //ta = (x3 - x4) * (y1 - y3) + (y3 - y4) * (x3 - x1) float ta = (c.x - d.x) * (a.y - c.y) + (c.y - d.y) * (c.x - a.x); //tb = (x3 - x4) * (y2 - y3) + (y3 - y4) * (x3 - x2) float tb = (c.x - d.x) * (b.y - c.y) + (c.y - d.y) * (c.x - b.x); if(ta < 0) { return -1; }else if(ta > 0){ return 1; } } bool b = false; /*---------------------------------------------------当たり判定----------------------------------------------------------------*/ void Collision_Update() { // std::unique_ptr<Position> xPlayer_up, xPlayer_down; // std::unique_ptr<Position> yPlayer_left, yPlayer_right; Position *xPlayer_up, *xPlayer_down; Position *yPlayer_left, *yPlayer_right; xPlayer_up = NULL; xPlayer_down = NULL; yPlayer_left = NULL; yPlayer_right = NULL; bool xR ,xL; xR = xL = false; Position now_x[2];//今いる座標 Position now_y[2];//今いる座標 Position future_x[2];//移動先 座標 Position future_y[2];//移動先 座標 now_x[0].x = pos->x + ((CELL) / 2); now_x[0].y = pos->y; now_x[1].x = pos->x + ((CELL) / 2); now_x[1].y = pos->y + (CELL); now_y[0].x = pos->x + ((CELL) / 2); now_y[0].y = pos->y + ((CELL) / 2); now_y[1].x = pos->x + ((CELL) / 2); now_y[1].y = pos->y + ((CELL) / 2); /*未来座標配列に現在座標をコピーする*/ future_x[0].x = now_x[0].x + move->x; future_x[0].y = now_x[0].y + move->y; future_x[1].x = now_x[1].x + move->x; future_x[1].y = now_x[1].y + move->y; future_y[0].x = now_y[0].x + move->x; future_y[0].y = now_y[0].y + move->y; future_y[1].x = now_y[1].x + move->x; future_y[1].y = now_y[1].y + move->y; for(int y = 0; y < 100; y++){ for(int x = 0;x < 100; x++){ if(Find_Map(x,y) == true){ // ofs<<"x y: "<<x <<" , "<<y<<std::endl; Position *u = NULL ,*d = NULL; u = cross_pos(now_x[0], future_x[0], Position(x * CELL + (CELL / 2), y * CELL), Position((x * CELL) + (CELL / 2), ((y + 1) * CELL))); if(u != NULL) { xPlayer_up = u; ofs<<"x: "<<xPlayer_up->x<<std::endl; } d = cross_pos(now_x[1], future_x[1], Position(x * CELL + (CELL / 2), y * CELL), Position((x * CELL) + (CELL / 2), ((y + 1) * CELL))); if(d != NULL) { xPlayer_down = d; } } } } /*→に移動するときの補正*/ if (Direction->x > 0) { /*上と下の当たった時の長さ 短いほうに補正する*/ ofs << "→ 移動" << std::endl; float up,down; up = down = -1; if(xPlayer_up != NULL){ up = xPlayer_up->x; up = up - (CELL + (CELL / 2)); ofs << "up: " << up << std::endl; } if (xPlayer_down != NULL) { down = xPlayer_down->x; down = down - (CELL + (CELL / 2)); ofs << "down: " << down << std::endl; } if( (down == -1) && (up > -1) ) { pos->x = up; }else if( (down > -1) && (up == -1) ) { pos->x = down; }else if( (down != -1 && up != -1) )//両方のが当たっている場合 { if(down >= up) { pos->x = up; }else{ pos->x = down; } } /**/ if((xPlayer_down == NULL) && (xPlayer_up == NULL)) { //pos->x = future_x[0].x; pos->x += move->x; } } /*←に移動するときの補正*/ if(Direction->x < 0) { /*上と下の当たった時の長さ 短いほうに補正する*/ ofs << "← 移動" << std::endl; float up, down; up = down = 1; if (xPlayer_up != NULL) { up = xPlayer_up->x; up = up + (CELL / 2); ofs << "up: " << up << std::endl; } if (xPlayer_down != NULL) { down = xPlayer_down->x; down = down + (CELL / 2); ofs << "down: " << down << std::endl; } if ((down == 1) && (up > 1)) { pos->x = up; } else if ((down > 1) && (up == 1)) { pos->x = down; } else if ((down != 1 && up != 1))//両方のが当たっている場合 { if (down >= up) { pos->x = down; } else { pos->x = up; } } /**/ if ((xPlayer_down == NULL) && (xPlayer_up == NULL)) { //pos->x = future_x[0].x; pos->x += move->x; } } } /*-------------------------------------------------------------------------------------------------------------------------*/

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

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

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

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

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

guest

回答4

0

ベストアンサー

何がベストかは場合によります。

std::optional を使った方法をコメントアウトしているのはそれが不満だということでしょうか? C++17 を利用できない環境への配慮だとかいった理由で使えないことはあるかもしれませんね。

ヌルを返すというか、ポインタで返す方法だと delete をうっかり忘れないようにするのが面倒という欠点はあります。

それぞれに利点も欠点もあって、単純には何がベストとは言えないのです。

なので、この場合に良いかどうかは別として、あくまでも提案のひとつとして回答します。 プロジェクトの事情、個別の事情に合わせてよりよいものを選択してください。


「値を返さない」という方法があります。

オブジェクト指向の方法論のひとつとして "Tell, Don't Ask" という考え方があり、詳しいことはググれば出てくるので詳細は述べませんけども、答えをもらってそれに基づいて処理するのではなく、やりたいことを伝えてやってもらうという方法です。

当たっているときの処理のパターンが色々とあるのならコールバック関数を受け取るようにすると良いでしょう。 おおざっぱに言えば以下のような要領です。

cpp

1void when_collided(Position a, Position b, Position c, Position d, std::function<void(const Position&)> effect) { 2 if(衝突した場合) effect(交差した座標); 3}

当たったか当たってないかくらいはオマケで返却値にしても良いかもしれませんが、そのあたりは柔軟に対処してください。

繰り返しますが、これが他の方法より良いとは言いません。 あくまでも状況によっては選択し得るひとつです。

投稿2019/12/30 08:33

SaitoAtsushi

総合スコア5684

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

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

退会済みユーザー

退会済みユーザー

2019/12/30 09:45

問題なのは上と下両方あるのでXの場合両方当たっている場合はどちらか先に当たる方に座標を書き換える処理で主に詰まっています。これはどうしたらいいのでしょうか?雑な悪いコードでは実は完成しているのですがいいコードが書けず困っています。
SaitoAtsushi

2019/12/31 08:55

コードが何を意図しているのか私には読み取れないのでよりよいのが何かわかりません。 たとえば Position というのが何の座標で、どうしてふたつの座標があるのでしょう? CELL とは何者で、どうしてその半分をずらすのでしょう? 喩え話をします。 そのへんを歩いている人がいるとして、見れば歩いていているのは分かります。 どこかに向かっていて間に合いそうにないということがわかれば乗り物を提案できるかもしれません。 健康のために歩いているなら食事などの他の健康法も提案できるかもしれません。 でも、意図を知らずにただ歩いているという事実だけを見てどう改善できるかというのは助言できることが少ないのです。 それと同様に、プログラムが何をしているのかはわかります。 でも、何を意図しているのかをそこから読み取るのは困難です。 そして、しばしばプログラムを書いている人自身も自分が何を意図して書いているのかよく自覚できていないことがあります。 意図していることをきちんと説明できるようにしてください。 たぶん漠然とした意図はあって、それに合致していないから「いいコードではない」だと感じているのだと思います。
guest

0

プロジェクトのコーディング規約があればそれに従うべきでしょう。
個人的にはこうすることが多いよ、というだけです。

質問のように戻り値をヒープに確保して返すというのは、処理時間とメモリ管理のわずらわしさを考またえるとこのような実装はあり得ません。関数がポインタで返すのでスタックに確保して返すわけにもいきません。
なので自分の回答のようになりました。

投稿2020/01/01 04:32

tatamyiwathy

総合スコア1045

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

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

tatamyiwathy

2020/01/01 04:35

すみません。コメントを返すつもりが回答してしまいました。 こちらhttps://teratail.com/questions/232501#reply-338947へのコメントです。
退会済みユーザー

退会済みユーザー

2020/01/01 05:02

あー私に対するコメントですか。質問者と1対1でやり取りしてくれれば良いと思うんですが...。 というかその様な考えのもと回答されたのであれば私ではなく最初から質問者の方に教えてあげればよかったのでは?
guest

0

nullptrでダメだと思う理由はなんでしょうか?
個人的にこの手の問題で大事なのは関数名と機能や返り値の対応が取れており、仕様が明瞭であることかと思います。
例えば関数名が getCrossPosition であれば Position* が返り値なのが望ましいので nullptr が返る仕様でも良いですし
(他言語のメソッドになりますが)TryGetValueやTryParseに習ってtryGetCrossPosition のようにするなら交点の有無をboolで返り値とし、交点自体は別途引数でやり取りするのが良いと思います。

本当に個人的に狭い範囲で使うだけで「そんな拘りは要らないよ!」って話なら正直何でもよいかなと思いますし、逆にそこそこでかいプロジェクトの一部なら上記に反してでも全体の方針に従った方が良いかと。

投稿2019/12/30 07:42

退会済みユーザー

退会済みユーザー

総合スコア0

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

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

0

自分ならcross_posはbool値で当たっていないときはfalse、当たってるときはtrueを返すようにします。
当たってるときは引数のresultに当たった座標を格納します。

c++

1bool cross_pos(Position* result, Position a, Position b, Position c, Position d)

投稿2019/12/30 05:08

tatamyiwathy

総合スコア1045

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

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

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.36%

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

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

質問する

関連した質問