# 敵に動きをつけよう(対数関数) _a 今回は敵にあたらしい動きをつける学習を行っていきます。 <img src = "https://i.imgur.com/BWlevXp.gif" width="320px"> 敵が動くようになりましたが、くまを動かすプログラムとは違うので、気を付けてくださいね! 今回は対数関数を利用します。 ## 対数関数とは aを1でない正の数とするとき、xを正の数として、$$ y=\log_a x $$で定められる関数のことです。 対数は以下のように定義できます ![](https://i.imgur.com/wb3gZ70.png) 例えば以下の問を解いてみます <b>$log_28$ の値を求めよ。</b> log という記号の意味を考えると、この問題は「2 を何乗すると 8 になりますか?」と、問われていることになります。 すなわち、$2^3=8$なので、 $log_28=3$ となります。 また$y=\log_2x$の表は以下のようになります。 ![](https://i.imgur.com/ChZHVMj.png) $y=\log_a x のグラフはaの値にかかわらず(1,0)を取ります$ ![](https://i.imgur.com/eyPVumV.png) ## 敵の動きに組み入れる 今回は一つの動きを作っていきます 今回の`this.my = this.log_graph(this.mx);`は対数関数を設定するためのプログラムと思ってください。 <details><summary><B><font color="blue">今回のプログラムの初期状態は以下のようになっています</font></B></summary> ```javascript= enchant(); var game; //ゲームの宣言 var bear; //くまの宣言 var bullet; //弾の宣言 var bulletList = null; var enemyList = null; var ENEMY_CREATE_INTERVAL = 15; var context; //コントラストの宣言 var line; //線の宣言 var surface; //サーフェスの宣言 //Array 拡張 Array.prototype.erase = function(elm){ //渡されたelmが配列の何番目かを保存 var index = this.indexOf(elm); //渡されたelmから一つの要素を削除して前に詰める == elmを削除する this.splice(index,1); //削除された配列を返す return this; }; window.onload = function() { game = new Game(320, 320); //ゲームサイズ game.preload('images/chara1.png', 'images/clear.png','images/bullet.png','images/enemy.png', 'images/masu_関数.png'); //画像の読み込み bulletList = []; //弾のリスト enemyList = []; //敵のリスト game.onload = function() { //背景画像を設定 var bg = new Sprite(320, 320); bg.image = game.assets["images/masu_関数.png"]; //背景画像が直交座標 game.rootScene.addChild(bg); /* くまをつくる */ bear = new Bear(-7.5, 7.5); //くまの座標 //線を作る line = new Sprite(320, 320); //Surfaceを作る surface = new Surface(320, 320); //spriteのimageにsurfaceを代入します line.image = surface; //コンテキストを取得します context = surface.context; //シーンにサーフェスを追加する ' game.rootScene.addChild(line); life = new LifeLabel(10, 10, 3); life.life = 3; game.rootScene.addChild(life); }; //更新処理 game.rootScene.onenterframe = function(){ //敵の出現するタイミング if(game.frame % ENEMY_CREATE_INTERVAL == 0){ //敵を生成 enemy = new Enemy(8); //enemyListに敵を追加 enemyList.push(enemy); } } game.start(); }; /* 独自クラスBear(くま)の定義 */ Bear = Class.create(Sprite, { initialize: function(x, y) { Sprite.call(this, 32, 32); //くまの画像設定 this.image = game.assets['images/chara1.png']; this.mx = x; //くまの座標(x座標) this.my = y; //くまの座標(y座標) this.x = from_mat_to_enc_x(this.mx);//座標変換 this.y = from_mat_to_enc_y(this.my);//座標変換 this.frame = 0; //くまの画像設定 this.mutekiflag = false; //くまが無敵になる(off状態) this.mutekitime = 0; //くまの無敵時間 /* [space]キーをbボタンとして割り当てる */ game.keybind(32, "space"); // space game.rootScene.addChild(this); }, /* enterframeイベントのリスナーを設定 */ //画面更新のたびに実行する処理 onenterframe: function() { /* 移動処理 */ if(game.input.up){ //もし[↑]キーを押したならば if(this.my <= 8) { //もしくまのy座標が8以下ならば this.my = this.my + 1 / 5; //くまのy座標を +(1 / 5) する } } if(game.input.down){ //もし[↓]キーを押したならば if(this.my >= -6.5) { //もしくまのy座標が-6.5以上ならば this.my = this.my - 1 / 5; //くまのy座標を -(1 / 5) する } } this.x = from_mat_to_enc_x(this.mx);//座標変換 this.y = from_mat_to_enc_y(this.my);//座標変換 /* 弾発射処理 */ if(game.input.space){ //もし[space]キーを押したならば bullet = new Bullet(this.mx + 1,this.my - 1 / 2); //bulletListに今作った弾を追加 bulletList.push(bullet); } /* 敵とくまの衝突判定 */ var enemys; //敵宣言 for(var i = 0, len = enemyList.length; i < len; i = i + 1){ //条件までループ enemys = enemyList[i]; //敵とくまとの衝突判定 if(enemys.within(this) && this.mutekiflag == false){ //弾を消去 enemys.destroy = true; //敵を消す(on状態) this.mutekiflag = true; ////くまが無敵になる(on状態) life.life = life.life - 1; //ライフが1つ減る if(life.life <= 0){ //もしライフが0以下なら game.rootScene.removeChild(this); //くまが消える game.end(); //ゲーム終了 } } } if(game.frame % game.fps % game.fps == 0){ this.mutekitime = this.mutekitime + 1; if(this.mutekitime >= 3){ //もし無敵時間が3秒以上なら this.mutekiflag = false; //くまが無敵になる(off状態) this.mutekitime = 0; //無敵時間が0秒になる } } if(this.mutekiflag){ //もしくまが無敵中なら this.opacity = 0.3; //くまの透明度(ちょい薄) } else{ //そうでないなら this.opacity = 1.0; //くまの透明度(はっきり) } }, }); //弾クラス Bullet = Class.create(Sprite , { //初期化処理 initialize:function(x, y){ Sprite.call(this,16,16); //弾画像の登録 this.image = game.assets["images/bullet.png"];//弾の画像 //初期位置設定 this.mx = x; //弾の座標(x座標) this.my = y; //弾の座標(y座標) this.x = from_mat_to_enc_x(this.mx);//座標変換 this.y = from_mat_to_enc_y(this.my);//座標変換 //何かに当たった時にtrueにする this.destroy = false; //弾を消す(off状態) game.rootScene.addChild(this); }, //更新処理 onenterframe : function(){ //弾の速さ this.mx = this.mx + 3 / 4; //ちょうどいい速度 this.x = from_mat_to_enc_x(this.mx); //座標変換 //削除処理 if(this.mx > 9 || this.destroy == true){ //親要素から自分を除外する this.parentNode.removeChild(this); //bulletListから削除する bulletList.erase(this); } } }); //敵クラス Enemy = Class.create(Sprite, { //初期化処理 initialize:function(x){ Sprite.call(this, 32, 32); //敵画像の登録 this.image = game.assets["images/enemy.png"];//敵の画像 this.frame = 0; //何かに当たった時にtrueにする this.destroy = false; //敵を消す(off状態) //初期位置設定 this.mx = x; //敵の座標(x座標) //y座標設定 this.my = this.log_graph(this.mx); //1次関数 this.x = from_mat_to_enc_x(this.mx);//座標変換 this.y = from_mat_to_enc_y(this.my);//座標変換 this.ox = this.mx; this.oy = this.my; game.rootScene.addChild(this); }, //更新処理 onenterframe : function(){ //x座標設定 this.mx = this.mx - (1 / 10); //敵の座標(x座標) //y座標設定 this.my = this.log_graph(this.mx); //対数関数 this.x = from_mat_to_enc_x(this.mx);//座標変換 this.y = from_mat_to_enc_y(this.my);//座標変換 //線を描画する //Spriteを作ります //線の描画を始める context.beginPath(); //パスを開始 context.moveTo(from_mat_to_enc_x(this.ox) - 1, from_mat_to_enc_y(this.oy) - 1); context.lineTo(from_mat_to_enc_x(this.mx) - 1, from_mat_to_enc_y(this.my) - 1); context.closePath(); //パスを終了 context.stroke(); //パスを描画する //1つ前の座標を保存する this.ox = this.mx; this.oy = this.my; //削除処理 if(this.mx < -9 || this.destroy == true){ //親要素から自分を除外する this.parentNode.removeChild(this); //enemyListから削除 enemyList.erase(this); } //全ての弾と敵との衝突判定 var bullets; //弾の宣言 for(var i = 0, len = bulletList.length; i < len; i = i + 1){ //条件までループ bullets = bulletList[i]; //敵と弾との衝突判定 if(bullets.within(this)){ //もし衝突したならば //弾を消去 bullets.destroy = true; //弾を消す(on状態) this.destroy = true; //敵を消す(on状態) } } }, log_graph : function(x){ var y = x; //ここを変更 return y; }, }); //x座標の値を直交座標系からenchantの座標系に変換 from_mat_to_enc_x = function(mat_x) { var enc_x = mat_x * 20 + 320 / 2; if(isNaN(enc_x)){ enc_x = 330; } return enc_x; }; //y座標の値を直交座標系からenchantの座標系に変換 from_mat_to_enc_y = function(mat_y) { var enc_y = 320 / 2 - mat_y * 20; if(isNaN(enc_y)){ enc_y = 330; } return enc_y; }; //log対数に変換 Log = function(a,x) { //a:定数 x:変数 return Math.log(x)/Math.log(a); } ``` </details> 今回のプログラムではクマの移動処理や敵に当たったときの処理がすでに記述されています。今回はクラスEnemyの移動処理を変更していきましょう。 ` this.my = this.log_graph(this.mx); `の` log_graph `部分の中身はこのようになっており、敵の動きはここから変更することができます。 257行目からのコードを変更しましょう ``` //対数関数でグラフを作る log_graph : function(x){ var y = x;//ここを変更 return y; }, ``` ではここに対数関数$y=\log_a x$の式をプログラムに追加していきましょう。 今回は独自関数<b>Log</b>を使います、この関数は以下のように使えます。 ``` 整数 = Log(低,x座標); ``` この時低は一定の数(定数)にしなければならないことに注意してください。 変数xは入れないようにしましょう。 例えば,$y=\log_2 x$の時、関数log_graphは以下のようになります。 ``` log_graph : function(x){ var y = Log(2,x); //Log(低,x座標) return y; }, ``` これで教材タイトルのような曲線が描けるようになっているはずです。 また、$y=2\log_2 2x-2$のときは以下のようにしましょう ``` log_graph : function(x){ var y = 2*Log(2,2*x)-2; //Log(低,x座標) return y; }, ``` ## 練習 <font color="red">それでは下の問題(1)~(6)に関して、各グラフごとに(a)のグラフと比較してどのような変化があるのかを確認してみましょう</font> もし時間内に終了した場合は繰り返し理解できるまで問題を解いてみましょう (a)$y=\log_2 x$ <img src = "https://i.imgur.com/BWlevXp.gif" width="320px"> (1)$y=\log_2 (x+2)$ (2)$y=\log_2 (x-3)$ (3)$y=(\log_2x)+3$ (4)$y=(\log_2x)-2$ (5)$y=\log_2 (-x)$ (6)$y=\log_2 2x$ (7)$y=\log_2{1 \over 2}x$ (8)$y=2\log_2x$ (9)$y={1 \over 2}\log_2x$ (10)$y=-\log_2 x$ (11)$y=\log_{1\over 2} x$