概要
目次
今回は通貨システムを実装します。
通貨パラメーターを追加して、タレットを強化する時に10減少し敵を倒した時に1増加するようにします。
以下gifアニメは
Dボタンを押してタレットを強化する際に通貨が10減少し敵を倒した時に1増加しています。
尚通貨はprivate変数なのでインスペクターのデバッグモードで確認します。
開発環境
IDE:Rider
Unity:2020.3.42(LTS)
OS:Windows10
UnityEditor上の設定
新規オブジェクトCurrencySystemを作成して CurrencySystemスクリプトをアタッチします。
実装のポイント
1.通貨システムを実装
新規クラスにCurrencySysytemクラスを作成し、TurretUpgradeクラスにメソッドを追加します。
下図は敵が死亡した時に通貨が得られるときの処理(Observeパターン)
下図はタレットをアプデした時に通過が減る時の処理
通貨の値はPlayerPrefsというクラスを使って操作します。PlayerPrefsはゲームデータのセーブ・ロードを行うクラスのようです。
2.どのEnemyがKillされたか識別するためにActionの引数にEnemyクラスを加える。
既存クラスSpawner LevelManager EnemyHealth Enemyで一部改修を行います。
コード部分
Enemy
using System;
using UnityEngine;
public class Enemy : MonoBehaviour
{
- public static Action OnEndReached;
+ public static Action<Enemy> OnEndReached;
[SerializeField] private float moveSpeed = 3f;
public Waypoint Waypoint { get; set; }
public Vector3 CurrentPointPosition => Waypoint.GetWaypointPosition(_currentWaypointIndex);
private int _currentWaypointIndex;
private EnemyHealth _enemyHealth;
public EnemyHealth EnemyHealth { get; set; }
private void Start()
{
_currentWaypointIndex = 0;
_enemyHealth = GetComponent<EnemyHealth>();
EnemyHealth = GetComponent<EnemyHealth>();
}
private void Update()
{
Move();
if (CurrentPointPositionReached())
{
UpdateCurrentPointIndex();
}
}
private void Move()
{
Vector3 currentPosition = Waypoint.GetWaypointPosition(_currentWaypointIndex);
transform.position = Vector3.MoveTowards(transform.position, currentPosition, moveSpeed * Time.deltaTime);
}
private bool CurrentPointPositionReached()
{
return Vector3.Distance(transform.position, CurrentPointPosition) < 0.1f;
}
private void UpdateCurrentPointIndex()
{
int lastWaypointIndex = Waypoint.Points.Length - 1;
if (_currentWaypointIndex < lastWaypointIndex)
{
_currentWaypointIndex++;
}
else
{
EndPointReached();
}
}
private void EndPointReached()
{
- OnEndReached?.Invoke();
+ OnEndReached?.Invoke(this);
_enemyHealth.ResetHealth();
ObjectPooler.ReturnToPool(gameObject);
}
public void ResetEnemy()
{
_currentWaypointIndex = 0;
}
}
EnemyHealth
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using UnityEngine;
using UnityEngine.UI;
public class EnemyHealth : MonoBehaviour
{
- public static Action OnEnemyKilled;
+ public static Action<Enemy> OnEnemyKilled;
// Start is called before the first frame update
[SerializeField] private GameObject healthBarPrefab;
[SerializeField] private Transform barPosition;
[SerializeField] private float initialHealth = 10f;
[SerializeField] private float maxHealth = 10f;
public float CurrentHealth { get; set; }
private Image _healthBar;
+ private Enemy _enemy;
void Start()
{
CreateHealthBar();
CurrentHealth = initialHealth;
+ _enemy = GetComponent<Enemy>();
}
private void Update()
{
if (Input.GetKeyDown(KeyCode.A))
{
DealDamage(5f);
}
_healthBar.fillAmount = Mathf.Lerp(_healthBar.fillAmount, CurrentHealth / maxHealth, Time.deltaTime * 10f);
}
private void CreateHealthBar()
{
GameObject newBar = Instantiate(healthBarPrefab,barPosition.position,Quaternion.identity);
newBar.transform.SetParent(transform);
EnemyHealthContainer container = newBar.GetComponent<EnemyHealthContainer>();
_healthBar = container.FillAmountImage;
}
public void DealDamage(float damageReceived)
{
CurrentHealth -= damageReceived;
if (CurrentHealth <= 0)
{
CurrentHealth = 0;
Die();
}
}
public void ResetHealth()
{
CurrentHealth = initialHealth;
_healthBar.fillAmount = 1f;
}
private void Die()
{
- OnEnemyKilled?.Invoke();
+ OnEnemyKilled?.Invoke(_enemy);
CurrentHealth = initialHealth;
_healthBar.fillAmount = 1f;
ObjectPooler.ReturnToPool(gameObject);
}
}
CurrencySystem
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CurrencySystem : Singleton<CurrencySystem>
{
[SerializeField] private int coinTest;
private string CURRENCY_SAVE_KEY = "MYGAME_CURRENCY";
public int TotalCoins { get; set; }
void Start()
{
PlayerPrefs.DeleteKey(CURRENCY_SAVE_KEY);
LoadCoins();
}
private void LoadCoins()
{
TotalCoins = PlayerPrefs.GetInt(CURRENCY_SAVE_KEY, coinTest);
}
public void AddCoins(int amount)
{
TotalCoins += amount;
PlayerPrefs.SetInt(CURRENCY_SAVE_KEY,TotalCoins);
PlayerPrefs.Save();
}
public void RemoveCoins(int amount)
{
if (TotalCoins >= amount)
{
TotalCoins -= amount;
PlayerPrefs.SetInt(CURRENCY_SAVE_KEY,TotalCoins);
PlayerPrefs.Save();
}
}
private void AddCoins(Enemy enemy)
{
AddCoins(1);
}
private void OnEnable()
{
EnemyHealth.OnEnemyKilled += AddCoins;
}
private void OnDisable()
{
EnemyHealth.OnEnemyKilled -= AddCoins;
}
}
LevelManager
using System;
using UnityEngine;
public class LevelManager : MonoBehaviour
{
[SerializeField] private int lives = 10;
public int TotalLives { get; set; }
private void Start()
{
TotalLives = lives;
}
- private void ReduceLives()
+ private void ReduceLives(Enemy enemy)
{
TotalLives--;
if (TotalLives <= 0)
{
TotalLives = 0;
}
}
private void OnEnable()
{
Enemy.OnEndReached += ReduceLives;
}
private void OnDisable()
{
Enemy.OnEndReached -= ReduceLives;
}
}
Spawner
using System;
using System.Collections;
using UnityEngine;
public class Spawner : MonoBehaviour
{
[SerializeField] private int enemyCount = 10;
// Btw = Between
[SerializeField] private float delayBtwSpawns;
[SerializeField] private float delayBtwWaves = 1f;
private int _enemiesRemaining;
private int _enemiesSpawned;
private float _spawnTimer;
private ObjectPooler _objectPooler;
private Waypoint _waypoint;
private void Start()
{
_objectPooler = GetComponent<ObjectPooler>();
_waypoint = GetComponent<Waypoint>();
_enemiesRemaining = enemyCount;
}
private void Update()
{
_spawnTimer -= Time.deltaTime;
if (_spawnTimer < 0)
{
_spawnTimer = delayBtwSpawns;
if (_enemiesSpawned < enemyCount)
{
SpawnEnemy();
_enemiesSpawned++;
}
}
}
// ReSharper disable Unity.PerformanceAnalysis
private void SpawnEnemy()
{
GameObject newInstance = _objectPooler.GetInstanceFromPool();
Enemy enemy = newInstance.GetComponent<Enemy>();
enemy.Waypoint = _waypoint;
enemy.transform.localPosition = transform.position;
enemy.ResetEnemy();
newInstance.SetActive(true);
}
private IEnumerator NextWave()
{
yield return new WaitForSeconds(delayBtwWaves);
_enemiesRemaining = enemyCount;
_spawnTimer = 0f;
_enemiesSpawned = 0;
}
private void RecordEnemy(Enemy enemy)
{
_enemiesRemaining--;
if (_enemiesRemaining <= 0)
{
StartCoroutine(NextWave());
}
}
private void OnEnable()
{
Enemy.OnEndReached += RecordEnemy;
EnemyHealth.OnEnemyKilled += RecordEnemy;
}
private void OnDisable()
{
Enemy.OnEndReached -= RecordEnemy;
EnemyHealth.OnEnemyKilled -= RecordEnemy;
}
}
TurretUpgrade
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TurretUpgrade : MonoBehaviour
{
[SerializeField] private int upgradeInitialCost;
[SerializeField] private int upgradeCostIncremental;
[SerializeField] private float damageIncremental;
[SerializeField] private float delayReduce;
private TurretProjectTile _turretProjectTile;
+ public int UpgradeCost { get; set;}
private void Start()
{
_turretProjectTile = GetComponent<TurretProjectTile>();
+ UpgradeCost = upgradeInitialCost;
}
// Update is called once per frame
private void Update()
{
if(Input.GetKeyDown(KeyCode.D))
{
UpgradeTurret();
}
}
private void UpgradeTurret()
{
+ if (CurrencySystem.Instance.TotalCoins >= UpgradeCost)
+ {
_turretProjectTile.Damage += damageIncremental;
_turretProjectTile.DelayPerShot -= delayReduce;
+ UpdateUpgrade();
+ }
}
+ private void UpdateUpgrade()
+ {
+ CurrencySystem.Instance.RemoveCoins(UpgradeCost);
+ UpgradeCost += upgradeCostIncremental;
+ }
}
参考
PlayerPrefs
Section6 36
github コミット分