コンテンツにスキップ

第9週:合体のバグを直して仕上げよう

前回、同じ番号の玉がぶつかったら合体するところまで作りました。 でも、合体後の玉が2個できてしまうバグが残っています。

原因も分かっています。玉AとBがぶつかると、Unityは AとBの両方OnCollisionEnter2Dを呼ぶため、 どちらも「自分が新しい玉を作らなければ」と動いてしまうからです。

今日はこのバグを直して、合体を完成させます。

  • 2つのうち片方だけが合体処理をするようにする
  • 3つ同時にぶつかったときのバグも防ぐ
  • 完成版のBallスクリプトを読み解く

「AとBのどちらが合体処理をするか」を決めるルールが必要です。

Unityのオブジェクトは、それぞれ違うID番号を持っています。 これを比べれば、機械的に片方を選べます。

// 自分のIDが相手より小さいなら、相手にまかせて何もしない
if (GetInstanceID() < other.GetInstanceID()) return;
// いちばん大きい玉は合体しない
if (nextPrefab == null) return;
// 2つのうち片方だけが処理する
if (GetInstanceID() < other.GetInstanceID()) return;
// 2つの真ん中の位置を計算
Vector3 center = (transform.position + other.transform.position) / 2f;

再生して確認してください。 合体後の玉が1個だけになりました!

まだ問題が残っています。 同じ番号の玉が3つ以上、同時にぶつかったときです。

「もう合体した」という印をつける

Section titled “「もう合体した」という印をつける”
// すでに合体したかどうかを覚えておく
private bool merged = false;

一度合体したらtrueにして、 2回目以降は何もしないようにします。

using UnityEngine;
public class Ball : MonoBehaviour
{
// この玉の番号(1〜6)
public int level = 1;
// 合体してできる玉(いちばん大きい玉はNone)
public GameObject nextPrefab;
// すでに合体したか
private bool merged = false;
void OnCollisionEnter2D(Collision2D collision)
{
// 相手のBallスクリプトを取り出す
Ball other = collision.gameObject.GetComponent<Ball>();
// 1. 相手が玉でない(壁・床)なら何もしない
if (other == null) return;
// 2. 番号が違うなら合体しない
if (other.level != level) return;
// 3. どちらかが合体済みなら何もしない
if (merged || other.merged) return;
// 4. いちばん大きい玉は合体しない
if (nextPrefab == null) return;
// 5. 2つのうち片方だけが処理する
if (GetInstanceID() < other.GetInstanceID()) return;
// ここまで来たら合体する
// 両方に「合体した」印をつける
merged = true;
other.merged = true;
// 2つの真ん中の位置を計算
Vector3 center = (transform.position + other.transform.position) / 2f;
// 1つ大きい玉を作る
Instantiate(nextPrefab, center, Quaternion.identity);
// 元の2つを消す
Destroy(other.gameObject);
Destroy(gameObject);
}
}
  • 同じ玉2つがぶつかると、1つ大きい玉に1個だけ変わる
  • 番号が違う玉同士はぶつかるだけで合体しない
  • 運がよければ連鎖が起きる(合体してできた玉がさらに合体)
  • いちばん大きい紫の玉は、それ以上合体しない
合体が成功した画面
合体が成功した画面

完成版にはifが5つ並んでいます。どれも必要です。

番号 条件 無いとどうなる
1 other == null 壁にぶつかるたびにエラーが出る
2 other.level != level 違う玉でも合体してしまう
3 `merged
4 nextPrefab == null レベル6でエラーが出る
5 IDの比較 合体後の玉が2個できる
症状 原因と対処法
まったく合体しない メソッド名が OnCollisionEnter2Dか確認(2Dを忘れがち)
合体しない(エラーも出ない) PrefabにBallスクリプトを付け忘れている
合体後の玉が2個できる IDを比べるifが入っていない
玉が3個以上あると増殖する mergedifが入っていない
違う番号の玉が合体する PrefabのLevelが全部1のまま。1〜6を設定する
無限に合体し続ける Ball6のNext PrefabにBall6などを入れている。空にする
NullReferenceException Next Prefabの設定漏れ。Ball1〜5すべて設定したか確認
合体しても大きくならない Next Prefabに同じ玉を入れている(Ball1にBall1など)
合体した玉が箱から飛び出す 大きい玉が壁に食い込んで押し出されている。 箱を広くするか、玉のLinear Dampingを上げる

基礎課題全員必須

合体を完成させる:

  • IDを比べるifと、mergedifBallスクリプトに入れる
  • 合体後の玉が1個だけであることを確認する
  • 同じ玉を3つ以上ぶつけても増えないことを確認する
  • 最後にCtrl + Sで保存する

標準課題時間があれば

バグを自分で再現して理解する:

  • IDを比べるifを一時的にコメントアウトし、 玉が2個できることを確認する(確認したら戻す)
  • Debug.Logで合体した番号を表示する
  • いちばん大きい紫の玉まで到達させてみる

発展課題チャレンジ

合体を演出する:

  • 合体したとき、新しい玉を小さく作って だんだん大きくする(ヒント:transform.localScale
  • レベルが上がるほど派手にする
  • 合体した回数をDebug.Logで数える

ゲームの中身ができました!

でも、まだ「うまくできたか」が数字で分かりません。 次回はスコアを画面に表示します。 合体したら点が入るようにして、ゲームらしく仕上げていきます。