初めて質問をさせていただきます。
プログラムの勉強を始めて間もない初心者です。
ボタンを連打するとスピードが上がっていき、やめると下がっていくプログラムを作りたいのですが、どうしていいかさっぱり状態です。
サンプルプログラムを頂ければ幸いですが、考え方かヒントをだけでも教えて欲しいです。
よろしくお願いいたします。
連打プログラム
- softya(ソフト屋)
- 副管理人
- 記事: 11677
- 登録日時: 13年前
- 住所: 東海地方
- 連絡を取る:
Re: 連打プログラム
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。
Re: 連打プログラム
おそらく考え方を提示することは容易ですが、
Octopusさんがどのような加速と減速を想定しているかにアルゴリズムは大きく依存します。
現実の現象に当てはめて加減速のイメージを説明していただくか、もしくは
イメージされている挙動そのものを説明していただかないと提案しづらいです。
特に減速の挙動はパターンがいくつもあります。
例えば僕が思う加減速だと次のようなものがあります
①ボタンを連打すると、ボタンを押すたびにスピードが回数に比例して上がる。
しばらく入力がないと、時間に比例して減速を始める。
②ボタンを連打すると、押す間隔が短いほどスピードが上がる。
時間に対して速度低下が線形になるよう一定の制動力が常に働くが、これはある程度以上押す間隔が短ければ
一回ボタンを押すことによる速度上昇を下回る。
連打速度が一定でも、加速している限り速度は一定値に収束しない。
ブレーキを掛けながら間欠的にアクセルを踏んでるようなものを考えればよい
③ボタンを連打する間隔が短いと速度が上昇するが、速度に比例した抵抗を受ける。
これは連打速度が一定だとある程度の速度に達したのち一定速になる。
(現実世界における空気抵抗などに相当)
連打をやめると速度は下がるが、下がり方は時間に対して線形でない。
④ 2と3を混ぜたもの。(現実世界におけるたとえば自転車の加減速に相当)
⑤ボタンを連打すると速度は上昇するが、速度には毎フレームごとに1未満の定数値が乗算される。
これも連打速度が一定だとある程度の速度に達したのち一定速になる。
やはり連打をやめると速度は下がるが、下がり方は時間に対して線形でない。
どんなものを実現したいのか、それがないと何とも言えません。
Octopusさんがどのような加速と減速を想定しているかにアルゴリズムは大きく依存します。
現実の現象に当てはめて加減速のイメージを説明していただくか、もしくは
イメージされている挙動そのものを説明していただかないと提案しづらいです。
特に減速の挙動はパターンがいくつもあります。
例えば僕が思う加減速だと次のようなものがあります
①ボタンを連打すると、ボタンを押すたびにスピードが回数に比例して上がる。
しばらく入力がないと、時間に比例して減速を始める。
②ボタンを連打すると、押す間隔が短いほどスピードが上がる。
時間に対して速度低下が線形になるよう一定の制動力が常に働くが、これはある程度以上押す間隔が短ければ
一回ボタンを押すことによる速度上昇を下回る。
連打速度が一定でも、加速している限り速度は一定値に収束しない。
ブレーキを掛けながら間欠的にアクセルを踏んでるようなものを考えればよい
③ボタンを連打する間隔が短いと速度が上昇するが、速度に比例した抵抗を受ける。
これは連打速度が一定だとある程度の速度に達したのち一定速になる。
(現実世界における空気抵抗などに相当)
連打をやめると速度は下がるが、下がり方は時間に対して線形でない。
④ 2と3を混ぜたもの。(現実世界におけるたとえば自転車の加減速に相当)
⑤ボタンを連打すると速度は上昇するが、速度には毎フレームごとに1未満の定数値が乗算される。
これも連打速度が一定だとある程度の速度に達したのち一定速になる。
やはり連打をやめると速度は下がるが、下がり方は時間に対して線形でない。
どんなものを実現したいのか、それがないと何とも言えません。
Re: 連打プログラム
すぐに実装できそうだったのでやってみました。
このプログラムで左マウス押下中にボールの速度を上げて
30フレーム毎に速度を1減速させてみました。
追加したのはmouse_input()とspeed()とint frame_cntです。
mouse_input()で左クリックしたらスピードを上げる処理をしています。
speed()でspeed関数を30回通過したら速度を1減速しています。
ちなみに今は初期値の2よりもスピードが下がらないようにガード処理も入っています。
ball_dxとball_dyが1フレーム内で移動する移動量になりますのでこれが速度になります。
先に述べるとなんでball_dxとball_dyを++したり--してるかと言うと
座標の移動なので右に行ったり左にいったり上にいったり下にいったりするので
そのために向きを判定してから加算や減算をしています。
今は簡単に左クリック押している間ボールの速度が上がります。
これをクリック連打で速度を上げる場合は以下を参考にして見てください。
http://hpcgi2.nifty.com/natupaji/bbs/pa ... ew&no=1196
備考
Microsoft VisualC++ 2010 ExpressでDXライブラリの環境があれば動かせますが
画像ファイルは適当なのを使ってください。
このプログラムで左マウス押下中にボールの速度を上げて
30フレーム毎に速度を1減速させてみました。
追加したのはmouse_input()とspeed()とint frame_cntです。
mouse_input()で左クリックしたらスピードを上げる処理をしています。
speed()でspeed関数を30回通過したら速度を1減速しています。
ちなみに今は初期値の2よりもスピードが下がらないようにガード処理も入っています。
ball_dxとball_dyが1フレーム内で移動する移動量になりますのでこれが速度になります。
先に述べるとなんでball_dxとball_dyを++したり--してるかと言うと
座標の移動なので右に行ったり左にいったり上にいったり下にいったりするので
そのために向きを判定してから加算や減算をしています。
今は簡単に左クリック押している間ボールの速度が上がります。
これをクリック連打で速度を上げる場合は以下を参考にして見てください。
http://hpcgi2.nifty.com/natupaji/bbs/pa ... ew&no=1196
備考
Microsoft VisualC++ 2010 ExpressでDXライブラリの環境があれば動かせますが
画像ファイルは適当なのを使ってください。
#include "DxLib.h"
#define MARGIN 5
#define RACKET_X 96
#define RACKET_Y 96
#define RACKET_SIZE 96 / 2
#define BALL_SIZE 16
#define BALL_CENTER 16 / 2
// ボール座標
int ball_x = 0;
int ball_y = 0;
// ラケット座標
int racket_x;
int racket_y;
// ボール色
int ball_color;
// ラケット色
int racket_color;
// ボールの移動量
int ball_dx = 2;
int ball_dy = 2;
// ラケット情報
int cnt = 0;
//ハンドル
int BALLHandle;
int RKTHandle;
// ラケット座標保存領域
int racket_oldx;
int racket_oldy;
// 30フレームカウンタ
int frame_cnt = 0;
// マウスイベント
void mouse_input()
{
// マウスの左クリックが押されている間
if( ( GetMouseInput() & MOUSE_INPUT_LEFT ) != 0 )
{
if(ball_dx < 0)
{
ball_dx--;
}else{
ball_dx++;
}
if(ball_dy < 0)
{
ball_dy--;
}else{
ball_dy++;
}
}
}
// 速度判定
void speed()
{
// 30フレーム以上回ったら
if(frame_cnt > 30)
{
// ボール移動量判定
if(ball_dx < -2 )
{
ball_dx++;
}
if(ball_dx > 2)
{
ball_dx--;
}
if(ball_dy < -2 )
{
ball_dy++;
}
if(ball_dy > 2)
{
ball_dy--;
}
// フレームカウント初期化
frame_cnt = 0;
}else{
// フレームカウント加算
frame_cnt++;
}
}
// 描画処理
void draw()
{
// ボール中心座標変換
ball_x = ball_x - BALL_CENTER;
ball_y = ball_y - BALL_CENTER;
// 枠描画
DrawBox( racket_x + MARGIN,
racket_y + MARGIN,
racket_x + RACKET_X - MARGIN,
racket_y,
GetColor(255,0,0),true);
DrawBox( racket_x + MARGIN,
racket_y + RACKET_Y - MARGIN,
racket_x + RACKET_X - MARGIN,
racket_y + RACKET_Y,
GetColor(255,255,0),true);
DrawBox( racket_x,
racket_y + MARGIN,
racket_x + MARGIN,
racket_y + RACKET_Y - MARGIN,
GetColor(0,255,0),true);
DrawBox( racket_x + RACKET_X - MARGIN,
racket_y + MARGIN,
racket_x + RACKET_X,
racket_y + RACKET_Y - MARGIN,
GetColor(0,0,255),true);
// ボール描画
DrawGraph(ball_x, ball_y, BALLHandle, TRUE);
// ラケット描画
DrawGraph(racket_x, racket_y, RKTHandle, TRUE);
}
// ラケット処理
void racket_move()
{
VECTOR lineA1, lineA2, lineB1,lineB2;
float length;
// ラケット座標取得
GetMousePoint(&racket_x, &racket_y);
switch(cnt)
{
case 0:
// 1フレーム間の移動した座標(ボール)を設定
lineB1 = VGet(ball_x-ball_dx, ball_y-ball_dy, 0.0f);
lineB2 = VGet(ball_x, ball_y, 0.0f);
// 線分の描画
DrawLine( lineB1.x, lineB1.y, lineB2.x, lineB2.y, GetColor(255,255,255));
// 1フレーム間の移動した座標(ラケット)を設定
lineA1 = VGet(racket_oldx, racket_oldy,0.0f);
lineA2 = VGet(racket_oldx + RACKET_Y, racket_oldy,0.0f);
// 線分の描画
DrawLine( lineA1.x, lineA1.y, lineA2.x, lineA2.y, GetColor(255,255,255));
racket_oldx = racket_x;
racket_oldy = racket_y;
cnt++;
break;
case 1:
// 1フレーム間の移動した座標(ボール)を設定
lineB1 = VGet(ball_x-ball_dx, ball_y-ball_dy, 0.0f);
lineB2 = VGet(ball_x, ball_y, 0.0f);
// 線分の描画
DrawLine( lineB1.x, lineB1.y, lineB2.x, lineB2.y, GetColor(255,255,255));
// 線分座標(ラケット)と点座標(ボール)を設定
lineA1 = VGet(racket_oldx, racket_oldy,0.0f);
lineA2 = VGet(racket_oldx + RACKET_Y, racket_oldy,0.0f);
// 線分の描画
DrawLine( lineA1.x, lineA1.y, lineA2.x, lineA2.y, GetColor(255,255,255));
racket_oldx = racket_x;
racket_oldy = racket_y;
cnt = 0;
break;
}
// 二線分の最近点どうしの距離を求める
length = Segment_Segment_MinLength( lineA1, lineA2, lineB1, lineB2 );
if( length < 1.0f )
{
// 当たった
ball_dy = -ball_dy;
ball_x += ball_dx;
ball_y += ball_dy;
}
}
// ボール処理
void ball_move()
{
// ボール中心座標変換
ball_x = ball_x + BALL_CENTER;
ball_y = ball_y + BALL_CENTER;
// ボール移動量加算
ball_x += ball_dx;
ball_y += ball_dy;
// 壁判定
// 上壁
if(ball_y < 0)
{
ball_dy *= -1;
ball_x += ball_dx;
ball_y += ball_dy;
}
// 下壁
if(ball_y > 720)
{
ball_dy *= -1;
ball_x += ball_dx;
ball_y += ball_dy;
}
// 左壁
if(ball_x < 0)
{
ball_dx *= -1;
ball_x += ball_dx;
ball_y += ball_dy;
}
// 右壁
if(ball_x > 1280)
{
ball_dx *= -1;
ball_x += ball_dx;
ball_y += ball_dy;
}
}
// 初期化
void init()
{
BALLHandle = LoadGraph("./data/ball.png");
RKTHandle = LoadGraph("./data/cursor02.png");
// ラケット座標取得
GetMousePoint(&racket_x, &racket_y);
}
// メイン関数
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow )
{
// 画面モードを32ビットカラーにセット
SetGraphMode(1280, 720, 32);
// タイトルを Test に変更
SetMainWindowText("Test");
// ウインドウモード起動
ChangeWindowMode(true);
// 描画先画面を表にする
SetDrawScreen(DX_SCREEN_BACK);
// 非アクティブでも処理実行
SetAlwaysRunFlag(true);
// DXライブラリ初期化処理
if( DxLib_Init() == -1 )
{
// エラーが起きたら直ちに終了
return -1;
}
// マウスを非表示にする
//SetMouseDispFlag(false);
// 初期化
init();
// メッセージループ処理
while( ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0 )
{
// 画面を初期化
ClearDrawScreen();
// ボール処理
ball_move();
// ラケット処理
racket_move();
// マウスイベント
mouse_input();
// 速度変更
speed();
// 描画処理
draw();
// 裏画面表示
ScreenFlip();
}
// DXライブラリ使用の終了処理
DxLib_End();
// ソフトの終了
return 0;
/* 寝違えて首が痛い */
}