ゲームプログラムめも日記 このページをアンテナに追加 RSSフィード


けんもほろろ  / オススメゲームプログラム本  / 作ったもの置き場  
ゲーム開発のデザパタまとめ  / めも日記まとめ  

2006-02-23

[]「えぐぜりにゃ〜」ソース解説2 「えぐぜりにゃ〜」ソース解説2を含むブックマーク

前回はざっくりと移動関数を見ましたので、今回は細かく見ていきます。

(※バージョンは「exelinya_0214.zip」です)

movemychar

まずはプレイヤーの移動関数です。(498行目から)

 

プレイヤーの移動関数は、以下の処理を行っています。

  1. ライフの自動回復
  2. キー入力処理
  3. 移動
  4. 当たり判定
ライフの自動回復

501行目でライフの自動回復を行っています。

	// ライフ自動回復
	if(mychar_life < MYCHAR_LIFE_MAX){
		mychar_life++
	}

見たままですね。

ライフが最大値よりも小さければ、ライフを1つプラスしています。

 

キー入力処理

508行目でキー入力の判定をしています。

	if((stick_state | key_state) & 1){vx = -1.0 : mychar_gr_rotate -= MYCHAR_ROT}
	if((stick_state | key_state) & 2){vy = -1.0 : mychar_gr_rotate -= MYCHAR_ROT}
	if((stick_state | key_state) & 4){vx =  1.0 : mychar_gr_rotate += MYCHAR_ROT}
	if((stick_state | key_state) & 8){vy =  1.0 : mychar_gr_rotate += MYCHAR_ROT}

これも見たままで、上下左右の入力処理を行っています。

 

ポイントは次の部分ですね。(513行目)

分かりやすいようにコメントを入れておきます。

	if(vx != 0.0 | vy != 0.0){
		// キー入力があった場合
		// ^榮偉未魄貭蠅砲垢
		vr = sqrt(vx * vx + vy * vy)
		vx = vx / vr
		vy = vy / vr
		// ▲ー入力に対する慣性をつける
		if(anchor_state == ANCHOR_STATE_STANDBY | anchor_state == ANCHOR_STATE_RETURN){
			// 何も掴んでいない
			mychar_ssx += vx * MYCHAR_SPEED1
			mychar_ssy += vy * MYCHAR_SPEED1
		}else{
			// 何か掴んでいる
			mychar_ssx += vx * MYCHAR_SPEED2
			mychar_ssy += vy * MYCHAR_SPEED2
		}
	}

,任魯ー入力に対する移動量を一定にしています。

例えば斜めに移動があった場合、

vr = sqrt(1*1 + 1*1) ≒ 1.4

vx = vx / vr = 1 / 1.4 ≒ 0.7

vy = vy / vr = 1 / 1.4 ≒ 0.7

 

移動量 = sqrt(0.7*0.7 + 0.7*0.7) ≒ 1

となり、斜めの移動量が水平/垂直方向と同じになります。

 

なぜこのようなことをするのかというと、

移動量を一定にすると、「操作性が安定」するためです。

 

△任蓮▲ー入力に対して、慣性をあらわすパラメータssx/ssyに値を足しこんでいます。

MYCHAR_SPEED1は「5.0」で定義してあるので、何も掴んでいないときは「かなりの力」が、

MYCHAR_SPEED2は「0.2」で定義してあるので、何か掴んでいる「わずかな力」が、

それぞれ働くことになります。

 

移動

527行目の反動と減衰を見てみます。

	// 反動と減衰
	// 〆舵犬旅洪
	mychar_x += (mychar_sx + mychar_ssx)
	mychar_y += (mychar_sy + mychar_ssy)
	// 慣性の減衰
	mychar_ssx *= MYCHAR_SPWEAKEN
	mychar_ssy *= MYCHAR_SPWEAKEN
	// 0榮偉未慮鎖
	mychar_sx *= MYCHAR_REACTWEAKEN
	mychar_sy *= MYCHAR_REACTWEAKEN
	// げ鹽廠囘戮慮鎖
	mychar_gr_rotate *= MYCHAR_ROTWEAKEN

,任郎舵犬更新を行っています。

ここでは単純に現在の座標に、「移動量」と「慣性」を足しこんでいます。

 

△任牢契の減衰をしています。

MYCHAR_SPWEAKENは「0.5」なので、急激に減速します。

しかし、このほんの少しだけ働く慣性が、プレイヤーを操作する感覚を心地よくさせています。

 

では移動量の減衰をしています。

sx/syは、アンカーの反動や敵に衝突したときの反動です。

MYCHAR_REACTWEAKENは「0.85」と結構大き目の値を取っています。

 

そのため、

  • アンカーは慎重に操作しなければならない(反動が大きいため)
  • 敵に連続でぶつかると、ピンボールのようにはじかれる

という、「戦略性」と「感覚の面白さ」を生み出しています。

 

い硫鹽廠囘戮慮鎖蠅呂修里泙泙任垢諭

これのおかげで、ふにゃふにゃとプレイヤーの画像が動いて、

妙な面白さをかもし出しています。

 

537〜540行目では、キャラが画面外に出ないように調整をしています。

	if(mychar_x <         32.0) : mychar_x = 32.0
	if(mychar_x > 640.0 - 32.0) : mychar_x = 640.0 - 32.0
	if(mychar_y <         32.0) : mychar_y = 32.0
	if(mychar_y > 480.0 - 32.0) : mychar_y = 480.0 - 32.0

 

当たり判定

544行目から589行目までは、プレイヤーvs敵の当たり判定を行っています。

とりあえず、551行目までをみてみます。

	// 敵とのコリジョン-----------------------
	repeat ENEMY_MAX
		// ‖減澆靴覆ぞ豺腓浪燭盻萢をしない。
		if(enemy_type(cnt) == ENEMY_TYPE_NULL) : continue
		// 円による当たり判定
		// 敵からプレイヤーへの距離
		vxx = enemy_x(cnt) - mychar_x
		vyy = enemy_y(cnt) - mychar_y
		// ぶつかる距離
		sz = MYCHAR_SIZE + enemy_spec_size(enemy_type(cnt))
		if(vxx * vxx + vyy * vyy < sz * sz){
			// 食らった

,任垢、「えぐぜりにゃ〜」のように敵を「配列」で管理する場合は、

必ず存在チェックが必要になります。

if文で囲んでもいいですが、continueを使う方がスッキリ書けるので、

この書き方がオススメです。

 

△亙かる人にはすぐ分かる「円」による当たり判定ですね。

分からない人は、呪文と思って丸暗記してしまってください(´∀`;

一応こちらが参考になるかもしれません。

2Dゲームの当たり判定

 

552行目から557行目です。

			if(mychar_dam_timer < 1){
				// .瀬瓠璽献織ぅ沺爾ない場合
				mychar_life -= enemy_spec_dam(enemy_type(cnt))
			}else{
				// ▲瀬瓠璽献織ぅ沺爾残っている場合
				mychar_life -= int(double(enemy_spec_dam(enemy_type(cnt))) * MYCHAR_DAMAGE)
			}
			// ダメージタイマー設定
			mychar_dam_timer = MYCHAR_DAM_TIME

ダメージタイマーがない場合

そのまま、ごっそり体力を奪われてしまいます。

 

▲瀬瓠璽献織ぅ沺爾残っている場合

MYCHAR_DAMAGEは「0.1」なので、少しだけ体力を奪われます。

連続で衝突したときに即死しないようにするための配慮ですね。

 

ダメージタイマー設定

これはそのままですね。ダメージタイマーをセットします。

 

559行目から573行目です。

			// “身力を求める
			vxx = vxx / sqrt(sz)
			vyy = vyy / sqrt(sz)
			// 移動量設定
			mychar_sx = -MYCHAR_DAM_SP * vxx
			mychar_sy = -MYCHAR_DAM_SP * vyy

			if(anchor_state == ANCHOR_STATE_GRAB){
				// D呂鵑任襪里賄蠅欧
				anchor_state = ANCHOR_STATE_RETURN
				// 敵を飛ばす移動量を求める
				vxx = anchor_sx / 2.0 + ANCHOR_RELEASE_SP * cos(anchor_rotate)
				vyy = anchor_sy / 2.0 + ANCHOR_RELEASE_SP * sin(anchor_rotate)
				// 掴んだ敵は投げられた敵になる
				addthrenemy grab_enemy_x , grab_enemy_y , vxx , vyy , grab_enemy_rotate , grab_enemy_type , grab_enemy_life
			}

,任蓮敵にぶつかったときの反発力を求めています。

	vxx = vxx / sqrt(sz)

sqrt(sz)により、敵のサイズによって移動量を変えています。

(サイズが大きいほど移動量が小さくなる)

 

そのあと△念榮偉未鮴瀋蠅靴討い泙后

mychar_sx = -MYCHAR_DAM_SP * vxx

MYCHAR_DAM_SPには「3.0」が入っているので、結構大きい反動が発生します。

vxx,vyyは「プレイヤーから敵へのベクトル」なので、

マイナスを掛けて力を反転させています。

 

はそのままですね。

ダメージを受けたときは、敵を放します。

そして、敵を飛ばす移動量を求め、敵を「掴んだ敵」から「投げられた敵」に変化させます。

(※ただ、ANCHOR_RELEASE_SPはどこにも定義されていなかったのが気になりますが…)

 

 

最後の部分です(575行目〜587行目)

			// 敵とプレイヤーの中間の座標を求める
			xxx = (enemy_x(cnt) + mychar_x) / 2.0
			yyy = (enemy_y(cnt) + mychar_y) / 2.0

			// 敵にダメージ
			enemy_life(cnt) -= 2
			if(enemy_life(cnt) < 1){
				// 敵破壊エフェクト
				destroypertics enemy_x(cnt) , enemy_y(cnt) , enemy_sx(cnt) , enemy_sy(cnt) , enemy_type(cnt)
				enemy_type(cnt) = ENEMY_TYPE_NULL
			}else{
				// ダメージエフェクト
				addeffect  xxx , yyy , 0.0 , 0.0 , 0.0 , 96.0 , EFFECT_TYPE_PERTIC1
				addpertics xxx , yyy , 64.0 , 32.0 , 0.0 , 0.0 , 15
			}
			// ダメージ音
			ds_play SND_MISS

まあ、そのままなので、コメントを見てください、、(´∀`ヾ

 

敵の破壊やエフェクトの生成を行っています。

 

 

moveanchor

アンカーの移動関数です。

 

596〜599行目では画面外に出ないようにチェックしています。

	if(anchor_x <   0.0) : anchor_x =   0.0
	if(anchor_x > 640.0) : anchor_x = 640.0
	if(anchor_y <   0.0) : anchor_y =   0.0
	if(anchor_y > 480.0) : anchor_y = 480.0

 

602〜605行目では、キー入力に対応する移動量を設定しています。

	if((stick_state | key_state) & 1){vx = -1.0}
	if((stick_state | key_state) & 2){vy = -1.0}
	if((stick_state | key_state) & 4){vx =  1.0}
	if((stick_state | key_state) & 8){vy =  1.0}

 

607〜739行目では、アンカーの「4つの状態」に応じた更新処理を行っています。

 

スタンバイ

まずはスタンバイ状態から見ていきます。

607〜640行目がスタンバイ状態の更新処理です。

 

610〜619行目では、移動量に対応したアンカーの回転を行なっています。

		// 移動してる場合はアンカーをそちらに向ける
		if((vx != 0.0 | vy != 0.0) & ((stick_state | key_state) & 32) == 0){
			// _鹽召垢襯薀献▲鵑鮗萋
			theta = atan(vy,vx)
			if(absf(mychar_anc_rotate - theta) > MATH_PI){
				// ◆岷Σ鵑蝓廚靴燭-π〜πを超える
				if(mychar_anc_rotate > theta){
					// プラス方向に振り切る
					mychar_anc_rotate -= MATH_PI * 2.0
				}else{
					// マイナス方向に振り切る
					mychar_anc_rotate += MATH_PI * 2.0
				}
			}
			// 「アンカーの角度:入力角度」を「0.82:0.12」の比率で按分
			mychar_anc_rotate = mychar_anc_rotate * (1.0 - ANCHOR_ROT_SP) + theta * ANCHOR_ROT_SP
		}

,任蓮移動量に応じたラジアンを取得しています。

「ラジアン」ってなんやねん!

と思った方はこちらが参考になるかもしれません。

初心者のためのフラッシュレベルアップ講座

 

ようは、0〜360°という範囲が、0〜2πという範囲に置き換わっただけです。

 

△任蓮↓の演算結果が「不自然」にならないかチェックしています。

ということで、先にを見ておきます。

 

では、「現在のアンカーの角度:キー入力角度」を「0.82:0.12」の比率で按分することにより、

  • 現在向いている方向が、動かしたい方向から「遠い」場合、「速く」そっちを向くようにする。
  • 現在向いている方向が、動かしたい方向から「近い」場合、「遅く」そっちを向くようにする。

という処理をしています。

 

これにより、微妙な角度調整を行えるようになっています。

 

そして、この場合は足しこんでいるので「左回り」をするようにしています。

 

ただ、単純に「左回り」をすると、

現在方向が「-0.785」、移動方向が「3.14」の場合、

このような不自然な動きをしてしまいます。

f:id:kenmo:20060223184121p:image

この場合「右回り」をしてくれると自然な動きになりますよね。

 

そのための準備が△任后

 

はてさて、△任浪燭鬚笋辰討い襪里というと、、、、。

で動かしたい方向は、「左回り」ですよね。

			if(absf(mychar_anc_rotate - theta) > MATH_PI){

その前に「右回り」してみたら、180°を超えちゃいました!

というチェックをしています。

 

反対回りしたら、180°を超えるということは、「右回り」の方が近い!

ということです。

そこで、現在向いている方向の意味を「反転」させています。

				if(mychar_anc_rotate > theta){
					// プラス方向に振り切る
					mychar_anc_rotate -= MATH_PI * 2.0
				}else{
					// マイナス方向に振り切る
					mychar_anc_rotate += MATH_PI * 2.0
				}

正直kenmoは文系で頭悪いので、これを理解するのに、

紙に円を何回も書いたり、計算したりで、3時間以上かかってしまいました、、、。

 

うーん、数学力が足りないですね。

 

 

…それはさておき、次です。(622〜626行目)

		// 移動量0
		anchor_sx = 0.0
		anchor_sy = 0.0
		// 自機から40の位置にアンカーを配置
		anchor_x = mychar_x + 40.0 * cos(mychar_anc_rotate)
		anchor_y = mychar_y + 40.0 * sin(mychar_anc_rotate)
		// プレイヤーと同じ方向を見る
		anchor_rotate = mychar_anc_rotate

そのままですね〜。

 

最後にアンカーの発射処理です。

		// 射出
		if(((stick_state | key_state) & 16) == 16 & ((ostick_state | okey_state) & 16) == 0){
			anchor_state = ANCHOR_STATE_THROW
			// プレイヤーの向いている方向に飛ばす
			anchor_sx = ANCHOR_SHOT_SP * cos(mychar_anc_rotate)
			anchor_sy = ANCHOR_SHOT_SP * sin(mychar_anc_rotate)
			// プレイヤーに反動をつける
			mychar_sx = -anchor_sx * ANCHOR_REACT_FORCE
			mychar_sy = -anchor_sy * ANCHOR_REACT_FORCE

			ds_play SND_ANCHOR_SHOT
			return
		}

これもそのままですー。

 

 

 

発射

発射処理を見ていきます。(641〜666行目)

 

643〜644行目は、移動量を座標に反映しています。

		anchor_x += anchor_sx
		anchor_y += anchor_sy

 

646〜656行目では、プレイヤーがアンカーを引っ張る力を計算・反映しています。

		// .▲鵐ーからプレイヤーまでの距離
		vxx = mychar_x - anchor_x
		vyy = mychar_y - anchor_y
		if(vx != 0.0 | vy != 0.0){
			// ▲廛譽ぅ筺爾アンカーを引っ張る力を足しこむ
			anchor_sx += vx * ANCHOR_SHOT_CONTROL
			anchor_sy += vy * ANCHOR_SHOT_CONTROL
			// 正規化
			rr = sqrt(anchor_sx * anchor_sx + anchor_sy * anchor_sy)
			anchor_sx = anchor_sx * ANCHOR_SHOT_SP / rr
			anchor_sy = anchor_sy * ANCHOR_SHOT_SP / rr
			// アンカーの向きを修正
			anchor_rotate = atan(-vyy , -vxx)
		}

,乃離を出しておいて、△如ANCHOR_SHOT_CONTROL」(0.5)を掛けて足しこみ、

正規化して、「ANCHOR_SHOT_SP」(16.0)の速度に設定しています。

 

最後に回収のチェックをします。(658〜664行目)

		// 発射をキャンセルして回収
		if(((stick_state | key_state) & 16) == 0){
			anchor_state = ANCHOR_STATE_RETURN
		}

		// 伸びきった場合、回収
		if(sqrt(vxx * vxx + vyy * vyy) > ANCHOR_LENGTH_LIMIT) : anchor_state = ANCHOR_STATE_RETURN
		// 画面外に出た場合、回収
		if(anchor_x > 640.0 | anchor_x < 0.0) : anchor_state = ANCHOR_STATE_RETURN
		if(anchor_y > 480.0 | anchor_y < 0.0) : anchor_state = ANCHOR_STATE_RETURN

 

 

 

掴み

掴み状態です。

		if(((stick_state | key_state) & 32) == 0){
			// ボタン2を離している場合、普通に動く
			anchor_sx += ANCHOR_GRAB_SP * vx 
			anchor_sy += ANCHOR_GRAB_SP * vy
			anchor_sx *= ANCHOR_GRAB_SPWEAKEN
			anchor_sy *= ANCHOR_GRAB_SPWEAKEN
		}else{
			// ボタン2を押している場合、ゆっくり動く
			anchor_sx += vx * MYCHAR_SPEED2
			anchor_sy += vy * MYCHAR_SPEED2
			anchor_sx *= MYCHAR_SPWEAKEN
			anchor_sy *= MYCHAR_SPWEAKEN
		}

定数はそれぞれ、

  • ANCHOR_GRAB_SP(1.6)
  • ANCHOR_GRAB_SPWEAKEN(0.9)
  • MYCHAR_SPEED2(0.2)
  • MYCHAR_SPWEAKEN(0.5)

と定義されています。

 

ここでも、「移動量の更新」→「減衰」というパターンを使って、

面白い動きを作り出しています。

 

681〜682行目では座標の更新をしています。

		anchor_x += anchor_sx
		anchor_y += anchor_sy

です。

 

684〜696行目では、アンカーの「近づきすぎ」「離れすぎ」のチェックをしています。

		// .▲鵐ーからプレイヤーまでの距離
		vxx = mychar_x - anchor_x
		vyy = mychar_y - anchor_y
		// 正規化
		rr = sqrt(vxx * vxx + vyy * vyy)
		if(rr < 20.0){
			// 近づきすぎなので、自機から「20」離す
			anchor_x = mychar_x - 20.0 * vxx / rr
			anchor_y = mychar_y - 20.0 * vyy / rr
			// 減衰
			anchor_sx *= 0.8
			anchor_sy *= 0.8
		}
		if(rr > ANCHOR_LENGTH_LIMIT){
			// 伸びきったら、それ以上伸びないようにする
			anchor_x = mychar_x - ANCHOR_LENGTH_LIMIT * vxx / rr
			anchor_y = mychar_y - ANCHOR_LENGTH_LIMIT * vyy / rr
		}

 

 

 

回収

718〜799行目が回収の処理です。

		// アンカーからプレイヤーの距離を求める
		vxx = mychar_x - anchor_x
		vyy = mychar_y - anchor_y
		// 正規化
		rr = sqrt(vxx * vxx + vyy * vyy)
		// 速度算出・設定
		anchor_sx = ANCHOR_RETURN_SP * vxx / rr
		anchor_sy = ANCHOR_RETURN_SP * vyy / rr
		// アンカーの方向調整
		anchor_rotate = atan(-vyy , -vxx)
		// 座標の更新
		anchor_x += anchor_sx
		anchor_y += anchor_sy

		if(rr < 32.0) {
			// 回収完了→スタンバイ状態へ
			mychar_sx = anchor_sx * ANCHOR_REACT_FORCE
			mychar_sy = anchor_sy * ANCHOR_REACT_FORCE
			mychar_anc_rotate = atan(-vyy , -vxx)
			anchor_state = ANCHOR_STATE_STANDBY

			ds_play SND_ANCHOR_RETURN
		}

そのまま、、、ですね。

 

 

 

当たり判定

最後に、アンカーの当たり判定の部分を、簡単に説明しておきます。

フローは以下のようになっています。

  1. 敵の配列(enemy)を全部チェック
  2. 当たり判定をし、当たっていたら以下を実行。そうでなければ1に戻る
  3. 発射状態なら掴む。1に戻る
  4. スタンバイ状態なら、はじかれる。1に戻る

大体こんな感じです。

 

 

 

 

とりあえず、きょうはここまでですー。