いよいよ最終のスコア実装でっす。
スコアの実装なんて今までの処理に比べたら楽勝ρ(^^ )♭
と高を括っていたのですが…
適当に実装して、PLAYしたらとんでも無い事になりました。
移動後に残っているピースの点数を合計していったら、
あっと言う間に天文学的数字に ( ゜.゜) ポカーン
こんなスコアの入り方でしたっけ( ̄~ ̄;) ウーン
少し調べないと行けません。
Google先生にお伺いを立てたのですが、
どこにもスコア計算の情報が出ておりません。
更に調べると、どうやら結合したピースのみ得点するようです。
そら、全部のピースを合計したら天文学的数字になるはずでっす。
(~-~;)ヾ(-_-;) オイオイ
完全に舐めてました。
改めて実装していきます。
エディターでテキストのオブジェクトを設置から

もはや適当に作ってますが、こんな感じで良いでしょう。
このテキストをスクリプトから操作すれば無問題。
スクリプトを修正していきます。
GameManager
using UnityEngine.UI;
public class GameManager : MonoBehaviour
{
private int SCORE=0; //スコア
private int HI_SCORE = 0; //ハイスコア
private int[] ScorList = { 2, 4, 8, 16, 32,
64, 128, 256, 512,
1024, 2048 }; //各ピースの点数
public GameObject SCOREText; //スコア表示用オブジェクト
public GameObject HI_SCOREText; //ハイスコア表示用オブジェクト
private Text scoreText; //スコアテキストコンポーネント取得
private Text HiscoreText; //ハイスコアテキストコンポーネント取得
各種変数を追加します。
テキストオブジェクトの編集をするので名前空間に
using UnityEngine.UI;を呼び出します。
続いて、Startでテキストコンポーネントを取得しておきます。
// Start is called before the first frame update
void Start()
{
//スコア系のテキストを取得してキャッシュする
scoreText = SCOREText.GetComponent<Text>();
HiScoreText = HI_SCOREText.GetComponent<Text>();
//初回ピース生成
SelectPosition();
//ピース生成のウェイト計算
waittime = width / PieceSpeed * fixedtime+proctime;
}
テキストの準備ができたのでスコアの計算を用意します。
スコアの加算はピース結合して出来たものが得点になるので、
CollisionEnterから生成したピースを加算しないといけないのですが、
SelectPosition()メソッドからもピース生成を行うので、
切り分けが必要です。
//ピースが結合した時の処理
public void CreatePiece(int x, int y, int p, bool c) ←ここに追加
{
GameObject Piece = (GameObject)Instantiate(PiecePrefab[p]);
Piece.transform.SetParent(PieceParent.transform, false);
Piece.transform.localPosition = new Vector3(PosNo[x], PosNo[Mathf.Abs(y - 3)], 0);
}
なので引数を増やします。
bool型変数cを引数に追加して識別します。
これに伴ってSelectPosition()と
OnCollisionEnter2Dの方も修正します。
PieceManager
//コラインダーが何かに衝突
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.gameObject.tag == "No" + PieceNo)
{
Destroy(this.gameObject);
if (Move == false)
{
gameManager.CreatePiece(LineX, LineY, PieceNo,true); ←結合側はtrue
}
}
}
OnCollisionEnter2Dはtrueにします。
GameManager
//空きマスからランダムに2カ所を選ぶ
public void SelectPosition()
{
//ピースをnum回生成する
while (rot < num)
{
int pos = list.GetAndRemoveAtRandom();
int x = pos % frame;
int y = pos / frame;
CreatePiece(x, y, 0,false); ←移動後の生成はfalse
rot++;
}
メソッドの中のwhile (rot < num)で
CreatePiece()を呼び出しているので、こちらはfalseにしておきます。
切り分けが出来たのでCreatePiece()を修正します。
//ピースが結合した時の処理
public void CreatePiece(int x, int y, int p, bool c)
{
//ピース結合していれば加算する
if (c)
{
SCORE += ScorList[p]; ←pはピースナンバーです
}
GameObject Piece = (GameObject)Instantiate(PiecePrefab[p]);
Piece.transform.SetParent(PieceParent.transform, false);
Piece.transform.localPosition = new Vector3(PosNo[x], PosNo[Mathf.Abs(y - 3)], 0);
}
引数にしているピースナンバーを、スコア用の配列ScorList[]に代入
これで点数がわかるので、変数SCOREに加算します。
これで、スコアの計算が出来るようになりました。
あとは表示のタイミングです。
ピース移動が完了して、生成後ぐらいがいいかと思うので、
//空きマスからランダムに2カ所を選ぶ
public void SelectPosition()
{
List<int> list = new List<int>(); //空きマスを取得する変数
int num = 2; //ピースの生成回数
int rot = 0; //ピースの生成カウント
int limit=0; //空マスのカウント
for(int i = 0; i < 16; i++)
{
if (Flag[i] == false)
{
list.Add(i);
limit++;
}
}
//空きマスが一つならnumを修正する
if (list.Count == 1)
{
num = list.Count;
}
//ピースをnum回生成する
while (rot < num)
{
int pos = list.GetAndRemoveAtRandom();
int x = pos % frame;
int y = pos / frame;
CreatePiece(x, y, 0,false);
rot++;
}
//スコアの表示
scoreText.text = "SCORE:"+SCORE.ToString(); ←ここで表示
HiscoreText.text = "HI-SCORE:" + HI_SCORE.ToString();
//空マスが2つ以下なら移動できるか判定する
if (limit<=2)
{
StartCoroutine("waitJudge");
}
create = false;
}
ハイスコアの表示もできるようにしておきます。
スコアの処理は、これで問題ないと思います。
続いて、ハイスコアの処理を作成します。
ハイスコアは保存が必要になるので、PlayerPrefsを使います。
データの読込みはStartで行い、
セーブはゲームオーバー時に行います。
読込み側
// Start is called before the first frame update
void Start()
{
//スコア系のテキストを取得してキャッシュする
scoreText = SCOREText.GetComponent();
HiscoreText = HI_SCOREText.GetComponent();
//保存されたハイスコアを呼び出す
HI_SCORE = PlayerPrefs.GetInt("HI_SCORE", 0); ←ここで読み込む
HiscoreText.text = HI_SCORE.ToString();
//初回ピース生成
SelectPosition();
//ピース生成のウェイト計算
waittime = width / PieceSpeed * fixedtime+proctime;
}
PlayerPrefsは、呼び出し時にセーブ用のキーを設定するので、
キーの名前で呼び出し、変数に代入します。
設定したキーに値が無い場合、エラーが掛からないように0を設定します。
PlayerPrefs.GetInt("HI_SCORE", 0)
今回は”HI_SCORE”をキーにしています。
HI_SCOREで呼び出して、なければ0を代入となります。
キーが存在するとキー内のデータを呼び出してくれます。
これでハイスコアが読み込めるので、
ハイスコアtextを更新しておきます。
保存側
//ピースの移動が出来るか判定する
void EndJudge()
{
//ピースナンバー仮取得する変数XY
int tempX=0;
int tempY=0;
for(int i = 0; i < 4; i++)
{
for(int a = 0; a < 4; a++)
{
}
tempX = 0;
tempY = 0;
}
//ゲームオーバーなら呼び出す
EndTxet.SetActive(true);
//ハイスコアを保存する
PlayerPrefs.SetInt("HI_SCORE", HI_SCORE); ←ここで保存
}
ループ内の処理が多いので端折ります。
ゲームオーバーの後にキーを"HI_SCORE"にして保存すれば問題ないかと。
同じ文字を使ってるので、分かりにくいのですが、
前の"HI_SCORE"がキーで、後ろが変数HI_SCOREでっす。
これで、ハイスコアの保存・読込みができたので、
PLAY中の更新処理を作成します。
//ピースが結合した時の処理
public void CreatePiece(int x, int y, int p, bool c)
{
//ピース結合していれば加算する
if (c)
{
SCORE += ScorList[p];
//ハイスコアが更新されたらスコアを代入する
if (HI_SCORE <= SCORE)
{
HI_SCORE = SCORE;
}
}
GameObject Piece = (GameObject)Instantiate(PiecePrefab[p]);
Piece.transform.SetParent(PieceParent.transform, false);
Piece.transform.localPosition = new Vector3(PosNo[x], PosNo[Mathf.Abs(y - 3)], 0);
}
単純です。上回れば更新ですね。
あとは、TextオブジェクトをGameManagerにアタッチすればOKです。
早速PLAYしてみます。
再生できない場合、ダウンロードは🎥こちら
ハイスコアが最初から入っていますが、
更新されたのが分かりやすいように前回データを残しておきました。
ハイスコアが更新された所からプレイ中のスコアがハイスコアに
なって行くようすが分かります。
ゲームオーバーまでPLAYすれば、ハイスコアも自動的に保存されます。
長々と2048の記事を書いてきましたが、
一応、完成までたどり着けました。
ただ、作ったコードを振り返ると
・無駄が多い
・分かりにくい
このあたりは、今後の課題ですね。
更に勉強しないと…
アセットストアに公開されている2048のモデルがあるのですが、
スクリプトを見てもチンプンカンプンでした。
でも、今回チャレンジして何となくですが、
モデルのスクリプトが理解できた気がします。
無駄の多い部分は、モデルと比較すると
勉強になりそうですね~
これからも精進、精進
新しいネタができたらブログを更新します。
お楽しみにして下さいませ。
それでは(o・・o)/~マタネェ
追記
Start時の注意点があったのを書き忘れてました。
TextをGetComponentしていますが、ピース生成前にしています。
これは、ピースの生成が先に来るとピース側のStartメソッドが働く為、
Textを取得できなくなります。
Textの取得が出来ないので、ハイスコアの表示もエラーが出ます。
なので、ピース生成より先に呼び出しています。
ちなみに、TextのGetComponentなどを優先したい場合は、
Awake()メソッドを使うのも一つです。
Awake()とStart()は、共に起動時に呼び出されるメソッドなのですが、
Awake()の方が少しだけ先に呼び出されるそうです。
なので、複数のスクリプトを同時に動かす時は、先に読み込ませたい
物などをAwake()で起動しておくと良いかと思います。
詳しく知りたい場合は、GoogleでStartとAwakeの違いで検索すると
情報が出てくると思います。
この記事へのコメント