オブジェクトの作成
16px × 16pxの画像を用意し、Pixels Per Unitを8にします。画像からSpriteを作成し、CirlceCollider2Dをアタッチします。適当にColliderの大きさを設定します。また、パックマンの当たり判定は無視しなければならないので、Layerの設定をIgnore Laycastにします。
クラスの作成
エサ
Dotクラスを作成します。パックマンに食べられるのでインターフェイスIEatableを継承します。
public class Dot : MonoBehaviour, IEatable
{
public event EventHandler OnEaten;
private static readonly int score = 10;
public int Score { get => score; }
public void Eaten(Pacman pacman = null)
{
OnEaten(this, EventArgs.Empty);
gameObject.SetActive(false);
}
public void Destroy()
{
Destroy(gameObject);
}
}
パワーエサ
PowerCokieクラスを作成します。パックマンに食べられるのでインターフェイスIEatableを継承します。
public class PowerCokie : MonoBehaviour, IEatable
{
public event EventHandler OnEaten;
private static readonly int score = 50;
public int Score { get => score; }
public void Eaten(Pacman pacman = null)
{
OnEaten(this, EventArgs.Empty);
gameObject.SetActive(false);
}
public void Destroy()
{
Destroy(gameObject);
}
}
フルーツ
フルーツ毎にクラスを作成します。まずインターフェイスIEatableを継承したFruitsクラスを作成し、これを継承した各フルーツのクラスを作成します(まぁ、中身はスコアだけですが)。
public class Fruits : MonoBehaviour, IEatable
{
public event EventHandler OnEaten;
protected int score;
private readonly static int limit = 10;
public int Score => score;
public void Eaten(Pacman pacman = null)
{
OnEaten(this, EventArgs.Empty);
Destroy(gameObject);
}
public void Destroy()
{
Destroy(gameObject);
}
private void Start()
{
StartCoroutine(CroutineAppear());
}
private IEnumerator CroutineAppear()
{
yield return new WaitForSeconds(limit);
Destroy(gameObject);
}
}
public class Cherry : Fruits
{
private void Start()
{
score = 100;
}
}
public class Strawberry : Fruits
{
private void Start()
{
score = 300;
}
}
public class Orange : Fruits
{
private void Start()
{
score = 500;
}
}
迷路に設置
各アイテムを迷路に設置します。各アイテムのオブジェクトにそれぞれのクラスをアタッチします。また、ゴーストのWaypointsMapの作成と同じ要領でDotMap、PowerCokieMapを作成し、エサとパワーエサの座標を取得します。
フルーツはエサを70、170個食べた時点で出現する仕様なのでちょっと大げさですが専用のクラスFruitsCreatorを作成します。
public class FruitsCreator : MonoBehaviour
{
[SerializeField] private Fruits[] fruitsPrefabs = default;
private readonly int[] limits = { 70, 170, int.MaxValue };
private int limit;
private int i;
public void Initialize()
{
limit = limits[0];
i = 0;
}
public bool HasEatenDotEnough(int cnt)
{
if (limit <= cnt)
{
i++;
limit = limits[i];
return true;
}
return false;
}
public Fruits Create(Maze maze, int mazeIndex)
{
var fruits = Instantiate(fruitsPrefabs[mazeIndex], maze.transform);
fruits.transform.localPosition = maze.PacmanStartPosition;
return fruits;
}
}
GameManagerクラスでゲーム開始時にアイテムを設置します。このときOnEatenイベントにパックマンが食べたときの処理を実装します。
public class GameManager : MonoBehaviour
{
[SerializeField] private Dot dotPrefab = default;
[SerializeField] private PowerCokie powerCokiePrefab = default;
[SerializeField] private Scorer scorer = default;
[SerializeField] private Text scoreText = default;
[SerializeField] private float scareSpan;
[SerializeField] private float nextSpan;
private List<AGhost> ghosts;
private Maze maze;
private FruitsCreator fruitsCreator;
private Fruits fruits;
private SoundManager soundManager;
private bool isScare;
private float scareEndTime;
private int mazeIndex;
private int score;
private int eatenDot;
private void SetGame()
{
pacman = Instantiate(pacmanPrefab, maze.transform);
pacman.Initialize(maze.PacmanStartPosition);
pacman.OnDead += (s, e) => CheckLives();
PlaceGhost();
PlaceDot();
PlacePowerCokie();
UpdateLives();
fruitsCreator = GetComponent<FruitsCreator>();
fruitsCreator.Initialize();
scareEndTime = 0;
eatenGhost = 0;
eatenDot = 0;
}
private void PlaceDot()
{
var poses = maze.DotPositions;
int all = poses.Count;
poses.ForEach(p =>
{
var dot = Instantiate(dotPrefab, maze.transform, false);
dot.transform.localPosition = p;
dot.OnEaten += (s, e) =>
{
var d = (Dot)s;
score += d.Score;
scoreText.text = score.ToString();
eatenDot++;
CreateFruits();
soundManager.PlaySE("EatDot", 0.4f);
all--;
if (all == 0)
{
StopAll();
soundManager.StopBGM();
soundManager.StopSE();
maze.Blinking();
StartCoroutine(CoroutineNext());
}
};
});
}
private void PlacePowerCokie()
{
var poses = maze.PowerCokiePositions;
poses.ForEach(p =>
{
var pow = Instantiate(powerCokiePrefab, maze.transform, false);
pow.transform.localPosition = p;
pow.OnEaten += (s, e) =>
{
isScare = true;
scareEndTime = Time.fixedTime + scareSpan;
ghosts.ForEach(g => g.Scare(scareSpan));
var pc = (PowerCokie)s;
score += pc.Score;
scoreText.text = score.ToString();
eatenDot++;
CreateFruits();
soundManager.PlaySE("EatPowerCokie", 0.2f);
soundManager.StopBGM();
soundManager.PlayBGM("GhostScare", 0.4f);
};
});
}
private void CreateFruits()
{
if (fruitsCreator.HasEatenDotEnough(eatenDot))
{
fruits = fruitsCreator.Create(maze, mazeIndex);
fruits.OnEaten += (s, e) =>
{
var f = (Fruits)s;
score += f.Score;
scoreText.text = score.ToString();
scorer.Set(f.Score, f.transform.position, eatSpan);
soundManager.PlaySE("EatPowerCokie", 0.2f);
};
}
}
}