자식 클래스에서 부모 클래스의 변수에 접근해서 사용하는데 부모 클래스에서 값이 바뀌어도 자식 클래스에서 값이 참조가 다르게 되길래 왜 이런가 했었다. 나는 상속이 부모의 값을 참조하여 같은 메모리를 공유하는 줄 알았는데 착각을 하고 있었다.
붕어빵 틀에 붕어빵을 찍는 것처럼 아예 다른 것이었다. 상속을 받는다고 참조가 아닌 서로 다른 각자의 변수에 값이 할당이 되는 것이었다. 튜터님의 도움 덕에 밑에 부분들에 대해서 다시 한번 인지하고 공부할 수 있는 좋은 기회였다.
Instance
상속
컴포지션
위 개념에 대해 다시한번 인지하고 코드들을 수정해보았다.
- PlayerAttackSystem.cs 에서 ItemManager의 상속을 뺏다.
- 이 스크립트에서는 단일책임원칙을 위해 PlayerItemState.cs에서 Item의 상태만 참조해서 그 Item에 맞는 공격만 하도록 수정하였다.
public class PlayerAttackSystem : MonoBehaviour
{
#region Example
//// Coroutine Caching
//private IEnumerator ItemCoolTime;
//private WaitForFixedUpdate fixedUpdate = new WaitForFixedUpdate();
#endregion
#region Global_Variale
// Scripts
TopDownCharacterController _controller;
PlayerItemState _itemManager;
// Component
Rigidbody2D bulletRigid; // bullet Prefab Clone Rigidbody
// Bullet Prefab
GameObject bullet; // ( bullet는 player가 발사하는 총알 Prefab )
GameObject PenetrateItemBullet;
GameObject bounceBullet;
GameObject guidedMissileBullet;
// Basic Value
public float Force { get; set; }
public bool CoolTimeCheck { get; set; }
#endregion
#region Initialization_Settings
private void Awake()
{
// Scripts Search
_controller = GetComponent<TopDownCharacterController>();
_itemManager = FindObjectOfType<PlayerItemState>();
// Resources Load
bullet = Resources.Load<GameObject>("Prefabs/Bullet");
PenetrateItemBullet = Resources.Load<GameObject>("Prefabs/PenetrateItemBullet");
bounceBullet = Resources.Load<GameObject>("Prefabs/BounceBullet");
guidedMissileBullet = Resources.Load<GameObject>("Prefabs/GuidedMissileBullet");
}
private void Start()
{
_controller.OnAttackEvent += Attack;
PlayerBasicValue();
}
#endregion
#region Player_AttackLogic
private void Attack()
{
// CoolTime Check
if (CoolTimeCheck == true)
{
CoolTimeCheck = false;
RecallBullet();
}
}
private void RecallBullet()
{
switch (_itemManager.currentItem)
{
case PlayerItemState.ItemType.Normal:
ItemNormalBullet();
break;
case PlayerItemState.ItemType.BulletUPItem:
ItemBulletCountUp();
break;
case PlayerItemState.ItemType.PenetrateItem:
ItemBulletPenetrateItem();
break;
case PlayerItemState.ItemType.BounceItem:
ItemBulletBounce();
break;
case PlayerItemState.ItemType.GuidedMissileItem:
ItemBulletGuidedMissile();
break;
default:
break;
}
}
private void ApplyAttck(GameObject obj)
{
bulletRigid = obj.GetComponent<Rigidbody2D>();
bulletRigid.AddForce(transform.up * Force, ForceMode2D.Impulse);
}
#endregion
#region BulletState
private void ItemNormalBullet()
{
GameObject playerbullet = Instantiate(bullet);
playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
ApplyAttck(playerbullet);
}
private void ItemBulletCountUp()
{
GameObject playerBullet1 = Instantiate(bullet);
GameObject playerBullet2 = Instantiate(bullet);
playerBullet1.transform.position = new Vector3(transform.position.x - 0.2f, transform.position.y + 0.4f, transform.position.z);
playerBullet2.transform.position = new Vector3(transform.position.x + 0.2f, transform.position.y + 0.4f, transform.position.z);
ApplyAttck(playerBullet1);
ApplyAttck(playerBullet2);
}
private void ItemBulletPenetrateItem()
{
GameObject playerbullet = Instantiate(PenetrateItemBullet);
playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
ApplyAttck(playerbullet);
}
private void ItemBulletBounce()
{
GameObject playerbullet = Instantiate(bounceBullet);
playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
ApplyAttck(playerbullet);
}
private void ItemBulletGuidedMissile()
{
GameObject playerbullet = Instantiate(guidedMissileBullet);
playerbullet.transform.position = new Vector3(transform.position.x, transform.position.y + 0.4f, transform.position.z);
}
#endregion
#region StateReset
private void PlayerBasicValue()
{
_itemManager.currentItem = PlayerItemState.ItemType.Normal;
Force = 8f;
CoolTimeCheck = true;
}
#endregion
}
- Player가 아이템을 먹으면 이 쪽에서 상태를 바꿔주고 아이템의 지속시간 관리를 하도록 하였다.
public class PlayerItemState : MonoBehaviour
{
#region ItemTypeEnum
public enum ItemType
{
Normal,
BulletUPItem, // bullet 갯수 증가
PenetrateItem, // 관통
BounceItem, // 튕김
GuidedMissileItem // 유도탄
}
// ItemType Reset Value
public ItemType currentItem;
#endregion
#region Global_Variale
public PlayerAttackSystem _player;
// Item Use DurationTime
public float itemDuration = 0f;
#endregion
private void Awake()
{
_player = FindObjectOfType<PlayerAttackSystem>();
}
private void Update()
{
BulletStateCheck();
}
#region Continuation_Item
public void BulletSpeedUp()
{
_player.Force += 0.5f;
}
#endregion
#region OneTime_Item
public void BulletCountUpItem()
{
BulletStateReset(_player);
currentItem = ItemType.BulletUPItem;
}
public void BulletPenetrateItem()
{
BulletStateReset(_player);
currentItem = ItemType.PenetrateItem;
}
public void BulletBounceItem()
{
BulletStateReset(_player);
currentItem = ItemType.BounceItem;
}
public void BulletGuidedMissileItem()
{
BulletStateReset(_player);
currentItem = ItemType.GuidedMissileItem;
}
#endregion
public void BulletCoolTimeReset()
{
_player.CoolTimeCheck = true;
}
private void BulletStateCheck()
{
if (currentItem != ItemType.Normal && itemDuration > 0f)
{
itemDuration -= Time.deltaTime;
}
if (itemDuration <= 0f && currentItem != ItemType.Normal)
{
itemDuration = 0f;
currentItem = ItemType.Normal;
}
}
private void BulletStateReset(PlayerAttackSystem player)
{
itemDuration = 0f;
itemDuration = 5;
}
}
- 각 아이템 오브젝트의 스크립트들 에도 상속을 빼주고 PlayerItemState.cs에게 아이템 상태만 전달하도록 바꾸었다.
- Bullet의 스크립트들 경우에는 작동방식을 쿨타임이 아닌 벽or공 에 부딪히면 공격을 다시 할수있게 바꾸어서 부딪힐 경우 리셋 메서드를 실행하게 하였다.
public class BulletBounceItem : MonoBehaviour
{
PlayerItemState _itemManager;
private void Awake()
{
_itemManager = FindObjectOfType<PlayerItemState>();
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Player")
{
// Bullet Bounce
_itemManager.BulletBounceItem();
Destroy(gameObject); // 추후 오브젝트 풀링
}
}
}
public class GuidedMissileBulletPrefabLogic : MonoBehaviour
{
PlayerItemState _itemManager;
List<GameObject> Balls;
GameObject LockOnTarget;
float nearDis;
// LockOn Setting
private void Awake()
{
_itemManager = FindObjectOfType<PlayerItemState>();
Balls = new List<GameObject>(GameObject.FindGameObjectsWithTag("Ball"));
nearDis = Vector3.Distance(gameObject.transform.position, Balls[0].transform.position);
}
// LockOn Search
private void Start()
{
// Null Value Ready
LockOnTarget = Balls[0];
// Search Near Distance Target
foreach (GameObject index in Balls)
{
float distance = Vector3.Distance(gameObject.transform.position, index.transform.position);
if (distance < nearDis)
{
LockOnTarget = index;
}
}
}
// LockOn Target Attack
private void FixedUpdate()
{
transform.position = Vector3.MoveTowards(transform.position, LockOnTarget.transform.position, _itemManager._player.Force * Time.deltaTime);
}
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.tag == "Ball" || collision.tag == "Wall" || collision.tag == "TopWall")
{
_itemManager.BulletCoolTimeReset();
Destroy(gameObject); // 추후 오브젝트 풀링
}
}
}
- 아이템이 떨어지는 스크립트 부분에 아이템이 바닥에 닿으면 1초 뒤에 10번 깜빡이는 효과를 주고 사라지게 하는 코드를 추가했다.
public class ItemFall : MonoBehaviour
{
GameObject item;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
float timer = 1;
bool filckerCheck = true;
private void Awake()
{
item = GetComponent<GameObject>();
rigid = transform.GetComponent<Rigidbody2D>();
spriteRenderer = transform.GetComponent<SpriteRenderer>();
}
private void Start()
{
rigid.AddForce(transform.up * -1, ForceMode2D.Impulse);
}
private void FixedUpdate()
{
if (transform.position.y <= -4.3f)
{
rigid.constraints = (RigidbodyConstraints2D)RigidbodyConstraints.FreezeAll;
timer -= Time.deltaTime;
if (timer <= 0 && filckerCheck == true)
{
timer = 0;
StartCoroutine(Filcker());
filckerCheck = false;
}
}
}
private IEnumerator Filcker()
{
float Counter = 0;
while ( Counter < 10)
{
float alpCol = 0;
while (alpCol < 1.0f)
{
alpCol += 0.1f;
yield return new WaitForSeconds(0.01f);
spriteRenderer.color = new Color(1, 1, 1, alpCol);
}
while (alpCol > 0f)
{
alpCol -= 0.1f;
yield return new WaitForSeconds(0.01f);
spriteRenderer.color = new Color(1, 1, 1, alpCol);
}
Counter++;
}
}
}