phina.js が面白そうなのでお勉強中　その４

[phina.js が面白そうなのでお勉強中　その３](https://qiita.com/hirotyankoutei/items/f9483534b511ba07b2c1)

#背景を作るのだ
今のところ、画面が真っ黒なので、背景を作ろうと思う。

## 星が流れる背景にする
宇宙空間ということで、星が沢山流れる背景にします。
黒い背景に、小さい丸を沢山描いて動かす、という感じ。
点滅もできればかっこいいなあ。

## 知識不足が邪魔をする
やりたいことをどうやってやるのか、いつも知識不足が邪魔をします。
今回解らないことを調べてみました。

 ・丸ってどう描くのだ？
CircleShape を使えば描けるっぽい。
ってことは、こいつを継承したクラスを作ればいいかな。
プロパティがいっぱいあってよく解らんが、縁取りの色を設定する stroke はいらないな。いらない時はどうするのだ？
falseを設定すると縁取りがなくなるっぽい。

・色ってどうやって変化させるの？
CircleShape.fill に色を設定すれば色が変わるっぽいけど、明るさを数値で変化させるにはどうすればいいのか？
スタイルシートで使う rgb(255,255,255) 表記を文字列で生成するってのも有りだな。
調べてみたら、Color なんてクラスがあるので、こいつを使ってみるか。
toStyleAsRGB()でrgb(255,255,255) 表記文字列を返してくれるそうなんで、なんとかなりそう。

・星をバラバラに出したい
乱数ってどうするのだ？
と思ったら、Randomクラスがあるっぽい。
Random.randint(min, max)で、最小値～最大値までを整数で返してくれるので直感的に使えそう。便利。

## 背景用のグループを追加する
phina.js はオブジェクトの順番を決めることができないそうなので、明らかに一番下に表示したいであろう背景なんかはグループを作成しておくと良いようです。

```javascript
		// 背景用のグループを作成
		this.backgroungGroup = DisplayElement().addChildTo(this);		
		this.backgroungGroup.width = this.width;
		this.backgroungGroup.height = this.height;
```
グループの大きさがどうやって決まるのかイマイチ謎でした。
グループに配置したオブジェクトの大きさや位置で決まっているみたいだけど。
よく解らないので、生成時に大きさも設定しています。

## Starクラスを作成
星のクラスを作ってみました。
パラメータに、星を表示するオブジェクト（上で生成した背景用グループを渡す想定）と、X座標、Y座標をもらうことにしました。

星の大きさと、流れる速度と、点滅するタイミングをランダムに決めます。
モードを切り替えることにより、通常表示、暗くなる、明るくなる、を切り替えています。

```javascript
// 背景の星クラス
phina.define('Star', {
	// CircleShapeを継承
	superClass: 'CircleShape',
	init: function(disp, x, y) {
		// 親クラスの初期化
		this.superInit({
			stroke: false,
		});
		// 星の半径の設定
		this.radius = Random.randint(1, 2);
		// 色の明るさ設定
		 this.light = 240;
		 // 色を設定
		 this.fill = Color(this.light, this.light, this.light, 1).toStyleAsRGB();
		// 座標の設定
		this.x = x;
		this.y = y;
		// 移動速度
		this.speed = Random.randint(1, 4);
		// 表示領域に表示する
		this.addChildTo(disp);
		
		// フレームカウント
		this.frameCounnt = 1;
		// 点滅タイミング
		this.twinkle = Random.randint(20, 100);
		// モード
		this.mode = 0;	// 0:通常、1:暗く、2:明るく
		
	},
	update: function(app) {
		// 星の点滅処理
		// 星が通常の場合
		if (this.mode == 0) {
			// 点滅タイミングの場合
			if (this.frameCounnt % this.twinkle == 0) {
				// 暗くするモードにする
				this.mode = 1;
				this.frameCounnt = 1;
			} else {
				// フレーム数をインクリメントする
				this.frameCounnt ++;
			}
		
		// 星が暗くなっている場合
		}　else if (this.mode == 1) {
			// 暗くする
			this.light -= 10;
			// 一番暗くなった場合は明るくするモードにする
			if (this.light < 0) {
				this.mode = 2;
				this.light = 0;
			}
		
		// 星が明るくなっている場合
		} else {
			// 明るくする
			this.light += 10;
			// 一番明るくなった場合は通常モードにする
			if (this.light > 240) {
				this.mode = 0;
				this.light = 240;
			}
		}
		
		// 星の色を変える
		this.fill = Color(this.light, this.light, this.light, 1).toStyleAsRGB();
		
		// 移動
		this.y += this.speed;
		
		// 下まで行った場合は消す
		if (this.top > this.parent.height) {
			this.remove();
			delete this;
		}
	},		
 });

```


## Starクラスを呼び出す

MainSceneの初期処理で、50個の星をランダムに表示します。

```javascript
// MainScene クラスを定義
phina.define('MainScene', {
	superClass: 'DisplayScene',
	init: function(option) {
		this.superInit(option);
		// 背景色を指定
		this.backgroundColor = '#000000';
		
		// 背景用のグループを作成
		this.backgroungGroup = DisplayElement().addChildTo(this);		
		this.backgroungGroup.width = this.width;
		this.backgroungGroup.height = this.height;
		
		// 星を生成
		(50).times(function() {
			Star(this.backgroungGroup, Random.randint(0, this.width), Random.randint(0, this.height));
		}, this);
```

MainSceneのupdateで、1/3の確率で星を生成します。
で、X座標をランダム、Y座標を0の位置にしています。

```javascript
	update: function () {
		// ランダムで星を生成する
		if (Random.randint(1, 3) == 1) {
			Star(this.backgroungGroup, Random.randint(0, this.width), 0);
		}
	},
```


## 今日の成果
今日の成果をここに上げました。
http://hirotyan.my.coocan.jp/phinajs/Shooting/003/index.html

なんか雪が降っている感じでもある・・・。

```javascript
// phina.js をグローバル領域に展開
phina.globalize();
// 画面サイズ
var SC_WIDTH = 320;
var SC_HEIGHT = 480;

// アセット
var ASSETS = {
	// 画像
	image: {
		'ship': 'ship.gif',
	},
	sound: {
		'shoot': 'shoot.wav',
	},
};

// MainScene クラスを定義
phina.define('MainScene', {
	superClass: 'DisplayScene',
	init: function(option) {
		this.superInit(option);
		// 背景色を指定
		this.backgroundColor = '#000000';
		
		// 背景用のグループを作成
		this.backgroungGroup = DisplayElement().addChildTo(this);		
		this.backgroungGroup.width = this.width;
		this.backgroungGroup.height = this.height;
		
		// 星を生成
		(50).times(function() {
			Star(this.backgroungGroup, Random.randint(0, this.width), Random.randint(0, this.height));
		}, this);
		
		// スプライトを作成
		var ship = Ship().addChildTo(this);
		// 初期位置
		ship.x = this.gridX.center();
		ship.y = this.gridY.center(5);		
		
		
	},
	update: function () {
		// ランダムで星を生成する
		if (Random.randint(1, 3) == 1) {
			Star(this.backgroungGroup, Random.randint(0, this.width), 0);
		}
	},
});

// 自機クラス
phina.define('Ship', {
	// Spriteを継承
	superClass: 'Sprite',
	// 初期化
	init: function() {
		// 親クラスの初期化
		this.superInit("ship", 32, 32);
		// 画像フレームの初期値
		this.frameIndex = 1;
		
		// 自機の速度
		this.speed = 8;
		// 連打のフラグ
		this.triggerFlag = false;
		// 弾を撃ってからのフレーム数
		this.trigerFrameCount = 0;
	},
	update: function(app) {
		var key = app.keyboard;
		// 上下左右移動
		// 画像フレームを一旦初期化する
		this.frameIndex = 1;
		if (key.getKey('left')) {	
			this.x -= this.speed;
			// 画像フレームを左移動に変更
			this.frameIndex = 0;
		}
		if (key.getKey('right')) {
			this.x += this.speed;
			// 画像フレームを右移動に変更
			this.frameIndex = 2;
		}
		if (key.getKey('up')) {
			this.y -= this.speed;
		}
		if (key.getKey('down')) {
			this.y += this.speed;
		}
		// 端まで行った場合は動かさない
		if (this.left <  0) {
			this.left = 0;
		} else if (this.right > this.parent.width) {
			this.right = this.parent.width;
		}
		if (this.top < 0) {
			this.top = 0;
		} else if (this.bottom > this.parent.height) {
			this.bottom = this.parent.height;
		}
		
		// 弾発射
		if (key.getKey('z')) {
			// 8フレームに１回、または連打した場合は自弾を発射する
			if (this.trigerFrameCount % 8 == 0 || !this.triggerFlag) {
				// 自弾を生成
				var myBullet = MyBullet(this.x, this.y).addChildTo(this.parent);
				// 発射音を鳴らす
				SoundManager.play('shoot');
				// 連打のフラグを立てる
				this.triggerFlag = true;
			}
			// 弾のフレーム数をインクリメントする
			this.trigerFrameCount++;			
		} else {
			// 連打のフラグを寝かせる
			this.triggerFlag = false;
			// 弾のフレーム数を0にする
			this.trigerFrameCount = 0;
		}
	},		
 });

// 自弾クラス
phina.define('MyBullet', {
	// Spriteを継承
	superClass: 'Sprite',
	init: function(x, y) {
		// 親クラスの初期化
		this.superInit("ship", 16, 16);
		// 座標の設定
		this.x = x;
		this.y = y;
		// 弾の速度
		this.speed = 16;
		// 画像から弾のフレームだけを設定
		this.frameIndex = 6;
	},
	update: function(app) {
		// 弾の移動
		this.y -= this.speed;
		
		// 弾が上まで行った場合は消す
		if (this.bottom < 0) {
			this.remove();
			delete this;
		}
	},		
 });

// 背景の星クラス
phina.define('Star', {
	// CircleShapeを継承
	superClass: 'CircleShape',
	init: function(disp, x, y) {
		// 親クラスの初期化
		this.superInit({
			stroke: false,
		});
		// 星の半径の設定
		this.radius = Random.randint(1, 2);
		// 色の明るさ設定
		 this.light = 240;
		 // 色を設定
		 this.fill = Color(this.light, this.light, this.light, 1).toStyleAsRGB();
		// 座標の設定
		this.x = x;
		this.y = y;
		// 移動速度
		this.speed = Random.randint(1, 4);
		// 表示領域に表示する
		this.addChildTo(disp);
		
		// フレームカウント
		this.frameCounnt = 1;
		// 点滅タイミング
		this.twinkle = Random.randint(20, 100);
		// モード
		this.mode = 0;	// 0:通常、1:暗く、2:明るく
		
	},
	update: function(app) {
		// 星の点滅処理
		// 星が通常の場合
		if (this.mode == 0) {
			// 点滅タイミングの場合
			if (this.frameCounnt % this.twinkle == 0) {
				// 暗くするモードにする
				this.mode = 1;
				this.frameCounnt = 1;
			} else {
				// フレーム数をインクリメントする
				this.frameCounnt ++;
			}
		
		// 星が暗くなっている場合
		}　else if (this.mode == 1) {
			// 暗くする
			this.light -= 10;
			// 一番暗くなった場合は明るくするモードにする
			if (this.light < 0) {
				this.mode = 2;
				this.light = 0;
			}
		
		// 星が明るくなっている場合
		} else {
			// 明るくする
			this.light += 10;
			// 一番明るくなった場合は通常モードにする
			if (this.light > 240) {
				this.mode = 0;
				this.light = 240;
			}
		}
		
		// 星の色を変える
		this.fill = Color(this.light, this.light, this.light, 1).toStyleAsRGB();
		
		// 移動
		this.y += this.speed;
		
		// 下まで行った場合は消す
		if (this.top > this.parent.height) {
			this.remove();
			delete this;
		}
	},		
 });



// メイン処理
phina.main(function() {
	// アプリケーション生成
	var app = GameApp({
		startLabel: 'main', // メインシーンから開始する
		// アセット読み込み
		assets: ASSETS,
		width: SC_WIDTH,
		height: SC_HEIGHT,
	});
	// アプリケーション実行
	app.run();
});
```

[phina.js が面白そうなのでお勉強中　その５](https://qiita.com/hirotyankoutei/items/b3f22981a43c2d7604d2)