日記2010年10月その1
にあるゲームを実装中。
http://www.geocities.jp/sinapusu2002/2dTanklGame/canvasTest.htm
で実際に動くコードを確認可能。
現在、マウスポインターを狙って弾を撃ってくる大砲を実装。
それを確認できる。



var KeyBuffer;//キー入力バッファ
var e_manage=new Array()//マップ上にいる敵データを管理する配列
var myUnit=CreateMyUnit(100,100) //自機の管理,当面ダミーデータ
var x_position=new Array()//敵味方の大雑把な位置を表現する
var eBullet_manage=new Array();//敵の弾を管理する配列
var myBullet_manage=new Array();//自機の弾を管理する配列。
var map;//画面マップ
var mapHeightDates=new Array()//地面の凸凹を現す変数データ、地面は1次元の折れ線として表現される、当たり判定がめんどくさくなる理由の王様;
var g=4.9//重力の値、必要に応じて変更予定
var baseClock=0.1;//弾の移動や敵の移動などの元となる1fpsでbasicClock時間が進む。

var mapHeight=600;
var mapWidth=1000;
var gd;



function setPict(bp){
BulletPict= bp;
}



function eCannon(x,y,hp,size,timer,rndTimer,BulletSpeed,debug,img) {

this.x = x;
this.y = y;
this.nowX=x;
this.nowY=y;
this.hp= hp;//ヒットポイント
//this.bmp = new Image("e_CannonBody.jpg");//キャノン本体の画像
//this.Cannon = new Image("e_Cannon.jpg");キャノンの砲身の画像
this.AttackTimer= timer;//攻撃間隔
this.rndTimer = rndTimer;//攻撃間隔のランダムな揺らぎの秒数、fps単位
this.BulletSpeed=BulletSpeed;//弾速
this.state=0;//0なら稼働中、1なら爆発エフェクト中、2なら大砲を発射後の反動アニメーション
this.round=0;//大砲の砲身の角度を表す変数
this.nextAttackTimer=50;//fps計算一回ごとに1引かれる変数、これが0以下になると弾発射処理が行われ、nextAttackTimerは再セットされる。
this.size=size//大砲のサイズ/大きいのや小さいのや色々作りたくなる変数
this.no=0;

this.img=img;//弾のエフェクト
this.img.width=size;
this.img.height=size;

this.attack = function(debug)
{
//放物線を描いて飛ぶ大砲の弾。これを発射しeBullet_manageに弾を登録するメソッド;
//弾の移動は全てeBullet_manageから取り出しmoveメソッドで行う。jscriptではスタックに複数の型を混在させることが出来たか記憶があいまい、混在出来るいい意味でいい加減な言語だったと思うけど?
// 主人公機と自機のX,Y座標の差分にあわせて、大砲の発射角が変更される
this.nextAttackTimer--;
if(this.nextAttackTimer<0)
{
var x1=myUnit.x-this.x;
var y1=myUnit.y-this.y;
//var cb=new CannonBullet(0,0,0,0);//ダミーデータ
//cb.img.onload = function()
//{

if(Math.abs(x1)<1){
	if(x1>0){
	x1=1;//真上にはうてないということで
	}else{
	x1=-1;
	}
}

var k=0.5*g*x1*x1/(this.BulletSpeed*this.BulletSpeed);
var d=x1*x1-4*k*(k+y1);

//弾が届く位置に敵がいたらd>0となるので発射
	if(d>0)
	{
		var s=(x1+Math.sqrt(d))/(2*k);
		var t=Math.sqrt(1+s*s);
		var direction=1;//弾を発射する向き1なら右向きに発射、-1なら左向きに発射
			if(Math.abs(x1)>x1)
			{
			direction=-1;
			}
			
		//debug.alert(t);
		//debug.alert("P100");
		
		//cb.setDates(this.x,this.y,this.x,this.y,direction*this.BulletSpeed/t,this.BulletSpeed*s/t);
		var cb=new CannonBullet(this.x,this.y,direction*this.BulletSpeed/t,direction*this.BulletSpeed*s/t);
		registerBallet(cb);
		//debug.alert("P101");
	}
	this.nextAttackTimer=this.AttackTimer+Math.random()*this.rndTimer;
}
//}
}




this.raunchingR_Set=function()
{
//大砲の砲身の傾きをセットする関数、十数FPSに一回程度の稼動で問題なし
}

}




function ground_dates(g_HeightDates,MapSplitSize){
this.g_HeightDates	=g_HeightDates;//地面の折れ線データ
this.cosDates		=new Array()//地面の傾きに対するCosの値
this.roundDates		=new Array();//地面の傾き角度、戦車を地面に設置させるのに必須
this.MapSplitSize	=MapSplitSize;//折れ線データの横幅、一定値
this.pointTypes		=new Array();//頂点データ、地面が凸形状か凹形状かを指し示す指標データ
this.a				=new Array();//折れ線の各線の傾きをあらわす関数

var c;
var t;
var s;
var d=MapSplitSize;//計算式を読みやすく
for(i=0;i<g_HeightDates.length-1;i++){
	t=g_HeightDates[i+1]-g_HeightDates[i];
	c=d/Math.sqrt(d*d+t*t);
	this.cosDates.push(c);//地面の傾きに対するcosの値を格納する
	//地面の傾き角を求める
	s=t/Math.sqrt(d*d+t*t)
	this.roundDates.push(Math.asin(s)*180/Math.PI);//傾きデータ
	this.a.push(s/c);
}

for(i=0;i<g_HeightDates.length-2;i++){
	t=(g_HeightDates[i+2]+g_HeightDates[i])/2-g_HeightDates[i+1];//結果が0以上なら凹地形、0以下なら凸地形
	this.pointTypes.push(t);
};



this.groundMove=function(x,y,speed){
	//地面の上を移動するとき、移動物体の次のx位置を決める関数,このゲームではxが決まればyは自動的にもとまるのでxのみを返す
	//位置エネルギーと運動エネルギーの等価と折れ線の境界線を越える場合の処理を考えて処理を記述する。
}





this.putGroundLine=function(ctx){
//地面の凸凹を折れ線で視覚化。
	ctx.beginPath();
	var d=this.MapSplitSize;
	ctx.moveTo(0,mapHeight-g_HeightDates[0]);
	for(i=0;i<g_HeightDates.length;i++){
	ctx.lineTo(d*i+1,mapHeight-this.g_HeightDates[i]);
	//ctx.lineTo(d,mapHeight-this.g_HeightDates[i]));
	}
	ctx.stroke();
}




this.groundHitCheck=function(x,y,nextX,nextY){
	//地面との当たり判定。
	//折れ線地面との当たり判定を行う
	//何かバグがあったら怖いので、ここはついでの判定
	if(nextX<=0 || mapWidth<=nextX){
		return true;
	}
	
	if(nextY<=0){
		return true;
	}
	
	var nXF;
	var XF;
	nXF=Math.floor(nextX/this.MapSplitSize);
	XF=Math.floor(x/this.MapSplitSize)
	
	if(nXF==XF){
	//折れ線の線分のみの場合
		var t1=this.a[XF]*(nextX-XF*this.MapSplitSize)+this.g_HeightDates[XF]-nextY;
		if(t1>=0){
		return true;
		}else{
		return false;
		}
	}else{
		//折れ線の凸か凹みを
		var s=Math.min(nXF,XF);
		var b=Math.max(nXF,XF);
		
			if(this.pointTypes[s]>0){
				//凹地形を間にはさんでいる場合
				//点が2本どちらかの折れ線の下にあるかの判別式の和集合
				var t1=this.a[s]*(nextX-s*this.MapSplitSize)+this.g_HeightDates[s]-nextY;
				var t2=this.a[b]*(nextX-b*this.MapSplitSize)+this.g_HeightDates[b]-nextY
				if(t1>=0 || t2>=0){
					return true;
				}else{
					return false;
				}
			}else{
				//凸地形の場合の判別式に限り、中間点での当たり判定を求める必要あり、中間点の数は後日修正予定。
				//凸地形を間に挟んでいる場合
				//点が2本の折れ線の下にあるかの判別式の共通集合
				var t1=this.a[s]*(nextX-s*this.MapSplitSize)+this.g_HeightDates[s]-nextY;
				var t2=this.a[b]*(nextX-b*this.MapSplitSize)+this.g_HeightDates[b]-nextY
				if(t1>=0 && t2>=0){
					return true;
				}else{
					return false;
				}
			}
			
	}
	
	return false;
}
}





function registerBallet(bc){
//弾を管理する配列に弾を登録する
bc.no=eBullet_manage.length;
eBullet_manage.push(bc);
}

function delBallet(delNo){
//弾を管理する配列から弾を削除する
//この関数を呼び出す場合、呼び出し元のループを-1する必要がある
//弾が一個のときこの関数はどうなるか?
if(eBullet_manage.length>1){
   eBullet_manage[delNo]=eBullet_manage.pop();
   eBullet_manage[delNo].no=delNo;
}else{
  	   var a=eBullet_manage.pop();
}

}








function CreateMyUnit(x,y){
this.x=x;
this.y=y;
}


function CannonBullet(x,y,xSpeed,ySpeed){
this.x=x;//弾のスタート位置
this.y=y;
this.nowX=x;//弾の今の位置
this.nowY=y;
this.xSpeed=xSpeed;
this.ySpeed=ySpeed;
this.r=10//弾の当たり判定、空中でこの範囲内に主人公機がいれば当たる
this.state=0//大砲の弾の状態、0なら移動中、1なら着弾し爆発エフェクト
this.explosionTimer=30;//爆発の持続時間 nFPS
this.explosionSize=20;//爆発時のサイズ,ゲーバラとあわせてよく考える必要あり
this.nextX=x;//弾の移動後の位置
this.nextY=y;//弾の移動後の位置
this.time=0;//弾が発射されてからの秒数、1fpsごとにbasicClockだけ増加する。
this.no=0;
this.img=BulletPict;
this.img.width=10;
this.img.height=10;
this.imgSize=10;
this.img2=BulletExplosion;//爆発画像

this.setDates=function(x,y,speedX,speedY){
this.nextX=x;//弾の移動後の位置
this.nextY=y;
this.x=x;//弾のスタート位置
this.y=y;
this.nowX=x;//弾の今の位置
this.nowY=y;
this.xSpeed=xSpeed;
this.ySpeed=ySpeed; 
}

this.move=function (debug)
{
//弾の移動state=0なら移動処理;
//state=1なら爆発アニメ,explosionTimeを1減らし、これが0以下になるまでは爆発効果
//計算により弾が地面とぶつかることが判明した場合、state=1に変更、弾を爆発させる。
//debug.alert("a");
if(this.state==1)
{
	//debug.alert("b");
	this.explosionTimer--;
	if(this.explosionTimer<0)
	{
	// eBullet_manageからこの弾を削除する関数を呼び出す
	delBallet(this.no);
	//return false;//弾を削除したのでそのことを呼び出し元に伝える
	}
}else
{
	//debug.alert("c");
	//弾の移動処理
   		this.time+=baseClock;
   		var t=this.time;
   		this.nowX=this.nextX;
   		this.nowY=this.nextY;
   		this.nextX=t*this.xSpeed+this.x;
   		this.nextY=t*this.ySpeed-0.5*g*t*t+this.y;	

	if(gd.groundHitCheck(this.nowX,this.nowY-this.r,this.nextX,this.nextY)==true)
	{
	this.state=1;
	this.img=this.img2;//爆発画像
	this.r=this.explosionSize;
	}
}
//return true;
}
}






function Ground_hit_check(x,y,nextX,nextY){
if(mapWidth<=nextX || nextX<=0){
	return true;
}
if(nextY<=0){
	return true;
}
return false;
}





















大砲テスト用ページ。
http://1ka2ka.com/archives/201006/09_203630.htmlを参考に作成。
大砲がマウスポインターを狙って定期的に玉を発射したならお慰み。

DHTML+JScript5.5で実装。
固定画面に次々と現れる雑魚敵を倒す超古典ゲームになる予定。


<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="ja" lang="ja">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=EUC-JP" />
<meta http-equiv="Content-Language" content="ja" />
<meta http-equiv="Content-Style-Type" content="text/css" />
<meta http-equiv="Content-Script-Type" content="text/javascript" />
<title>Canvas</title>
<!--[if IE]><script type="text/javascript" src="excanvas.js"></script><![endif]-->









<script type="text/javascript" src="2dGameTest2.js">
</script>
<script type="text/javascript">

var cW=1000;
var cH=600;
var mouseX;
var mouseY;

var canvas;
var ctx;
window.alert("test");
var cannonPict=new Image();
cannonPict.src="hibana2.jpg";//?" + new Date().getTime();
var BulletPict=new Image();
var BulletExplosion=new Image();
BulletExplosion.src="bakuhatu.jpg";
BulletPict.src="hibana2.jpg";
BulletPict.width=5;
BulletPict.height=5;


e_manage.push(new eCannon(100,400.5,30,10,25,5,40,window,cannonPict));
e_manage.push(new eCannon(200,400,10,5,30,15,40,window,cannonPict));
e_manage.push(new eCannon(300,410.5,30,10,25,5,40,window,cannonPict));
e_manage.push(new eCannon(400,400,10,5,30,15,40,window,cannonPict));
e_manage.push(new eCannon(500,510.5,30,10,25,5,40,window,cannonPict));
myUnit=new CreateMyUnit(200,200);
var l=0;
setTimeout("timer()",2000);



function timer(){
canvas = document.getElementById('Map');
ctx = canvas.getContext('2d');
ctx.clearRect(0, 0, cW, cH);

for(i=0;i<e_manage.length;i++){
e_manage[i].attack(window);
}

for(i=0;i<eBullet_manage.length;i++){
ctx.fillText(eBullet_manage[0].nextY,5,72);
ctx.fillText(eBullet_manage[0].nextX,5,84);
eBullet_manage[i].move(window);

}
gd.putGroundLine(ctx);






for(i=0;i<e_manage.length;i++){
ctx.drawImage(e_manage[i].img,e_manage[i].x , cH-e_manage[i].y);

}
for(i=0;i<eBullet_manage.length;i++){
ctx.drawImage(eBullet_manage[i].img,eBullet_manage[i].nextX,cH-eBullet_manage[i].nextY,
eBullet_manage[i].r,eBullet_manage[i].r);
}



l++;





ctx.globalAlpha = 1;
ctx.fillStyle = "#666666";
ctx.fillText("X座標:" + mouseX, 5, 12);
ctx.fillText("Y座標:" + mouseY, 5, 24);
ctx.fillText("test;"+l,5,36)
ctx.fillText("eCount;"+e_manage.length,5,48)
ctx.fillText("eCount;"+eBullet_manage.length,5,60)
setTimeout("timer()",30);




}



window.onload = function() {


draw();
window.alert("test1");

}


function draw(){
canvas = document.getElementById('Map');
canvas2= document.getElementById('MapBack');
window.alert("a01");
mapHeightDates=new Array(200,230,234,277,320,368,339,374,348,357,362,326,317,309,279,322,294,267,267,100,200);
window.alert("a02");
gd=new ground_dates(mapHeightDates,50);
window.alert("a03");

window.alert("a04");
canvas.onmousemove = mouseMoveListner;
function mouseMoveListner(e) {
var rect = e.target.getBoundingClientRect();
mouseX = e.clientX;
mouseY = rect.height+rect.top-e.clientY;
myUnit.x=mouseX;
myUnit.y=mouseY;

}
}
</script>
<style type="text/css">
canvas { border: 1px solid black; }
</style>
</head>
<body onload="draw();">
<canvas id="Map" top="0" left="0" width="1000" height="600">
</canvas>
<canvas id="MapBack" top="0" left="0" width="1000" height="600"></canvas>

<p id="debug" name="debug">avc</p>
</body>
</html>

タグ:

+ タグ編集
  • タグ:

このサイトはreCAPTCHAによって保護されており、Googleの プライバシーポリシー利用規約 が適用されます。

最終更新:2010年10月18日 18:31