アマゾンバナーリンク

弾幕シューティングゲーム制作その11 スコアの表示

2020年2月25日

前回に背景のスクロールとエフェクト処理を追加して、だいぶゲームらしくなってきました。今回はスコア表示をさせてみます。

スコア用画像の読み込み

いつも通りですが、画像を読み込みましょう。

public uint[] Color = new uint[8];
public int[][] ImageNum = new int[2][];
public int[][] ImageItem = new int[6][];
public int ImageStar { set; get; } 
public int ImageStar { set; get; }  
public int PlayerNum { set; get; } //プレイヤー残機数           
public int Power { set; get; }  //パワー
public int Point { set; get; }  //ポイント
public int Score { set; get; }  //スコア
public int HighScore { set; get; }  //ハイスコア
public int Graze { set; get; }    //グレイズ
public int Money { set; get; }    //お金
public void Initalize() 
{
    PlayerNum = 2;
    Power = 0;
    Point = 0;
    Score = 0;
    
    Color[0] = DX.GetColor(255, 255, 255);//白
    Color[1] = DX.GetColor(0, 0, 0);//黒
    Color[2] = DX.GetColor(255, 0, 0);//赤
    Color[3] = DX.GetColor(0, 255, 0);//緑
    Color[4] = DX.GetColor(0, 0, 255);//青
    Color[5] = DX.GetColor(255, 255, 0);//黄色
    Color[6] = DX.GetColor(0, 255, 255);//青緑
    Color[7] = DX.GetColor(255, 0, 255);//紫
    ImageNum[0] = new int[10];
    ImageNum[1] = new int[10];
    // 数字画像を読み込む
    for (int i = 0; i < ImageNum.Length; ++i)
    {
        if(DX.LoadDivGraph($"dat/img/num/{i+1}.png", 10, 10, 1, 16, 18, ImageNum[i]) == -1)
        {
            MessageBox.Show($"dat/img/num/{i+1}.pngが開けませんでした");
        }
    }
    //☆マークの画像を読み込む
    ImageStar = DX.LoadGraph("dat/img/board/hoshi.png");
    if (ImageStar == -1)
    {
        MessageBox.Show($"dat/img/board/hoshi.pngが開けませんでした");
    }

    // アイテム画像を読み込む
    int[] image_item_size = { 35, 35, 15, 35, 35, 35 };
    for(int i = 0; i < ImageItem.Length; i++)
    {
        ImageItem[i] = new int[2];
        if(DX.LoadDivGraph($"dat/img/item/p{i}.png", 2, 2, 1, image_item_size[i], image_item_size[i], ImageItem[i]) == -1)
        {
            MessageBox.Show($"dat/img/item/p{i}.pngが開けませんでした");
        }
    }
}

次にアイテム用のクラスを作りShooingGame.csの方でも専用のリストを作ります。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DxLibDLL;
using static ShootingGame.CShootingGame;

namespace ShootingGame
{
    public class CItem : CMover
    {
        int Type = 0;
		    double Yaw = 0.0;
        public CItem(double x, double y): base(x, y, 1,-5,-5,5,5)
        {
            VX = 0.8;
            VY = 0.8;
            X += SGP.Rang(40);
            Y += SGP.Rang(40);
			      Type = DX.GetRand(5);//power point,score,mony,power,mony
		}
    
    public void Move()
    {
			// 自動回収の距離、自動回収時の速さ(速い、遅い)
			const double
				DIST = 100, SPD_FAST = 7.0, SPD_SLOW = 0.5f;

			// 回転角度の更新
			Yaw += 0.01f;

			if (Cnt > 30)
			{
				 if (0 < SGP.Player.Count)
				 {
					  // 自機に近いときは急速に接近する
					  double vx, vy, l;
					  vx = (SGP.Player[0].X - X);
					  vy = (SGP.Player[0].Y - Y);
					  l = Math.Sqrt(vx * vx + vy * vy);
					  if (l > 0)
					  {
						  if (l < DIST)
						  {
							  X += vx / l * SPD_FAST;
							  Y += vy / l * SPD_FAST;
						  }
						  else
						  {
							  X += vx / l * SPD_SLOW;
							  Y += vy / l * SPD_SLOW;
						  }
					  }
					  // 自機に接触したとき:
					  // スコアを増やしてから消滅する
					  if (Hit(SGP.Player[0]))
					  {
						  switch (Type)
						  {
							  case 0: SGP.Power += 3; break;
							  case 1: SGP.Point += 1; break;
							  case 2: SGP.Score += 1; break;
							  case 3: SGP.Money += 1; break;
							  case 4: SGP.Power += 50; break;
							  case 5: SGP.Money += 10; break;
						  }
						  SGP.PlaySound(12);
						  if (SGP.Power > 500) SGP.Power = 500;
						  if (SGP.Point > 9999) SGP.Point = 9999;
						  if (SGP.Score > 999999999) SGP.Score = 999999999;
					  	Life = 0;
				   	}
				  }
				  else
				  {
				  	Y += SPD_SLOW;
			   	}
			  }
			  ++Cnt;
        }
        public void Draw()
        {             // power point,score,mony,power,mony
            double[] r = { 0.6, 0.6, 1.0, 0.6, 1.0, 1.0 };//dat/img/itemの画像の拡大率
            DX.DrawRotaGraphF((float)X, (float)Y, r[Type], PI2 * (Cnt%120) / 120, SGP.ImageItem[Type][1], 1);
            DX.DrawRotaGraphF((float)X, (float)Y, r[Type] * 0.8, -PI2 * (Cnt%120) / 120, SGP.ImageItem[Type][1], 1);
            DX.DrawRotaGraphF((float)X, (float)Y, r[Type], 0, SGP.ImageItem[Type][0], 1);
        }
    }
}

コンストラクタ での処理

  1. 加速度を0.8にする
  2. 敵の座標から0~40までのランダムな数を足してずらす
  3. power(小) point,score,mony(小),power(大),mony(大)をTypeにいれ区別する

Move関数の処理

  1. プレイヤーがいる場合、アイテムとの座標を引いてX軸、Y軸の値の差を求める
  2. 2点間の距離の公式を使い、アイテムからプレイヤーまでの距離を求める。
  3. DISTより近かったら速い速度で、遠かったら遅い速度で、プレイヤーに近づく
  4. プレイヤーと当たっていたら、それぞれのアイテムの点数を加算して、アイテムのLifeを0にして消す。

Draw関数の処理

Typeを使って描画すべきアイテムを判断して、DrawRotaGraphで描画する。

敵を倒した時のスコアの追加

CPlayer.csのCShotのMove関数で敵とショットが当たった時の処理に以下を追加します。
SGP.Score += enemy.EnemyStatus.Score;

スコアボードの数字の表示

CBoradクラスをスコアを表示できるようにします。

今回は0-9までの10種類の画像を組み合わせて複数の桁を表示します。これには「余り」という概念が大事です。10で割った余りを表示すればその桁を表示することが出来ます。 今「29」という数字があったとして、0-9はそれぞれ0-9番の画像番号に対応しているとします。

まず一桁目の表示です。 int score = 29;において、29 % 10 は9ですね。画像番号9を表示します。

少し表示座標を左にずらし、score /= 10;そしてsocre % 10は2 % 10と同じ意味ですから、ここに画像番号2を表示します。

こうしてみると、「29」が表示できたと思います。 このように、10で割った余りを表示させ、10で割り、10で割った余りを表示させ、10で割り・・・ と、数がなくなるまで続けてやればいいのです。

後はスコアだけでなく、お金やパワーやポイントも同じ原理で処理できます。

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using DxLibDLL;
using static ShootingGame.CShootingGame;

namespace ShootingGame
{
    public class CBorad : CMover
    {
        public CBorad() : base(0,0,1)
        {

        }
        public void Move()
        {

        }
        public void Draw()
        {
			// ボードの表示
			int[] x = new int[] { 0, 0, 0, 416 };
			int[] y = new int[] { 0, 16, 464, 0 };
			for (int i = 0; i < SGP.ImageBorad.Length; ++i)
			{
				DX.DrawGraphF(x[i], y[i], SGP.ImageBorad[i], 0);
			}

			int highscore = SGP.HighScore;
			int score = SGP.Score;
			int power = SGP.Power;
			int graze = SGP.Graze;
			int point = SGP.Point;
			int money = SGP.Money;
			//スコア表示
			for (int i = 0; i < 9; i++)
			{
				DX.DrawRotaGraph(625 - 15 * i, 30, 1.0f, 0.0f, SGP.ImageNum[1][highscore % 10], 1);
				DX.DrawRotaGraph(625 - 15 * i, 50, 1.0f, 0.0f, SGP.ImageNum[1][score % 10], 1);
				highscore /= 10;
				score /= 10;
			}

			for (int i = 0; i < SGP.PlayerNum; i++)//残機数表示
			{
				DX.DrawGraph(499 + 12 * i, 63, SGP.ImageStar, 1);
			}
				
			//パワー表示
			DX.DrawRotaGraph(547, 91, 0.9f, 0.0f, SGP.ImageNum[1][power % 10], 1); power /= 10;
			DX.DrawRotaGraph(536, 91, 0.9f, 0.0f, SGP.ImageNum[1][power % 10], 1); power /= 10;
			DX.DrawRotaGraph(513, 91, 1.0f, 0.0f, SGP.ImageNum[1][power % 10], 1);
			DX.DrawString(522, 82, ".", SGP.Color[0]);//チョン

			for (int i = 0; i < 6; i++)
			{//グレイズ表示
				DX.DrawRotaGraph(578 - 14 * i, 111, 1.0f, 0.0f, SGP.ImageNum[1][graze % 10], 1);
				graze /= 10;
			}

			for (int i = 0; i < 4; i++)
			{//ポイント表示
				DX.DrawRotaGraph(550 - 14 * i, 131, 1.0f, 0.0f, SGP.ImageNum[1][point % 10], 1);
				point /= 10;
			}
			for (int i = 0; i < 6; i++)
			{//マネー表示
				DX.DrawRotaGraph(578 - 14 * i, 154, 1.0f, 0.0f, SGP.ImageNum[1][money % 10], 1);
				money /= 10;
			}
		}
    }
}

実行結果

今回のプロジェクトをダウンロードする。 プロジェクトに画像は同梱してませんのでこちらからダウンロードしてください。画像を置く階層はデバッグモードで実行するなら、bin/Debugでリリースモードで実行するならbin/Releaseです。

これでスコア周りの処理はできました。次回は、いよいよボスを作ってゲームの骨格を完成させる予定です。