2016-04-12 15 views
0

私はGLFWとOpenGLには一般的に新しく、小さなモデルレンダラーで作業しています。 私は現在入力に取り組んでおり、GLFWがどのように入力を処理するかという問題に直面しています。 すべてのチュートリアルでglfwGetKeyと「if forest」を使用してそのようなキーが押されているかどうかを確認しています。私が持っている問題は、たくさんのキーをマッピングすると遅くなることがありますが、それは醜いことです。 私は関数ポインタテーブルとglfwSetKeyCallbackを使用して、これを高速化し、より洗練されたコードを作成します。 私が持っている問題は、競合状態に見えるものに直面していることです。カメラは吃音に見えます。 一定のスピードを持つために、各フレームで計算されたデルタタイムを使用しています。 私が見ることができたように、キーコールバック関数が毎回呼び出され、キーが繰り返されるたびに各フレームで一度だけ呼び出されるようには思われません... githubからglfw3の最新バージョンを使用しています各ループの始めにバッファーを置き、最後にglfwPollEvents()を使います。GLFWキーコールバックの同期

私の質問は次のとおりです:レンダリングループとコールバック関数の間に吃音やdeltatimeの違いを避けるために、glfwPollEvents呼び出しとレンダリングを同期させる方法がありますか? ご協力いただきありがとうございます!

+0

"フォレストであれば"パフォーマンスの問題が発生するとはどういうことでしょうか?実際に測定しましたか?はい、それは醜く見えるし、それを維持することは難しいそれを避けるために正当な理由です。しかし、パフォーマンスは理由ではありません。 – datenwolf

+0

確かに、現代のCPUではパフォーマンスの差はあまりありませんが、1000 if ... else if文があり、現在のケースが最後のものであると想像してください。実際には999個の可能性がすべてチェックされます関数ポインタテーブルの場合には簡単な関数呼び出しと比較します。 また、あなたが言っているように、それは醜いと維持することが困難なので、 "森林は"どのコストパフォーマンスのコストで避けるべきかIMHOではありません。 – Tab

+0

そこには部分的な答えがあります: http://stackoverflow.com/questions/6805026/is-switch-faster-than-if 森林であればコンパイラがどう解釈するのか分かりませんが、合理的に仮定します条件付きチェックのトンは、単純な関数ポインタテーブルよりも論理的に遅いでしょう... – Tab

答えて

2

一般に、入力を処理する方法は、キーのリストを保持し、最後の入力状態を記録することです。

レンダリングループ(または、あなたのマルチスレッド+同期能力を持つ自信を持っている場合は、別のループに分離することができます)あなたはこのようなコードを書くことができますでその後
struct key_event { 
    int key, code, action, modifiers; 
    std::chrono::steady_clock::time_point time_of_event; 
} 

std::map<int, bool> keys; 
std::queue<key_event> unhandled_keys; 
void handle_key(GLFWwindow* window, int key, int code, int action, int modifiers) { 
    unhandled_keys.emplace_back(key, code, action, modifiers, std::chrono::steady_clock::now()); 
} 

、:

float now = glfwGetTime(); 
static float last_update = now; 
float delta_time = now - last_update; 
last_update = now; 
handle_input(delta_time); 

handle_inputは次のようになります。

float external_position[2]; 
std::map<int, std::function<void(/*args*/)>> key_functions; 
void handle_input(float delta_time) { 
    //Anything that should happen "when the users presses the key" should happen here 
    while(!unhandled_keys.is_empty()) { 
     key_event event = unhandled_keys.front(); 
     unhandled_keys.pop(); 
     key_functions[event.key](/*args*/); 
     bool pressed = event.action == GLFW_PRESS || event.action == GLFW_REPEAT; 
     keys[event.key] = pressed; 
    } 
    //Anything that should happen "while the key is held down" should happen here. 
    float movement[2] = {0,0}; 
    if(keys[GLFW_KEY_W]) movement[0] += delta_time; 
    if(keys[GLFW_KEY_S]) movement[0] -= delta_time; 
    if(keys[GLFW_KEY_A]) movement[1] -= delta_time; 
    if(keys[GLFW_KEY_D]) movement[1] += delta_time; 
    external_position[0] += movement[0]; 
    external_position[1] += movement[1]; 
} 

EDITを:私は「プレスに」/「リリースの」型の関数を処理するためのロジックを追加しました。例えば、このコードはレンダラにあった、のであれば:

key_functions[GLFW_KEY_SPACE] = [&renderer] {renderer.pause();}; 

その後[Space]キーを押すと、レンダラを一時停止します。

+0

こんにちは、あなたの答えに非常に感謝します! このコードの問題は、私が "フォレスト"と呼ぶことは、それぞれのキーを一度チェックするという意味であるということです。私が探しているのは、どのキーが押されているかを知り、キーコードを取得する方法です(glfwSetKeyCallbackキーコード、アクション、改造、キー[キーコード] [アクション] .arg); どれが速くてきれいなIMOです...問題は、glfwSetKeyCallbackを使用すると同期の問題が発生することです... – Tab

+0

@Tab私が作っているポイントは、コールバックが非常に軽量でなければならないということです。コールバック関数が数マイクロ秒以上かかるタスクを実行する必要があるのは悪いことです。 "on press"型のロジックを扱うためのコードをいくつか追加します。 – Xirema

+0

@Tab私はいくつかの追加情報で投稿を更新しました。 – Xirema

0

わかりましたので、私は自分の質問に答える(そして、彼は正しかったとして有効なものとして@Xiremaの答えを残して)よ

私はそれは、繰り返しにフレームレートとコールバックの間で同期されなかった理由を私は理解し考えて、それはOSがキーリピートを処理する方法のためです。4000 +キーリピート信号を1秒間に送信しません(4000 + fpsを取得するため)、60コール/秒に制限されます! この問題を解決するには、コールバック関数で最後に押されたキーを60回/秒だけ登録し、フレームごとに1回(メインループ内で)GLFWkeyfunの外で関数を実行するか、動きを正規化する方法を見つけます吃音を避ける!

とにかく、もう一度@Xiremaにお返事いただきありがとうございます。私の質問が誰かに役立つことができれば幸いです。 (実際には;-))

P.S:key_repeatコールバック関数の固定deltatime値を使用して、何度もキーリピート信号を送信することがわかっていれば、このトリックもできます!

P.P.S:キーボードのリピートは、OSやユーザーの設定、さらにはCPUの能力によっても変わる可能性があるので、最適な解決策は、GLFWkeyfunに別のデルタタイムを設定することです。この方法でコールバック関数に送信して、キーの繰り返しのためのデルタ時間とすべての吃音を避ける!