「3D] ボール自由落下-カメラの概念

フォーラム(掲示板)ルール
フォーラム(掲示板)ルールはこちら  ※コードを貼り付ける場合は [code][/code] で囲って下さい。詳しくはこちら
たらお
記事: 19
登録日時: 9年前

「3D] ボール自由落下-カメラの概念

#1

投稿記事 by たらお » 9年前

現在、2Dで作ったボール発生→自由落下のプログラムがあるのですが、これを3Dに変更しようとして、以下のようなソースコードを書きました。

コード:

//----------[インクルードファイル ]----------------------------------------
#include "DxLib.h"
#include <math.h>
//----------[マクロ定義部 ]----------------------------------------
#define g 9.8//重力加速度
#define BMAX 100 //ボールの最大出現数を100個に
//----------[ グローバル変数 ]----------------------------------------
int clickcount;//クリック格納変数
int	beforeMouseInput;//1フレーム前のマウスの状態
int mx,my;      //マウス座標
double F, ax,ay,az; //力と加速度
static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード
//----------[ ボール構造体 ]----------------------------------------
struct Ball{
	int   flag;//1:使用 0:消去
	double x ; //x座標
	double y ; //y座標
	double z ; //z座標
	double vx ; //x軸方向の速度
	double vy ; //y軸方向の速度
	double vz ; //z軸方向の速度
	double m ; //質量
	double r ; //半径
	double k ; //ばね定数
	double c ; //粘性係数
	double dt; //時間変化
}ball[BMAX];
//----------[ 初期値 ]----------------------------------------
void Ini_Game(){
	clickcount=0;//クリック初期化
	beforeMouseInput = 0;//1フレーム前のマウスの状態
}
//----------[ ボール作成 ]----------------------------------------------
void Createball(double x,double y){

	//構造体の中で、空いている(未使用)データを探す
	for(int i=clickcount;i<BMAX;i++){
		//データ詰める
		ball[i].x=x;
		ball[i].y=y;
		ball[i].z=0;
		ball[i].vx=0;
		ball[i].vy=0;
		ball[i].vz=0;
		ball[i].m=1.0;
		ball[i].r=40.0;
		ball[i].k=0.5;
		ball[i].c=2.5;
		ball[i].dt=0.1;
		ball[i].flag=1;//使用にする
	}
}
//----------[ ボール発射判定 ]----------------------------------------------
void Shot_Hantei(){

	//クリックした瞬間
	if( (beforeMouseInput & MOUSE_INPUT_LEFT ) != MOUSE_INPUT_LEFT
		&& (GetMouseInput() & MOUSE_INPUT_LEFT)== MOUSE_INPUT_LEFT){		
			clickcount+=1;//クリックカウント増やす
			Createball(mx,my);//マウスの座標にボール作成
	}
	beforeMouseInput = GetMouseInput();
}
//----------[ ボール移動 ]----------------------------------------------
void Moveball(){

	for(int i=0;i<BMAX;i++){

		if(ball[i].flag==1&&clickcount>=1){//ボールが使用中なら
			F = -ball[i].m * g; //自由落下
			//バウンド処理
			if( ball[i].y+ball[i].r < 0 ) {//地面に当たったら
				F = ball[i].m * g;//バウンドさせる。
				//沈み込み量に応じてバウンド減衰
				if( ball[i].y < ball[i].r ) {
					F += ball[i].k * ( ball[i].r -ball[i]. y) - ball[i].c *ball[i]. vy;
				}
			}
			ax = F / ball[i].m; //加速度
			ay = F / ball[i].m; //加速度
			az = F / ball[i].m; //加速度
			ball[i].vx += ax * ball[i].dt; //加速度から速度
			ball[i].vy += ay * ball[i].dt; //加速度から速度
			ball[i].vz += az * ball[i].dt; //加速度から速度
      ball[i].x += ball[i].vx*ball[i].dt; //速度から座標
			ball[i].y += ball[i].vy*ball[i].dt; //速度から座標
      ball[i].z += ball[i].vz*ball[i].dt; //速度から座標
		}
	}
}
//----------[ ボール描画 ]----------------------------------------------
void Drawball(){

	for(int i=0;i<BMAX;i++){
		if(ball[i].flag==1&&clickcount>=1){//ボールが使用中なら
			DrawSphere3D( VGet( ball[i].x,ball[i].y,ball[i].z), 40.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;
		}
	}
}
//カメラ回転(x,y)の点を(mx,my)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float mx, const float my){
	const float ox = *x-mx, oy = *y-my;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += mx;
	*y += my;
}
//----------[ メイン関数 ]----------------------------------------------
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
	float cameraX=0, cameraZ=-20;    //カメラの座標
	const float targetX=0, targetZ=0;//カメラの視線の先ターゲットの座標
	VECTOR CameraPos;

	// カメラの座標を初期化
	CameraPos.x = 0.0f ;
	CameraPos.y = 0.0f ;
	CameraPos.z = 0.0f ;
	//奥行0.1~1000までをカメラの描画範囲とする
	SetCameraNearFar( 0.1f, 1000.0f ) ;

	Ini_Game();//初期値

	//----------[ メインループ ]----------------------------------------------
	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア,ESCAPEで終了 )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0 ) {

		// 方向キーでカメラの座標を移動
		if( CheckHitKey( KEY_INPUT_UP ) == 1 )
		{
			CameraPos.y += 20.0f ;
		}
		if( CheckHitKey( KEY_INPUT_DOWN ) == 1 )
		{
			CameraPos.y -= 20.0f ;
		}
		if( CheckHitKey( KEY_INPUT_LEFT ) == 1 )
		{
			CameraPos.x -= 20.0f ;
		}
		if( CheckHitKey( KEY_INPUT_RIGHT ) == 1 )
		{
			CameraPos.x += 20.0f ;
		}

		// カメラの位置と注視点をセット、注視点は原点
		SetCameraPositionAndTarget_UpVecY( CameraPos, VGet( 0.0f, 0.0f, 0.0f ) ) ;

		if( CheckHitKey(KEY_INPUT_LEFT) > 0 ){//左キーが押されていたら
			rotate(&cameraX, &cameraZ, +ROTATE_SPEED, targetX, targetZ);//回転
		}
		if( CheckHitKey(KEY_INPUT_RIGHT) > 0 ){//右キーが押されていたら
			rotate(&cameraX, &cameraZ, -ROTATE_SPEED, targetX, targetZ);//回転
		}
		GetMousePoint( &mx, &my);//マウスの入力状態更新

		Shot_Hantei();//ボール発射判定

		Moveball();//ボール移動

		Drawball();//ボール描画

	}   

	DxLib_End(); // DXライブラリ終了処理
	return 0;
}
このメイン関数内のカメラの概念がうまく理解できず、ボールを画面上に上手く表示できません。
カメラの概念はどのようにして勉強すればよいでしょうか?
また、このソースコードの場合、どこを改良すれば、きちんと画面上にボールを表示することができるでしょうか?
アドバイスをお願いいたします。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#2

投稿記事 by softya(ソフト屋) » 9年前

前の質問で問題点を書いたのですが大丈夫でしょうか?
カメラについては、いきなりアプリ化するのではなく、カメラと物体1個をキーで自由に動くテストアプリを作ってカメラの感触を掴んだほうが良いと思います。
新しいものを導入するときはシンプルな形でテストするのが無駄なデバッグをしなくて良いのでどんな時でも有効です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#3

投稿記事 by たらお » 9年前

返信ありがとうございます。以前の質問はこちらでは特に問題ありませんでした。
アドバイスの通りに、いろいろなサイトを参考にしながら、とカメラをキーで動かせるようにしたのですが、ライトの当て方が上手くわからず、
ところどころ黒くなってしまいます。ちなみに現在のソースコードはこちらです。

コード:

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

static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード

// (x,y)の点を(mx,my)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float mx, const float my){
    const float ox = *x-mx, oy = *y-my;
    *x =  ox * cos(ang) + oy * sin(ang);
    *y = -ox * sin(ang) + oy * cos(ang);
    *x += mx;
    *y += my;
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
    ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );

    float cameraX=0, cameraZ=-20;    //カメラの座標
    const float targetX=0, targetZ=0;//カメラの視線の先ターゲットの座標


    //奥行0.1~1000までをカメラの描画範囲とする
    SetCameraNearFar( 0.1f, 1000.0f ) ;

    while(!ScreenFlip()&&!ProcessMessage()&&!ClearDrawScreen()){

        if( CheckHitKey(KEY_INPUT_LEFT) > 0 ){//左キーが押されていたら
            rotate(&cameraX, &cameraZ, +ROTATE_SPEED, targetX, targetZ);//回転
        }
        if( CheckHitKey(KEY_INPUT_RIGHT) > 0 ){//右キーが押されていたら
            rotate(&cameraX, &cameraZ, -ROTATE_SPEED, targetX, targetZ);//回転
        }

        //第一引数の視点から第二引数のターゲットを見る角度にカメラを設置
        SetCameraPositionAndTarget_UpVecY( VGet( cameraX, 20, cameraZ ), VGet( targetX, 10.0f, targetZ ) ) ;

		

	   // 標準ライトの位置をモデルの上に移動する
		SetLightPosition( VGet( targetX, 20.0f, targetZ ) ) ;


        // 3Dモデルの描画
        DrawSphere3D( VGet(targetX, 10.0f, targetZ), 5.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;

    }

    DxLib_End();
    return 0;
}
何か良い改善案はないでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#4

投稿記事 by softya(ソフト屋) » 9年前

2Dの方はWaitTimer(200から500);をループに入れてみるとスローになるので問題があるか確認できると思います。背後に隠れていて見えない可能性もありますが。

3Dの方は光の物理の話になりますが、空気により光の散乱や壁などの反射光で光源以外の方向から光が当たるので光の当たらない部分が見えるわけです。
宇宙のスペースシャトルなどの写真を見るとわかりますが、地球など光の反射物がないと光が当たらない所が真っ暗になるのが正常です。
で、3Dゲームの場合はまじめに反射光を計算すると膨大な計算量になるため擬似的に表現します。
それがアンビエント(環境光)ですが、 DrawSphere3Dはアンビエントパラメータを持たないため下記のような方法が必要になります。
http://hpcgi2.nifty.com/natupaji/bbs/pa ... ew&no=2609
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#5

投稿記事 by たらお » 9年前

コード:

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

static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード

// (x,y)の点を(mx,my)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float mx, const float my){
	const float ox = *x-mx, oy = *y-my;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += mx;
	*y += my;
}

int WINAPI WinMain(HINSTANCE,HINSTANCE,LPSTR,int){
	ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK );

	float cameraX=45,cameraY=20.0f, cameraZ=-40;    //カメラの座標
	const float targetX=0,targetY=10.0f,targetZ=0;//カメラの視線の先ターゲットの座標
	//ボール輝度設定
	int white = GetColor( 255, 255, 255 );

	MATERIALPARAM MatParam;
	MatParam.Diffuse = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Ambient = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Specular = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Emissive = GetColorF( 0.250f, 0.0f, 0.0f, 0.0f );
	MatParam.Power = 25.0f;

	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

	//奥行0.1~1000までをカメラの描画範囲とする
	SetCameraNearFar( 0.1f, 1000.0f ) ;

	while(!ScreenFlip()&&!ProcessMessage()&&!ClearDrawScreen()){

		if( CheckHitKey(KEY_INPUT_LEFT) > 0 ){//左キーが押されていたら
			rotate(&cameraX, &cameraZ, +ROTATE_SPEED, targetX, targetZ);//左回転
		}
		if( CheckHitKey(KEY_INPUT_RIGHT) > 0 ){//右キーが押されていたら
			rotate(&cameraX, &cameraZ, -ROTATE_SPEED, targetX, targetZ);//右回転
		}

		if( CheckHitKey(KEY_INPUT_UP) > 0 ){//上キーが押されていたら
			rotate(&cameraZ, &cameraY, +ROTATE_SPEED, targetZ, targetY);//上回転
		}
		if( CheckHitKey(KEY_INPUT_DOWN) > 0 ){//下キーが押されていたら
			rotate(&cameraZ, &cameraY, -ROTATE_SPEED, targetZ, targetY);//下回転
		}
		//第一引数の視点から第二引数のターゲットを見る角度にカメラを設置
		SetCameraPositionAndTarget_UpVecY( VGet( cameraX,cameraY, cameraZ ), VGet( targetX, targetY, targetZ ) ) ;
		//ボール輝度セット
		SetMaterialParam( MatParam );

		// 3Dモデルの描画
		DrawSphere3D( VGet(targetX, targetY, targetZ), 5.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;

	}

	DxLib_End();
	return 0;
} 
アドバイスを参考に、ボールの輝度を設定したら明るくなりました。ありがとうございます。
次に、現在、上下左右ボタンで変えているカメラの座標をマウスのドラッグで変えられるようにしたいのですが、何か良い方法はないでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#6

投稿記事 by softya(ソフト屋) » 9年前

仕様が明確じゃないですが、右クリックされている間マウスの座標の1フレーム前との差分を使って移動してはどうでしょうか? マウスの座標差分に係数をかけてカメラ座標に加算すれば良いと思います。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#7

投稿記事 by たらお » 9年前

アドバイス通りに制作したところカメラの移動は上手くできるようになりました。以下が現在のコードです。

コード:

////----------[インクルードファイル ]----------------------------------------
#include "DxLib.h"
#include <vector>
#include <math.h>
#include<stdlib.h>
#include<time.h>
////----------[マクロ定義部 ]----------------------------------------
using namespace std;
#define WINDOW_SIZE_X 800
#define WINDOW_SIZE_Y 800
////----------[ グローバル変数 ]----------------------------------------
static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード
float cameraX=320,cameraY=480,cameraZ=-300;    //カメラの座標
const float targetX=320,targetY=0.0f,targetZ=0;//カメラの視線の先ターゲットの座標
int lastMouseX = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
int lastMouseY = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
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;
}////----------[構造体]----------------------------------------
struct{
	int x;
	int y;          //座標
}start_t,start;
////----------[ボールクラス]----------------------------------------
class Ball{
public:
	double x,y,z; //ボールの座標
	double vx,vy,vz; //ボールの速度
	Ball(int X, int Y, int Z);//コンストラクタ。
	void accel(double ax, double ay,double az,double dt); //加速度ax,ay,azが加わった時のdt時間後の状態を決定
};
////----------[ボール初期値]----------------------------------------
Ball::Ball(int X,int Y,int Z)
{
	x = X;
	y = Y;
	z = Z;
	vx=0;
	vy=0;
	vz=0;
}
//ボールに加速度が加わった際の挙動をシミュレート
void Ball::accel(double ax, double ay,double az,double dt)
{
	vx += ax * dt;
	vy += ay * dt;
	vz += ay * dt;
	x += vx * dt;
	y += vy * dt;
	z += vz * dt;
}
//カメラ回転(x,y)の点を(nx,ny)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float nx, const float ny){
	const float ox = *x-nx, oy = *y-ny;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += nx;
	*y += ny;
}

////----------[マウスのドラッグによるカメラの回転]----------------------------------------
void MoveCamera(){
	//第一引数の視点から第二引数のターゲットを見る角度にカメラを設置		
	SetCameraPositionAndTarget_UpVecY( VGet( cameraX,cameraY,cameraZ ), VGet( targetX,targetY,targetZ) ) ;
	if( (GetMouseInput()& MOUSE_INPUT_LEFT) != 0 ){ //マウスの左ボタンが押されたら
		int MouseX,MouseY;
		int MoveX,MoveY;//移動量
		GetMousePoint( &MouseX , &MouseY );//マウスの座標取得
		if (lastMouseX >= 0)
		{
			// 前回のマウスX座標との差分を角度として、現在の姿勢をY軸周りで回転する
			MoveX=(0.6f * (lastMouseX - MouseX));
			rotate(&cameraX, &cameraZ, +MoveX*ROTATE_SPEED, targetX, targetZ);//横回転
		}

		if (lastMouseY >= 0)
		{
			// 前回のマウスY座標との差分を角度として、現在の姿勢をX軸周りで回転する
			MoveY=(0.6f * (lastMouseY - MouseY));
			rotate(&cameraZ, &cameraY, +MoveY*ROTATE_SPEED, targetZ, targetY);//縦回転
		}

		lastMouseX = MouseX;
		lastMouseY = MouseY;
	}
	else
	{
		// マウスドラッグ解除
		lastMouseX = -1;
		lastMouseY = -1;
	}
}
////----------[ メイン関数 ]----------------------------------------------
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
	double F=0;
	double d,e12;
	int ball_num = 0;
	bool PrevPressFlag = false;
	vector<Ball> ball; //ボールクラスの配列。STL
	srand((unsigned)time(NULL));//乱数設定

	//奥行0.1~1000までをカメラの描画範囲とする
	SetCameraNearFar( 0.1f, 1000.0f ) ;
	//ボールの輝度設定
	MATERIALPARAM MatParam;
	MatParam.Diffuse = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Ambient = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Specular = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Emissive = GetColorF( 0.250f, 0.0f, 0.0f, 0.0f );
	MatParam.Power = 25.0f;

	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

	////----------[ メインループ]----------------------------------------------
	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア,ESCAPEで終了 )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0&& gpUpdateKey()==0 ) {
		//ボールの輝度セット
		SetMaterialParam( MatParam );

		MoveCamera();//カメラ移動

		//スペースボタンを入力したら新規オブジェクトの生成
		if( (  Key[KEY_INPUT_SPACE] ) != 0 && PrevPressFlag==false){

			ball.push_back(Ball(rand()%640+1,rand()%480+1,rand()%240+1));//初期座標をランダムに設定 
			ball[ball_num].x=rand()%640+1;
			ball[ball_num].y=rand()%480+1;
			ball[ball_num].z=rand()%240+1;
			ball_num++;
			PrevPressFlag=true;
		}
		if((Key[KEY_INPUT_SPACE]) == 0){
			PrevPressFlag=false;
		}

		// 落下処理。各ボールが自由落下運動する。衝突も考えて判定。位置、速度、加速度に関する構造体
		for(int i=0;i<ball_num;i++){

			if(ball[i].y>0){
				ball[i].accel(0,-1.0,0,1.0);
			}else{
				ball[i].accel(F,-ball[i].y*0.1-ball[i].vy*0.1,F,1.0);//「めり込み量」に応じた反発力が生じるという物理モデル。粘性項も入れることで反発係数を模擬できる
			}
			DrawSphere3D( VGet( ball[i].x,ball[i].y,ball[i].z), 40.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;
			
		}

		DxLib_End() ;				// DXライブラリ使用の終了処理

		return 0 ;				// ソフトの終了 
	}
次に、異なるボールが当たったときに衝突する判定を行いたいのですが、どのようにすればよいでしょうか?
一応、考えているのは、ボールの中心同士の距離dがボールの半径rの2倍より小さいときに当たり判定を起こし、
その時の反発力FをF=0.1*(2*r-d)
次に、当たった時の方向ベクトルeを計算して、先ほど計算したFから
F+=F*e
で、当たったボールにそれぞれ逆向きの力を与えるようにしているのですが、これでよろしいでしょうか?

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#8

投稿記事 by softya(ソフト屋) » 9年前

球体同士の当たり判定は半径を使えば良いので結構シンプルです。ただ、反射はそう簡単でもありません。
「その1 誰もがやりたいパーティクルの衝突」
http://marupeke296.com/COL_MV_No1_HowTo ... ocity.html
ちなみに3つ以上が同時衝突する場合は、私も挑戦したことないです。

【補足】そういうのが大変そうなら、私の場合は物理エンジンの利用を検討します。
物理のシミュレーションを勉強するのが目的ではなく、物理法則をゲームに利用するのが目的ならばって話です。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#9

投稿記事 by たらお » 9年前

異なるボールに対して、衝突処理の計算をしたいのですが、どのように異なるボールを判別すればよいでしょうか?
考え方だけでも教えていただければ幸いです。

アバター
softya(ソフト屋)
副管理人
記事: 11677
登録日時: 13年前
住所: 東海地方
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#10

投稿記事 by softya(ソフト屋) » 9年前

異なるとは何が異なるのでしょうか?
大きさが異なるのであれば、紹介したサイトの説明のとおりです。
by softya(ソフト屋) 方針:私は仕組み・考え方を理解して欲しいので直接的なコードを回答することはまれですので、すぐコードがほしい方はその旨をご明記下さい。私以外の方と交代したいと思います(代わりの方がいる保証は出来かねます)。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#11

投稿記事 by たらお » 9年前

例えばボールを10個出した時、それぞれ10個のボールの中心距離を比較して半径の2倍未満だったら
衝突フラグを立てて、衝突計算を行いたいのですが、どのようにして違うボールだと判定すればよいかがわかりません。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 「3D] ボール自由落下-カメラの概念

#12

投稿記事 by usao » 9年前

>衝突フラグ

というのは どういう形をお考えなのでしょう?
(例えば「玉1号と玉8号が衝突したぞ」という情報を記憶するのであれば
 どの玉がぶつかったのかはそこから自明だと思うのですが)
オフトピック
現在の問いに関する答えではないので,offtopicとします.

No.7あたりで書かれている内容と,No.8で示されたリンク先とではそもそも方法論が異なっていて,
リンク先のものは反発係数を用いた計算ですが
質問者様が考えられている方式は,めり込み量に応じたPenalty Forceを玉に与える方法なのだと見受けます.

さて,Penalty Forceによる方法をとる場合…
例えばある玉がある時刻に2か所にぶつかった場合,この時刻においてこの玉が受けることになる力は2か所からの力の合力になるでしょうから
単純に 衝突したとわかった時点でPenalty Forceを加算 とすれば良いように思います.

(>衝突フラグを立てて、衝突計算を行いたい
 とのことですが, 衝突判定 と その後の力の計算 とを分ける必要があるのか?という話.)

アバター
みけCAT
記事: 6734
登録日時: 13年前
住所: 千葉県
連絡を取る:

Re: 「3D] ボール自由落下-カメラの概念

#13

投稿記事 by みけCAT » 9年前

たらお さんが書きました:どのようにして違うボールだと判定すればよいかがわかりません。
ボールの情報を持っている構造体のアドレスが異なれば違うボール、ボールの情報を格納している配列の添字が異なれば違うボール、
ボールを生成するときにユニーク(一意)なIDを持たせてIDが異なれば違うボール、などの判定方法のことでしょうか?
複雑な問題?マシンの性能を上げてOpenMPで殴ればいい!(死亡フラグ)

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#14

投稿記事 by たらお » 9年前

皆様のアドバイスを参考にコードを書いてみました。

コード:

////----------[インクルードファイル ]----------------------------------------
#include "DxLib.h"
#include <vector>
#include <math.h>
#include<stdlib.h>
#include<time.h>
////----------[マクロ定義部 ]----------------------------------------
using namespace std;
#define WINDOW_SIZE_X 800
#define WINDOW_SIZE_Y 800
////----------[ グローバル変数 ]----------------------------------------
static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード
float cameraX=320,cameraY=400,cameraZ=-500;    //カメラの座標
const float targetX=320,targetY=0.0f,targetZ=0;//カメラの視線の先ターゲットの座標
int lastMouseX = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
int lastMouseY = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
////----------[ キー関数 ]----------------------------------------
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;
}
////----------[ボールクラス]----------------------------------------
class Ball{
public:
	double x,y,z; //ボールの座標
	double F;//ボールに加わる力
	double vx,vy,vz; //ボールの速度
	Ball(int X, int Y, int Z);//コンストラクタ。
	void accel(double ax, double ay,double az,double dt); //加速度ax,ay,azが加わった時のdt時間後の状態を決定
};
////----------[ボール初期値]----------------------------------------
Ball::Ball(int X,int Y,int Z)
{
	x = X;
	y = Y;
	z = Z;
	vx=0;
	vy=0;
	vz=0;
}
//ボールに力が加わった際の挙動をシミュレート
void Ball::accel(double ax, double ay,double az,double dt)
{
	vx += ax * dt;
	vy += ay * dt;
	vz += ay * dt;
	x += vx * dt;
	y += vy * dt;
	z += vz * dt;
}
//カメラ回転(x,y)の点を(nx,ny)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float nx, const float ny){
	const float ox = *x-nx, oy = *y-ny;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += nx;
	*y += ny;
}
////----------[マウスのドラッグによるカメラの回転]----------------------------------------
void MoveCamera(){
	//第一引数の視点から第二引数のターゲットを見る角度にカメラを設置		
	SetCameraPositionAndTarget_UpVecY( VGet( cameraX,cameraY,cameraZ ), VGet( targetX,targetY,targetZ) ) ;
	if( (GetMouseInput()& MOUSE_INPUT_LEFT) != 0 ){ //マウスの左ボタンが押されたら
		int MouseX,MouseY;
		int MoveX,MoveY;//移動量
		GetMousePoint( &MouseX , &MouseY );//マウスの座標取得
		if (lastMouseX >= 0)
		{
			// 前回のマウスX座標との差分を角度として、現在の姿勢をY軸周りで回転する
			MoveX=(0.6f * (lastMouseX - MouseX));
			rotate(&cameraX, &cameraZ, +MoveX*ROTATE_SPEED, targetX, targetZ);//横回転
		}

		if (lastMouseY >= 0)
		{
			// 前回のマウスY座標との差分を角度として、現在の姿勢をX軸周りで回転する
			MoveY=(0.6f * (lastMouseY - MouseY));
			rotate(&cameraZ, &cameraY, +MoveY*ROTATE_SPEED, targetZ, targetY);//縦回転
		}

		lastMouseX = MouseX;
		lastMouseY = MouseY;
	}
	else
	{
		// マウスドラッグ解除
		lastMouseX = -1;
		lastMouseY = -1;
	}
}
////----------[ メイン関数 ]----------------------------------------------
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
	double d;//2つのボールの中心間距離
	double e12;//方向単位ベクトル
	int ball_num = 0;
	bool PrevPressFlag = false;
	vector<Ball> ball; //ボールクラスの配列。STL
	srand((unsigned)time(NULL));//乱数設定

	//奥行0.1~1000までをカメラの描画範囲とする
	SetCameraNearFar( 0.1f, 1000.0f ) ;
	//ボールの輝度設定
	MATERIALPARAM MatParam;
	MatParam.Diffuse = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Ambient = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Specular = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Emissive = GetColorF( 0.250f, 0.0f, 0.0f, 0.0f );
	MatParam.Power = 25.0f;

	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

	////----------[ メインループ]----------------------------------------------
	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア,ESCAPEで終了 )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0&& gpUpdateKey()==0 ) {

		SetMaterialParam( MatParam );//ボールの輝度セット

		MoveCamera();//カメラ移動

		//スペースボタンを入力したら新規オブジェクトの生成
		if( (  Key[KEY_INPUT_SPACE] ) != 0 && PrevPressFlag==false){

			ball.push_back(Ball(rand()%640+1,rand()%480+1,rand()%240+1));//初期座標をランダムに設定 
			ball[ball_num].x=rand()%640+1;
			ball[ball_num].y=rand()%480+1;
			ball[ball_num].z=rand()%240+1;
			ball[ball_num].F=0;
			ball_num++;
			PrevPressFlag=true;
		}
		if((Key[KEY_INPUT_SPACE]) == 0){
			PrevPressFlag=false;
		}

		// 落下処理。各ボールが自由落下運動する。衝突も考えて判定。位置、速度、加速度に関する構造体
		for(int i=0;i<ball_num;i++){	
			if(ball[i].y>0){
				ball[i].accel(0,-0.5,0,1.0);
			}else{
				ball[i].accel(0,(-ball[i].y*0.1)-(ball[i].vy*0.1),0,1.0);//「めり込み量」に応じた反発力が生じる。				
			}
		// 衝突処理
			for(int j=0;j<ball_num;j++){	
				if(i!=j){//異なるボールに対して
					d=sqrt((ball[i].x-ball[j].x)*(ball[i].x-ball[j].x)+(ball[i].y-ball[j].y)*(ball[i].y-ball[j].y)+(ball[i].z-ball[j].z)*(ball[i].z-ball[j].z));//ボールの中心間距離
					e12=1/d*(ball[i].x-ball[j].x,ball[i].y-ball[j].y,ball[i].z-ball[j].z);//方向単位ベクトル
					if(d<80.0f){//中心間距離がボールの半径の2倍未満だったら当たり判定
						ball[i].F=0.05*(80.0f-d);//反発力
						ball[i].F +=ball[i].F*e12;//反発力に方向ベクトルを掛け合わせたもの。
						ball[i].accel(ball[i].F,ball[i].F,ball[i].F,1.0);//「めり込み量」に応じた正の反発力が生じる
						ball[j].accel(-ball[i].F,-ball[i].F,-ball[i].F,1.0);//「めり込み量」に応じた負の反発力が生じる				
					}
				}	
			}
			DrawSphere3D( VGet( ball[i].x,ball[i].y,ball[i].z), 40.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;
		}

	}

	DxLib_End() ;				// DXライブラリ使用の終了処理

	return 0 ;				// ソフトの終了 
}

一応、これで衝突はするのですが、なんか不自然な感じで衝突してしまいます。
このコードで何かおかしい部分、直したほうが良い個所等教えていただければ嬉しいです。
よろしくお願いします。

アバター
usao
記事: 1887
登録日時: 11年前

Re: 「3D] ボール自由落下-カメラの概念

#15

投稿記事 by usao » 9年前

36行目の
>double F;//ボールに加わる力

とか,105行目の
>double e12;//方向単位ベクトル

が,3次元ベクトルじゃなくて良いのでしょうか?


158行目
>e12=1/d*(ball.x-ball[j].x,ball.y-ball[j].y,ball.z-ball[j].z);//方向単位ベクトル

とか見ると,3次元ベクトルのつもりで書いてるんじゃないかと思われる箇所
(結果は想定外のスカラー値になっていると思われる.)があったりするけど…

オフトピック
計算内容の部分を一度2次元でやってから3D化した方が…という気もする.

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#16

投稿記事 by たらお » 9年前

アドバイスを参考に下記のようにソースコードを修正したら、衝突がうまくいくようになりました。
ありがとうございます。

コード:

////----------[インクルードファイル ]----------------------------------------
#include "DxLib.h"
#include <vector>
#include <math.h>
#include<stdlib.h>
#include<time.h>
////----------[マクロ定義部 ]----------------------------------------
using namespace std;
#define WINDOW_SIZE_X 800
#define WINDOW_SIZE_Y 800
////----------[ グローバル変数 ]----------------------------------------
int Handle;//音声格納変数
static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード
float cameraX=400,cameraY=600,cameraZ=-500;    //カメラの座標
const float targetX=400,targetY=0.0f,targetZ=0;//カメラの視線の先ターゲットの座標
int lastMouseX = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
int lastMouseY = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
////----------[ キー関数 ]----------------------------------------
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;
}
//----------[ 音声ファイルロード ]----------------------------------------
void File_load(){
	Handle=  LoadSoundMem( "se1.mp3" );//効果音のロード
}
////----------[ボールクラス]----------------------------------------
class Ball{
public:
	double x,y,z; //ボールの座標
	double Fx,Fy,Fz;//ボールに加わる力
	double vx,vy,vz; //ボールの速度
	Ball(int X, int Y, int Z);//コンストラクタ。
	void accel(double ax, double ay,double az,double dt); //加速度ax,ay,azが加わった時のdt時間後の状態を決定
};
////----------[ボール初期値]----------------------------------------
Ball::Ball(int X,int Y,int Z)
{
	x = X;
	y = Y;
	z = Z;
	vx=0;
	vy=0;
	vz=0;
}
//ボールに力が加わった際の挙動をシミュレート
void Ball::accel(double ax, double ay,double az,double dt)
{
	vx += ax * dt;
	vy += ay * dt;
	vz += az * dt;
	x += vx * dt;
	y += vy * dt;
	z += vz * dt;
}
//カメラ回転(x,y)の点を(nx,ny)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float nx, const float ny){
	const float ox = *x-nx, oy = *y-ny;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += nx;
	*y += ny;
}
////----------[マウスのドラッグによるカメラの回転]----------------------------------------
void MoveCamera(){
	//第一引数の視点から第二引数のターゲットを見る角度にカメラを設置		
	SetCameraPositionAndTarget_UpVecY( VGet( cameraX,cameraY,cameraZ ), VGet( targetX,targetY,targetZ) ) ;
	if( (GetMouseInput()& MOUSE_INPUT_LEFT) != 0 ){ //マウスの左ボタンが押されたら
		int MouseX,MouseY;
		int MoveX,MoveY;//移動量
		GetMousePoint( &MouseX , &MouseY );//マウスの座標取得
		if (lastMouseX >= 0)
		{
			// 前回のマウスX座標との差分を角度として、現在の姿勢をY軸周りで回転する
			MoveX=(0.6f * (lastMouseX - MouseX));
			rotate(&cameraX, &cameraZ, +MoveX*ROTATE_SPEED, targetX, targetZ);//横回転
		}

		if (lastMouseY >= 0)
		{
			// 前回のマウスY座標との差分を角度として、現在の姿勢をX軸周りで回転する
			MoveY=(0.6f * (lastMouseY - MouseY));
			rotate(&cameraZ, &cameraY, +MoveY*ROTATE_SPEED, targetZ, targetY);//縦回転
		}

		lastMouseX = MouseX;
		lastMouseY = MouseY;
	}
	else
	{
		// マウスドラッグ解除
		lastMouseX = -1;
		lastMouseY = -1;
	}
}
////----------[ メイン関数 ]----------------------------------------------
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
	double d;//2つのボールの中心間距離
	double e12x,e12y,e12z;//方向単位ベクトル
	int ball_num = 0;
	bool PrevPressFlag = false;
	vector<Ball> ball; //ボールクラスの配列。STL
	srand((unsigned)time(NULL));//乱数設定

	//音声ロード
	File_load();

	// 画面モードの変更
	SetGraphMode( WINDOW_SIZE_X , WINDOW_SIZE_Y , 32 ) ;

	//ボールの輝度設定
	MATERIALPARAM MatParam;
	MatParam.Diffuse = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Ambient = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Specular = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Emissive = GetColorF( 0.250f, 0.0f, 0.0f, 0.0f );
	MatParam.Power = 25.0f;
	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

	////----------[ メインループ]----------------------------------------------
	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア,ESCAPEで終了 )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0&& gpUpdateKey()==0 ) {

		SetMaterialParam( MatParam );//ボールの輝度セット

		MoveCamera();//カメラ移動

		//スペースボタンを入力したら新規オブジェクトの生成
		if( (  Key[KEY_INPUT_SPACE] ) != 0 && PrevPressFlag==false){

			ball.push_back(Ball(rand()%640+1,rand()%480+1,rand()%240+1));//初期座標をランダムに設定 
			ball[ball_num].x=rand()%640+1;
			ball[ball_num].y=rand()%480+1;
			ball[ball_num].z=rand()%240+1;
			ball[ball_num].Fx=0;
			ball[ball_num].Fy=0;
			ball[ball_num].Fz=0;
			ball_num++;
			PrevPressFlag=true;
		}
		if((Key[KEY_INPUT_SPACE]) == 0){
			PrevPressFlag=false;
		}

		// 落下処理。各ボールが自由落下運動する。衝突も考えて判定。位置、速度、加速度に関する構造体
		for(int i=0;i<ball_num;i++){	
			if(ball[i].y>0){
				ball[i].accel(0,-0.2,0,1.0);
			}else{
				ball[i].accel(0,(-ball[i].y*0.1)-(ball[i].vy*0.1),0,1.0);//「めり込み量」に応じた反発力が生じる。	
			}

			// 衝突処理
			for(int j=0;j<ball_num;j++){	
				if(i!=j){//異なるボールに対して
					d=pow((ball[i].x-ball[j].x)*(ball[i].x-ball[j].x)+(ball[i].y-ball[j].y)*(ball[i].y-ball[j].y)+(ball[i].z-ball[j].z)*(ball[i].z-ball[j].z),0.5);//ボールの中心間距離
					e12x=1/d*(ball[i].x-ball[j].x);//x方向単位ベクトル
					e12y=1/d*(ball[i].y-ball[j].y);//y方向単位ベクトル
					e12z=1/d*(ball[i].z-ball[j].z);//z方向単位ベクトル
					if(d<80.0f){//中心間距離がボールの半径の2倍未満だったら当たり判定
						ball[i].Fx=0.02*(80.0f-d);//x方向反発力
						ball[i].Fy=0.02*(80.0f-d);//y方向反発力
						ball[i].Fz=0.02*(80.0f-d);//z方向反発力
						ball[i].Fx+=ball[i].Fx*e12x;//x方向力ベクトル
						ball[i].Fy+=ball[i].Fy*e12y;//y方向力ベクトル
						ball[i].Fz+=ball[i].Fz*e12z;//z方向力ベクトル
						ball[i].accel(ball[i].Fx,ball[i].Fy,ball[i].Fz,1.0);//「めり込み量」に応じた正の反発力が生じる
						ball[j].accel(-ball[i].Fx,-ball[i].Fy,-ball[i].Fz,1.0);//「めり込み量」に応じた負の反発力が生じる
						PlaySoundMem(Handle,DX_PLAYTYPE_BACK);//ボール衝突音再生
					}
				}	
			}
			DrawSphere3D( VGet( ball[i].x,ball[i].y,ball[i].z), 40.0f, 32, GetColor( 255,0,0 ), GetColor( 255, 255, 255 ), TRUE ) ;
		}
	}

	DxLib_End() ;				// DXライブラリ使用の終了処理

	return 0 ;				// ソフトの終了 
}
最後に、ボールが地面に衝突するときに衝突の強さに応じてバウンド音を再生するようにしたいのですが、どのようにすれば良いかわかりますでしょうか?
アドバイスお願いいたします。

たらお
記事: 19
登録日時: 9年前

Re: 「3D] ボール自由落下-カメラの概念

#17

投稿記事 by たらお » 9年前

3Dモデルの地面を表示させたいのですが描画されません。
以下が現在のコードなのですが、何かおかしな部分はございますでしょうか?

コード:

////----------[インクルードファイル ]----------------------------------------
#include "DxLib.h"
#include <vector>
#include <math.h>
#include<stdlib.h>
#include<time.h>
////----------[マクロ定義部 ]----------------------------------------
using namespace std;
#define WINDOW_SIZE_X 800
#define WINDOW_SIZE_Y 800
#define PI 3.141592654f
////----------[ グローバル変数 ]----------------------------------------
int Handle,ModelHandle;//ファイル格納変数
int count=0;
static const float ROTATE_SPEED = DX_PI_F/90;//回転スピード
float cameraX=400,cameraY=500,cameraZ=-500;    //カメラの座標
const float targetX=400,targetY=0.0f,targetZ=0;//カメラの視線の先ターゲットの座標
int lastMouseX = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
int lastMouseY = -1;	// マウスドラッグ量を調べるための変数 (前回のクリック位置)
////----------[ キー関数 ]----------------------------------------
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;
}
//----------[ ファイルロード ]----------------------------------------
void File_load(){
	Handle=  LoadSoundMem( "se1.mp3" );//効果音のロード
	ModelHandle = MV1LoadModel("racket.mv1" ) ;// 3Dモデルの読み込み
}
////----------[ボールクラス]----------------------------------------
class Ball{
public:
	double x,y,z; //ボールの座標
	double Fx,Fy,Fz;//ボールに加わる力
	double vx,vy,vz; //ボールの速度
	Ball(int X, int Y, int Z);//コンストラクタ。
	void accel(double ax, double ay,double az,double dt); //加速度ax,ay,azが加わった時のdt時間後の状態を決定
};
////----------[ボール初期値]----------------------------------------
Ball::Ball(int X,int Y,int Z)
{
	x = X;
	y = Y;
	z = Z;
	vx=0;
	vy=0;
	vz=0;
}
//ボールに力が加わった際の挙動をシミュレート
void Ball::accel(double ax, double ay,double az,double dt)
{
	vx += ax * dt;
	vy += ay * dt;
	vz += az * dt;
	x += vx * dt;
	y += vy * dt;
	z += vz * dt;
}
//カメラ回転(x,y)の点を(nx,ny)を中心にang角回転する
void rotate(float *x, float *y, const float ang, const float nx, const float ny){
	const float ox = *x-nx, oy = *y-ny;
	*x =  ox * cos(ang) + oy * sin(ang);
	*y = -ox * sin(ang) + oy * cos(ang);
	*x += nx;
	*y += ny;
}
////----------[マウスのドラッグによるカメラの回転]----------------------------------------
void MoveCamera(){
	//第一引数の視点から第二引数のターゲットを見る角度にカメラを設置		
	SetCameraPositionAndTarget_UpVecY( VGet( cameraX,cameraY,cameraZ ), VGet( targetX,targetY,targetZ) ) ;
	if( (GetMouseInput()& MOUSE_INPUT_LEFT) != 0 ){ //マウスの左ボタンが押されたら
		int MouseX,MouseY;
		int MoveX,MoveY;//移動量
		GetMousePoint( &MouseX , &MouseY );//マウスの座標取得
		if (lastMouseX >= 0)
		{
			// 前回のマウスX座標との差分を角度として、現在の姿勢をY軸周りで回転する
			MoveX=(0.6f * (lastMouseX - MouseX));
			rotate(&cameraX, &cameraZ, +MoveX*ROTATE_SPEED, targetX, targetZ);//横回転
		}

		if (lastMouseY >= 0)
		{
			// 前回のマウスY座標との差分を角度として、現在の姿勢をX軸周りで回転する
			MoveY=(0.6f * (lastMouseY - MouseY));
			rotate(&cameraZ, &cameraY, +MoveY*ROTATE_SPEED, targetZ, targetY);//縦回転
		}

		lastMouseX = MouseX;
		lastMouseY = MouseY;
	}
	else
	{
		// マウスドラッグ解除
		lastMouseX = -1;
		lastMouseY = -1;
	}
}
////----------[地面描画]----------------------------------------
void DrawGround(){
	// 画面に映る位置に3Dモデルを移動  
	MV1SetPosition( ModelHandle, VGet( 400.0f, 0, 0 ) ) ;  
	// 3Dモデルの描画  
	MV1DrawModel( ModelHandle ) ; 
}
////----------[ メイン関数 ]----------------------------------------------
int WINAPI WinMain( HINSTANCE, HINSTANCE, LPSTR, int ) {
	ChangeWindowMode( TRUE ), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
	int count=0;
	double d;//2つのボールの中心間距離
	double e12x,e12y,e12z;//方向単位ベクトル
	int ball_num = 0;
	bool PrevPressFlag = false;
	vector<Ball> ball; //ボールクラスの配列。STL
	srand((unsigned)time(NULL));//乱数設定

	//音声ロード
	File_load();

	// 画面モードの変更
	SetGraphMode( WINDOW_SIZE_X , WINDOW_SIZE_Y , 32 ) ;

	//ボールの輝度設定
	MATERIALPARAM MatParam;
	MatParam.Diffuse = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Ambient = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Specular = GetColorF( 0.0f, 0.0f, 0.0f, 0.0f );
	MatParam.Emissive = GetColorF( 0.250f, 0.0f, 0.0f, 0.0f );
	MatParam.Power = 25.0f;
	SetUseZBuffer3D( TRUE );
	SetWriteZBuffer3D( TRUE );

	////----------[ メインループ]----------------------------------------------
	// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア,ESCAPEで終了 )
	while( ScreenFlip() == 0 && ProcessMessage() == 0 && ClearDrawScreen() == 0 && CheckHitKey( KEY_INPUT_ESCAPE ) == 0&& gpUpdateKey()==0 ) {

		SetMaterialParam( MatParam );//ボールの輝度セット

		MoveCamera();//カメラ移動

		DrawGround();//3D地面描画

		//スペースボタンを入力したら新規オブジェクトの生成
		if( (  Key[KEY_INPUT_SPACE] ) != 0 && PrevPressFlag==false){

			ball.push_back(Ball(rand()%640+1,rand()%320+1,rand()%240+1));//初期座標をランダムに設定 
			ball[ball_num].x=rand()%640+1;
			ball[ball_num].y=rand()%320+1;
			ball[ball_num].z=rand()%240+1;
			ball[ball_num].Fx=0;
			ball[ball_num].Fy=0;
			ball[ball_num].Fz=0;
			ball_num++;
			PrevPressFlag=true;
		}
		if((Key[KEY_INPUT_SPACE]) == 0){
			PrevPressFlag=false;
		}

		// 落下処理。各ボールが自由落下運動する。衝突も考えて判定。位置、速度、加速度に関する構造体
		for(int i=0;i<ball_num;i++){	
			if(ball[i].y>-20.0f+20*sin(PI*2/120*count)){
				ball[i].accel(0,-0.2,0,1.0);
				count++;
			}else{
				ball[i].accel(0,0.1*(-ball[i].y-20.0f+20*sin(PI*2/120*count))-(ball[i].vy*0.1),0,1.0);//「めり込み量」に応じた反発力が生じる。
				count++;
				if(ball[i].vy>1.0)
					PlaySoundMem(Handle,DX_PLAYTYPE_BACK);//ボール衝突音再生
			}

			// 衝突処理
			for(int j=0;j<ball_num;j++){	
				if(i!=j){//異なるボールに対して
					d=pow((ball[i].x-ball[j].x)*(ball[i].x-ball[j].x)+(ball[i].y-ball[j].y)*(ball[i].y-ball[j].y)+(ball[i].z-ball[j].z)*(ball[i].z-ball[j].z),0.5);//ボールの中心間距離
					e12x=1/d*(ball[i].x-ball[j].x);//x方向単位ベクトル
					e12y=1/d*(ball[i].y-ball[j].y);//y方向単位ベクトル
					e12z=1/d*(ball[i].z-ball[j].z);//z方向単位ベクトル
					if(d<80.0f){//中心間距離がボールの半径の2倍未満だったら当たり判定
						ball[i].Fx=0.02*(80.0f-d);//x方向反発力
						ball[i].Fy=0.02*(80.0f-d);//y方向反発力
						ball[i].Fz=0.02*(80.0f-d);//z方向反発力
						ball[i].Fx+=ball[i].Fx*e12x;//x方向力ベクトル
						ball[i].Fy+=ball[i].Fy*e12y;//y方向力ベクトル
						ball[i].Fz+=ball[i].Fz*e12z;//z方向力ベクトル
						ball[i].accel(ball[i].Fx,ball[i].Fy,ball[i].Fz,1.0);//「めり込み量」に応じた正の反発力が生じる
						ball[j].accel(-ball[i].Fx,-ball[i].Fy,-ball[i].Fz,1.0);//「めり込み量」に応じた負の反発力が生じる
						// 音量の設定
						ChangeVolumeSoundMem( 255*d/80, Handle ) ;
						PlaySoundMem(Handle,DX_PLAYTYPE_BACK);//ボール衝突音再生
					}
				}	
			}
			DrawSphere3D( VGet( ball[i].x,ball[i].y,ball[i].z), 40.0f, 32, GetColor( 255,255,255 ), GetColor( 255, 255, 255 ), TRUE ) ;
		}
	}

	DxLib_End() ;				// DXライブラリ使用の終了処理

	return 0 ;				// ソフトの終了 
}


閉鎖

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