第8回 javascriptでキャラクターを歩行させる

やっと今回で、キャラクターが本当の意味で歩行させることができる。ただ単純に移動するだけでも結構な量の知識が必要になるんだと実感するだろう。

歩行プログラム

c.html

<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<title>歩行</title>
</head>
<body>

<div id="trim">
<img src="./img/girl.png">
</div>

<script type="text/javascript" src="./c.js"></script>
</body>
</html>

<style>
#trim{
	top:10px;
	left:10px;
	position:absolute;
}
</style>

c.htmlは前回とかわらない。

c.js

var count=0;
var rect=[0,0,48,-32];	//上右下左の順
var margin=[0,0,0,32];	//上右下左の順

//押されたキーによってこの値が1になったり0になったりする
var left=0;
var up=0;
var right=0;
var down=0;

window.onload=loop;	//ページ読み込み時に発動

function ch_move(){

	if(count==4)
	{
		rect[1]=32;
		rect[3]=0;
		margin[3]=0;
		count=0;
	}
	else{
		rect[1]=rect[1]+32;
		rect[3]=rect[3]+32;
		margin[3]=margin[3]-32;
	}
	count++;
	
	//取得画像
	re="rect("+rect[0]+"px,"+rect[1]+"px,"+rect[2]+"px,"+rect[3]+"px)";
	ma=margin[0]+"px "+margin[1]+"px "+margin[2]+"px "+margin[3]+"px";
	document.getElementById("trim").style.clip= re;
	document.getElementById("trim").style.margin=ma;
}

function loop(){
	if(left==1)
	{
		rect[0]=48;
		rect[2]=96;
		margin[0]=-48;
	}
	else if(up==1)
	{
		rect[0]=144;
		rect[2]=192;
		margin[0]=-144;
	}
	else if(right==1)
	{
		rect[0]=96;
		rect[2]=144;
		margin[0]=-98;
	}
	else if(down==1)
	{
		rect[0]=0;
		rect[2]=48;
		margin[0]=0;
	}
	
	//向きの初期化
	re="rect("+rect[0]+"px,"+rect[1]+"px,"+rect[2]+"px,"+rect[3]+"px)";
	ma=margin[0]+"px "+margin[1]+"px "+margin[2]+"px "+margin[3]+"px";
	document.getElementById("trim").style.clip= re;
	document.getElementById("trim").style.margin=ma;
	//座標
	document.getElementById("trim").style.left=x+"px";
	document.getElementById("trim").style.top=y+"px";
}

setInterval("loop()",20);
setInterval("ch_move()",500);


document.onkeydown=kdown;	//キーが押されたらdown関数を実行
document.onkeyup=kup;		//キーが話されたらup関数を実行

var x=10;	//画像のx座標
var y=10;	//画像のy座標
var move=32;	//移動量

function kdown(e)
{

	//押されたキーの判別
	if(e.keyCode==37){ left=1;}
	if(e.keyCode==38){ up=1;}
	if(e.keyCode==39){ right=1;}
	if(e.keyCode==40){ down=1;}
	
	//x軸,y軸の更新
	if(left==1){x=x-move;}
	if(right==1){x=x+move;}
	if(up==1){y=y-move;}
	if(down==1){y=y+move;}

}

function kup(e)
{
	left=0;
	up=0;
	right=0;
	down=0;
}

c.jsは前回に比べて大幅に変更されているので、説明していく。といっても、全く新しいことが出てくるというわけではないので前回の説明が理解できていればそこまで難しいものではない。

javascriptファイルは大きく分けて、3つに分けることができる。一つ目はメインループ、二つ目は歩行ループ、三つめはキー判別だ。

メインループ

var count=0;
var rect=[0,0,48,-32];	//上右下左の順
var margin=[0,0,0,32];	//上右下左の順

//押されたキーによってこの値が1になったり0になったりする
var left=0;
var up=0;
var right=0;
var down=0;

window.onload=loop;	//ページ読み込み時に発動

/*...*/

function loop(){
	if(left==1)
	{
		rect[0]=48;
		rect[2]=96;
		margin[0]=-48;
	}
	else if(up==1)
	{
		rect[0]=144;
		rect[2]=192;
		margin[0]=-144;
	}
	else if(right==1)
	{
		rect[0]=96;
		rect[2]=144;
		margin[0]=-98;
	}
	else if(down==1)
	{
		rect[0]=0;
		rect[2]=48;
		margin[0]=0;
	}
	
	//向きの初期化
	re="rect("+rect[0]+"px,"+rect[1]+"px,"+rect[2]+"px,"+rect[3]+"px)";
	ma=margin[0]+"px "+margin[1]+"px "+margin[2]+"px "+margin[3]+"px";
	document.getElementById("trim").style.clip= re;
	document.getElementById("trim").style.margin=ma;
	//座標
	document.getElementById("trim").style.left=x+"px";
	document.getElementById("trim").style.top=y+"px";
}

setInterval("loop()",20);

まず、HTMLファイルを読み込まれたときにjsファイルも実行されるというwindow.onload=loop;を使っている。ここではloop関数を読み込んでいる。

この時点ではループしていない。初期値がすべて0なので何も表示されない(画像外の部分が表示される)ということになる。

その次にsetIntervalが実行され、メインループへと続いていく。

歩行ループ

function ch_move(){

	if(count==4)
	{
		rect[1]=32;
		rect[3]=0;
		margin[3]=0;
		count=0;
	}
	else{
		rect[1]=rect[1]+32;
		rect[3]=rect[3]+32;
		margin[3]=margin[3]-32;
	}
	count++;
	
	//取得画像
	re="rect("+rect[0]+"px,"+rect[1]+"px,"+rect[2]+"px,"+rect[3]+"px)";
	ma=margin[0]+"px "+margin[1]+"px "+margin[2]+"px "+margin[3]+"px";
	document.getElementById("trim").style.clip= re;
	document.getElementById("trim").style.margin=ma;
}


/*...*/

setInterval("ch_move()",500);

歩行ループはメインループが始まった次の瞬間に実行される。内容的にはかに歩きの応用で、キーフラグの値によって場合分けをして判別している。が、すでにmarginと初期位置はloop関数で確定しているのであとは横にずらすという作業だけでいい。ということはわざわざここでif文で分岐をする必要はないということだ。

なぜメインループと歩行ループを分けているのかと疑問を持つ人もいるだろう。それは、キーの取得スピードと、歩行スピードにずれがあるからだ。例えば、メインと歩行を一つにまとめた関数をsetIntervalで500ミリ秒ごとにループさせたとする。その場合は、歩行スピードはちょうどいいのだが、キーの判別が0.5秒も遅れて反映されてしまう。逆にループを20ミリ秒にすると、即座にキーの反映が可能になるが、歩行スピードが速すぎるということになる。

ここで初めてrectの値が実際の画像の範囲を表すので、画像が表示されることになる。要するに画像が表示されるのはHTMLファイルが読み込まれてから500ミリ秒後となる。


キー判別

document.onkeydown=kdown;	//キーが押されたらdown関数を実行
document.onkeyup=kup;		//キーが話されたらup関数を実行

var x=10;	//画像のx座標
var y=10;	//画像のy座標
var move=32;	//移動量

function kdown(e)
{

	//押されたキーの判別
	if(e.keyCode==37){ left=1;}
	if(e.keyCode==38){ up=1;}
	if(e.keyCode==39){ right=1;}
	if(e.keyCode==40){ down=1;}
	
	//x軸,y軸の更新
	if(left==1){x=x-move;}
	if(right==1){x=x+move;}
	if(up==1){y=y-move;}
	if(down==1){y=y+move;}

}

function kup(e)
{
	left=0;
	up=0;
	right=0;
	down=0;
}

キーを話したときに初期化することで、いちいちメインループで代入することを防ぐ。これは前回とほとんど変わっていない。

最後に

このように複雑な処理を行うにつれて、ソースコードの量が増えてきてしまう。どこを変更したいのかがぱっと見よくわからない。そんなときは、プログラミングしやすいように、ファイルを分けることが有効な手段である。オブジェクト指向のプログラミング言語ならばclass分けがあるが、classの考え方は少し難しいので、このサイトでは使わないことにする。

次は、このプログラムを見やすいようにファイル分けを行う。