ページ 1 / 1
新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 18:54
by Hiragi(GKUTH)
タイトルの通りですが、このサイトにある
新・ゲームプログラミングの館の2.9の全てのキーの監視をする関数を
SDLで動かしたいと思っています。そこでDXライブラリのGetHitKeyStateAll関数にほぼ同等の動作をすると思われる
SDL_GetKeyState関数を使用することにしました。ほぼ形は同じのままで関数だけ変えています。コンパイル、ビルドは通りますが、キー入力に反応してくれないようです。どこに問題があるのでしょうか?
コード
コード:
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
int Key[256]; // キーが押されているフレーム数を格納する
// キーの入力状態を更新する
int gpUpdateKey(){
int tmpKey[256]; // 現在のキーの入力状態を格納する
SDL_GetKeyState(tmpKey);
for( int i=0; i<256; i++ ){
if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
int main()
{
SDL_Surface *mainScr;
SDL_Surface *img = IMG_Load("image/Reimu.png");
SDL_Rect img_rect,scr_img_rect;
img_rect.x = 0;
img_rect.y = 0;
img_rect.w = 320;
img_rect.h = 320;
scr_img_rect.x = 0;
scr_img_rect.y = 0;
SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化
mainScr = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); //ウィンドウ作成
while(1)
{
gpUpdateKey();//呼び出す
if(Key[SDLK_UP] >= 1) //上キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y--; //上に移動
}
if(Key[SDLK_DOWN] >= 1) //下キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y++; //下に移動
}
SDL_BlitSurface(img, &img_rect, mainScr, &scr_img_rect); //画像描画をする
SDL_Delay(33); //だいたい30FPSぐらいで待つ
SDL_Flip(mainScr);//画像を表に出す
}
SDL_FreeSurface(mainScr);//開放
return 0;
}
1.画像はちゃんと存在します、if文なしでの表示を確認しています。
2.if文なしでの画像の移動を確認しています。
よろしくおねがいします。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 18:58
by みけCAT
SDL_GetKeyState関数の使い方が違うようです。
http://www.tacoworks.jp/software/SDLdoc ... state.html
このページによると、キーの情報はSDL_GetKeyState関数の戻り値のポインタの先に入っているようです。
ついでに、SDL_PumpEvents関数は呼ばなくても大丈夫なのですか?
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 19:04
by Hiragi(GKUTH)
SDL_PumpEventsで変化ありませんでした、
それと戻り値のポインタの先という表現がわかりません・・・
離席するので返信はかなり後になりそうです。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 19:30
by みけCAT
SDL_GetKeyStateから返されるポインタを配列として見たときのSDLK_*番目の要素が、
対応するキーが押されていたら真、押されていなかったら偽になるようです。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:10
by Hiragi(GKUTH)
みけCAT さんが書きました:SDL_GetKeyStateから返されるポインタを配列として見たときのSDLK_*番目の要素が、
対応するキーが押されていたら真、押されていなかったら偽になるようです。
...まだわかりません、まずキーの状態が戻り値として帰ってくることがわかったので
コード:
int Key[256]; // キーが押されているフレーム数を格納する
// キーの入力状態を更新する
int gpUpdateKey(){
Uint8 *tmpKey; // 現在のキーの入力状態を格納する
tmpKey = SDL_GetKeyState(NULL);
SDL_PumpEvents();
for( int i=0; i<256; i++ )
{
if( tmpKey[i] == 1){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
のようにしましたが変わりません、配列の指定を間違っているように思いますが、どう間違っていて何をすればいいのかがわかりません、
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:21
by みけCAT
最初のコードと同じように、tmpKey == 1ではなくtmpKey != 0にしたらどうなりますか?
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:23
by Hiragi(GKUTH)
変化ありませんでした、
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:34
by みけCAT
SDLK_UPが256を超えている可能性があります。
自分の環境ではSDL_GetKeyState関数に渡したポインタに入った要素数が323、
SDLK_UPが273、SDLK_DOWNが274でした。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:42
by Hiragi(GKUTH)
みけCAT さんが書きました:SDLK_UPが256を超えている可能性があります。
自分の環境ではSDL_GetKeyState関数に渡したポインタに入った要素数が323、
SDLK_UPが273、SDLK_DOWNが274でした。
どうやら自分がascii範囲に全て入っているものだと思い込んでいました。範囲を256から512に変更した結果
動作したのでコレで解決とします。
ありがとうございました。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:43
by Hiragi(GKUTH)
Hiragi(GKUTH) さんが書きました:みけCAT さんが書きました:SDLK_UPが256を超えている可能性があります。
自分の環境ではSDL_GetKeyState関数に渡したポインタに入った要素数が323、
SDLK_UPが273、SDLK_DOWNが274でした。
どうやら自分がascii範囲に全て入っているものだと思い込んでいました。範囲を256から512に変更した結果
動作したのでコレで解決とします。
ありがとうございました。
コード貼るの忘れていました。
コード:
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
int Key[256]; // キーが押されているフレーム数を格納する
// キーの入力状態を更新する
int gpUpdateKey(){
Uint8 *tmpKey; // 現在のキーの入力状態を格納する
tmpKey = SDL_GetKeyState(NULL);
SDL_PumpEvents();
for(int i=0; i<512; i++ )
{
if( tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
int main()
{
SDL_Surface *mainScr;
SDL_Surface *img = IMG_Load("image/Reimu.png");
SDL_Rect img_rect,scr_img_rect;
img_rect.x = 0;
img_rect.y = 0;
img_rect.w = 320;
img_rect.h = 320;
scr_img_rect.x = 0;
scr_img_rect.y = 0;
SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化
mainScr = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); //ウィンドウ作成
while(1)
{
gpUpdateKey();
if(Key[SDLK_UP] >= 1) //上キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y--; //上に移動
}
if(Key[SDLK_DOWN] >= 1) //下キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y++; //下に移動
}
SDL_BlitSurface(img, &img_rect, mainScr, &scr_img_rect); //画像描画をする
SDL_Delay(33); //だいたい30FPSぐらいで待つ
SDL_Flip(mainScr);//画像を表に出す
}
SDL_FreeSurface(mainScr);//開放
return 0;
}
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 20:48
by みけCAT
Keyの要素数が256のままなので、確保された領域の外にアクセスすることになり、危険です。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月29日(日) 21:25
by Hiragi(GKUTH)
みけCAT さんが書きました:Keyの要素数が256のままなので、確保された領域の外にアクセスすることになり、危険です。
単純に要素数を512に増やしました。
コード:
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
int Key[512]; // キーが押されているフレーム数を格納する
// キーの入力状態を更新する
int gpUpdateKey(){
Uint8 *tmpKey; // 現在のキーの入力状態を格納する
tmpKey = SDL_GetKeyState(NULL);
SDL_PumpEvents();
for(int i=0; i<512; i++ )
{
if( tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
int main()
{
SDL_Surface *mainScr;
SDL_Surface *img = IMG_Load("image/Reimu.png");
SDL_Rect img_rect,scr_img_rect;
img_rect.x = 0;
img_rect.y = 0;
img_rect.w = 320;
img_rect.h = 320;
scr_img_rect.x = 0;
scr_img_rect.y = 0;
SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化
mainScr = SDL_SetVideoMode(640, 480, 32, SDL_SWSURFACE); //ウィンドウ作成
while(1)
{
gpUpdateKey();
if(Key[SDLK_UP] >= 1) //上キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y--; //上に移動
}
if(Key[SDLK_DOWN] >= 1) //下キーが押されていれば(=該当の配列が1以上であれば)
{
scr_img_rect.y++; //下に移動
}
SDL_BlitSurface(img, &img_rect, mainScr, &scr_img_rect); //画像描画をする
SDL_Delay(33); //だいたい30FPSぐらいで待つ
SDL_Flip(mainScr);//画像を表に出す
}
SDL_FreeSurface(mainScr);//開放
return 0;
}
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月30日(月) 00:26
by ISLe
SDLが返すtmpKeyの要素は512も無いのであいかわらず領域外をアクセスしますよ。
SDL_keysym.hによると要素数としてSDLK_LASTが使えます。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月30日(月) 08:45
by Hiragi(GKUTH)
ISLe さんが書きました:SDLが返すtmpKeyの要素は512も無いのであいかわらず領域外をアクセスしますよ。
SDL_keysym.hによると要素数としてSDLK_LASTが使えます。
SDLK_LASTをどこかで作られた定数だと仮定して、tmpKeyのアクセス領域をSDLK_LASTを「含まない」それ以下にし、Keyも要素を
SDLK_LAST(=323)個にして組み直しました。
ご指摘ありがとうございました。
以下、解決後のソース
コード:
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
int Key[SDLK_LAST]; // キーが押されているフレーム数を格納する
// キーの入力状態を更新する
int gpUpdateKey(){
Uint8 *tmpKey; // 現在のキーの入力状態を格納する
tmpKey = SDL_GetKeyState(NULL);
SDL_PumpEvents();
for(int i=0; i <= SDLK_LAST; i++ )
{
if( tmpKey[i] != 0){ // i番のキーコードに対応するキーが押されていたら
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
class Fps
{
int mStartTime; //測定開始時刻
int mCount; //カウンタ
float mFps; //fps
bool mUnlock;
int FLAG_UNLOCK;
static const int N = 20;//平均を取るサンプル数
public:
static const int uFPS = 75; //設定したFPS
Fps()
{
mStartTime = 0;
mCount = 0;
mFps = 0;
mUnlock = 0;
}
bool Update()
{
if( mCount == 0 )
{ //1フレーム目なら時刻を記憶
mStartTime = SDL_GetTicks();
}
if( mCount == N )
{ //Nフレーム目なら平均を計算する
int t = SDL_GetTicks();
mFps = 1000.f/((t-mStartTime)/(float)N);
mCount = 0;
mStartTime = t;
}
mCount++;
return true;
}
void Wait()
{
int tookTime = SDL_GetTicks() - mStartTime; //かかった時間
int waitTime = mCount*1000/uFPS - tookTime; //待つべき時間
if( waitTime > 0 )
{
SDL_Delay(waitTime); //計算上待つべき時間を待つ
}
}
};
int main()
{
Fps mainfps;
SDL_Surface *mainScr;
SDL_Surface *img = IMG_Load("image/Reimu.png");
SDL_Rect img_rect,scr_img_rect,fsc = {0,0,640,480};
img_rect.x = 0;
img_rect.y = 0;
img_rect.w = 320;
img_rect.h = 320;
scr_img_rect.x = 0;
scr_img_rect.y = 0;
SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化
mainScr = SDL_SetVideoMode(1280, 720, 32, SDL_HWSURFACE); //ウィンドウ作成
while(1)
{
mainfps.Update();
gpUpdateKey();
if(Key[SDLK_UP] >= 1){ scr_img_rect.y-=1;} //上に移動
if(Key[SDLK_DOWN] >= 1){ scr_img_rect.y+=1;} //下に移動
if(Key[SDLK_RIGHT] >= 1){ scr_img_rect.x+=1;} //上に移動
if(Key[SDLK_LEFT] >= 1){ scr_img_rect.x-=1;} //上に移動
SDL_BlitSurface(img, &img_rect, mainScr, &scr_img_rect); //画像描画をする
SDL_Flip(mainScr);//画像を表に出す
mainfps.Wait();
SDL_FillRect(mainScr, &fsc, SDL_MapRGB(mainScr->format, 0,0,0)); //画面のクリア
}
SDL_FreeSurface(mainScr);//開放
return 0;
}
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月30日(月) 16:54
by ISLe
Hiragi(GKUTH) さんが書きました:SDLK_LASTをどこかで作られた定数だと仮定して、tmpKeyのアクセス領域をSDLK_LASTを「含まない」それ以下にし、Keyも要素を
SDLK_LAST(=323)個にして組み直しました。
仮定じゃなくてSDL_keysym.hで定義されていますけど。
13行目の終了条件だとSDLK_LASTを「含む」ようになっていますからやっぱり領域外をアクセスします。
Key配列に対する領域外アクセスも復活してます。
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月31日(火) 17:39
by Hiragi(GKUTH)
返信遅れてすいません。
条件式を
for(int i=0; i <= SDLK_LAST; i++ )
から
for(int i=0; i < SDLK_LAST; i++)
に変更しました。
また、配列の宣言も
Key[SDLK_LAST - 1];
に変更しました、が相変わらず領域外にアクセスできてしまいます(もちろんKey[X]の部分に値を直接入力した時のみですが)。
しかし、この時点でかなり元の質問から離れてしまっていて現時点でちゃんと動作するので、このトピックは終了しようと思います。
この問題はまた別の機会に別のトピックで解決しようと思います。
答えて下さった方々、ありがとうございました。
ルールに従い、現時点のソースの提示。
コード:
#include <stdio.h>
#include "SDL/SDL.h"
#include "SDL/SDL_image.h"
int Key[SDLK_LAST - 1]; // キーが押されているフレーム数を格納する
int gpUpdateKey()
{
Uint8 *tmpKey; // 現在のキーの入力状態を格納する
tmpKey = SDL_GetKeyState(NULL);
SDL_PumpEvents();
for(int i=0; i < SDLK_LAST; i++ )
{
if( tmpKey[i] != 0) // i番のキーコードに対応するキーが押されていたら
{
Key[i]++; // 加算
} else { // 押されていなければ
Key[i] = 0; // 0にする
}
}
return 0;
}
bool tick(SDL_Rect *scr_m_rect,SDL_Rect *m_rect)
{
if(Key[SDLK_UP] >= 1){ scr_m_rect->y-=4;} //上に移動
if(Key[SDLK_DOWN] >= 1){ scr_m_rect->y+=4;} //下に移動
if(Key[SDLK_RIGHT] >= 1){ scr_m_rect->x+=4;} //上に移動
if(Key[SDLK_LEFT] >= 1){ scr_m_rect->x-=4;} //上に移動
return true;
}
bool Init(SDL_Surface **mainScr,SDL_Surface **img,SDL_Rect *fsc,SDL_Rect *m_rect,SDL_Rect *scr_m_rect)
{
fsc->x = 0,fsc->y = 0,fsc->w = 1280,fsc->h = 1024;
*img = IMG_Load("image/Reimu.png");
m_rect->x = 0;
m_rect->y = 0;
m_rect->w = 48;
m_rect->h = 48;
scr_m_rect->x = 0;
scr_m_rect->y = 0;
SDL_Init(SDL_INIT_EVERYTHING); //SDL初期化
*mainScr = SDL_SetVideoMode(1280, 1024, 32,SDL_HWSURFACE + SDL_DOUBLEBUF + SDL_FULLSCREEN); //ウィンドウ作成
return true;
}
class Fps
{
int mStartTime; //測定開始時刻
int mCount; //カウンタ
float mFps; //fps
bool mUnlock;
int FLAG_UNLOCK;
static const int N = 20;//平均を取るサンプル数
public:
static const int uFPS = 75; //設定したFPS
Fps()
{
mStartTime = 0;
mCount = 0;
mFps = 0;
mUnlock = 0;
}
bool Update()
{
if( mCount == 0 )
{ //1フレーム目なら時刻を記憶
mStartTime = SDL_GetTicks();
}
if( mCount == N )
{ //Nフレーム目なら平均を計算する
int t = SDL_GetTicks();
mFps = 1000.f/((t-mStartTime)/(float)N);
mCount = 0;
mStartTime = t;
}
mCount++;
return true;
}
void Wait()
{
int tookTime = SDL_GetTicks() - mStartTime; //かかった時間
int waitTime = mCount*1000/uFPS - tookTime; //待つべき時間
if( waitTime > 0 )
{
SDL_Delay(waitTime); //計算上待つべき時間を待つ
}
}
};
int main()
{
Fps mainfps;
SDL_Surface *mainScr,*img;
SDL_Rect m_rect,scr_m_rect,s_rect,scr_s_rect,fsc;
Init(&mainScr,&img,&fsc,&m_rect,&scr_m_rect);
while(1)
{
if(Key[SDLK_ESCAPE] == 1) {break;}
mainfps.Update();
mainfps.Wait();
gpUpdateKey();
tick(&scr_m_rect,&m_rect);
SDL_BlitSurface(img, &m_rect, mainScr, &scr_m_rect); //画像描画をする
SDL_Flip(mainScr);//画像を表に出す
SDL_FillRect(mainScr, &fsc, SDL_MapRGB(mainScr->format, 0,0,0)); //画面のクリア
}
SDL_FreeSurface(mainScr);//開放
SDL_Quit();
return 0;
}
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月31日(火) 19:54
by みけCAT
Hiragi(GKUTH) さんが書きました:条件式を
for(int i=0; i <= SDLK_LAST; i++ )
から
for(int i=0; i < SDLK_LAST; i++)
に変更しました。
また、配列の宣言も
Key[SDLK_LAST - 1];
に変更しました、が相変わらず領域外にアクセスできてしまいます(もちろんKey[X]の部分に値を直接入力した時のみです が)。
Key[X]の部分に値を直接入力しなくても、普通に範囲外にアクセスしていると思います。
せっかく範囲内に収めるようにループの範囲を減らしたのに、その配列の要素数も同じだけ減らしたら意味ないですよね?
Re: 新・ゲームプログラミングの館2.9の関数のSDL化
Posted: 2013年12月31日(火) 21:15
by Hiragi(GKUTH)
気付いていませんでした...
Key[SDLK_LAST];
にすれば今度こそ大丈夫なはず・・・!