以下プログラムコードと一応自機とボールに使っている画像です。
当たり判定を管理している関数は93行目~120行目ZikiTamaAtariJudge()関数と122行目~130行目のZikiTamaAtariSyori()関数で、132行目~147行目のTamaMove()関数の中で実行しています。
自機の画像の赤い円形が当たり判定の範囲、青い点が当たり判定の中心、緑の点が画像の中心です。
#include "DxLib.h"
#include <math.h>
#define startx 0//玉の初期値x座標の右壁からの距離(0~max_x+TP.at-1)
#define starty 0//玉の初期値y座標の床からの距離(0~max_y+TP.at-1)
#define max_y 340//ゲーム部分下端
#define min_y 40 //ゲーム部分上端
#define max_x 620//ゲーム部分右端
#define min_x 20 //ゲーム部分左端
#define EnemyMax 30//同時に出現する敵の最大数
#define EnemyOrderMax 200//敵の出現情報の数
//定数
#define PMAX 500.0//最大高さ到達位置(単位m)
#define g 9.8//重力加速度(単位m/s^2)
#define fps 60.0//フレーム/秒
#define PI 3.141592//円周率
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 GameCnt=0;//ゲーム全体のフレームカウンター
int StageCnt=0;//ステージのカウント
int GameStop=0;//ゲーム全体の停止スイッチ
int waku_img,enemy_img;//枠の画像格納用,敵の画像格納用
//定数
double TM_gpf;//(使用関数:TamaMove()):1フレームあたりの重力加速度
//座標と速度
double TM_y=0,TM_x=0,TM_v0pf=0,TM_vypf=0,TM_vxpf=0;//(使用関数:TamaMove()):ボールの位置(単位m),初速(単位m/フレーム),ボールのyベクトル速度(単位m/フレーム),ボールのxベクトル速度(単位m/フレーム)
double bektolx,bektoly,high,kyori;//(使用関数:TamaMove(),ZikiTamaAtariJudge()):ボールのxベクトル,yベクトル(単位ピクセル/フレーム),ボールの高さ,距離(単位 ピクセル)
//座標系
double TM_sy,TM_PMAXf;//(使用関数:TamaMove()):発射地点の高さ,最高点到達時間(単位フレーム)
typedef struct{//玉のパラメータ
int x,y;//x座標,y座標
double at,ymax,xmax,ang;//当たり判定、現軌道の最高点、投射角
}TamaPara_t;TamaPara_t TP;
int TP_img;
typedef struct{//自機のパラメータ
int x,y,img,zanki,muteki;//座標、画像の番号,残機の数,無敵時間
double sp,at;//自機の動く速さ,当たり判定の大きさ
}ZikiPara_t;ZikiPara_t ZP;
int ZP_img[4];
typedef struct{
int img,hp,hp_max,flag,nolma,cnt,pattern,item,knd;//表示画像,HP,最大HP,生存フラグ,敵判定,カウンター,行動パターン,所持アイテム,敵の種類
int at,attime,stage;//攻撃するかしないか,弾発射の間隔,出現ステージ
double x,y,vx,vy;//座標,xベクトル,yベクトル
}EnemyPara_t;EnemyPara_t EP[EnemyMax];
int EP_img;
typedef struct{
int hp_max,nolma,pattern,item,knd;//最大HP,敵判定,行動パターン,所持アイテム,敵の種類
int at,attime,stage,timing;//攻撃の可否,攻撃間隔,出現ステージ,出現タイミング
double x,y;//座標
}EnemyOrder_t;EnemyOrder_t EO[EnemyOrderMax];
void GamenAtariSyori(){
if( TP.y > max_y-TP.at ) {// 床の下に来たら無理やり反射。反発係数80%
TM_y = (-1)*PMAX*starty/((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy));
TM_vypf = -TM_vypf * 0.8;
TM_sy = TP.y;
}
else if( TP.y <min_y+TP.at){
TM_y = (PMAX*(-1)*(min_y-max_y+2.00*TP.at+starty))/((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy));
TM_vypf = -TM_vypf;
}
if( TP.x<min_x+TP.at){//左壁の当たり判定
TM_x = (min_x-max_x+2.00*TP.at+startx)*PMAX/(TP.xmax*cos(TP.ang*PI));
TM_vxpf = -TM_vxpf;
}
else if( TP.x > max_x-TP.at ){//右壁の当たり判定
TM_x = (startx*PMAX)/(TP.xmax*cos(TP.ang*PI));
TM_vxpf = -TM_vxpf;
}
}
int ZikiTamaAtariJudge(){//自機と玉の当たり判定
int i;
double x=(double)(ZP.x)-(double)(TP.x);
double y=(double)(ZP.y+9)-(double)(TP.y);
double r=(ZP.at)+(TP.at);
if(sqrt(bektolx*bektolx+bektoly*bektoly)>sqrt(r*r)){
double prey = max_y - (high-bektoly) - TP.at-starty; //1フレーム前の表示y座標
double prex = max_x - TP.at - startx + (kyori+bektolx);//1フレーム前の表示x座標
double imay = max_y - high - TP.at-starty;//現在の表示y座標
double imax = max_x - TP.at - startx + kyori;//現在の表示x座標
double px,py;
for(i=0;i<sqrt(bektolx*bektolx+bektoly*bektoly)/*/sqrt(r*r)*/;i++){//進んだ分÷当たり判定分ループ
px=imax-ZP.x;
py=imay-ZP.y+9;
if(px*px+py*py<r*r){
TP.x=(int)prex;
TP.y=(int)prey;
return 1;
}
prex-=/*sqrt(r*r)*/1*sqrt(bektolx*bektolx+bektoly*bektoly)/bektolx;
prey+=/*sqrt(r*r)*/1*sqrt(bektolx*bektolx+bektoly*bektoly)/bektoly;
}
}
if(x*x+y*y<r*r){//当たり判定内なら
return 1;//当たり
}
return 0;
}
void ZikiTamaAtariSyori(){//自機と玉の当たり判定処理
if(ZP.muteki==0){
if(ZikiTamaAtariJudge()){
ZP.zanki--;
ZP.muteki=100;
GameStop=10;
}
}
}
void TamaMove(){
// ボールを表示する(メーター単位からピクセル単位への換算と+/-の逆転)。
high = ( (double)((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy)) / PMAX ) * TM_y; //単位メーターから座標系への変換
kyori = ((double)(TP.xmax*cos(TP.ang*PI))/PMAX)*TM_x; //単位メーターから座標系への変換
bektoly = ( (double)((TP.ymax*sin(TP.ang*PI))-(max_y-TP.at-TM_sy)) / PMAX ) * TM_vypf;
bektolx = ((double)(TP.xmax*cos(TP.ang*PI))/PMAX)*TM_vxpf;
TP.y = max_y - (int)high - (int)TP.at-starty; //表示座標
TP.x = max_x - (int)TP.at - startx + (int)kyori;//表示座標
// 次のボールの位置の計算
TM_vypf = TM_vypf - TM_gpf;
TM_y = TM_y + TM_vypf;
TM_x = TM_x - TM_vxpf;
ZikiTamaAtariSyori();
GamenAtariSyori();
}
void ZikiMove(){
if( Key[ KEY_INPUT_RIGHT ] >= 1 && ZP.x<(max_x-12)){//左移動
ZP.x+=(int)ZP.sp;
}
if( Key[ KEY_INPUT_LEFT ] >= 1 && ZP.x>(min_x+12)){//右移動
ZP.x-=(int)ZP.sp;
}
if(Key[KEY_INPUT_A]>=1 && Key[ KEY_INPUT_RIGHT ] >= 1 && Key[ KEY_INPUT_LEFT ] <= 0){
ZP.img=3;
}
else if(Key[KEY_INPUT_A]>=1 && Key[ KEY_INPUT_RIGHT ] <= 0 && Key[ KEY_INPUT_LEFT ] >= 1){
ZP.img=2;
}
else if(Key[KEY_INPUT_A]>=1){
ZP.img=1;
}
else{
ZP.img=0;
}
}
void EnemyOrderload(){
int n,num,i,fp;
char fname[]={"Estate.csv"};
int input[27];
char inputc[27];
fp = FileRead_open(fname);//ファイル読み込み
if(fp == NULL){
printfDx("read error\n");
return;
}
for(i=0;i<2;i++){//最初の2行読み飛ばす
while(FileRead_getc(fp)!='\n');
}
n=0 , num=0;
while(1){
for(i=0;i<64;i++){
inputc[i]=input[i]=FileRead_getc(fp);//1文字取得する
if(inputc[i]=='/'){//スラッシュがあれば
while(FileRead_getc(fp)!='\n');//改行までループ
i=-1;//カウンタを最初に戻して
continue;
}
if(input[i]==',' || input[i]=='\n'){//カンマか改行なら
inputc[i]='\0';//そこまでを文字列とし
break;
}
if(input[i]==EOF){//ファイルの終わりなら
goto EXFILE;//終了
}
}
switch(num){
case 0: EO[n].hp_max =atoi(inputc);break;
case 1: EO[n].nolma =atoi(inputc);break;
case 2: EO[n].pattern =atoi(inputc);break;
case 3: EO[n].item =atoi(inputc);break;
case 4: EO[n].knd =atoi(inputc);break;
case 5: EO[n].at =atoi(inputc);break;
case 6: EO[n].attime =atoi(inputc);break;
case 7: EO[n].stage =atoi(inputc);break;
case 8: EO[n].timing =atoi(inputc);break;
case 9: EO[n].x =atoi(inputc);break;
case 10: EO[n].y =atoi(inputc);break;
}
num++;
if(num==11){
num=0;
n++;
}
}
EXFILE:
FileRead_close(fp);
}
int EPnumsearch(){
int i;
for(i=0;i<EnemyMax;i++){
if(EP[i].flag==0){
return i;
}
}
return -1;
}
void EnemyEnter(){
int i,j;
for(i=0;i<EnemyOrderMax;i++){
if(StageCnt==EO[i].stage){
if(GameCnt==EO[i].timing){
if((j=EPnumsearch())!=-1){
EP[j].flag=1;
EP[j].cnt=0;
EP[j].nolma=EO[i].nolma;
EP[j].hp_max=EO[i].hp_max;
EP[j].hp=EP[0].hp_max;
EP[j].pattern=EO[i].pattern;
EP[j].item=EO[i].item;
EP[j].knd=EO[i].knd;
EP[j].at=EO[i].at;
EP[j].attime=EO[i].attime;
EP[j].x=EO[i].x;
EP[j].y=EO[i].y;
EP[j].vx=0;
EP[j].vy=0;
}
}
}
}
}
void EnemyPattern0(int z){
}
void Enemyact(){
int i;
for(i=0;i<EnemyMax;i++){
if(EP[i].flag==1){
EnemyPattern0(i);
EP[i].cnt++;
}
}
}
void DrawEnemy(){
int i;
for(i=0;i<EnemyMax;i++){
if(EP[i].flag==1){
DrawRotaGraph(EP[i].x,EP[i].y,0.5,0.0,enemy_img,TRUE);
}
}
}
int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance,LPSTR lpCmdLine, int nCmdShow ){
ChangeWindowMode(TRUE), DxLib_Init(), SetDrawScreen( DX_SCREEN_BACK ); //ウィンドウモード変更と初期化と裏画面設定
// 定数
TM_gpf = g/fps;
// 座標系
TP.ymax=480;
TP.xmax=960;
TP.ang=1.0/4.0;
TP.at=5;
TM_sy = max_y-TP.at-starty;
TM_PMAXf = sqrt(PMAX * 2.0 / TM_gpf);
// 最大高さ到達までの時間(単位フレーム)から初速値単位(m/フレーム)を計算する。
// v0 = gt
TM_v0pf = TM_gpf * TM_PMAXf;//初速度
// 初速の設定
TM_vypf = TM_v0pf;
TM_vxpf = PMAX/(TM_PMAXf*2);
TP_img=LoadGraph("img/tama.png");//玉の画像のロード
ZP.img=0;//LoadGraph("img/ziki.png");//自機の画像のロード
LoadDivGraph("img/ziki[4]_ver0.10.png",4,1,4,53,35,ZP_img);//自機の画像の分割ロード
waku_img=LoadGraph("img/waku.png");
ZP.x=(max_x+min_x)/2;//自機のx座標の初期化
ZP.y=max_y-17;//自機のy座標の初期化
ZP.zanki=5;//自機の残機の初期化
ZP.muteki=0;
ZP.sp=4.000;//自機のスピードの初期化
ZP.at=8.000;//自機の当たり判定の初期化
memset(EP,0,sizeof(EnemyPara_t)*EnemyMax);//敵データの初期化
memset(EO,0,sizeof(EnemyOrder_t)*EnemyOrderMax);//敵出現データの初期化
enemy_img=LoadGraph("img/block.png");
EnemyOrderload();
StageCnt++;
// while( 裏画面を表画面に反映, メッセージ処理, 画面クリア )
while( ScreenFlip()==0 && ProcessMessage()==0 && ClearDrawScreen()==0 && gpUpdateKey()==0 ){
if(GameStop>0){
GameStop--;
}
if(GameStop==0){
if(ZP.muteki>0){
ZP.muteki--;
}
}
EnemyEnter();
if(GameStop==0){
Enemyact();
ZikiMove();
TamaMove();
}
DrawRotaGraph(ZP.x,ZP.y,1.0,0.0,ZP_img[ZP.img],TRUE);//自機の描画
DrawEnemy();
DrawRotaGraph( TP.x , TP.y ,1.0,0.0, TP_img , TRUE );//玉の描画
DrawGraph(0,0,waku_img,TRUE);//枠の描画
DrawFormatString( 0,0,GetColor(255,255,0),"残機:%d",ZP.zanki);
//if(GameCnt==0){
// GameStop=100;
//};//デバッグ用
if(ZP.muteki>0){
DrawFormatString( 0,20,GetColor(255,255,255),"無敵状態");
}
if(GameStop>0){
DrawFormatString( 0,20,GetColor(0,0,255),"停止中");
}
if(GameStop==0){
GameCnt++;
}
}
DxLib_End(); // DXライブラリ終了処理
return 0;
}