๐Ÿ–ฅ Unity2D Project : ์ปดํ“จํ„ฐ ์‚ด๋ฆฌ๊ธฐ - ๋ชฌ์Šคํ„ฐ, ๊ณต๊ฒฉ ํƒ€์›Œ ๊ตฌํ˜„

Se0ng_1lยท2023๋…„ 5์›” 14์ผ
0

์ปดํ“จํ„ฐ ์‚ด๋ฆฌ๊ธฐ

๋ชฉ๋ก ๋ณด๊ธฐ
4/4
post-thumbnail

๐Ÿ“Œ ์ฐธ๊ณ ์ž๋ฃŒ : ๊ณจ๋“œ๋ฉ”ํƒˆ ์–ธ๋ฐ๋“œ์„œ๋ฐ”์ด๋ฒ„

GameManager.cs

๊ฒŒ์ž„ ์‹œ์Šคํ…œ์„ ์—ฌ๋Ÿฌ ํด๋ž˜์Šค์—์„œ ๊ฐ„ํŽธํžˆ ์ ‘๊ทผํ•  ์ˆ˜ ์žˆ๊ฒŒํ•œ๋‹ค.

  • ์™ธ๋ถ€ ํด๋ž˜์Šค์—์„œ Player์™€ PoolManager์— ์ ‘๊ทผ์ด ๊ฐ€๋Šฅํ•˜๋‹ค.
  • ์‹ฑ๊ธ€ํ†ค ํŒจํ„ด์„ ์ด์šฉํ•ด ๊ตฌํ˜„ํ•˜์—ฌ, ํ˜น์—ฌ๋‚˜ GameMangerํด๋ž˜์Šค๊ฐ€ 2๊ฐœ ์ƒ์„ฑ๋˜๋Š” ๊ฒƒ์„ ๋ง‰๋Š”๋‹ค.

๐Ÿ—’๏ธ

using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class GameManager : MonoBehaviour
{
    public static GameManager instance;
    public PoolManager pool;
    public Player player;

    private void Awake()
    {
        if (instance == null)
        {
            instance = this;
            DontDestroyOnLoad(gameObject);
        }
        else
        {
            if(instance != null)
                Destroy(this.gameObject);
        }
    }
}

PoolManager.cs

์˜ค๋ธŒ์ ํŠธ ํ’€๋ง์„ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ

  • ๊ฒŒ์ž„ ์ค‘ ์˜ค๋ธŒ์ ํŠธ๋ฅผ ์ƒ์„ฑ ๋ฐ ํŒŒ๊ดด๋ฅผ ํ•˜๊ฒŒ๋˜๋ฉด ์„ฑ๋Šฅ์˜ ์ €ํ•˜๊ฐ€ ๋ฐœ์ƒํ•  ์ˆ˜ ์žˆ๋‹ค.
  • SetActive๋ฅผ ์‚ฌ์šฉํ•ด ์˜ค๋ธŒ์ ํŠธ์˜ ํ™œ์„ฑํ™”๋กœ ์žฌํ™œ์šฉํ•˜์—ฌ ํ•ด๊ฒฐํ•œ๋‹ค.
  • ๋ณธ ๊ฒŒ์ž„์€ ๋งŽ์€ ์  ๋ฐ ๊ณต๊ฒฉ ํƒ„ํ™˜์„ ์‚ฌ์šฉํ•˜๊ธฐ ๋•Œ๋ฌธ์— ํ•„์ˆ˜๋ถˆ๊ฐ€๊ฒฐํ•˜๋‹ค.
  • pools์— ๋‹ด๊ธด ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ๋น„์–ด์žˆ๊ฑฐ๋‚˜ ๋ชจ๋‘ ํ™œ์„ฑํ™” ์ƒํƒœ์ผ ๊ฒฝ์šฐ Instantiate์œผ๋กœ ์ƒˆ๋กœ ์ƒ์„ฑํ•œ๋‹ค.
  • ๋น„ํ™œ์„ฑํ™”๋œ ์˜ค๋ธŒ์ ํŠธ๊ฐ€ ์žˆ๋‹ค๋ฉด, ํ™œ์„ฑํ™” ์‹œ์ผœ์ค€๋‹ค.

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class PoolManager : MonoBehaviour
{
    public GameObject[] prefabs;

    private List<GameObject>[] pools;

    private void Awake()
    {
        pools = new List<GameObject>[prefabs.Length];

        for (int i = 0; i < pools.Length; i++)
        {
            pools[i] = new List<GameObject>();
        }
    }

    public GameObject Get(int index)
    {
        GameObject select = null;

        foreach (GameObject item in pools[index])
        {
            if (item.activeSelf == false)
            {
                select = item;
                select.SetActive(true);
                break;
            }
        }

        if (select == null)
        {
            select = Instantiate(prefabs[index], transform);
            pools[index].Add(select);
        }
        return select;
    }
}

Scanner.cs

์  ๊ฐ์ง€๋ฅผ ์œ„ํ•œ ์Šคํฌ๋ฆฝํŠธ

  • Raycast๋ฅผ ์ด์šฉํ•ด ์ ์„ ํƒ์ง€ํ•œ๋‹ค.
  • CircleCastAll : ํŠน์ • ๋ฒ”์œ„ ๋ฐ ์œ„์น˜์— ์˜ค๋ธŒ์ ํŠธ๋“ค์„ ๊ฐ์ง€ํ•œ๋‹ค.

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Scanner : MonoBehaviour
{
    public float scanRange;
    public LayerMask targerLayer;
    public RaycastHit2D[] targets;
    public Transform nearestTarget;

    private void FixedUpdate()
    {
        targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector3.zero, 0, targerLayer);
        nearestTarget = GetNearest();
    }

    Transform GetNearest()
    {
        Transform result = null;
        float diff = 100;
        
        foreach (RaycastHit2D target in targets)
        {
            Vector3 myPos = transform.position;
            Vector3 targetPos = target.transform.position;
            float curDiff = Vector3.Distance(myPos, targetPos);

            if (curDiff < diff)
            {
                diff = curDiff;
                result = target.transform;
            }
        }

        return result;
    }
}

Spawner.cs

์  ์ƒ์„ฑ ์Šคํฌ๋„ˆ

  • ์ ์„ ์†Œํ™˜ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ
  • SpawnData ํด๋ž˜์Šค ์‚ฌ์šฉ

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using Random = UnityEngine.Random;

[System.Serializable]
public class SpawnData
{
    public float spawnTime;
    public int spriteType;
    public int health;
    public float speed;
}

public class Spawner : MonoBehaviour
{
    //public Transform[] spawnPoint; // ์Šคํฐ์ง€์ ์ด ๋‹ค์ˆ˜์ผ ๋•Œ
    public Transform spawnPoint;
    public SpawnData[] spawnDatas;
    
        
    private int level;
    private float timer;

    private void Awake()
    {
        //spawnPoint = GetComponentsInChildren<Transform>();
    }

    void Update()
    {
        timer += Time.deltaTime;
        // level = Mathf.Min(Mathf.FloorToInt(GameManager.instance.gameTime / 10f), spawnDatas.Length - 1);
        // Mathf.FloorToInt : ์†Œ์ˆ˜์  ์•„๋ž˜๋Š” ๋ฒ„๋ฆฌ๊ณ  Intํ˜•์œผ๋กœ ๋ฐ”๊พธ๋Š” ํ•จ์ˆ˜ ex) 3.4 -> 3
        // Mathf.CeilToInt : ์†Œ์ˆ˜์  ์•„๋ž˜๋ฅผ ์˜ฌ๋ฆฌ๊ณ  Intํ˜•์œผ๋กœ ๋ฐ”๊พธ๋Š” ํ•จ์ˆ˜ ex) 3.4 -> 4
        
        if (timer > spawnDatas[level].spawnTime)
        {
            timer = 0f;
            Spawn();
        }
    }

    void Spawn()
    {
        GameObject enemy = GameManager.instance.pool.Get(1);
        enemy.transform.position = spawnPoint.position;
        enemy.GetComponent<Enemy>().Init(spawnDatas[level]);
    }
}

Enemy.cs

์ ์˜ ์†๋„, ์ฒด๋ ฅ, ์ด๋™ ๋“ฑ์„ ๊ด€๋ฆฌํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ

  • Dead() ํ•จ์ˆ˜๋Š” ์• ๋‹ˆ๋ฉ”์ด์…˜ ์ด๋ฒคํŠธ๋ฅผ ์ด์šฉํ•˜์—ฌ ์  ์‚ฌ๋ง์‹œ 1์ดˆ ๋’ค์— ์‹œ์ฒด๊ฐ€ ์‚ฌ๋ผ์ง„๋‹ค.

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float speed;
    public float health; // ํ˜„์žฌ ์ฒด๋ ฅ
    public float maxHealth;
    public RuntimeAnimatorController[] animCon;
    public Rigidbody2D target;

    private bool isLive;

    private Rigidbody2D rigid;
    private Collider2D coll;
    private Animator anim;
    private SpriteRenderer spriter;
    private WaitForFixedUpdate wait;
    
    void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
        spriter = GetComponent<SpriteRenderer>();
        anim = GetComponent<Animator>();
        wait = new WaitForFixedUpdate();
        coll = GetComponent<Collider2D>();
    }

    void FixedUpdate()
    {
        // GetCurrentAnimatorStateInfo : ํ˜„์žฌ ์• ๋‹ˆ๋ฉ”์ดํ„ฐ์˜ ์ƒํƒœ ์ •๋ณด๋ฅผ ๊ฐ€์ ธ์˜ค๋Š” ํ•จ์ˆ˜
        if(!isLive || anim.GetCurrentAnimatorStateInfo(0).IsName("Hit"))
            return;
        
        // 1. ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์œ„์น˜ํ•œ ๋ฐฉํ–ฅ ๊ตฌํ•˜๊ธฐ
        Vector2 dirVec = target.position - rigid.position;
        // 2. ํ”Œ๋ ˆ์ด์–ด๊ฐ€ ์œ„์น˜ํ•œ ๊ณณ์œผ๋กœ ์–ด๋–ป๊ฒŒ ์›€์ง์ผ์ง€ ๊ณ„์‚ฐ
        Vector2 nextVec = dirVec.normalized * speed * Time.fixedDeltaTime;
        // 3. MovePosition์œผ๋กœ ํ˜„์žฌ ์œ„์น˜์—์„œ ๋‹ค์Œ ์œ„์น˜๋กœ ์ด๋™ ์ˆ˜ํ–‰
        rigid.MovePosition(rigid.position + nextVec);
        // 4. ๋ฆฌ์ง€๋“œ๋ฐ”๋””์˜ ๋ฌผ๋ฆฌ ์†๋„๊ฐ€ ์ด๋™์— ์˜ํ–ฅ์„ ์ฃผ์ง€ ์•Š๋„๋ก ๋ณด์™„ํ•˜๊ธฐ
        rigid.velocity = Vector2.zero;
    }

    private void LateUpdate()
    {
        if(!isLive)
            return;
        spriter.flipX = target.position.x < rigid.position.x;
    }

    private void OnEnable()
    {
        target = GameManager.instance.player.GetComponent<Rigidbody2D>();
        isLive = true;
        coll.enabled = true;
        rigid.simulated = true; // ๋ฆฌ์ง€๋“œ๋ฐ”๋”” ์ปดํฌ๋„ŒํŠธ ๋น„ํ™œ์„ฑํ™”๋Š” simulated์—์„œ ๊ด€๋ฆฌ
        spriter.sortingOrder = 2;
        anim.SetBool("Dead", false);
        health = maxHealth;
    }

    public void Init(SpawnData data)
    {
        anim.runtimeAnimatorController = animCon[data.spriteType];
        speed = data.speed;
        maxHealth = data.health;
        health = data.health;
    }

    void Dead()
    {
        gameObject.SetActive(false);
    }

    IEnumerator KnockBack()
    {
        yield return wait; // ํ•˜๋‚˜์˜ ๋ฌผ๋ฆฌํ”„๋ ˆ์ž„์„ ๋”œ๋ ˆ์ด
        Vector3 playerPos = GameManager.instance.player.transform.position;
        Vector3 dirVec = transform.position - playerPos;
        rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse);
    }
    
    private void OnTriggerEnter2D(Collider2D col)
    {
        if (!col.CompareTag("Bullet") || !isLive)
            return;
        
        health -= col.GetComponent<Bullet>().damage;
        StartCoroutine("KnockBack");
        
        if (health > 0)
        {
            // .. Live, Hit Action
            anim.SetTrigger("Hit");
        }
        else
        {
            // .. Die
            isLive = false;
            coll.enabled = false;
            rigid.simulated = false; // ๋ฆฌ์ง€๋“œ๋ฐ”๋”” ์ปดํฌ๋„ŒํŠธ ๋น„ํ™œ์„ฑํ™”๋Š” simulated์—์„œ ๊ด€๋ฆฌ
            spriter.sortingOrder = 1;
            anim.SetBool("Dead", true);
        }
    }
}

Tower.cs

์ ์„ ๊ณต๊ฒฉํ•˜๋Š” ํƒ€์›Œ ์Šคํฌ๋ฆฝํŠธ

  • Scanner์Šคํฌ๋ฆฝํŠธ์—์„œ ๋ฐœ๊ฒฌํ•œ ๊ฐ€์žฅ ๊ฐ€๊นŒ์šด ์ ์„ ๊ณต๊ฒฉํ•œ๋‹ค.

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;


public class Tower : MonoBehaviour
{
    public int towerType; // ํƒ€์›Œ ์œ ํ˜•
    public int damage; // ํƒ€์›Œ ๋ฐ๋ฏธ์ง€
    public float firePeriod; // ๋ฐœ์‚ฌ ์ฃผ๊ธฐ
    public float speed; // ๋ฐœ์‚ฌ ์†๋„
    
    private float timer;
    private Scanner scanner;
    private Quaternion towerLook;

    private void Awake()
    {
        scanner = GetComponent<Scanner>();
    }
    
    void Update()
    {
        timer += Time.deltaTime;

        if (timer > firePeriod)
        {
            timer = 0f;
            Fire();
        }
    }

    void Fire()
    {
        if (!scanner.nearestTarget)
            return;
        Debug.Log("๋ฐœ์‚ฌ์ค‘ : " + scanner.nearestTarget);
        Vector3 targetPos = scanner.nearestTarget.position;
        Vector3 dir = targetPos - transform.position;
        dir = dir.normalized;
        Transform bullet = GameManager.instance.pool.Get(towerType).transform;
        bullet.position = transform.position;
        bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir);
        bullet.GetComponent<Bullet>().Init(0, damage, speed, dir);
    }
}

Bullet.cs

ํƒ€์›Œ์—์„œ ๋ฐœ์‚ฌ๋œ ์ด์•Œ ์Šคํฌ๋ฆฝํŠธ

๐Ÿ—’๏ธ

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    public float damage;
    public int towerType;

    private Rigidbody2D rigid;
    
    private void Awake()
    {
        rigid = GetComponent<Rigidbody2D>();
    }

    public void Init(int towerType, float damage, float speed, Vector3 dir)
    {
        this.damage = damage;

        if (towerType == 0) // normal Tower
        {
            rigid.velocity = speed * dir;
        }
    }
    
    private void OnTriggerEnter2D(Collider2D col)
    {
        if (!col.CompareTag("Enemy"))
        {
            return;
        }
        rigid.velocity = Vector2.zero;
        gameObject.SetActive(false);
    }
    
    private void OnTriggerExit2D(Collider2D col)
    {
        if (col.CompareTag("Border"))
        {
            rigid.velocity = Vector2.zero;
            gameObject.SetActive(false);
        }
        
    }
}
profile
์น˜ํƒ€๊ฐ€ ๋˜๊ณ  ์‹ถ์€ ์ทจ์ค€์ƒ

0๊ฐœ์˜ ๋Œ“๊ธ€