コンテンツにスキップ

重力ジャンプをマスターしよう

ここでは重力がある場でのジャンプ(重力ジャンプ)について説明しています(注1)。重力ジャンプは、最初は勢い良く上昇しますがだんだん遅くなり、最高点でいったん止まってから、ゆっくり下降し始めてだんだん速くなり、着地したところで止まります。この動きを実現するには、プレーヤーのジャンプ・落下の速度(下記の例では yspeed )を刻々と変えていくというのがポイントです。

STEP1 真上にジャンプしよう

最初は、上矢印キー(UPキー)を押したら真上にジャンプして降りてくるようにしよう。

float px, py; //プレーヤーのx座標、y座標
float yspeed; //プレーヤーのジャンプ・落下速度
boolean ue; //上矢印キーが押されていればtrue、押されていなければfalse
boolean onGround; //プレーヤーが着地しているときはtrue、ジャンプ中はfalse
void setup() {
size(600, 400);
px = 300;
py = height-50; //プレーヤは最初は床に接しています。プレーヤー高さが50なので、pyはheight-50になります。
yspeed = 0;
ue = false;
onGround = true;
}
void draw() {
background(0);
//上矢印キーが押された時、床に接していれば、ジャンプ開始
if (ue) {
if (onGround) {
yspeed = -30; //ポイント1:ジャンプスピードは最初はマイナスの大きな値にします
onGround = false;
}
}
//ジャンプ処理
py += yspeed;
yspeed += 2; //ポイント2:ジャンプスピードを刻々と変化させていきます
//プレーヤーが床に接している(または着地した)場合
if (py+50 > height) {
yspeed = 0;
onGround = true;
py = height - 50;
}
//プレーヤー表示
rect(px, py, 50, 50);
}
void keyPressed() {
if (keyCode == UP) {
ue = true;
}
}
void keyReleased() {
if (keyCode == UP) {
ue = false;
}
}

解説

ジャンプの動きを実現するには、drawのループの中で下記のようにします。

py += yspeed; // ジャンプし始めのyspeedは-30
yspeed += 2;

プレーヤーは最初は勢い良く上昇するので、 ジャンプをし始めるときには、yspeed を最初はマイナスの大きな値にします(上記の例では-30)。それから徐々に遅くなる、つまり徐々にマイナスの小さな値にしていきます。このyspeedの変化は、 yspeed += 2; で実現しています。

最高点でいったん止まるので、このとき yspeed は0にします。それからゆっくり下降し始めてだんだん速くなるので、 yspeed はプラスの小さな値からどんどん大きな値にしていきます。後半のこの変化も、同じ yspeed += 2; で実現しています。yspeedは以下のように変化します。

yspeedの変化 -30 → -28 → -26 → …… → -2 → 0 → 2 → …… → 28 → 30

着地は、(STEP1では)プレーヤーのy座標 py をチェックして判断します。ここでは、 py >= 350 で着地したと判断します。着地したら、yspeed を0にします。また、このプログラムでは床に接していないとジャンプできないことにしているので、着地したら onGround を true (床に接している)に戻します。

STEP2 右や左にもジャンプできるようにしよう

keyPressed()などでUPキーだけでなく、LEFTキー、RIGHTキーも受け付けるようにしよう。

float px, py; //プレーヤーのx座標、y座標
float yspeed; //プレーヤーのジャンプ・落下速度
float migi, hidari; //左右矢印キーを押したときの移動量
boolean ue; //上矢印キーが押されていればtrue、押されていなければfalse
boolean onGround; //プレーヤーが着地しているときはtrue、ジャンプ中はfalse
void setup() {
size(600, 400);
px = 300;
py = height-50;
yspeed = 0;
ue = false;
onGround = true;
}
void draw() {
background(0);
//上矢印キーが押された時、床に接していれば、ジャンプ開始
if (ue) {
if (onGround) {
yspeed = -30;
onGround = false;
}
}
//ジャンプ処理
py += yspeed;
yspeed += 2;
//プレーヤーが床に接している(または着地した)場合
if (py+50 > height) {
yspeed = 0;
onGround = true;
py = height - 50;
}
//左右の移動処理
px += migi + hidari;
//プレーヤー表示
rect(px, py, 50, 50);
}
void keyPressed() {
if (keyCode == UP) {
ue = true;
}
if (keyCode == RIGHT) {
migi = 10;
}
if (keyCode == LEFT) {
hidari = -10;
}
}
void keyReleased() {
if (keyCode == UP) {
ue = false;
}
if (keyCode == RIGHT) {
migi = 0;
}
if (keyCode == LEFT) {
hidari = 0;
}
}

※ここでは、キー入力で図形をスムーズに動かすためのテクニックを使っています。このテクニックについては、「キーで図形をスムーズに動かす小技」を参照してください。

STEP3 上に当たる場合も考えよう

上昇中に天井に当たった場合の処理を考えよう。

STEP2のプログラムで、ジャンプ開始の時のyspeedを -30 ではなく -50 にすると天井を突き抜けてしまいます。このSTEP3では、天井に当たったらそこで跳ね返るようにしましょう。

float px, py; //プレーヤーのx座標、y座標
float yspeed; //プレーヤーのジャンプ・落下速度
float migi, hidari; //左右矢印キーを押したときの移動量
boolean ue; //上矢印キーが押されていればtrue、押されていなければfalse
boolean onGround; //プレーヤーが着地しているときはtrue、ジャンプ中はfalse
void setup() {
size(600, 400);
px = 300;
py = height-50;
yspeed = 0;
ue = false;
onGround = true;
}
void draw() {
background(0);
//上矢印キーが押された時、床に接していれば、ジャンプ開始
if (ue) {
if (onGround) {
yspeed = -50;
onGround = false;
}
}
//ジャンプ処理
py += yspeed;
yspeed += 2;
//上に当たった
if (py < 0) { //天井に当たった場合、一旦止まってから落ち始める
py = 0;
yspeed = 0;
}
//プレーヤーが床に接している(または着地した)場合
if (py+50 > height) {
yspeed = 0;
onGround = true;
py = height - 50;
}
//左右の移動処理
px += migi + hidari;
//プレーヤー表示
rect(px, py, 50, 50);
}
void keyPressed() {
if (keyCode == UP) {
ue = true;
}
if (keyCode == RIGHT) {
migi = 10;
}
if (keyCode == LEFT) {
hidari = -10;
}
}
void keyReleased() {
if (keyCode == UP) {
ue = false;
}
if (keyCode == RIGHT) {
migi = 0;
}
if (keyCode == LEFT) {
hidari = 0;
}
}

STEP4 背景マップを加えよう

背景に床、壁、天井のブロックがあるようにしよう。ここではプレーヤーと壁との当たり判定を考えよう。

※ヒント:「マップをマスターしよう」の「プレーヤーがドット(x座標・y座標)で動く場合」などを参考にしてください。

float px, py; //プレーヤーの現在位置のx座標、y座標
float next_px, next_py; //移動先のxy座標(当たり判定用)
float xstep; //左右方向の移動量
float yspeed, xspeed; //その時々のプレーヤーの上昇・下降速度、左右方向速度
float migi, hidari; //左右矢印キーを押したときのそれぞれの移動量
boolean ue; //上矢印キーが押されていればtrue、押されていなければfalse
boolean onGround; //プレーヤーが着地しているときはtrue、ジャンプ中はfalse
int[][] map={
{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
{1, 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, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 0, 1, 1, 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, 1},
{1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1},
{1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 0, 0, 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}};
int imax = map.length;
int jmax = map[0].length;
void setup() {
size(1000, 500); //画面の大きさはマップデータから自動計算したいところだが、Processing3の仕様でsizeの引数に変数は使えないので、jmax,imax,ブロックの大きさ(=50)から手計算してここに書いた。
xstep = 2;
ue = false;
migi = 0;
hidari = 0;
px=50;
py=50;
next_px = px;
next_py = py;
}
void draw() {
background(0);
//マップの描画
for (int i = 0; i < imax; i++) {
for (int j = 0; j < jmax; j++) {
if (map[i][j] == 0) {
fill(0);
} else if (map[i][j] == 1) {
fill(255, 0, 0);
}
rect(j * 50, i * 50, 50, 50); //1マス50の大きさ
}
}
//上矢印キーが押された時、床に接していれば、ジャンプ開始
if (ue) {
if (onGround) {
yspeed = -30; //-30は、いわゆる初速度
onGround = false;
}
}
//ジャンプ処理
next_py = py + yspeed;
//上が空いているか
if (yspeed < 0) { //yspeed < 0ということは上昇中
if ((map[int(next_py / 50)][int(next_px / 50)] != 1 ) //左上頂点
&& (map[int(next_py / 50)][int((next_px+49) / 50)] != 1 ) ) { //右上頂点
py = next_py; //壁(=1)ではないので移動できるので、py(現在位置)を更新(移動先 next_py に)する
yspeed += 2; //2は、いわゆる重力加速度。ここ(上昇中)ではspeedyは負の値なので、上昇速度がどんどん減速する
} else {
next_py = py; //壁(=1)であり移動できないので、next_py(移動先)もpy(現在位置)に戻しておく
yspeed = 0; //上側にぶつかったら、上昇をやめる
}
}
//下が空いているか
if (yspeed >= 0) { //yspeed >= 0ということは下降中または着地状態
if ((map[int((next_py + 49) / 50)][int(next_px / 50)] != 1 ) //左下頂点
&& (map[int((next_py + 49) / 50)][int((next_px+49) / 50)] != 1 ) ) { //右下頂点
py = next_py; //壁(=1)ではないので移動できるので、pyを更新する
yspeed += 2; //2は、いわゆる重力加速度。ここ(下降中)ではspeedyは正の値なので、下降速度がどんどん加速する
} else {
next_py = py;
yspeed = 0;
onGround = true; //着地したとみなす
}
}
//プレーヤーの左右の移動処理
xspeed = migi + hidari;
next_px = px + xspeed;
//左が空いているか
if (xspeed < 0) { //xspeed < 0ということは左へ移動中
if ((map[int(next_py / 50)][int(next_px / 50)] != 1 ) //左上頂点
&& (map[int((next_py+49) / 50)][int(next_px / 50)] != 1 )) { //左下頂点
px = next_px; //壁(=1)ではないので移動できるので、px(現在位置)を更新(移動先 next_px に)する
} else {
next_px = px; //壁(=1)であり移動できないので、next_px(移動先)はpx(現在位置)に戻しておく
}
}
//右が空いているか
if (xspeed >= 0) { //xspeed > 0ということは静止または右へ移動中
if ((map[int(next_py / 50)][int((next_px+49) / 50)] != 1 ) //右上頂点
&&(map[int((next_py+49) / 50)][int((next_px+49) / 50)] != 1 ) ) { //右下頂点
px = next_px;
} else {
next_px = px;
}
}
//プレーヤー表示
fill(0, 0, 255);
rect(px, py, 50, 50);
}
void keyPressed() {
if (keyCode == UP) {
ue = true;
}
if (keyCode == RIGHT) {
//migi = 10;
migi = xstep;
}
if (keyCode == LEFT) {
//hidari = -10;
hidari = -xstep;
}
}
void keyReleased() {
if (keyCode == UP) {
ue = false;
}
if (keyCode == RIGHT) {
migi = 0;
}
if (keyCode == LEFT) {
hidari = 0;
}
}