ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 유니티 AOS게임 만들기 -포탑과 미니언 만들기
    유니티 2022. 5. 28. 00:24

    AOS게임은 포탑과 미니언그리고 중립몬스터를 사냥하여 성장하며 상대와 싸우는게임입니다.

    오늘은 미니언과 중립몬스터 그리고 포탑을 만들어보겠습니다.

    미니언은 player와 동일하지만 좀더 작고 색깔도 빨간색 혹은 파란색으로 만들어봅시다.

    - 미니언만들기 -

    i.미니언 오브젝트만들기

    ii.미니언 팀별 색깔 나누기

    iii.미니언 스크립트만들기

    1.공격스크립트

    2.이동스크립트

    3.애니메이션 스크립트(플레이어처럼)

     

    i.미니언 오브젝트만들기

    전에 쓰던 enemy 오브젝트의 transform.scale 을 0.7,  0.7,   0.7 로 전체의 70퍼센트의 크기로 만들어줬습니다.

    i.미니언 오브젝트만들기

    2.이동스크립트

    이친구를 미니언으로 만들겠습니다 그리고 enemy_moving_points라는 오브젝트도 하나 만들겠습니다. 이렇게해줘야 어디로 가야하는지 지정해줄때 편하기 떄문입니다.

     

    포인트의 위치는 마음대로 해도 좋지만 Y값은 0으로 해줍시다.

    enemy 오브젝트에 minion_moveing 스크립트를 추가하겠습니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class minion_moveing : MonoBehaviour
    {
        public NavMeshAgent agent;
        public GameObject moving_target;
        void Start(){
            agent = GetComponent<NavMeshAgent>();
            agent.SetDestination(moving_target.transform.position);
        }
    }

    이렇게 코드를 작성하면 moving_target의 위치로 이동 포인트가 찍히게 되는겁니다.

    2.이동스크립트

    3.애니메이션 스크립트(플레이어처럼)

    애니메이션 스크립트는 플레이어의 스크립트와 동일합니다.

    그러나 구분해주고싶다면 스크립트를하나더 만들어주면됩니다.

    Minion_Animation 으로 스크립트를 작성하겠습니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class Minion_Animation : MonoBehaviour
    {
        public Animator anim; //애니메이션 지정
        
        public UnityEngine.AI.NavMeshAgent agent; //네비게이션 지정
        public Stats stats;
    
        public float speed; //속도 지정
    
        float motionSmoothTime = 0.1f; //애니메이션 스무스틱 시간 지정
        void Start()
        {   stats = gameObject.GetComponent<Stats>(); //스텟 스크립트 지정
            agent = gameObject.GetComponent<NavMeshAgent>();//네비게이션 컴포넌트를 스크립트로 지정
            anim = gameObject.GetComponentInChildren<Animator>(); //자식오브젝트의 animator 컴포넌트를 스크립트로 지정
        }
        // Update is called once per frame
        void Update()
        {
            speed = agent.velocity.magnitude / agent.speed; //속도지정
            anim.SetFloat("speed", speed); //에니메이션 파라미터 speed를 변경함
            anim.SetFloat("attackspeed", stats.atk_speed); //에니메이션 재생속도 atk_speed를 변경함
        }
    }

    3.애니메이션 스크립트(플레이어처럼)

    1.공격스크립트

     

    이제 공격 스크립트를 만들건데 공격스크립트는 트리거를 이용해 만들어줍시다.

    우선 enemy에 빈 게임 오브젝트를하나 만들어 target_col로 하겠습니다

    만드는 이유는 enemy의 콜라이더는 레이캐스트를 통해 클릭을 받는 용으로 만 써야하는데

    같은 오브젝트에 두개의 콜라이더는 사용할 수 없기 떄문에 자식오브젝트에 콜라이더를 추가하고 리지드바디드도 추가해주겠습니다.

    1.레이어 Ignore Raycast(Raycast 무시하는 레이어)

    2.sphere collider 생성 트리거 체크 반지름 10(사정거리)

    3.Target_to_pa(스크립트)

    4.리지드바디 생성 중력사용 체크해제 포지션고정 XYZ 회전고정 XYZ

    저희가 만들 이동범위내에 들어오면 공격하는것은 자식오브젝트의 트리거에서 감지한값을 부모에게 보내줌으로써

    타겟을 지정할 수 있게해주는 스크립트를 짜주겠습니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class target_to_pa : MonoBehaviour
    {   public Minion_attack minion_attack;
        void Start(){
            minion_attack = GetComponentInParent<Minion_attack>();//부모에서 minion_attack 스크립트를 가져옴
        }
        private void OnTriggerEnter(Collider other) {//콜라이더가 다른 콜라이더와 만날때 한번 실행(other는 부딪힌 오브젝트)
            if(other.GetComponent<Stats>()!=null){//콜라이더에 닿은 오브젝트가 Stats 스크립트가 있으면(바닥같은 오브젝트 감지하지않게하기위해서)
                if(other.GetComponent<Stats>().team!=gameObject.GetComponentInParent<Stats>().team){//부모의 팀과 닿은 오브젝트의 팀이 다르면
                    minion_attack.target_list.Add(other.gameObject);//리스트에 닿은 오브젝트 추가
                }
            }
        }
        //private void OnTriggerStay(Collider other){여기에 사용할 코드를 입력하세요}//콜라이더가 다른 콜라이더와 만날때 매 프레임 실행
        private void OnTriggerExit(Collider other) {//콜라이더가 다른 콜라이더와 더이상 만나지 않을때 한번 실행
                minion_attack.target_list.Remove(other.gameObject);//리스트에 닿은 오브젝트 제거
                minion_attack.target=null;//타겟 초기화
        }
    }

    위의 스크립트에서 team이라는게 처음나왔습니다.

    저는 스크립트 Stats에 team 이라는 값을 추가해서 팀을 구분하도록 코드를 만들었습니다.

    Stats 스크립트에

    public team;

    을 추가하여 0팀 중립 1팀 레드 2팀 블루

    나중에는 자신이 미니언일 경우 0팀은 공격하지않게하는 코드를 작성해봅시다.

    이제 enemy의 컴포넌트에 

    Minion_attack 스크립트를 만들어줍시다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class Minion_attack : MonoBehaviour
    {
        public float rotateVelocity;//회전속도
        public float rotateSpeedForAttack;//공격회전속도
        public NavMeshAgent agent;
        public GameObject target;
        public Stats stats;
        public List<GameObject> target_list;
        public Animator anim;
        public minion_moveing minion_move;
        public SphereCollider attack_collider;
        void Start()
        {   
            agent = GetComponent<NavMeshAgent>();
            stats = GetComponent<Stats>();
            target_list = new List<GameObject>();
            anim = GetComponentInChildren<Animator>();
            minion_move = GetComponent<minion_moveing>();
            attack_collider = GetComponentInChildren<SphereCollider>();
        }
    
        void Update(){
            //리스트가 비어있지 않으면
            if(target_list.Count>0){ 
            target = target_list[0];//항상 첫번째를 타겟으로 설정
            }
            //리스트에서 가장 가까운 적을 타겟으로 지정
                if(target!=null){
                    if(Vector3.Distance(transform.position,target.transform.position)>stats.atk_range){//target의 위치와 플레이어의 위치의 거리가 공격범위보다 크면
                    agent.SetDestination(target.transform.position);//목표지점을 타겟의 위치 로 지정
                    agent.stoppingDistance = stats.atk_range;//공격범위를 넘어가면 멈춤
                if(Vector3.Distance(transform.position,target.transform.position)>10){//타겟과 일정거리를 벗어나면
                    agent.SetDestination(transform.position);//자기 자신의 위치로 이동
                    agent.stoppingDistance = 0;//멈춤
                    }
                    }
                    else{
                        Quaternion rotationToLookAt = Quaternion.LookRotation(target.transform.position - transform.position);//타겟의 위치와 플레이어의 위치의 거리를 계산하여 회전값을 계산
                        float rotationY = Mathf.SmoothDampAngle(transform.eulerAngles.y,rotationToLookAt.eulerAngles.y,ref rotateVelocity,rotateSpeedForAttack * (Time.deltaTime * 5));
                        transform.eulerAngles = new Vector3(0, rotationY, 0); //위의 계산을 통해 회전함 (타겟을 쳐다봄)
                        agent.SetDestination(transform.position);
                        if(target != null){
                            anim.SetBool("attack",true);//어택 시작
                        }    
                }
                }
                else{
                    agent.SetDestination(minion_move.moving_target.transform.position);
                    anim.SetBool("attack",false);//어택 종료
                }
        }
        public void attack_dmg(){
            target.GetComponent<Stats>().TakeDamage(stats.atk);//타겟의 stats의 takeDamage함수를 호출하여 오브젝트의 공격력을 인수로 넘겨줌
        }
    }

    미니언 또한 player와 같이 애니메이터 컴포넌트가 있는 boxman_stand 오브젝트에서

    attack_dmg라는 신호를 발생하게되는데

    이것을 체크하는 

    attack_cheak_Minion 스크립트를 만들고

    작성해줍시다. 

    플레이어가 쓰는것과 비슷합니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class attack_cheack_Minion : MonoBehaviour
    {   public Minion_attack M_A;
        void Start()=>M_A=this.GetComponentInParent<Minion_attack>();
        public void attack_dmg()=>M_A.attack_dmg();
    }

    1.공격스크립트

    ii.미니언 팀별 색깔 나누기

    이제 파란색과 빨간색 미니언을 만들어봅시다.

     

     

    메테리얼을 cube,큐브~ 에 하나씩 드래그해서 넣어주면 색이 변하는것을 알 수 있습니다. 

     

    우선 미니언을 지속적으로 생산하려면 복사붙여넣기를 계속해야할까요?

    아닙니다.

    저희가 이렇게 만들어놓은것을 프리팹으로 만들어준뒤 이프리팹을 생성하도록 함으로써

    필요할때 만들어 쓸수 있습니다.

    가령 총알을 발사할때 저희가 게임할떄마다 계속 총알을 복사붙여넣기 할 순 없죠?

    프리팹은 프리팹생성으로 만들수있지만 저희는 이미 만들어놓은게 있기때문에 드래그해서 끌어오겠습니다.

    이렇게 드래그만해주어도 프리팹으로 생성해줍니다.

    이제 한번더 해주시면 원본으로 만들건지 물어보는데 원본으로 만들면됩니다.

    enemy_red 1 과 enemy_red가 있습니다.

    이중 red1을 blue로 바꿔줍시다.

    그후 프리팹을 더블클릭해주세요.

    아까의 메테리얼을 넣는것을 이용해 파란색으로만들어준뒤

    stats 스크립트의 team을 2로 세팅해주시면 완성입니다.

    2번팀파랑이&nbsp; &nbsp; &nbsp; &nbsp;1번팀빨강이

    자이렇게 파랑색 프리팹과 빨간색프리팹을 만들어주었습니다.

    ii.미니언 팀별 색깔 나누기

    이제 미니언이 생성되는곳을 만들어봅시다.

    nexus.blend
    0.75MB

    제가만든 넥서스 입니다. 전과같이 애니메이션에 루프를 지정해줍시다

    그리고 

    애니메이션을 nexus 오브젝트에 넣어주면 자동으로 에니메이션 컨트롤러가 생성됩니다.

    넥서스에게 전에 만든 Stats 스크립트를 추가합니다.체력과 최대체력을 100으로 설정합니다.

    이렇게 넥서스를 만든뒤에는 미니언이 생성될 위치와 미니언을 생성해주는 스크립트가 필요합니다.

    nexus_make_minion 스크립트를 작성하겠습니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class nexus_make_minion : MonoBehaviour
    {
        public GameObject minion;
    //코루틴
    
        IEnumerator make_minion()//3초마다 실행
        {   
            yield return new WaitForSeconds(3f);//3초뒤 실행
            Instantiate(minion, transform.position, transform.rotation); //미니언을 인스턴스화 시킴 (오브젝트 ,위치, 회전);
            StartCoroutine(make_minion());//코루틴 재실행
        }
        void Start()
        {
            StartCoroutine(make_minion());
        }
    
    }

    정말 간단하게 만들어보았습니다.

    그러나 이렇게 만들어진 오브젝트는 씬의 정보를 알수 없기 때문에

    스크립트에서 지정해주었던것이 풀리는 경우가 있습니다.

    minion 같은경우 minion_moving 스크립트의 moving_target이 지정되지않은 상태로 생성되게 되기떄문에

    생성된자리에서 멈춰있는것을 볼 수 있습니다.

    이것을 해소하기위해 스크립트를 조금 변경했습니다.

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    using UnityEngine.AI;
    
    public class minion_moveing : MonoBehaviour
    {
        public NavMeshAgent agent;
        public GameObject moving_target;
        void Start(){
            agent = GetComponent<NavMeshAgent>();
            moving_target=GameObject.Find("enemy_moving_points");
            agent.SetDestination(moving_target.transform.position);
        }
    }

     

    미니언 오브젝트를 지정할때 꼭 에셋에서 찾아줍시다.

    블루팀과 레드팀의 넥서스를 만든뒤 프리팹화합시다.

    블루=팀2 레드=팀1

    만들어둔 프리팹을 씬에 끌어서 생성한뒤 플레이해봅시다.

     

    댓글

Designed by Tistory.