慣性移動が出来ない!

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
soodbt

慣性移動が出来ない!

#1

投稿記事 by soodbt » 14年前

こんばんわ、今回はキャラクターの座標移動の計算で悩んだので質問させてもらいます。

コード:

void move(double *lx,*ly){
    int x=0,y=0;
 
    if(key[key_input_w]!=0){
        y++;
        if(key[key_input_w]<=100){//速度リミッター
if(player.speed_ine<key[key_input_w]/10)//wを離すとkey変数が0になるのでその時の代入阻止
{
player.speed_ine=key[key_input_w]/10;//加速する
}
}else{
if(0<player.speed_ine)player.speed_ine-=0.05;//押されていなくて、値が0以上なら慣性力を徐々に下げる
}
    }
    if(key[key_input_s]!=0){
        x--;
    }
    if(key[key_input_d]!=0){
        x++;
    }
    if(key[key_input_a]!=0){
        y--;
    }
    if(x!=0 || y!=0){
        player.angle_ine=player.angle;
        *lx=cos(player.angle_ine+atan2(x,y))*player.speed+player.speed_ine;
        *ly=sin(player.angle_ine+atan2(x,y))*player.speed+player.speed_ine;
    }
    else{
        *lx=0;
        *ly=0;
    }
}

key[key_input_w]という変数はwキーボードを押し続けるとその変数に+1し続けるという関数から送られて来た変数です。
_ineは慣性の総称です。
例えばplayer.speed_ineは慣性速度を記憶するための変数です。angleも同様です。

このコードだと、wを押すと前進し、加速していつかリミッターに触ります。
そして、wキーから手を離すとスピードを落としながら前進します。
しかし、aもしくはdキーを押すと、直角に慣性速度のまま移動します。(wキーは押してないのでいつか止まります。)
これはつまり、相対角度によった慣性とは名ばかりの別物なわけですが、これをどうしたら
目的とする操作方法になるでしょうか?
キャラクターの角度操作は別にあります。

1.w,a,s,dキーを押すとそれぞれキャラクター主観で↑←↓→と動かしたい。(キャラクターがしたを向いていたらwキーで下に行く)
2.wキーで加速、a.dキーは固定数値速度の移動、sキーで減速がしたい。
3.wキーで加速後wキーを離し、キャラクター角度を変える、例えば180度回転して
再度wキーを押すと今まで加速していた分だけ時間を(この表現は適切でない)かけながらUターンしたい。

簡単に言うとスリップしたいです。
カービィのエアライドで玉が3つ付いた滑るやつみたいに。
ドリフト?に近いかもしれません。
このコードをどう改変すればできるようになりますか?

soodbt

Re: 慣性移動が出来ない!

#2

投稿記事 by soodbt » 14年前

iPhoneだとTABが使えないので手書き入力の所は字下げできませんでした。
字下げされてない一列目の所は大体wキー判断文の内容です。
(4行目辺りからif文です。)

hidden

Re: 慣性移動が出来ない!

#3

投稿記事 by hidden » 14年前

このプログラムはどれだけ入力され続けたか?が速度になっているようですが
そうではなくて、入力があったときに速度を増やす と云う考え方のほうがいいですよ。

player構造体に座標と速度の変数を持たせてやって
v=v0+at をそのままmove関数に書けばいいのです。
現在の速度=前のフレームの速度+加速 です。

soodbt

Re: 慣性移動が出来ない!

#4

投稿記事 by soodbt » 14年前

ご返答ありがとうございます。
よく考えて見たら確かにそのままおし続けた時間を
速度にいれるのはあまりよくないですね。

その後、色々とやってみましたが
どうやってもスリップしてくれません。
今は

wキーをおすと加速度の増加と別キーでキャラクターの角度を変更するのと、移動する角度を変更してます。また、平行移動が可能です。
wキーを離すと加速度の減少とキャラクターの角度を変更しますが、移動する角度は変わりません。また、平行移動が可能です。

ただし、wキーを離した後、キャラクターの角度だけ90°変更し、wキーを押すと直角に曲がります。

これを、弧を描くように移動制御するにはどうしたらいいのでしょうか?
角度ごとに速度を割り振って、上限を共有しながら角度を合成してもできると思いますが現実的じゃありません。
何かいい方法ありませんか?

template

Re: 慣性移動が出来ない!

#5

投稿記事 by template » 14年前

はじめまして.
弧を描くように制御したいとのことでしたが,オブジェクト(機体とでもしときますか)のプロパティとしてX方向,Y方向の速度を持たせます.そしてフレームごとにX方向,Y方向に移動させれば弧を描くように移動させることができると思います.
それと注意として機体を回転させたとしても力の方向(速度とか)は変わらないので変換が必要です.
稚拙な言葉での説明でわからない点も多いかと思いますが,参考にでもと.

hidden

Re: 慣性移動が出来ない!

#6

投稿記事 by hidden » 14年前

player構造体がもつ変数
座標  x,y   速度  vX,vY   角度  angle   角速度 vAngle

関数内のローカル変数
加速度  aX,aY   角加速度  aAngle

1 vX,vY,vAngleを減らす(vX=vX*0.8とか。これが最高速とかにか関わります)
2 入力を受けてaX,aY,aAngleに値を入れる
3 aX,aYをangleで変換
4 aX,aYをvX,vYに、aAngleをvAngleに加算
5 vX,vY,vAngleをx,y,angleに加算


おそらくこんな感じが目標になると思います。
上限を考えるのではなく、加速よりも空気抵抗的な減速によって上限が勝手にできる
と考えたほうが楽だと思いますよ。 最高速度を設定し辛くはなりますが。

hidden

Re: 慣性移動が出来ない!

#7

投稿記事 by hidden » 14年前

あ、最高速度以上の速度になった時に
x,yをsqrt(x*x+y*y)で割ってそれぞれに最高速度をかければ最高速度での制限できますね。
ただ、等加速度運動していたのが突然最高速度で加速しなくなるって不自然に私は感じるので
毎フレーム速度に0.8をかけるとかのほうがいい・・・かな?

soodbt

Re: 慣性移動が出来ない!

#8

投稿記事 by soodbt » 14年前

パソコンから書きこむ機会があったので、現在のコードを貼ります。
あまり関係ないかもしれませんが、不自然に思われると思うので先に解説しておくと将来的にAIで3機動かしたいので構造体の数は4になります。
player[0]. ~player[3].

コード:


#include "head.h"
void move(double *l_tmpx,double *l_tmpy){
	double l_x = 0;//

	if(Key[KEY_INPUT_A]!=0){
		l_x--;
	}
	if(Key[KEY_INPUT_D]!=0){
		l_x++;
	}

	if(Key[KEY_INPUT_W]!=0){
		player[0].angle_inertia = player[0].angle;
		if(player[0].speed_max > player[0].speed_inertia){
			player[0].speed_inertia += player[0].axel;
		}
	}else{
		if(Key[KEY_INPUT_S]==0){
			if(player[0].axel <= player[0].speed_inertia){//-防止、orで0以上、にするのもアリ
				player[0].speed_inertia -= player[0].axel;//摩擦、加速度減少
			}
		}else{//Wを押していなくてSを押しているならば//普通ならブレーキとかの概念で逆なんだろうけど・・・
			player[0].angle_inertia = player[0].angle;//曲がる
		}
	}

	if((l_x != 0)||(player[0].speed_inertia>0)){
		*l_tmpx = cos(player[0].angle_inertia + l_x) * (player[0].speed + player[0].speed_inertia);//構造体にすべし。(多人数対策)
		*l_tmpy = sin(player[0].angle_inertia + l_x) * (player[0].speed + player[0].speed_inertia);//と思ったけどtmpに入れてるのがすでに構造体なわけでいいんだった
	}else{
		*l_tmpx=0;
		*l_tmpy=0;
	}
}
void check_move(double *l_tmpx,double *l_tmpy){//先に判定して可能なら移動する。l_p_x,yはcharacterの座標のtmp、そこにmove関数から来る数値を足して可能かどうか判断する。
	if((player[0].x + *l_tmpx) <=X_MAX && (player[0].x + *l_tmpx) >=0 ){
		player[0].x+=*l_tmpx;
	}
	if((player[0].y + *l_tmpy) <=Y_MAX && (player[0].y + *l_tmpy) >=0 ){
		player[0].y+=*l_tmpy;
	}
}
void player_main(){//4人characterを作るにはここにfor文入れるのかな??
	double l_tmpx=0,l_tmpy=0;

	move(&l_tmpx,&l_tmpy);//←こいつはキー操作なのでCPUキャラには必要ないはず・・・
	check_move(&l_tmpx,&l_tmpy);
}


non
記事: 1097
登録日時: 15年前

Re: 慣性移動が出来ない!

#9

投稿記事 by non » 14年前

コード:

void move(double *l_tmpx,double *l_tmpy){
    double l_x = 0;//
 
    if(Key[KEY_INPUT_A]!=0){
        l_x--;
    }
    if(Key[KEY_INPUT_D]!=0){
        l_x++;
    }
どうして、ローカル変数のdouble l_x = 0なのに
l_x++;
なの?

メンバーで
angle_inertia やspeed_inertiaは何の値ですか?加速度ですか?
>player[0].angle_inertia = player[0].angle;
そうすると、角加速度に角度を入れてることになるから変だし?
non

soodbt

Re: 慣性移動が出来ない!

#10

投稿記事 by soodbt » 14年前

遅くなりました。

>どうして、ローカル変数のdouble l_x = 0なのに
>l_x++;
>なの?
本来はキャラクターから見た角度八方行をatan2で取得する為のもので
l_yが消え、そのままでした。完成したときに不要ならintに治そうかと思ってました。

>メンバーで
>angle_inertia やspeed_inertiaは何の値ですか?加速度ですか?
>>player[0].angle_inertia = player[0].angle;
>そうすると、角加速度に角度を入れてることになるから変だし?
慣性が働いている角度と、慣性のスピードです。
おっしゃる通り、このコードだと緩やかなカーブは無理です。
これだと上に書いたとおり、wキーを押している間慣性の角度とスピードを記憶し続け
話したときにキャラクターがどの角度を向いていても同じ角度に慣性移動します。

しかし、hiddenさんの書いてある通り、角度とスピードではなく
xとyでステータスを持っていたほうがいいのでしょうか?
キャラクターの向きに依存した操作方法を目指してたので
hiddenさんの方法がうまく当てはめられなかったので・・・
レースゲームのドリフトやスリップみたいな考え方がわかりません。
自分の向いている方向に一定の量づつ進む角度を変えて行けばいいんですか?

soodbt

Re: 慣性移動が出来ない!

#11

投稿記事 by soodbt » 14年前

あ!
引用文に自分のコメントが・・・

hiddenさんの、加速度をマイフレームずつ少なくして
上限を作るっていう方法すばらしいとおもいます。
ぜひ取り入れさせてもらいます!

soodbt

Re: 慣性移動が出来ない!

#12

投稿記事 by soodbt » 14年前

実行ファイルを直接見てもらったほうが早いですかね・・・?

hidden

Re: 慣性移動が出来ない!

#13

投稿記事 by hidden » 14年前

角度と速度でやっても大丈夫ですよ。どちらにせよやることは同じです。

4 aX,aYをvX,vYに、aAngleをvAngleに加算
のところを正しく実装すればキャラクターの向き依存に加速できます。

soodbt

Re: 慣性移動が出来ない!

#14

投稿記事 by soodbt » 14年前

http://ll.la/gyr(
passはdxです。
hiddenさん返答ありがとうございます。
ちょっとやってみます!

soodbt

Re: 慣性移動が出来ない!

#15

投稿記事 by soodbt » 14年前

あれ?ちょっとやってて思ったんですけど
これだと弧を描くように慣性移動できないような・・・

最高速度や加速のあり方については試行錯誤中です^^;

soodbt

Re: 慣性移動が出来ない!

#16

投稿記事 by soodbt » 14年前

>レースゲームのドリフトやスリップみたいな考え方がわかりません。
>自分の向いている方向に一定の量づつ進む角度を変えて行けばいいんですか?

これを実際にやってみたコードが此方です。
動作は目指している物に類似していますが、止まったあとの初動ががおかしい(慣性によって移動する角度がしつこい??)
また、ぐるぐる一定時間回ると横にスライドするように移動してしまいます。

コード:

#include "head.h"
void move(double *l_tmpx,double *l_tmpy){
	double l_x = 0;//

	if(Key[KEY_INPUT_A]!=0){
		l_x--;
	}
	if(Key[KEY_INPUT_D]!=0){
		l_x++;
	}

	if(Key[KEY_INPUT_W]!=0){//慣性の角度??

			if(player[0].angle<player[0].angle_inertia){
				player[0].angle_inertia -= (player[0].angle_inertia-player[0].angle)/30;
			}else if(player[0].angle>player[0].angle_inertia){
				player[0].angle_inertia += (player[0].angle-player[0].angle_inertia)/30;
			}

		if(player[0].speed_max > player[0].speed_inertia){//最高速度の設定
			player[0].speed_inertia += player[0].axel;
		}
	}else{
		if(Key[KEY_INPUT_S]==0){//Wを押していてSを押していないとき。
			if(player[0].axel <= player[0].speed_inertia){//-防止、orで0以上、にするのもアリ
				player[0].speed_inertia -= player[0].axel;//摩擦、加速度減少
			}

			if(player[0].angle<player[0].angle_inertia){
				player[0].angle_inertia -= (player[0].angle_inertia-player[0].angle)/60;
			}else if(player[0].angle>player[0].angle_inertia){
				player[0].angle_inertia += (player[0].angle-player[0].angle_inertia)/60;
			}
		}else{//Wを押していなくてSを押しているならば//普通ならブレーキとかの概念で逆なんだろうけど・・・

		}
	}

	if((l_x != 0)||(player[0].speed_inertia>0)){
		*l_tmpx = cos(player[0].angle_inertia + l_x) * (player[0].speed + player[0].speed_inertia);//構造体にすべし。(多人数対策)
		*l_tmpy = sin(player[0].angle_inertia + l_x) * (player[0].speed + player[0].speed_inertia);//と思ったけどtmpに入れてるのがすでに構造体なわけでいいんだった
	}else{
		*l_tmpx=0;
		*l_tmpy=0;
	}
}
void check_move(double *l_tmpx,double *l_tmpy){//先に判定して可能なら移動する。l_p_x,yはcharacterの座標のtmp、そこにmove関数から来る数値を足して可能かどうか判断する。
	if((player[0].x + *l_tmpx) <=X_MAX && (player[0].x + *l_tmpx) >=0 ){
		player[0].x+=*l_tmpx;
	}
	if((player[0].y + *l_tmpy) <=Y_MAX && (player[0].y + *l_tmpy) >=0 ){
		player[0].y+=*l_tmpy;
	}
}
void player_main(){//4人characterを作るにはここにfor文入れるのかな??
	double l_tmpx=0,l_tmpy=0;

	move(&l_tmpx,&l_tmpy);//←こいつはキー操作なのでCPUキャラには必要ないはず・・・
	check_move(&l_tmpx,&l_tmpy);
}
http://www1.axfc.net/uploader/Sc/so/257599.zip

soodbt

Re: 慣性移動が出来ない!

#17

投稿記事 by soodbt » 14年前

とりあえず解決とさせてもらいます・・・

hidden

Re: 慣性移動が出来ない!

#18

投稿記事 by hidden » 14年前

なんか目指してるのがよくわからなくなったのでちょっとサンプルを作ってみました。
キーボードの1 2 3を押してからz xを押すとそれぞれ抵抗 力 重さが変更できますが
0以下にならないような処理をしてないのでとりあえず0以下にしないでください。
あと、あんまり良く考えずに書いていったのでツッコミは御容赦ください(笑)

a=F/m-kv/m
v=v0+(F/m-kv0/m)t
v=V0(1-kt/m)+Ft/m
をそのまま書いた(つもりの)ものです。

main.cpp

コード:

#include <DxLib.h>
#include <math.h>

#define	SPDLINE_SIZE	(10)
#define	ANGLINE_SIZE	(50)



#include "Struct.h"

extern int	gpUpdateKey();
extern int	CheckStateKey(unsigned char);

int ProcessLoop(){
	if(ProcessMessage()!=0)return -1;//プロセス処理がエラーなら-1を返す
	if(ClearDrawScreen()!=0)return -1;//画面クリア処理がエラーなら-1を返す
	gpUpdateKey();//現在のキー入力処理を行う
	return 0;
}


int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定

	ch_t	player;

	player.x			= 320.0f;				// 座標
	player.y			= 240.0f;		
	player.spdX		= 0.0f;				// 速度(世界座標)
	player.spdY		= 0.0f;
	player.angle		= 90.0f;				// 初期角度は90度(上向き)
	player.spdAngle	= 0.0f;				// 回転速度0
	player.resist		= 0.02f;				// 空気抵抗
	player.force		= 0.2f;				// 力
	player.weight		= 1.0f;				// 重さ
	player.arrowColor1	= GetColor(255,0,0);
	player.arrowColor2	= GetColor(0,255,255);

	int i = 0;
	int debugStringColor = GetColor(0,0,255);

	while( ProcessLoop()==0 ){
		float	aX = 0.0f, aY = 0.0f, aAngle = 0.0f;	// 加速量
		float	accele	= player.force/player.weight;	// 加速力


		if( CheckStateKey(KEY_INPUT_1)		== 1 ){ i = 0; }
		if( CheckStateKey(KEY_INPUT_2)		== 1 ){ i = 1; }
		if( CheckStateKey(KEY_INPUT_3)		== 1 ){ i = 2; }

		if( (CheckStateKey(KEY_INPUT_Z)	== 1)&&(i==0) ){ player.resist	+= 0.001f;	}
		if( (CheckStateKey(KEY_INPUT_Z)	== 1)&&(i==1) ){ player.force	+= 0.1f;	}
		if( (CheckStateKey(KEY_INPUT_Z)	== 1)&&(i==2) ){ player.weight	+= 0.1f;	}
		if( (CheckStateKey(KEY_INPUT_X)	== 1)&&(i==0) ){ player.resist	-= 0.001f;	}
		if( (CheckStateKey(KEY_INPUT_X)	== 1)&&(i==1) ){ player.force	-= 0.1f;	}
		if( (CheckStateKey(KEY_INPUT_X)	== 1)&&(i==2) ){ player.weight	-= 0.1f;	}

		if( CheckStateKey(KEY_INPUT_W)		>= 1 ){	aY		+= accele; }
		if( CheckStateKey(KEY_INPUT_A)		>= 1 ){	aX		-= accele; }
		if( CheckStateKey(KEY_INPUT_D)		>= 1 ){	aX		+= accele; }
		if( CheckStateKey(KEY_INPUT_LEFT)	>= 1 ){ aAngle	+= accele; }
		if( CheckStateKey(KEY_INPUT_RIGHT)	>= 1 ){ aAngle	-= accele; }

		// 減速する V=V0(1-kt/m)+at の1項目
		player.spdX		*= 1.0f-player.resist/player.weight;
		player.spdY		*= 1.0f-player.resist/player.weight;
		player.spdAngle	*= 1.0f-player.resist/player.weight;

		// 加速量を速度に加算する 加速はローカル、速度は世界座標なのでangleで変換してから
		player.spdX		+= aY*cos(player.angle*PHI_F/180.0f) + aX*sin(player.angle*PHI_F/180.0f); 
		player.spdY		-= aY*sin(player.angle*PHI_F/180.0f) - aX*cos(player.angle*PHI_F/180.0f);
		player.spdAngle += aAngle;

		// 画面から出ないようにする
		player.x		+= player.spdX;
		if( player.x > 640.0f	){ player.x = 640.0f;	}
		if( player.x < 0.0f		){ player.x = 0.0f;		}
		player.y		+= player.spdY;
		if( player.y > 480.0f	){ player.y = 480.0f;	}
		if( player.y < 0.0f		){ player.y = 0.0f;		}
		// 角度の値が大きくなり過ぎないようにする
		player.angle	+= player.spdAngle;
		if( player.angle > 360.0f	){ player.angle -= 360.0f;	}
		if( player.angle < 0.0f		){ player.angle += 360.0f;	}



		// 描画
		DrawLine( (int)player.x, (int)player.y,
			(int)(player.x+player.spdX*SPDLINE_SIZE), (int)(player.y+player.spdY*SPDLINE_SIZE), player.arrowColor1 );
		DrawLine( (int)player.x, (int)player.y,
			(int)(player.x+ANGLINE_SIZE*cos(player.angle*PHI_F/180.0f)), (int)(player.y-ANGLINE_SIZE*sin(player.angle*PHI_F/180.0f)),
			player.arrowColor2);
		DrawCircle( (int)(player.x), (int)(player.y), 5, player.arrowColor2 );

		DrawFormatString(0,0,debugStringColor,"X=%.2f",player.x);
		DrawFormatString(100,0,debugStringColor,"Y=%.2f",player.y);

		DrawFormatString(0,20,debugStringColor,"spd=%.2f",sqrt(player.spdX*player.spdX+player.spdY*player.spdY));
		DrawFormatString(100,20,debugStringColor,"ang=%.2f",player.angle);

		DrawFormatString(0,40,debugStringColor,"Vmax=%.2f",player.force/player.resist);
		DrawFormatString(100,40,debugStringColor,"A=%.4f",accele);
		DrawFormatString(200,40,debugStringColor,"κ=%.4f",player.resist);
		DrawFormatString(300,40,debugStringColor,"F=%.4f",player.force);
		DrawFormatString(400,40,debugStringColor,"M=%.4f",player.weight);
		ScreenFlip();
	}

	DxLib_End();
	return 0;
}
key.cpp

コード:

#include <DxLib.h>

int Key[256]; // キーが押されているフレーム数を格納する

int gpUpdateKey(){
	char tmpKey[256]; // 現在のキーの入力状態を格納する
	GetHitKeyStateAll( tmpKey ); // 全てのキーの入力状態を得る
	for( int i=0; i<256; i++ ){ 
		if( tmpKey[i] != 0 ){ // i番のキーコードに対応するキーが押されていたら
			Key[i]++;     // 加算
		} else {              // 押されていなければ
			Key[i] = 0;   // 0にする
		}
	}
	return 0;
}

int CheckStateKey(unsigned char Handle){
	return Key[Handle];
}
struct.h

コード:

typedef struct
{
	float	x, y;
	float	spdX, spdY;
	float	angle;
	float	spdAngle;
	float	resist;
	float	force;
	float	weight;
	int		arrowColor1;
	int		arrowColor2;
}ch_t;

閉鎖

“C言語何でも質問掲示板” へ戻る