質問は3つあります。
<マップについて>
キャラに当たるのが1(白)、当たらないのが0(黒)なのですが実行してみると1と0がマップデータと少しずれてしまいます。
00000001111100000
実行するとキャラが下線部分に当たるみたいな感じです。
(説明が下手ですみません)
どこがおかしくこのようなことが起きるのか教えて欲しいです。
<スクロールについて>
キャラを右に動かすとマップもスクロールされるのですが、マップのスクロールがキャラの移動に間に合わずキャラがマップ右端まで行って見えなくなってしまいます。
キャラ移動とマップスクロールを同じ速度にするにはどうすれば良いですか?
どこが間違えているのか教えてくれませんか?
<弾当たり判定について>
マップに表示したコインをキャラの弾をあてることによってスコアを取得し、弾が当たったコインは消えるというプログラムを作りたいのですが、うまい方法が見つかりません。
このプログラムに合ったサンプルを作ってくれるととてもありがたいです。
大変ご迷惑をおかけします。
よろしくお願いします。
#define _USE_MATH_DEFINES
#define _CRT_SECURE_NO_WARNINGS
#include "DxLib.h"
#include <math.h>
#include <string.h>
#define SCREEN_WIDTH (640) // 画面の横幅
#define SCREEN_HEIGHT (480) // 画面の縦幅
#define MAP_PIXEL_WIDTH (1280)
#define MAP_PIXEL_HEIGHT (480)
#define CHIP_SIZE (32) // 一つのチップのサイズ
#define MAP_WIDTH ( MAP_PIXEL_WIDTH / CHIP_SIZE) // マップの横幅
#define MAP_HEIGHT (MAP_PIXEL_HEIGHT / CHIP_SIZE) // マップの縦幅
#define G (0.3F) // キャラに掛かる重力加速度
#define JUMP_POWER (9.0F) // キャラのジャンプ力
#define SPEED (3.0F) // キャラの移動スピード
#define CHAR_SIZE (30) // プレイヤーのサイズ
// ショットの最大数
#define MAX_SHOT 10000
// マップデータ
char MapData[MAP_HEIGHT][MAP_WIDTH] =
{
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 2, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
};
int Player;
float PlX, PlY; // プレイヤーの座標(中心座標)
float PlDownSp; // プレイヤーの落下速度
char PlJumpFlag; // プレイヤーがジャンプ中か、のフラグ
int Shot;//弾
int ShotValid[MAX_SHOT]; // ショットが存在するか、フラグ
int ShotX[MAX_SHOT], ShotY[MAX_SHOT]; // ショットの位置
int coin;
int Input, EdgeInput; // 入力状態
int FrameStartTime; // 60FPS固定用、時間保存用変数
int camerax = 0;//カメラの左上座標
int charx;//自キャラ位置
int view_charx;//自キャラの表示位置。この座標にdrawする。
////プロトタイプ宣言////////////////////////////////////////////////////////////////////////////
// マップチップの値を取得する関数
int GetChipParam(float X, float Y);
// キャラクタをマップとの当たり判定を考慮しながら移動する関数
int CharMove(float *X, float *Y, float *DownSP,
float MoveX, float MoveY, float Size, char *JumpFlag);
// アクションサンプルプログラムメイン関数
int ActMain();
// マップとの当たり判定( 戻り値 0:当たらなかった 1:左辺に当たった 2:右辺に当たった
// 3:上辺に当たった 4:下辺に当たった
int MapHitCheck(float X, float Y, float *MoveX, float *MoveY);
///////////////////////////////////////////////////////////////////////////////////////////////
// WinMain関数
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
LPSTR lpCmdLine, int nCmdShow)
{
// 警告避け
(void)hInstance; (void)hPrevInstance; (void)lpCmdLine; (void)nCmdShow;
ChangeWindowMode(TRUE);
SetWaitVSyncFlag(FALSE); //画面更新が遅くならないようにする
// DXライブラリの初期化
if (DxLib_Init() == -1) return -1;
PlayMusic("test.mp3", DX_PLAYTYPE_BACK); // test.midのバックグラウンド演奏
// アクションゲームのメイン関数を呼ぶ
ActMain();
// DXライブラリの後始末
DxLib_End();
// 終了
return 0;
}
// アクションサンプルプログラムメイン
int ActMain(void)
{
int i;
// 描画先を裏画面にセット
SetDrawScreen(DX_SCREEN_BACK);
// 垂直同期信号を待たない
SetWaitVSyncFlag(FALSE);
//ロード
Player = LoadGraph("marimo.jpg");
Shot = LoadGraph("ビーム2.jpg");
coin = LoadGraph("coin.jpg");
// プレイヤーの座標を初期化
PlX = 320.0F; PlY = 240.0F;
// プレイヤーの落下速度を初期化
PlDownSp = 0.0F;
// ジャンプ中フラグを倒す
PlJumpFlag = FALSE;
// ショットの存在を初期化する
for (i = 0; i < MAX_SHOT; i++)
ShotValid[i] = 0;
// 入力状態の初期化
Input = 0;
EdgeInput = 0;
// 60FPS固定用、時間保存用変数を現在のカウント値にセット
FrameStartTime = GetNowCount();
// メインループ開始、ESCキーで外に出る
while (ProcessMessage() == 0 && CheckHitKey(KEY_INPUT_ESCAPE) == 0)
{
int Key;
int OldKey; // 前のキー入力状態
// キー入力取得
Key = 0;
OldKey = Key;
Key = GetJoypadInputState(DX_INPUT_KEY_PAD1);
// 画面のクリア
ClsDrawScreen();
// 1/60秒立つまで待つ
while (GetNowCount() - FrameStartTime < 1000 / 60) {}
// 現在のカウント値を保存
FrameStartTime = GetNowCount();
// 入力状態を更新
{
int i;
// パッド1とキーボードから入力を得る
i = GetJoypadInputState(DX_INPUT_KEY_PAD1);
// エッジを取った入力をセット
EdgeInput = i & ~Input;
// 入力状態の保存
Input = i;
}
////////// プレイヤーの移動処理////////////////////////////////////////////////////////////
{
float MoveX, MoveY;
// 移動量の初期化
MoveX = 0.0F;
MoveY = 0.0F;
// 左右の移動を見る
if ((Input & PAD_INPUT_LEFT) != 0) MoveX -= SPEED;
if ((Input & PAD_INPUT_RIGHT) != 0) MoveX += SPEED;
// 地に足が着いている場合のみジャンプボタン(ボタン1 or Zキー)を見る
if (PlJumpFlag == FALSE && (EdgeInput & PAD_INPUT_B) != 0)
{
PlDownSp = -JUMP_POWER;
PlJumpFlag = TRUE;
}
// 落下処理
PlDownSp += G;
// 落下速度を移動量に加える
MoveY = PlDownSp;
// 移動量に基づいてキャラクタの座標を移動
CharMove(&PlX, &PlY, &PlDownSp, MoveX, MoveY, CHAR_SIZE, &PlJumpFlag);
}
//////////// ショットの移動処理/////////////////////////////////////////////////////////////
for (i = 0; i < MAX_SHOT; i++)
{
// ショットデータが無効だったらスキップ
if (ShotValid[i] == 0) continue;
// 位置を上にずらす
ShotX[i] += 8;
// 画面外に出ていたらショットデータを無効にする
if (ShotX[i] < -32) ShotValid[i] = 0;
}
// ショットボタンを押していたらショットを出す
// 一つ前のループでショットボタンを押していたらショットは出さない
if ((Key & ~OldKey) & PAD_INPUT_A)
{
// 使われていないショットデータを探す
for (i = 0; i < MAX_SHOT; i++)
{
if (ShotValid[i] == 0) break;
}
// もし使われていないショットデータがあったらショットを出す
if (i != MAX_SHOT)
{
// ショットの位置を設定
ShotX[i] = (int)PlX + 17;
ShotY[i] = (int)PlY + 5;
// ショットデータを使用中にセット
ShotValid[i] = 1;
}
}
////////// マップの描画///////////////////////////////////////////////////////////////////////
//自キャラの座標の代入
charx = (int)PlX;
//カメラ位置を計算
camerax = charx - SCREEN_WIDTH / 2;
//左右の補正
if (camerax < 0)
{
camerax = 0;
}
if (camerax >(MAP_PIXEL_WIDTH - SCREEN_WIDTH))
{
camerax = (MAP_PIXEL_WIDTH - SCREEN_WIDTH);
}
//画面内の位置を求める。
view_charx = charx - camerax;
int i, j;
for (i = 0; i < MAP_HEIGHT; i++)
{
for (j = 0; j < MAP_WIDTH; j++)
{
// 1は当たり判定チップを表しているので1のところだけ描画
if (MapData[i][j] == 1)
{
DrawBox(j * CHIP_SIZE - camerax, i * CHIP_SIZE,
j * CHIP_SIZE + CHIP_SIZE - camerax, i * CHIP_SIZE + CHIP_SIZE,
GetColor(255, 255, 255), TRUE);
}
// 2でコインを表示
if (MapData[i][j] == 2)
{
DrawGraph(j * CHIP_SIZE - camerax, i * CHIP_SIZE, coin, TRUE);
}
}
}
/////////////////////////////////////////////////////////////////////////////////////////////
// キャラクタの描画
DrawGraph((int)(PlX - CHAR_SIZE * 0.5F), (int)(PlY - CHAR_SIZE * 0.5F) + 1, Player, TRUE);
// ショットを描画する
for (i = 0; i < MAX_SHOT; i++)
{
// ショットデータが有効な時のみ描画
if (ShotValid[i] == 1)
{
DrawGraph(ShotX[i], ShotY[i], Shot, TRUE);
}
}
// 画面の更新
ScreenFlip();
}
// 終了
return 0;
}
// キャラクタをマップとの当たり判定を考慮しながら移動する
int CharMove(float *X, float *Y, float *DownSP,
float MoveX, float MoveY, float Size, char *JumpFlag)
{
float Dummy = 0.0F;
float hsize;
// キャラクタの左上、右上、左下、右下部分が当たり判定のある
// マップに衝突しているか調べ、衝突していたら補正する
// 半分のサイズを算出
hsize = Size * 0.5F;
// 先ず上下移動成分だけでチェック
{
// 左下のチェック、もしブロックの上辺に着いていたら落下を止める
if (MapHitCheck(*X - hsize, *Y + hsize, &Dummy, &MoveY) == 3) *DownSP = 0.0F;
// 右下のチェック、もしブロックの上辺に着いていたら落下を止める
if (MapHitCheck(*X + hsize, *Y + hsize, &Dummy, &MoveY) == 3) *DownSP = 0.0F;
// 左上のチェック、もしブロックの下辺に当たっていたら落下させる
if (MapHitCheck(*X - hsize, *Y - hsize, &Dummy, &MoveY) == 4) *DownSP *= -1.0F;
// 右上のチェック、もしブロックの下辺に当たっていたら落下させる
if (MapHitCheck(*X + hsize, *Y - hsize, &Dummy, &MoveY) == 4) *DownSP *= -1.0F;
// 上下移動成分を加算
*Y += MoveY;
}
// 後に左右移動成分だけでチェック
{
// 左下のチェック
MapHitCheck(*X - hsize, *Y + hsize, &MoveX, &Dummy);
// 右下のチェック
MapHitCheck(*X + hsize, *Y + hsize, &MoveX, &Dummy);
// 左上のチェック
MapHitCheck(*X - hsize, *Y - hsize, &MoveX, &Dummy);
// 右上のチェック
MapHitCheck(*X + hsize, *Y - hsize, &MoveX, &Dummy);
// 左右移動成分を加算
*X += MoveX;
}
// 接地判定
{
// キャラクタの左下と右下の下に地面があるか調べる
if (GetChipParam(*X - Size * 0.5F, *Y + Size * 0.5F + 1.0F) == 0 &&
GetChipParam(*X + Size * 0.5F, *Y + Size * 0.5F + 1.0F) == 0)
{
// 足場が無かったらジャンプ中にする
*JumpFlag = TRUE;
}
else
{
// 足場が在ったら接地中にする
*JumpFlag = FALSE;
}
}
// 終了
return 0;
}
// マップとの当たり判定( 戻り値 0:当たらなかった 1:左辺に当たった 2:右辺に当たった
// 3:上辺に当たった 4:下辺に当たった
int MapHitCheck(float X, float Y,
float *MoveX, float *MoveY)
{
float afX, afY;
// 移動量を足す
afX = X + *MoveX;
afY = Y + *MoveY;
// 当たり判定のあるブロックに当たっているかチェック
if (GetChipParam(afX, afY) == 1)
{
float blx, bty, brx, bby;
// 当たっていたら壁から離す処理を行う
// ブロックの上下左右の座標を算出
blx = (float)((int)afX / CHIP_SIZE) * CHIP_SIZE; // 左辺の X 座標
brx = (float)((int)afX / CHIP_SIZE + 1) * CHIP_SIZE; // 右辺の X 座標
bty = (float)((int)afY / CHIP_SIZE) * CHIP_SIZE; // 上辺の Y 座標
bby = (float)((int)afY / CHIP_SIZE + 1) * CHIP_SIZE; // 下辺の Y 座標
// 上辺に当たっていた場合
if (*MoveY > 0.0F)
{
// 移動量を補正する
*MoveY = bty - Y - 1.0F;
// 上辺に当たったと返す
return 3;
}
// 下辺に当たっていた場合
if (*MoveY < 0.0F)
{
// 移動量を補正する
*MoveY = bby - Y + 1.0F;
// 下辺に当たったと返す
return 4;
}
// 左辺に当たっていた場合
if (*MoveX > 0.0F)
{
// 移動量を補正する
*MoveX = blx - X - 1.0F;
// 左辺に当たったと返す
return 1;
}
// 右辺に当たっていた場合
if (*MoveX < 0.0F)
{
// 移動量を補正する
*MoveX = brx - X + 1.0F;
// 右辺に当たったと返す
return 2;
}
// ここに来たら適当な値を返す
return 4;
}
// どこにも当たらなかったと返す
return 0;
}
// マップチップの値を取得する関数
int GetChipParam(float X, float Y)
{
int x, y;
// 整数値へ変換
x = (int)X / CHIP_SIZE;
y = (int)Y / CHIP_SIZE;
// マップからはみ出ていたら 0 を返す
if (x >= MAP_WIDTH || y >= MAP_HEIGHT || x < 0 || y < 0) return 0;
// 指定の座標に該当するマップの情報を返す
return MapData[y][x];
}