49157


Dxライブラリで"30度の角度で上に移動したと画面の上に行ったらまた30度下に動かして元の座標に戻る"という処理を書きたいのですが 逆30度？
左下の実装がわからないので教えてれますでしょうか？
コメント部に戻す処理を書いたのですがこれだと進んだときに戻る際角度がずれて初期の座標に戻らないので困っています

[code]

    #include "DxLib.h"
    #include "math.h"
    //#include <iostream>
    
    const double PI = 3.14159265359;
    const double angle = PI / 180;
    
    int hblock;
    /*画面のサイズ*/
    const int screensize_x = 640 - 64;
    const int screensize_y = 480 - 64;
    
    int color = GetColor(255, 255, 255);
    int pos_x = 0;
    int pos_y = 480-64;
    int speed = 2;
    
    bool flag = false;//画面の端に到達したかどうかのを判定するフラグ変数
    bool f = false;
    
    void pos_flag() {
    
        if (pos_x >= 640  - 64  || pos_y >= 480 - 64 || pos_x < 0 || pos_y < 0) {
            flag = true;
        }
        else {
            flag = false;
        }
    
    }
    
    /*座標を表示する関数*/
    void Player_Log() {
        DrawFormatString(0, 0, color, "pos_x:%d", pos_x);
        DrawFormatString(0, 15, color, "pos_y:%d", pos_y);
        DrawFormatString(0, 30, color, "Flag:%d", flag);    
    }
    
    /*操作関数*/
    void Player_Control(){
        if (CheckHitKey(KEY_INPUT_UP) >= 1) {
            pos_y -= speed + 2;
        }
    
        if (CheckHitKey(KEY_INPUT_DOWN) >= 1) {
            pos_y += speed + 2;
        }
    
        if (CheckHitKey(KEY_INPUT_LEFT) >= 1) {
            pos_x -= speed + 2;
        }
    
        if (CheckHitKey(KEY_INPUT_RIGHT) >= 1) {
            pos_x += speed + 2;
        }
    }
    
    /*初期化*/
    void Player_Initialization() {
        hblock = LoadGraph("Block.png");
    }
    
    /*描画*/
    void Player_Draw() {
        DrawGraph(pos_x, pos_y, hblock, TRUE);
    }
    
    /*更新*/
    /////////////////////////////////////////////ここです。
    void Player_Update() {
        Player_Control();
        pos_flag();//画面の判定関数
    
        if (pos_x < screensize_x && pos_y > 0) {
            pos_x += speed * cos(angle * 30);
            pos_y -= speed * sin(angle * 30);
        }
    
    //  pos_x -= speed * cos(angle * 30);
    //  pos_y += speed * sin(angle * 30);
    
    }
    ////////////////////////////////////////////
    
    int WINAPI WinMain(HINSTANCE hInstanve, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int nCmdShow)
    {   ChangeWindowMode(true), DxLib_Init(), SetDrawScreen(DX_SCREEN_BACK);
    
        Player_Initialization();//初期化
        while (ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0) {
    
            Player_Update();
            Player_Draw();
            Player_Log();
    
            if (CheckHitKey(KEY_INPUT_ESCAPE) == 1 || CheckHitKey(KEY_INPUT_RETURN) == 1) {
                return -1;
            }
        }
    
        WaitKey();
        return 0;
    }
    
[/code]


CC BY-SA 4.0
2018-10-10T12:09:29.707
49144
2018-10-11T00:55:04.543
2018-10-11T00:55:04.543
3060
question
c++
DXライブラリ 右上30度に移動さたあと30度左下に移動させ元の位置に戻る処理


元に戻すときに座標がずれてしまうのは、doubleとintの間の数の変換が何度も起きているからです。

[code]

    #include "math.h"
    #include <iostream>
    
    static double const PI = 3.14159265359;
    static double const angle = PI / 180;
    
    int main()
    {
        int pos_x = 2;
    
        std::cout << "cos(30)=" << cos(angle * 30) << '\n';
    
        pos_x += cos(angle * 30);
        std::cout << pos_x << '\n';
    
        pos_x -= cos(angle * 30);
        std::cout << pos_x << '\n';
    
        return 0;
    }
    
[/code]

上のコードを手元の Visual C++ 2017 で実行したところ、

[code]

    cos(30)=0.866025
    2
    1
    
[/code]

という結果が得られました。2からcos(30)の値を足して、引いているのに元の2に戻りません。計算の過程を説明すると、最初に2にcos(30)の値を足すときは

  1. 最初の式: (int)2 + (double)0.866025
  2. intとdoubleを計算するためintをdoubleに変換: (double)2.0 + (double)0.866025
  3. 合計: (double)2.866025
  4. intの変数に代入するため、合計をintに変換(小数点以下切り捨て): (int)2

という順番で計算されています。逆に2からcos(30)を引くときは

  1. (int)2 - (double)0.866025
  2. (double)2.0 - (double)0.866025
  3. (double)1.133975
  4. (int)1

になります。不用意にdoubleとintを混ぜて計算すると、このような桁落ちが起きるので注意が必要です。

* * *

直し方の一例としては、計算用の座標はすべてdoubleで持ち、描画の時にintに変換する方法があります。お示しのコードでは`pos_x`,`pos_y`,`speed`を全てdoubleにし、描画の時だけ

[code]

    void Player_Draw() {
        DrawGraph(static_case<int>(pos_x), static_cast<int>(pos_y), hblock, TRUE);
    }
    
[/code]

としてしまえば、計算中にintとdoubleの変換が起きないので、上で述べたような桁落ちは起こりません。

* * *

しかし、まだ問題があります。数学的には小数はいくらでも桁をとることができますが、コンピュータのメモリは有限です。小数の絡む計算を何度もするうちに、メモリに入りきれなかった部分が切り捨てられ、少しずつ誤差がたまり、そのうち目で見てわかるようになる可能性があります。

これを防ぐには、すべてをintにしてしまう方法があります。そのままだと、cos(30)は切り捨てられて0になってしまうので、座標値として、例えば実際の値を1000倍した値を保持するようにします。1000倍すると、画面の座標(10,15)は(10000,15000)になり、cos(30)の値は866になります。これで座標の計算を行い、描画の時に1000で割るようにすればいいです。

* * *

ちなみにcos(30)の値は、いつ計算しても同じなので、いちいち計算せず、最初に`const`変数に代入しておきましょう。


CC BY-SA 4.0
2018-10-10T17:42:04.047
49157
2018-10-10T17:42:04.047
3605
49144
answer


別解として、必ず同じ軌跡を通る必要があるということなら、行き行程でブツの座標をリストに保存しておいて、帰り行程ではリストを逆にたどるとかもありそうです。いわゆる
undo/redo と同じ考えっすね。

もう一歩進めて、軌跡は事前計算しておいて、行き行程も帰り行程もリスト（なりベクタなり）をたどるとか、事前計算結果は別リソースにしておいて差し替えることができるようにしておくとかすると、よりゲームっぽいかもしれません。

`int` による固定小数点数演算は、途中で固定小数点数同士の乗除算が出たりすると `double`
による浮動小数点数演算より精度が落ちたりします。なので原理を知ったうえで長所短所を両方理解して使うならアリなんですが、オイラ個人としてはあまりお勧めしていません。


CC BY-SA 4.0
2018-10-11T00:05:40.723
49162
2018-10-11T00:05:40.723
8589
49144
answer