NPC登場(衝突判定)

左から、NPCとPC まずは言葉の説明から。

NPCとは


PC プレイヤーキャラクター
プレイヤーが操作しているキャラクターのこと。
人物に限らず、車やロボットなども含む。
NPC ノンプレイヤーキャラクター
プレイヤーが操作していないキャラクターのこと。
例:敵や自分で操作できない仲間、プレイヤーがいない村人など。

要するにPCとは自分のことで、NPCは敵やあるいは見方など自分以外のことです。
いままでPCは登場していましたが、今回はNPCにも登場してもらいます。


ところで、ふと思ったのですが、ネットゲームで「自分以外のプレイヤーが操作しているキャラクター」は、…やっぱりPCって呼んでいいんですよね?


衝突判定とは

 壁やNPCにぶつかったのか、何にもぶつかっていないのかどうか?
これを判定する作業のことです。

 問題は、ぶつかったと分かった後どうするかです。
何もしなければ、壁をすり抜け、敵キャラをすり抜け、降り注ぐ弾丸の雨をすり抜け、…こんな幽霊みたいなキャラクターじゃ ゲームらしくないですね。(だからといってこれでゲームが出来ないかといえばそうではありませんが…でもこれは別の話。ここではやらない。)
やっぱり壁や登場人物にぶつかったらそれ以上は前には進めないのが、それらしい動きです。

 今回はこの、それらしい動きをやってみます。


PCとNPCを同時に動かす


 キャラクターを2つ以上表示するのは、形状データIDをうまく使えばさほど難しいことではありません。
これについてはサンプルをゆっくりながめてみてください。
配列変数など工夫して、各自うまいこと処理してください。


キャラクタどうしの当たり判定

 まず今回はPCがNPCにぶつかった場合だけを考えます。
衝突後の処理を必要とするのは1つのキャラクターだけだと複数の場合より簡単にいけそうです。
※ベクトルの計算を行いますので、ベクトルが分からない方はベクトルについて調べてきてください。

 まずは衝突の判定を行います。衝突判定だけなら結構簡単に出来ます。
衝突判定をする命令もいろいろありますが、今回はE3DChkConflict命令を使用します。

衝突判定関係の命令
E3DChkConfBillboard、E3DChkConfBillboard2、E3DChkConfBySphere、E3DChkConfBySphere2、E3DChkConfGround、E3DChkConflict、E3DChkConflict2、E3DChkConfLineAndFace

 PCとNPCが衝突したら、NPCからPCに向かうベクトルを取得します。

    (NPCからPCに向かうベクトル)=(PC座標ベクトル)−(NPC座標ベクトル)

このベクトルの方向にPCを移動すれば、PCはNPCから離れることが出来ますね。

 次にこのベクトルを、PCを移動する距離と同じ長さにします。
これにはE3DVec3Normalize命令を使用します。
この命令は指定したベクトルを正規化し、multを掛けた値を出力します。
 ベクトルの正規化とは、ある長さを持つベクトルを、向きを維持したまま長さを1に変換することです。
Easy3Dは整数しか扱えないので、方向を維持したまま長さ1のベクトルをあらわすことが出来ません。
そこでE3DVec3Normalize命令では、長さ1のベクトルにmultをかけて長くすることで整数表記でも 方向を維持できるようにされています。
この結果、ベクトルの大きさは、1×multなのでmultになります。

 現在のPC座標に、計算したベクトルを足します。
NPCと衝突したPCは、NPCから距離をとるように動きます。
E3DVec3Normalize命令で指定したmultが、移動量ということになります。

 衝突した後の移動量ですが、これは大きすぎればあまりに不自然だし、小さすぎても効果はあまりありません。マイナスにすると… これはこれで面白いかもしれません。(^ ^;
 移動量が小さいと例えば…勢いよくぶつかれば減速するもののすり抜けることが出来てしまいます。
また、すり抜けないまでも一時的にぶつかったキャラクター同士が重なってしまうことがあります。
これがこの手法の欠点であり、数値の調整が必要な部分です。


サンプル

タコが地上を移動して山を登ったり空を飛んだり出来るサンプルです。
一番近い山を越えるとNPCが見えると思います。
衝突したときに押し戻されるのはPCだけで、NPCはぶつかっても強引に動きつづけます。
操作は、キーボードの上下左右キーで操作します。

タコモデルのファイルtako.sigとタコ用テクスチャファイルtako01.bmpを用意しておいてください。
実行に必要なファイル:e3d006.zip

大きな更新個所のみ赤色で表示しています。細かい変更点は自分で見つけてください。


;
;	NPC登場(衝突判定)
;

#include "e3dhsp.as"

	;/////////////////
	;
	;	初期化
	;
	E3DInit
	dim keybuf, 256		;キー入力
	E3DCreateFont 24,12,400,,,,"MS ゴシック",fontid	;フォントの設定
	fkabe = 1		;壁データとの当たり判定(0:off / 1:on)
	fcolibound = 1	;PCとNPCが衝突したとき重ならないようにする(0:off / 1:on)


	;/////////////////	
	;
	;	カメラの初期化
	;
	E3DSetProjection 100, 50000


	;/////////////////	
	;
	;	ライトの設定
	;
	E3DCreateLight lid1		;光源を作成
	lightdirx1 = 1		;平行光の向き
	lightdiry1 = -1
	lightdirz1 = 0
	lightr1 = 255	;平行光の色
	lightg1 = 255
	lightb1 = 255
	E3DSetDirectionalLight lid1, lightdirx1, lightdiry1, lightdirz1, lightr1, lightg1, lightb1	;光源を平行光源に設定


	;/////////////////	
	;
	;	形状データのロード
	;
	sdim mediadir, 2048
	mediadir = curdir + "\\tako.sig"
	E3DSigLoad mediadir, hsid1
	E3DSetPos hsid1, 5000, 0, 5000
	E3DSetBeforePos hsid1
	;NPC
	E3DSigLoad mediadir, hsid3
	E3DSetPos hsid3, 15000, 0, 25000
	E3DSetBeforePos hsid3


	E3DCreateQ axisqid


	;/////////////////	
	;
	;	地面の作成
	;
	sdim pathbuf, 2048, 4
	pathbuf.0 = curdir + "\\yama.bmp"	;地面の座標情報
	pathbuf.1 = curdir + "\\michi.bmp"	;地面の道の情報
	pathbuf.2 = curdir + "\\kawa.bmp"	;地面の川の情報
	pathbuf.3 = curdir + "\\bazou.bmp"	;地面、道、川の模様を決める、BMPファイル

	;地面作成用の値
	mapsize = 60000		;X,Z座標の最大値
	mapdiv = 120		;座標の分割数
	mapheight = 3000	;高さの最大値
	E3DLoadGroundBMP pathbuf.0, pathbuf.1, pathbuf.2, pathbuf.3, mapsize, mapsize, mapdiv, mapdiv, mapheight, hsid0

	;	壁の作成
	;道のマップをそのまま壁の生成に使います。
	E3DSetMovableArea pathbuf.1, mapsize, mapsize, mapdiv, mapdiv, mapheight+100, hsid2

;////////////////////////////////////////////////////////////////

;/////////////////
;
;	メインループ
;
*main

	E3DGetKeyboardState keybuf	;キー状態取得
	if keybuf.VK_ESCAPE = 1 : goto *bye ; [ESC]で終了

	gosub *MoveChara		;キャラクター移動
	gosub *MoveNPChara

	;バックバッファへの書き込み作業を行う
	E3DBeginScene	;-----シーンスタート
		;モデルが、視野内にあるか判定
		E3DChkInView hsid1	;キャラクター(PC)
		E3DChkInView hsid3	;キャラクター(NPC)
		E3DChkInView hsid0	;地面
		E3DChkInView hsid2	;壁

		gosub *ChkConfAxis	;キャラクター同士の当たり判定
		gosub *ChkConf	;地面、壁との当たり判定(キャラクター同士の判定の後)(押し出し防止)
		gosub *MoveCamera		;カメラ移動

		;バックバッファにレンダリングする。
		E3DRender hsid1, 0
		E3DRender hsid3, 0
		E3DRender hsid0, 0

	E3DEndScene		;-----シーン終了
	E3DPresent		;バックバッファの内容を、プライマリバッファに転送。描画する。

	E3DSetBeforePos hsid1
	E3DSetBeforePos hsid3

	E3DWaitbyFPS 60 : await 0
goto *main

;////////////////////////////////////////////////////////////////
;
;	サブルーチン
;

;/////////////////
;
;	終了処理
;
*bye
	E3DDestroyLight lid1	;ライトを破棄
	E3DBye
	end



;/////////////////
;
;	キャラクター移動
;
;	PC
*MoveChara
	forwardstep = 200	;移動速度(前進)
	backstep = -forwardstep	;移動速度(後退)
	degstep = 5
	mdegstep = -degstep

	if keybuf.VK_UP    = 1 : E3DPosForward hsid1, forwardstep	;矢印上
	if keybuf.VK_DOWN  = 1 : E3DPosForward hsid1, backstep	;矢印下
	if keybuf.VK_LEFT  = 1 : E3DRotateY hsid1, mdegstep	;矢印左
	if keybuf.VK_RIGHT = 1 : E3DRotateY hsid1, degstep	;矢印右
	return

;	NPC
*MoveNPChara
	forwardstep = 50	;移動速度(前進)
;	backstep = -forwardstep	;移動速度(後退)
	degstep = 5
	mdegstep = -degstep

	E3DRotateY hsid3, mdegstep, 3
	E3DPosForward hsid3, forwardstep
	return


;/////////////////
;
;	カメラ移動(キャラクター追跡型)
;
*MoveCamera
	;キャラクターを後ろに2500移動して座標を取得。backpos
	E3DGetPos hsid1, saveposx1, saveposy1, saveposz1
	E3DPosForward hsid1, -2500
	E3DGetPos hsid1, backposx, backposy, backposz
	E3DSetPos hsid1, saveposx1, saveposy1, saveposz1
	;カメラをキャラクタ後方に設置
	E3DSetCameraPos backposx, backposy + 1500, backposz
	E3DSetCameraTarget saveposx1, saveposy1 + 800, saveposz1, 0, 1, 0

	return


;/////////////////
;
;	キャラクタの地面との当たり判定
;
*ChkConf
	;	壁データとキャラクター
	if fkabe ! 0 {
		; 壁(hsid2)とキャラクタ(hsid1)のあたり判定
		resultwall = 0
		E3DChkConfWall hsid1, hsid2, 10, resultwall, adjustx1, adjusty1, adjustz1, nx1, ny1, nz1
		if resultwall!0 {
			E3DDrawTextByFontID fontid, 10,10,"壁に激突しました!", 255,255,255,255	;壁に激突したときのメッセージ。
			E3DSetPos hsid1, adjustx1, adjusty1, adjustz1
		}
	}

	;	地面データとキャラクター
	mapminy = -100	;地面データのY座標の最小値。実際の最小値より、少し小さな値を入れる。
	repeat 2	;キャラ2つ
		if cnt = 0 : hsid_c = hsid1
		if cnt = 1 : hsid_c = hsid3
		E3DChkConfGround hsid_c, hsid0, 1, mapheight, mapminy, result, adjustx, adjusty, adjustz, nx, ny, nz
		if ( result != 0 ) : E3DSetPos hsid_c, adjustx, adjusty, adjustz	;地面の上に移動
	loop

	return


;/////////////////
;
;	キャラクタどうしの当たり判定
;
*ChkConfAxis
	E3DChkConflict hsid1, hsid3, confflag
	if confflag!0 {
		if fcolibound ! 0 {
			;衝突したときにキャラクターが重ならないように移動する
			;PCを衝突したNPCと反対方向に50移動させる
			E3DGetPos hsid1, posx1, posy1, posz1
			E3DGetPos hsid3, posx3, posy3, posz3
			posx = posx1 - posx3	;NCPからPCに向かうベクトル
			posy = posy1 - posy3
			posz = posz1 - posz3
			;ベクトルを正規化(長さ1に)して、長さを50倍する。
			E3DVec3Normalize posx, posy, posz, 50, posx, posy, posz
			posx = posx + posx1	;移動ベクトルに現在位置を加算
			posy = posy + posy1
			posz = posz + posz1
			E3DSetPos hsid1, posx, posy, posz
		}
		E3DDrawTextByFontID fontid, 10,10,"NPCと激突しました!", 255,255,255,255	;激突したときのメッセージ。
	}
	return





- HOME -

GHP(仮)