반응형

플레이어가 공격하였을 때 몬스터에게 충격을 가하는 이팩트를 만들겠습니다

 

Player를 선택하고 Animation을 들어갑니다

 

 

Player의 animation의 Hikkick을 선택합니다

이것은 Player가 공격할 때 취하는 애니메이션입니다

지금 이것이 Read-Only로 읽기 전용으로 되어 있습니다

이것을 바꾸어야 합니다 

 

 

 

 

 

 

 

 

Hikkick 애니메이션을 찾아 Ctrl +D를 눌러서 복사합니다

 

복사본 이름을 Hikick_new로 바꾸고  Animator에 들어가서 attack에 복사본을  motion에 드래그해서 바꾸어 놓습니다

 

PlayerFSM을 선택하고 수정합니다

 

 

 

 

 

 

 

PlayerFSM 함수 추가


public class PlayerFSM : MonoBehaviour
{    
    public enum State
    {
        Idle,
        Move,
        Attack,
        AttackWait,
        Dead
    }
    //idle 상태를 기본 상태로 지정
    public State currentState = State.Idle;

    //마우수 클릭 지점, 플레이어가 이동할 목적지의 좌표를 저장할 예정
    Vector3 curTargetPos;

    GameObject curEnemy;

    public float rotAnglePerSecond = 360f; //1초에 플레이어의 방향을 360도 회전한다

    public float moveSpeed = 2f; //초당 2미터의 속도로 이동

    float attackDelay = 2f; // 공격을 한번 하고 다시 공격할 때까지의 지연시간

    float attackTimer = 0f; //공격을 하고 난 뒤에 경과되는 시간을 계산하기 위한 변수

    float attackDistance = 1.5f; // 공격 거리 (적과의 거리)

    float chaseDistance = 2.5f; // 전투 중 적이 도망가면 다시 추적을 시작하기 위한 거리

    PlayerAni myAni;
    void Start()
    {
        myAni = GetComponent<PlayerAni>();
        //  myAni.ChangeAni(PlayerAni.ANI_WALK);

        ChangeState(State.Idle, PlayerAni.ANI_IDLE);
    }


    public void AttackCalculate()
    {
        if (curEnemy == null)
        {
            return;
        }
        curEnemy.GetComponent<EnemyFSM>().ShowHitEffect();
    }

    // 적을 공격하기 위한 함수 
    public void AttackEnemy(GameObject enemy)
    {
        if (curEnemy != null && curEnemy == enemy)
        {
            return;
        }
        curEnemy = enemy;
        curTargetPos = curEnemy.transform.position;

        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }
    void ChangeState(State newState, int aniNumber)
    {
        if (currentState == newState)
        {
            return;
        }
        myAni.ChangeAni(aniNumber);
        currentState = newState;
    }

    //캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의
    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:
                IdleState();
                break;
            case State.Move:
                MoveState();
                break;
            case State.Attack:
                AttackState();
                break;
            case State.AttackWait:
                AttackWaitState();
                break;
            case State.Dead:
                DeadState();
                break;
            default:
                break;
        }
    }
   void IdleState()
    {
    }
    void MoveState()
    {
        TurnToDestination();
        MoveToDestination();
    }
    void AttackState()
    {
        attackTimer = 0f;

        //transform.LookAt(목표지점 위치) 목표지점을 향해 오브젝트를 회전시키는 함수
        transform.LookAt(curTargetPos);
        ChangeState(State.AttackWait, PlayerAni.ANI_ATKIDLE);
    }
    void AttackWaitState()
    {
        if (attackTimer > attackDelay)
        {
            ChangeState(State.Attack, PlayerAni.ANI_ATTACK);

        }

        attackTimer += Time.deltaTime;
    }
    void DeadState()
    {

    }

    //MoveTo(캐릭터가 이동할 목표 지점의 좌표)
   public void MoveTo(Vector3 tPos)
    {
        curEnemy = null;
        curTargetPos = tPos;
        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }

    void TurnToDestination()
    {
        // Quaternion lookRotation(회전할 목표 방향) : 목표 방향은 목적지 위치에서 자신의 위치를 빼면 구함
        Quaternion lookRotation = Quaternion.LookRotation(curTargetPos - transform.position);

        //Quaternion.RotateTowards(현재의 rotation값, 최종 목표 rotation 값, 최대 회전각)
        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        //Vector3.MoveTowards(시작 지점, 목표지점, 최대 이동거리)
        transform.position = Vector3.MoveTowards(transform.position, curTargetPos,moveSpeed * Time.deltaTime);

        if (curEnemy == null)
        {
            //플레이어의 위치와 목표지점의 위치가 같으면, 상태를 Idle 상태로 바꾸라는 명령
            if (transform.position == curTargetPos)
            {
                ChangeState(State.Idle, PlayerAni.ANI_IDLE);
            }
        }
        else if (Vector3.Distance(transform.position,curTargetPos) < attackDistance)//Vector3.Distance(A,B):A와B사이의 거리
        {
            ChangeState(State.Attack, PlayerAni.ANI_ATTACK);
        }
    }
    void Update()
    {
        UpdateState();
    }
}

 

 

 

 

 

 

 

 

 

그리고 Animation을 활성화시켜서 Player 공격하는 시점 프레임에 Add Event를 추가시켜 AttackCalculate() 함수를 등록시킵니다.

 

 

Asset Store에 들어가서 Simple Partice Pack을 다운로드합니다

 

Simpe Particle Pack에서 Explosions에 Burst를 임포트 합니다

 

 

 

 

 

 

 

 

 

Enemy 오브젝트를 선택하고 자식으로 빈 오브젝트를 만들고 이름을 EffectPos 라 하고  임포트 한 Burst 프리 팹을 찾아 드래그해서 자식으로 놓습니다 

그리고 Enemy 오브젝트에 머리 위로 위치시켜 이팩트가 터지면 잘 보이도록 위치시킵니다 

 

EnemyFSM 스크립트를 선택하고 수정합니다

 

 

 

 

 

 

 

 

 



public class EnemyFSM : MonoBehaviour
{
    public enum State
    {
        Idle,  //정지
        Chase,  //추적
        Attack,  //공격
        Dead,   //사망
        NoState  //아무 일도 없는 상태
    }

    public State currentState = State.Idle;

    EnemyAni myAni;

    Transform player;

    float chaseDistance = 5f; // 플레이어를 향해 몬스터가 추적을 시작할 거리
    float attackDistance = 2.5f; // 플레이어가 안쪽으로 들오오 게 되면 공격을 시작
    float reChaseDistance = 3f; // 플레이어가 도망갈 경우 얼마나 떨어져야 다시 추적

    float rotAnglePerSecond = 360f; // 초당 회전 각도
    float moveSpeed = 1.3f; // 몬스터의 이동 속도

    float attackDelay = 2f;
    float attackTimer = 0f;

    public ParticleSystem hitEffect;

    void Start()
    {
        myAni = GetComponent<EnemyAni>();
        ChangeState(State.Idle, EnemyAni.IDLE);

        player = GameObject.FindGameObjectWithTag("Player").transform;

        hitEffect.Stop();
    }

    public void ShowHitEffect()
    {
        hitEffect.Play();
    }

    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:
                IdleState();
                break;
            case State.Chase:
                ChaseState();
                break;
            case State.Attack:
                AttackState();
                break;
            case State.Dead:
                DeadState();
                break;
            case State.NoState:
                NoState();
                break;          
        }
    }

 

 


    public void ChangeState(State newState, string aniName)
    {
        if (currentState == newState)
        {
            return;
        }

        currentState = newState;
        myAni.ChangeAni(aniName);
    }
    void IdleState()
    {
        if (GetDistanceFromPlayer() < chaseDistance)
        {
            ChangeState(State.Chase, EnemyAni.WALK);
        }
    }

    void ChaseState()
    {
        //몬스터가 공격 가능 거리 안으로 들어가면 공격 상태
        if (GetDistanceFromPlayer() < attackDistance)
        {
            ChangeState(State.Attack, EnemyAni.ATTACK);
        }
        else
        {
            TurnToDestination();
            MoveToDestination();
        }   
    }

    void AttackState()
    {
        if (GetDistanceFromPlayer() > reChaseDistance)
        {
            attackTimer = 0f;
            ChangeState(State.Chase, EnemyAni.WALK);
        }
        else
        {
            if (attackTimer > attackDelay)
            {
                transform.LookAt(player.position);
                myAni.ChangeAni(EnemyAni.ATTACK);

                attackTimer = 0f;
            }

            attackTimer += Time.deltaTime;
        }
    }
    void DeadState()
    {

    }
    void NoState()
    {

    }

    void TurnToDestination()
    {
        Quaternion lookRotation = Quaternion.LookRotation(player.position - transform.position);

        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        transform.position = Vector3.MoveTowards(transform.position, player.position, moveSpeed * Time.deltaTime);
    }

    //플레이어와 거리을 재는 함수
    float GetDistanceFromPlayer()
    {
        float distance = Vector3.Distance(transform.position, player.position);
        return distance;
    }

    // Update is called once per frame
    void Update()
    {
        UpdateState();
    }
}

 

 

 

Enemy를 선택하고 자식으로 두었던 Burst 이팩트를 수정한 스크립트에 등록합니다

 

Burst 이팩트를 활성화시킵니다

 

 

 

 

게임을 실행시켜 플레이어를 적에게 공격하면 이팩트가 잘 터지는지 확인합니다

 

https://youtu.be/y79imRjp1rM

 

반응형
반응형

몬스터인 적이 플레이어를 공격하고 추적하는 스크립트를 만들겠습니다

 

먼저 EnemyFSM 스크립트를 만들고 Enemy 오브젝트에 붙힘니다.

 

그리고 EnemyFSM 스크립트 작성

 

 

 

 

 

 

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

public class EnemyFSM : MonoBehaviour
{
    public enum State
    {
        Idle,  //정지
        Chase,  //추적
        Attack,  //공격
        Dead,   //사망
        NoState  //아무 일도 없는 상태
    }

    public State currentState = State.Idle;

    EnemyAni myAni;

    Transform player;

    float chaseDistance = 5f; // 플레이어를 향해 몬스터가 추적을 시작할 거리
    float attackDistance = 2.5f; // 플레이어가 안쪽으로 들오오게 되면 공격을 시작
    float reChaseDistance = 3f; // 플레이어가 도망 갈 경우 얼마나 떨어져야 다시 추적

    float rotAnglePerSecond = 360f; // 초당 회전 각도
    float moveSpeed = 1.3f; // 몬스터의 이동 속도

    float attackDelay = 2f;
    float attackTimer = 0f;

    void Start()
    {
        myAni = GetComponent<EnemyAni>();
        ChangeState(State.Idle, EnemyAni.IDLE);

        player = GameObject.FindGameObjectWithTag("Player").transform;
    }

    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:
                IdleState();
                break;
            case State.Chase:
                ChaseState();
                break;
            case State.Attack:
                AttackState();
                break;
            case State.Dead:
                DeadState();
                break;
            case State.NoState:
                NoState();
                break;          
        }
    }

    public void ChangeState(State newState, string aniName)
    {
        if (currentState == newState)
        {
            return;
        }

        currentState = newState;
        myAni.ChangeAni(aniName);
    }
    void IdleState()
    {
        if (GetDistanceFromPlayer() < chaseDistance)
        {
            ChangeState(State.Chase, EnemyAni.WALK);
        }
    }

    void ChaseState()
    {
        //몬스터가 공격 가능 거리 안으로 들어가면 공격 상태
        if (GetDistanceFromPlayer() < attackDistance)
        {
            ChangeState(State.Attack, EnemyAni.ATTACK);
        }
        else
        {
            TurnToDestination();
            MoveToDestination();
        }   
    }

    void AttackState()
    {
        if (GetDistanceFromPlayer() > reChaseDistance)
        {
            attackTimer = 0f;
            ChangeState(State.Chase, EnemyAni.WALK);
        }
        else
        {
            if (attackTimer > attackDelay)
            {
                transform.LookAt(player.position);
                myAni.ChangeAni(EnemyAni.ATTACK);

                attackTimer = 0f;
            }

            attackTimer += Time.deltaTime;
        }
    }

    void DeadState()
    {

    }

    void NoState()
    {

    }

    void TurnToDestination()
    {
        Quaternion lookRotation = Quaternion.LookRotation(player.position - transform.position);

        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        transform.position = Vector3.MoveTowards(transform.position, player.position, moveSpeed * Time.deltaTime);
    }


    //플레이어와 거리을 재는 함수
    float GetDistanceFromPlayer()
    {
        float distance = Vector3.Distance(transform.position, player.position);
        return distance;
    }

    // Update is called once per frame
    void Update()
    {
        UpdateState();
    }
}

 

 

 

 

 

그리고 EnemyAni 스크립트를 만들고 Enemy 오브젝트에 붙힘니다

EnemyAni 스크립트 작성

 

 

 

 

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

public class EnemyAni : MonoBehaviour
{
    public const string IDLE = "idle";
    public const string WALK = "walk";
    public const string ATTACK = "attack1";
    public const string DIE = "death1";

    Animation anim;
    // Start is called before the first frame update
    void Start()
    {
        anim = GetComponentInChildren<Animation>();
    }

    public void ChangeAni(string aniName)
    {
        anim.CrossFade(aniName);
    }
    // Update is called once per frame
    void Update()
    {
        
    }
}

 

 

 

 

게임을 실행시켜서 player가 적에게 다가갔을때 적이 공격하고 Player가 달아 났을 때 적이 추적하는지 확인 합니다

https://youtu.be/w1RNqHVehIc

 

반응형
반응형

적을 공격하는 함수 만들기

 

//Player를 선택하고 PlayerFSM 스크립트을 수정합니다

// PlayerFSM 스크립트 수정

 

 

 

 


public class PlayerFSM : MonoBehaviour
{
  
    public enum State
    {
        Idle,
        Move,
        Attack,
        AttackWait,
        Dead
    }
    //idle 상태를 기본 상태로 지정
    public State currentState = State.Idle;

    //마우수 클릭 지점,플레이어가 이동할 목적지의 좌표를 저장할 예정
    Vector3 curTargetPos;

    GameObject curEnemy;

    public float rotAnglePerSecond = 360f; //1초에 플레이어의 방향을 360도 회전한다

    public float moveSpeed = 2f; //초당 2미터의 속도로 이동

    float attackDelay = 2f; // 공격을 한번하고 다시 공격할때 까지의 지연시간

    float attackTimer = 0f; //공격을 하고 난 뒤에 경과되는 시간을 계산하기 위한 변수

    float attackDistance = 1.5f; // 공격 거리 (적과의 거리)

    float chaseDistance = 2.5f; // 전투 중 적이 도망가면 다시 추적을 시작 하기 위한 거리



    PlayerAni myAni;


    void Start()
    {
        myAni = GetComponent<PlayerAni>();
        //  myAni.ChangeAni(PlayerAni.ANI_WALK);

        ChangeState(State.Idle, PlayerAni.ANI_IDLE);
    }

    // 적을 공격하기 위한 함수 
    public void AttackEnemy(GameObject enemy)
    {
        if (curEnemy != null && curEnemy == enemy)
        {
            return;
        }

        curEnemy = enemy;
        curTargetPos = curEnemy.transform.position;

        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }


    void ChangeState(State newState, int aniNumber)
    {
        if (currentState == newState)
        {
            return;
        }

        myAni.ChangeAni(aniNumber);
        currentState = newState;
    }

    //캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의
    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:

                IdleState();

                break;
            case State.Move:

                MoveState();

                break;
            case State.Attack:

                AttackState();

                break;
            case State.AttackWait:

                AttackWaitState();

                break;
            case State.Dead:

                DeadState();

                break;
            default:
                break;
        }

    }

    void IdleState()
    {

    }

    void MoveState()
    {
        TurnToDestination();
        MoveToDestination();
    }

    void AttackState()
    {
        attackTimer = 0f;

        //transform.LookAt(목표지점 위치) 목표지점을 향해 오브젝트를 회전 시키는 함수
        transform.LookAt(curTargetPos);
        ChangeState(State.AttackWait, PlayerAni.ANI_ATKIDLE);
    }

    void AttackWaitState()
    {
        if (attackTimer > attackDelay)
        {
            ChangeState(State.Attack, PlayerAni.ANI_ATTACK);

        }

        attackTimer += Time.deltaTime;
    }

    void DeadState()
    {

    }

    //MoveTo(캐릭터가 이동할 목표 지점의 좌표)
   public void MoveTo(Vector3 tPos)
    {
        curEnemy = null;
        curTargetPos = tPos;
        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }

    void TurnToDestination()
    {
        // Quaternion lookRotation(회전할 목표 방향) : 목표 방향은 목적지 위치에서 자신의 위치를 빼면 구함
        Quaternion lookRotation = Quaternion.LookRotation(curTargetPos - transform.position);

        //Quaternion.RotateTowards(현재의 rotation값, 최종목표rotation 값, 최대 회전각)
        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        //Vector3.MoveTowards(시작지점, 목표지점,최대이동거리)
        transform.position = Vector3.MoveTowards(transform.position, curTargetPos,moveSpeed * Time.deltaTime);

        if (curEnemy == null)
        {
            //플레이어의 위치와 목표지점의 위치가 같으면, 상태를 Idle 상태로 바꾸라는 명령
            if (transform.position == curTargetPos)
            {
                ChangeState(State.Idle, PlayerAni.ANI_IDLE);
            }
        }
        else if (Vector3.Distance(transform.position,curTargetPos) < attackDistance)
        {
            ChangeState(State.Attack, PlayerAni.ANI_ATTACK);
        }

  
    }
    void Update()
    {
        UpdateState();
    }
}

 

 

 

 

 

InputManager 오브젝트를 선택하고 InputManager 스크립트를 수정합니다

//InputManager 스크립트 수정

 


public class InputManager : MonoBehaviour
{

    GameObject player;
    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
    }

    void CheckClick()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //카메라로부터 화면사의 좌표를 관통하는 가상의 선(레이)을 생성해서 리턴해 주는 함수
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            RaycastHit hit;

            //Physics.Raycast(래이 타입 변수, out 레이캐스트 히트 타입변수) : 
            //가상의 레이저선(래이)이 충돌체와 충돌하면, true(참) 값을 리턴하면서 동시에 레이캐스트 히트 변수에 충돌 대상의 정보를 담아 주는 함수           
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.gameObject.name == "Terrain")
                {
                    // player.transform.position = hit.point;

                    //마우스 클릭 지점의 좌표를 플레이어가 전달받은뒤,상태를 이동상태로 바뀜
                    player.GetComponent<PlayerFSM>().MoveTo(hit.point);
                }
                else if (hit.collider.gameObject.tag == "Enemy")//마우스 클릭한 대상이 적 캐릭터인 경우
                {
                    player.GetComponent<PlayerFSM>().AttackEnemy(hit.collider.gameObject);
                }
            }
        }

    }
    // Update is called once per frame
    void Update()
    {
        CheckClick();
    }
}

 

 

 

 

게임을 실행시켜서 플레이어가 적을 공격하는지 확인합니다

0

반응형
반응형

Asset Store를 열고 free Fantasy Spider를 찾아 임포트 합니다

 

 

 

 

 

 

 

 

Assets/fantasySpider/spider_myOldOne.FBX 를 선택하여 Model을 선택하고 Scale Factor를 0.015를 입력합니다

 

 

 

 

 

빈 게임오브젝트를 Hierarchy에 만들고 이름을 Enemy 라 합니다

 

 

 

 

 

 

 

새로 만든 Enemy 오브젝트를 선택하고 Inspector에서 tah를 Enemy 만들고 선택합니다

 

 

 

 

Enemy 오브젝트의 자식으로 Asset Store 에서 다운로드한 Spider_myOldOne을 드래그하여 올려놓습니다

 

 

spider_myOldOne의 이름을 Spider로 바꿉니다

 

 

Hierarchy의 Enemy Spider를 선택하고 기본 애니메이션을 Idle로 바꿉니다

 

 

 

 

 

 

 

Enemy 오브젝트를 선택하고 Component -> Physics -> Box Collider를 생성합니다

 

Box Collider의 Size를 Enemy 캐릭터에 맞게 조정합니다

 

 

Enemy를 선택하고 Component -> Physics -> Rigidbody를 선택하여 RigidBody를 생성합니다

 

Box Collider에서 is Trigger를 클릭하고 RigidBody 애 서 use Gravity를 선택 해제하고 is Kinematic을 클릭합니다 

 

 

 

 

 

 

 

 

 

게임을 실행하면 Enemy Idle 동작을 하는 모습  

0

 

반응형
반응형

 

0
Camera following

 

 

 

 

 

PlayerFSM 스크립트를 열고 각각의 상태가 되면 자동으로 실행되는 함수들을 별도로 만들어 보겠습니다


public class PlayerFSM : MonoBehaviour
{
  
    public enum State
    {
        Idle,
        Move,
        Attack,
        AttackWait,
        Dead
    }
    //idle 상태를 기본 상태로 지정
    public State currentState = State.Idle;

    //마우수 클릭 지점,플레이어가 이동할 목적지의 좌표를 저장할 예정
    Vector3 curTargetPos;

    public float rotAnglePerSecond = 360f; //1초에 플레이어의 방향을 360도 회전한다

    public float moveSpeed = 2f; //초당 2미터의 속도로 이동

    PlayerAni myAni;


    void Start()
    {
        myAni = GetComponent<PlayerAni>();
        ChangeState(State.Idle, PlayerAni.ANI_IDLE);
    }

    void ChangeState(State newState, int aniNumber)
    {
        if (currentState == newState)
        {
            return;
        }

        myAni.ChangeAni(aniNumber);
        currentState = newState;
    }

    //캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의
    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:

                IdleState();

                break;
            case State.Move:

                MoveState();

                break;
            case State.Attack:

                AttackState();

                break;
            case State.AttackWait:

                AttackWaitState();

                break;
            case State.Dead:

                DeadState();

                break;
            default:
                break;
        }

    }

    void IdleState()
    {

    }

    void MoveState()
    {
        TurnToDestination();
        MoveToDestination();
    }

    void AttackState()
    {

    }

    void AttackWaitState()
    {

    }

    void DeadState()
    {

    }

    //MoveTo(캐릭터가 이동할 목표 지점의 좌표)
   public void MoveTo(Vector3 tPos)
    {
        curTargetPos = tPos;
        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }

    void TurnToDestination()
    {
        // Quaternion lookRotation(회전할 목표 방향) : 목표 방향은 목적지 위치에서 자신의 위치를 빼면 구함
        Quaternion lookRotation = Quaternion.LookRotation(curTargetPos - transform.position);

        //Quaternion.RotateTowards(현재의 rotation값, 최종 목표 rotation 값, 최대 회전각)
        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        //Vector3.MoveTowards(시작지점, 목표지점, 최대 이동거리)
        transform.position = Vector3.MoveTowards(transform.position, curTargetPos,moveSpeed * Time.deltaTime);


        //플레이어의 위치와 목표지점의 위치가 같으면, 상태를 Idle 상태로 바꾸라는 명령
        if (transform.position == curTargetPos)
        {
            ChangeState(State.Idle, PlayerAni.ANI_IDLE);
        }
    }
    void Update()
    {
        UpdateState();
    }
}

 

 

 

CameraControl 스크립트를 새로 만들고 CameraControl 스크립트를 Main Camera에 붙입니다

 

CameraControl 스크립트 생성

 

Main Camera에 붙입니다

 

 

 

 

//CameraControl 스크립트 작성

 

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

public class CameraControl : MonoBehaviour
{
    public Transform player;

    Vector3 offset;
    // Start is called before the first frame update
    void Start()
    {
        offset = player.position - transform.position;
    }

    //카메라가 플레이어 움직임에 한 템포 늦게 움직임을 준다
    void LateUpdate()
    {
        //플레이어의 위치와 카메라의 위치를 최초 저장한 위치  차이만큼 자동으로 유지시켜주게 됨
        transform.position = player.position - offset;
    }
}

 

 

 

CameraControl 스크립트 작성하고 Player 오브젝트를 드래그하여 inspector 변수에 넣습니다

 

게임을 실행시키면 카메라가 플레이어를 따라가는 것을 확인할 수 있습니다

 

반응형
반응형

0

 

플레이어를 이동 시키기 위해서 InputManager 스크립트를 수정 하겠습니다

InputManager 스크립트를 엽니다

 

 

 

 

 

 

 

//InputManager 스크립트 수정

 

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

public class InputManager : MonoBehaviour
{

    GameObject player;
    // Start is called before the first frame update
    void Start()
    {
        player = GameObject.FindGameObjectWithTag("Player");
    }

    void CheckClick()
    {
        if (Input.GetMouseButtonDown(0))
        {
            //카메라로부터 화면사의 좌표를 관통하는 가상의 선(레이)을 생성해서 리턴해 주는 함수
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);

            RaycastHit hit;

            //Physics.Raycast(래이 타입 변수, out 레이캐스트 히트 타입변수) : 
            //가상의 레이저선(래이)이 충돌체와 충돌하면, true(참) 값을 리턴하면서 동시에 레이캐스트 히트 변수에 충돌 대상의 정보를 담아 주는 함수           
            if (Physics.Raycast(ray, out hit))
            {
                if (hit.collider.gameObject.name == "Terrain")
                {
                    // player.transform.position = hit.point;

                    //마우스 클릭 지점의 좌표를 플레이어가 전달받은뒤,상태를 이동상태로 바뀜
                    player.GetComponent<PlayerFSM>().MoveTo(hit.point);                

                 }
            }
        }

    }
    // Update is called once per frame
    void Update()
    {
        CheckClick();
    }
}

 

 

 

 

 

 

 

 

 

PlayerFSM 스크립트를 선택하고 스크립트를 엽니다

그리고 수정합니다

 

 

//PlayerFSM 스크립트 수정

 

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

public class PlayerFSM : MonoBehaviour
{    
    public enum State
    {
        Idle,
        Move,
        Attack,
        AttackWait,
        Dead
    }
    //idle 상태를 기본 상태로 지정
    public State currentState = State.Idle;

    //마우수 클릭 지점,플레이어가 이동할 목적지의 좌표를 저장할 예정
    Vector3 curTargetPos;

    public float rotAnglePerSecond = 360f; //1초에 플레이어의 방향을 360도 회전한다

    public float moveSpeed = 2f; //초당 2미터의 속도로 이동

    PlayerAni myAni;


    void Start()
    {
        myAni = GetComponent<PlayerAni>();
        //  myAni.ChangeAni(PlayerAni.ANI_WALK);

        ChangeState(State.Idle, PlayerAni.ANI_IDLE);
    }


    void ChangeState(State newState, int aniNumber)
    {
        if (currentState == newState)
        {
            return;
        }

        myAni.ChangeAni(aniNumber);
        currentState = newState;
    }

    //캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의
    void UpdateState()
    {
        switch (currentState)
        {
            case State.Idle:
                break;
            case State.Move:
                TurnToDestination();
                MoveToDestination();
                break;
            case State.Attack:
                break;
            case State.AttackWait:
                break;
            case State.Dead:
                break;
            default:
                break;
        }

    }

    //MoveTo(캐릭터가 이동할 목표 지점의 좌표)
   public void MoveTo(Vector3 tPos)
    {
        curTargetPos = tPos;
        ChangeState(State.Move, PlayerAni.ANI_WALK);
    }

    void TurnToDestination()
    {
        // Quaternion lookRotation(회전할 목표 방향) : 목표 방향은 목적지 위치에서 자신의 위치를 빼면 구함
        Quaternion lookRotation = Quaternion.LookRotation(curTargetPos - transform.position);

        //Quaternion.RotateTowards(현재의 rotation값, 최종목표rotation 값, 최대 회전각)
        transform.rotation = Quaternion.RotateTowards(transform.rotation, lookRotation, Time.deltaTime * rotAnglePerSecond);
    }

    void MoveToDestination()
    {
        //Vector3.MoveTowards(시작지점, 목표지점,최대이동거리)
        transform.position = Vector3.MoveTowards(transform.position, curTargetPos,moveSpeed * Time.deltaTime);


        //플레이어의 위치와 목표지점의 위치가 같으면, 상태를 Idle 상태로 바꾸라는 명령
        if (transform.position == curTargetPos)
        {
            ChangeState(State.Idle, PlayerAni.ANI_IDLE);
        }
    }
    void Update()
    {
        UpdateState();
    }
}

 

 

 

 

 

 

스크립트를 수정하고 플레이 버튼을 눌러서 플레이어가 마우스 클릭지점으로 이동하는지 확인 합니다

 

 

0

 

반응형
반응형

이제 애니메이터 컨트롤러에 등록한 애니메이션들을 스크립트로 컨트롤 하겠습니다

Scripts 폴더안에 PlayerAni 스크립트를 만듭니다

 

Player를 선택하고 PlayerAni 스크립트를 붙힘니다

 

PlayerAni.cs 에서 만든 함수를 PlayerFSM 에서 활용해 보도록 하겠습니다

 

 

 

 

 

 

//PlayerFSM 스크립트 작성

 

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

public class PlayerFSM : MonoBehaviour 
{ 
   
    public enum State 
    { 
        Idle, 
        Move, 
        Attack, 
        AttackWait, 
        Dead 
    } 
    //idle 상태를 기본 상태로 지정 
    public State currentState = State.Idle; 

    PlayerAni myAni; 


    void Start() 
    { 
        myAni = GetComponent<PlayerAni>(); 
        //  myAni.ChangeAni(PlayerAni.ANI_WALK); 

        ChangeState(State.Idle, PlayerAni.ANI_IDLE); 
    } 

    void ChangeState(State newState, int aniNumber) 
    { 
        if (currentState == newState) 
        { 
            return; 
        } 

        myAni.ChangeAni(aniNumber); 
        currentState = newState; 
    } 

    //캐릭터의 상태가 바뀌면 어떤 일이 일어날지 를 미리 정의 
    void UpdateState() 
    { 
        switch (currentState) 
        { 
            case State.Idle: 
                break; 
            case State.Move: 
                break; 
            case State.Attack: 
                break; 
            case State.AttackWait: 
                break; 
            case State.Dead: 
                break; 
            default: 
                break; 
        } 

    } 
    // Update is called once per frame 
    void Update() 
    { 
        UpdateState(); 
    } 
}

 

 

 

//PlayerAni 스크립트 작성

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

public class PlayerAni : MonoBehaviour 
{ 
    // 애니메이터 컨트롤러의 전이 관계에서 설정한 번호에 맞춤니다 
    public const int ANI_IDLE = 0; 
    public const int ANI_WALK = 1; 
    public const int ANI_ATTACK = 2; 
    public const int ANI_ATKIDLE = 3; 
    public const int ANI_DIE = 4; 

    Animator anim; 
    void Start() 
    { 
        anim = GetComponent<Animator>(); 
    } 

    //애니메이션 번호를 입력 받아서 플레이어의 애니메이션을 해당되는 애니메이션으로 바꿔주는 함수 
    public void ChangeAni(int aniNumber) 
    { 
        anim.SetInteger("aniName", aniNumber); 
    } 
    // Update is called once per frame 
    void Update() 
    { 
         
    } 
}

 

 

스크립트를 작성하고 게임을 실행시키면 플레이어가 Idle 상태로 됩니다

 

0

반응형
반응형

 

Scrips 폴더 안에 PlayerFSM 스크립트를 만듭니다

 

 

PlayerFSM 스크립트를 Player 오브젝트에 붙입니다

 

 

그리고 Asset Store에 들어가서 찾기 에서 HQ Fighting Animation을 찾아 임포트 합니다

 

 

임포트 파일 중에서 아래 사진 세 개의 파일만 선택하고 임포트 합니다

 

 

 

 

 

 

그리고 임포트 한 파일을 선택하여 inspector에서 Rig에서 Animation Type을 Hunanoid로 바꾸고 Apply 누릅니다

 

그리고 Animation으로 들어가서 Bake into Pose를 체크합니다

 

 

 

 

 

 

그리고 PlayerControl에서 등록한 WAIT00의 이름을 idle로 바꿉니다

 

그리고 Assets/unity-chan!/Unity-chan! Model/Art/Animations/WALK00_F을 드래그하여 
Animator에 올려놓습니다

이름을 walk로 바꿉니다

 

 

Parameters에 있는 + 버튼을 눌러서 int를 추가하고 이름을 aniName을 등록합니다 

 

idle   ->  walk 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고 

Conditions에서 aniName , Eqals , 1을 선택한다

 

 

 

 

 

idle  <-  walk 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고 

Conditions에서 aniName , Eqals , 0을 선택한다

 

 

 

Assets/FightingUnityChan_FreeAsset/FightingUnityChan_FreeAsset/Animations/FUCM_04_0001_RHiKick에서 Hikick을 드래그 하여  Animator에 올려놓고 이름을 attack으로 한다 

 

walk ->  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고 

Conditions에서 aniName , Eqals , 2를 선택한다

 

 

 

 

walk <-  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고 

Conditions에서 aniName , Eqals , 1을 선택한다

 

Assets/FightingUnityChan_FreeAsset/FightingUnityChan_FreeAsset/Animations/FUCM05_0000_Idle에서 Idle을 선택하여 Animator에 드래그하고 이름을 attackidle로 바꿉니다

 

 

 

 

 

attackidle <-  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하지 말고

Conditions에서 aniName , Eqals , 3을 선택한다

Has Exit Time을 체크 해제하지 않으면 상태가 바뀌었을 때 애니메이션이 즉각적으로 바뀌지 않고,
기존 애니메이션이 다 플레이된 다음에야 바뀌게 됩니다

 

attackidle -> attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 2를 선택한다

 

idle <-  attackidle 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 0을 선택한다

 

 

 

 

 

idle ->  attackidle 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 3을 선택한다

 

walk <-  attackidle 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 1을 선택한다

 

idle <-  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 0을 선택한다

 

idle ->  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 2를 선택한다

 

 

 

 

 

Assets/FightingUnityChan_FreeAsset/FightingUnityChan_FreeAsset/Animations/FUCM02_0025_MYA_TF_DOWN에서 DamageDown을 드래그해서  Animator에 놓고 이름을 die 한다

 

die <-  attack 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 4를 선택한다

 

die <-  attackidle 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 4를 선택한다

 

die <-  idle 사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 4를 선택한다

 

 

 

 

 

die <-  walk사이에  Make Transition을 연결하고 화살표를 선택하여 Has Exit Time을 체크 해제하고

Conditions에서 aniName , Eqals , 4를 선택한다

 

Player의 Animator 등록을 완료하였습니다 

반응형
반응형

 

유니티 Hierarchy에 빈 오브젝트를 만들고 이름을 InputManager 라 합니다

InputManager 만들기

 

 

폴더를 하나 생성하여 이름을 Scripts라고 합니다

Scripts폴더생성

Scripts폴더 안에 InputManager 새 스크립트를 생성하고 스크립트를 드래그하여 InputManager 오브젝트에 붙입니다

 

 

 

 

InputManager Scripts 작성

 

아래와 같이 스크립트를 작성합니다

 

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

public class InputManager : MonoBehaviour 
{ 

    GameObject player; 
    // Start is called before the first frame update 
    void Start() 
    { 
        player = GameObject.FindGameObjectWithTag("Player"); 
    } 

    void CheckClick() 
    { 
        if (Input.GetMouseButtonDown(0)) 
        { 
            //카메라로부터 화면사의 좌표를 관통하는 가상의 선(레이)을 생성해서 리턴해 주는 함수 
            Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition); 

            RaycastHit hit; 

            //Physics.Raycast(래이 타입 변수, out 레이 캐스트 히트 타입변수) :  
            //가상의 레이저선(래이)이 충돌체와 충돌하면, true(참) 값을 리턴하면서 동시에 레이캐스트 히트 변수에 충            돌 대상의 정보를 담아 주는 함수    

     
            if (Physics.Raycast(ray, out hit)) 
            { 
                if (hit.collider.gameObject.name == "Terrain") 
                { 
                    player.transform.position = hit.point; 
                } 
            } 
        } 

    } 
    // Update is called once per frame 
    void Update() 
    { 
        CheckClick(); 
    } 
}

 

 

 

 

아래 동영상과 같이 동작하면 정상작동입니다

 

 

반응형
반응형

 

window -> Asset Store를 들어갑니다

 

window -> Asset Store

 

Asset Store에서 unity-chan을 입력하고 찾기 버튼을 누릅니다

Asset Store -> unity-chan

 

 

임포트 합니다

import

 

 

 

 

전체 파일 임포트

 

 

 

 

 

 

 

 

project 안에 unity-chan! 폴더가 생성된 것을 확인할 수 있습니다

unity-chan! 폴더 생성

 

 

Models 안에 있는 unity chan 오브젝트를 드래그하여 Hierarchy에 올려놓습니다 

 

 

캐릭터 의 모습

 

 

 

 

 

 

 

 

카메라를 캐릭터 중심에 잡아놓고 GameObject -> Align with View를 클릭합니다 

GameObject -> Align with View 

 

 

unity chan 오브젝트를 선택하고 Inspector 창에 이름과 tag를 Player라고 바꿉니다 

 

 

Project Assets에 새폴더를 만들고 이름을 Animator 라합니다

Animator 폴더 생성

 

Animator 폴더를 선택하고 Assets - > Animator Controller를 클릭합니다

 

 

Animatro Controller 이름을 PlayerControl 라고 바꿉니다

PlayerControl 생성

 

 

PlayerControl를 두 번 클릭하여 들어가면 나오는 화면

 

unity-chan! -> Unity-cha! Model -> Art -> Animations의 폴더를 들어가서 unitychan_WAIT00 -> WAIT00을 드래그하여 그림과 같이 올려놓습니다

 

 

 

 

 

 

 

 

그리고 그림과 같이 Player를 선택하고  PlayerCotrol을 드래그해서 Player의 Inspector에서 Animator에 드래그하여 올려놓습니다 

그리고 플레이 버튼을 누르면 Player 가 기본 동작으로 Idle  하는 모습을 볼 수 있습니다

 

반응형
반응형

 

unity project를 생성하고 Scenes 폴더를 생성 한다음 Scene 이름을 RPGGame 을 하고 저장 합니다

 

씬 저장

 

GameObject -> Terrain 을 생성 합니다

GameObject -> Terrain 

 

 

Terrain 생성한 모습

Terrain 생성

 

그리고 Window -> Asset Store 를 열고 Standard Assets 을 찾아 임포트 합니다

Asset Store 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

Standard Assets에서  임포트 하면 unity Asset 에 임포트 할 SurfaceTexture 을 선택하고 import 합니다

SurfaceTextures 만 선택 임포트

 

 

 

그리고 생성된 Terrain 을 선택하고 Inspector 창에서 브러쉬 모양을 선택한후 

 

PaintTexture 를 선택합니다.

그리고 Edit Terrain Layers 를 누르고 create Layer  에서 GrassHillAlbedo 를 선택하여 Terrain 전체를 색깔을 덮습니다

그리고 다시 Edit Terrain Layers 를 누르고 create Layer 에서 GrassRockyAlbedo 를 선택하고 

brush Size 를 19

Opacity 를 100 로 합니다

 

Terrain 속성

 

 

 

Terrain 에 브러쉬를 모양이 나오면 그림과 같이 길을 만듭니다 

Terrain 길 만들기

반응형
반응형

먼저 자바 사이트에서 자바를 설치합니다

 

그리고 제어판 -> 시스템 및 보안 -> 시스템 -> 고급 시스템 설정

을 들어감니다

고급에서 환경변수를 클릭합니다

 

 

 

환경변수에서  시스템 변수 -> 새로 만들기를 들어가서

변수 이름 CLASSPATH    변숫값  %classpath%;.            

지정하고 확인 누릅니다 

CLASSPATH 

 

 

 

 

 

 

그리고 다시 시스템 변수에서 새로 만들기를 누르고

변수 이름  JAVA_HOME

변수값 (자바가 설치된 경로 지정) C:\Program Files\Java\jdk-12.0.1

하고 확인

JAVA_HOME 지정

 

 시스템 변수에서 Path를 찾아 편집을 누르고

제일 앞단에 추가합니다  %JAVA_HOME%\bin;

그리고 확인

  %JAVA_HOME%\bin; 를  Path 앞단에 지정한다

 

 

 

 

 

자바 환경설정이 잘되었는지 확인하기 위해  cmd를 들어갑니다

cmd 에 들어감

 

 

javac를 입력하고 엔터를 치면 아래와 같이 프로그램 정보가 쭈르르 뜨면 자바 환경변수 설정을 성공!!!

자바 환경변수 성공 

 

반응형
반응형

unity 2D animation을 만들어 보았습니다

unity 엔진을 이용하여 2D 스프라이트를 만들어서 애니메이션을 적용하였습니다

 

유니티 엔진에 내장되어 있는 2D Sprite Editor를 이용하여 애니메이션을 만들어 보았습니다

 

 먼저 네 가지 동작을 구현하였는데 추후 동작을 추가하려고 합니다

 

플레이어가 가장 기본 무기인 칼빈소총을 들고 있는 애니메이션 

 

Player  idle
Player walk

 

 

 

 

 

 

 

플레이어의 경쾌한 슈팅 애니메이션

Player Shot

 

Player die

 

게임을 만들면서 무기를 추가 하고 애니메이션을 추가하려고 합니다

 

반응형
반응형

3DS MAX 비행기를 만들고 맵핑을 하였습니다

처음 하는 것이라 맵을 피는 작업이 손이 많이 가더 군요 ㅎㅎ

그래도 작업하고 나니깐 성취감이 많이 드네요 ㅎㅎ

 

 

맵핑 작업 후 회전 애니메이션

 

잘 도네요 ㅎㅎ

 

 

 

 

 

 

비행기 몸통 맵핑작업 

 

 

전투기 날개 맵핑 작업

 

 

 

그냥 부담 없이 작업 감상 하시면 됩니다

특별난 것은 없습니다 

 

전투기 측면1

 

전투기 앞1

 

전투기 옆1

 

전투기 뒷면1

 

전투기 뒷면2

좀 더 쉐이더 공부를 하여서 떼 깔 좀 빛나게 하여야겠습니다.

그리고 전투기 조종석의 앞유리 색깔 나오는 Material 공부도 해야겠어요 ㅎㅎ

 

 

 

 

3DS MAX 작업창

3DS MAX 전투기 모델링

 

F-86 세이버  

제조   국가 미국 유형 단좌 야간 전천후 요격기

동력   장치 33.3kN(7,500lb) 제너럴 일렉트릭 J47-GE-17B 터보제트 엔진 1개

성능   최대 속도 1,138km/h, 항속 거리 1,344km, 실용 상승 한도 16,640m(54,600ft)

무게   적재 중량 7,756kg

크기   날개폭 11.3m, 길이 12.29m, 높이 4.57m

무장   7mm(2.75인치) ‘마이티 마우스’ 공대공 유도 없는 로켓 24발

 

반응형
반응형

3DS MAX 모델링 비행기 만들기 F-86 세이버를 만들고 있는 중입니다

아직 3DS MAX를 배우고 있는 중이어서 3DS MAS Tool 기본적인 모델링 연습을 하고 있습니다

위에서 바라본 모습

 

 

 

 

측면

 

측면 모습이 참 잘나온 것 같습니다 

특히 곡선 부분을 만들기가 어려운데 잘 뺐습니다

 

측면2

측면 2도 조명과 어울려져 멋져 보이네요 ㅎㅎ

 

뒷모습

모델링할 때 f-86 전투기 사진을 많이 보고 만들었습니다 

그래도 어느정도 균형 있게 잘 나왔네요 ㅎㅎ

다음엔 UV 배워서 색깔을 덮어 보겠습니다 ㅎㅎ

반응형
반응형

unity 게임 2D animate 만들기 두 번째 시간입니다 

 

 

 

 

 

 

 


Sprite Editor -> Geometry 를 조정할 수 있습니다

각 각 레이어 로 나누어진 스프라이트를 버텍스를 추가할 수 있고 삭제할 수 있습니다.

특히 움직이 많은 부분은 버텍스를 따로 추가할 수 있어서 애니메이션 움직임을 유연하게 할 수 있습니다

뼈대의 Geonetry의 영향력을 색깔로 나타냄 

 

 

 

뼈대를 스킨에 붙히는 작업을 합니다

그리고 각각의 스프라이트의 Geometry의 영향력을 제어할 수 있고 각각의 뼈대의 움직임의 맞게 뼈대의 영향력을 추가 또는 삭제시킬 수  있습니다

 

 

 

 

Skinning 작업을 마친 캐릭터를 하이라키에 올리고 Ik Manager를 추가합니다

캐릭터의  Ik Manager 설치

Ik Manager의 Lim을 눌러 추가합니다

 

 

그리고 Ik 가 들어갈 뼈대에 빈 GomeObjet를 만들고 위치를 IK를 움직일 수 있는 중심에 맞춥니다

빈 오브젝트 만들기

 

 

 

 

그리고 새로 생긴 Limb Solver 2D dp Effector에 빈 오브젝트를 드래그하여 넣습니다

그리고 Create Target을 누릅니다 

오브젝트를 넣고 -> create Target

 

ik 가 적용이 되면 그림과 같이 뼈대 중심이 녹색으로 바뀝니다

ik가 적용이 된 뼈대를 빈 오브젝트의 중심을 움직이면 ik 가 적용된 모습을 볼 수 있습니다

다리에  ik 가 설치된 모습 

반응형
반응형

unity 게임을 만들기 위해서 2D sprite를 이용하여 캐릭터 animation을 만들어 보았습니다

 

먼저 unity를 열고 window -> package Manager를 들어갑니다

 

유니티 pakage Manager를 엽니다

 

Pakages를 열면 2D Animation, 2D IK, 2D PSD Importer를 install 합니다

2D 캐릭터 애니메이션을 구현할수 있는 툴을 설치 합니다

 

 

 

 

potoshop에서 캐릭터 작업 파일을 저장합니다 각각의 팔 , 다리 , 몸통을 레이어로 나누어 저장합니다 

potoshop 작업파일

 

포토샵의 저장 파일을 unity 에 끌고 옵니다

 

potoshop 저장파일을 드래그하여 unity로 끌고 온다 

포토샵의 저장파일을 sprite Editor를 열고  skinning Editor 를 들어갑니다

Sprite Editor 의 Skinning Editor 를 들어 간 모습

 

 

 

 

Create Bone을 눌러 캐릭터의 중심이 되는 허리 약간 아래 부분의 기준을 잡고 뼈대를 설치합니다

반응형
반응형

어도비 포토샵으로 시퀀스를 불러올 때 시퀀스를 레이어로 변환하여 불러오고 쉽을 때 가 있다

먼저 포토샵에서  Scripts -> Load Files into Stack을 연다

Scripts -> Load Files into Stack

 

 

 

 

 

 

 

 

 

 

Browse 를 눌러 시퀀스 파일이 있는 경로를 찾아 시퀀스를 등록한다

등록을 하면 OK 버튼을 누른다

 

만약 시퀀스 레이어 순서를 바꾸고 싶으면 

Arrange ->Reverse 를 하면 순서가 바뀐다

 

 

반응형
반응형

하늘 높이 올라가며 괴수를  무찌르는 무한 액션 RPG 게임

슈퍼 액션 영웅 캐릭터를 선택하여  무한으로 하늘을 올라가며 괴수와 싸우는 복합 액션 슈팅 어드밴처  신규 RPG 게임입니다.  하늘을 올라가며 캐릭터의 레벨을 올리십시오

- 최대 레벨 999까지 한계를 모르는 레벨업을 캐릭터가 합니다
- 캐릭터마다 고유의 공격무기를  가지고 있습니다
- 높이 올라갈수록 현상금이 높고 강력한 괴수 캐릭터가 등장합니다
- 게임 머니를 획득하여 강력한 아이템을 구매할 수 있습니다
- 자신의 베스트 이동 높이를 사진을 찍어 공유할 수 있습니다

 

통쾌한 액션을 모바일 게임으로 즐기실 수 있습니다

 

구글 플레이

https://play.google.com/store/apps/details?id=com.Magatron.MonsterSky

 

괴수의 하늘 Monster Sky - Google Play 앱

수퍼 액션 영웅 캐릭터를 선택하여 무한으로 하늘을 올라가며 괴수와 싸우는 복합 액션 슈팅 어드밴처 RPG 게임 입니다 . 하늘을 올라가며 캐릭터의 레벨을 올리십시요 - 최대 레벨 999 까지 한계를 모르는 레벨업을 캐릭터가 합니다 - 캐릭터마다 고유의 공격무기를 가지고 있습니다 - 높이 올라갈수록 현상금이 높고 강력한 괴수 캐릭터가 등장합니다 - 게임 머니를 획득하여 강력한 아이템을 구매 할수 있습니다 - 자신의 베스트 이동높이를 사진을 찍어 공유 할수

play.google.com

 

 

하늘을 날으는 광폭한 불꽃 멧돼지 괴수 엄청난 힘의 괴수 상금 금화 500

 

머리가 큰 바위 미친 도룡뇽 하늘을 날으면 미치게 발광한다  처치하면 상금 금화 1000

 

사마귀 권법을 취득한 대머리 사마귀 얍샵하면서 이리저리 피해 다녀 빠른 괴수다

상금은 금화 1500

 

머리가 3개 달리 킹 괴드라 입에서 강력한 불꽃을 발사 한다

상금 금화 2000

 

상어가 좀비가 되어 날아 다니는 좀비 괴물 괴수 머리에 칼이 달려 있다 아주 강력 하게 이동한다

상금 금화 2500

 

 

아주 멧집이 강하고 광폭하다 하늘을 날으는 미친 고릴라 괴수 짱 고릴라 콩 

상금 금화 10000

멧집이 쎔

반응형
반응형

 
 
구글플레이 Google Play
 
 

모바일 중력 가속센서를 이용한 모바일 게임 입니다

귀여운 펫을 조종하여 하늘높이 날아가 보세요

 

그리고 게임 만드는 법을 블로그로 만들었습니다

https://magatron.tistory.com/32

하루만에 게임 만드는법

 

 

즐겁고 신나는 게임

 

DownLoad

 

google-site-verification: google60cf64b427a4064a.html

반응형
반응형


화면과 같이 유니티 엔진으로 무한 점프 게임을 만들어 보겠습니다



제작게임 해보기 :  Google Play  





Game 씬 화면을 16:9 맞춤니다



Scene 폴더를 만들고 씬을 생성하여 이름을 MainGame이라 합니다



Texture 폴더를 만들고 그림 리소스를 드래그 하여 폴더 안에 넣습니다


  

Audio폴더를 만들고  드래그 하여 사운드 리소스를 넣습니다



MainCamera를 선택하여 그림과 같이 맞춤니다


Sky 텍스쳐를 선택하고 그림과 같이 속성을 바꿈니다


Hierarchy에 Quad를 생성 하여 이름을 Sky라 합니다



Materials폴더를 만들고 Material를 생성하여 Sky라 이름을 붙힙니다


Hierarchy에  Sky를 선택하고 만든 Material을 드래그 하여 붙히고 Shader -> Unlit -> Texture

를 선택합니다 



Select창에 sky Texture를 붙힙니다


Sky 오브젝트를 MainCamera자식으로 이동 합니다

그리고 그림과 같이 스케일과 포지션을 바꿉니다



Scripts폴더를 생성하고 SkyCtrl 스크립트를 생성하여 넣습니다


SkyCtrl 스크립트 작성

Sky를 배경으로 하여 배경 스크롤 하는 오브젝트를 짭니다



using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class SkyCtrl : MonoBehaviour {


    float speed = 0.03f;


    //------------------------------

    // 화면 스크롤

    //------------------------------

    void Update()

    {

        float ofsX = speed * Time.time;

        transform.GetComponent<Renderer>().material.mainTextureOffset = new Vector2(ofsX, 0);

    }

}




SkyCtrl 스크립트를 Sky 오브젝트에 드래그하여 붙힙니다



배경이 좌로 스크롤 되는 것을 확인합니다


Jump_tile 텍스쳐를 선택하여 그림과 같이 속성을 바꿉니다



Jump_tile 텍스쳐를 선택하여 Hierarchy창에 드래그하여 놓고 이름을 Tile 로 바꿉니다


Tile 오브젝트를 선택하여 Tag를 Tile을 생성하여 선택하고 Layer를 Tile를 만들어 선택합니다




TilCtrl 스크립트를 만들어 저장합니다


TilCtrl 스크립트 작성

그림과 같이 Camera 시선에 좌표를 만들어 화면에 벗어나면 삭제되는 스크립트를 작성합니다


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class TilCtrl : MonoBehaviour {


    //-------------------

    // 화면 아래를 벗어나면 제거

    void Update()

    {

        // 월드 좌표를 스크린 좌표로 변환 

        Vector3 view = Camera.main.WorldToScreenPoint(transform.position);

        if (view.y < -50)

        {

            Destroy(gameObject);

        }

    }

}




Tile오브젝트에 Tile스크립틀 붙힙니다


Tile 오브젝트를 Prefabs폴더에 드래그하여 프리팹을 만듭니다



tile Sorting Layers tile 을 만들고 선택합니다 Sorting Layer는 아래로 선택할수록 텍스쳐가 위로 보이는 기능입니다 


Hierarchy 창에 Tile 오브젝트를 하나더 만들어서 포지션을 조금 높게 설정하여 저장합니다



rabbit_jump2 텍스쳐를 선택하여 드래그하여 Hierarchy창에 올리고 이름을 Player라 합니다

 

Sorting Layer를 Player를 만들어 선택합니다



 MainCamera를 선택하여 빈 오브젝트를 만들고 이름을 spPoint 라고 하고 포지션을 그림과 같이 합니다


Player를 선택하고 그림과 같이 자식으로 오브젝트를 만들어 StartPos 와 EndPos 이름을 붙힙니다  


그림과 같이 오브젝트포지션을 Player 발끝에 EndPos 를 놓고  바로 위에 StartPos를 놓습니다



Player를 선택하고 Audio Source를 붙힙니다


PlayerCtrl스크립트를 만들고 PlayerCtrl 스크립트를 작성합니다


PlayerCtrl 스크립트 작성




using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class PlayerCtrl : MonoBehaviour {


    public Transform tile;

    public Transform startPos;

    public Transform endPos;


    public AudioClip sndJump;        // 효과음 및 배경음악 

    public AudioClip sndGift;

    public AudioClip sndBird;

    public AudioClip sndStage;

    public AudioClip sndOver;


    Transform spPoint;

    Transform newTile;

    float maxY = 0;


    int speedSide = 10;              // 좌우 이동 속도 

    int speedJump = 16;             // 점프 속도 

    int gravity = 25;                  // 추락 속도 


    Vector3 moveDir = Vector3.zero;


    bool isDead = false;

    Animator anim;

    public Button retryGame;



    void Start()

    {

        anim = GetComponent<Animator>();


        // 모바일 단말기 설정

        Screen.orientation = ScreenOrientation.LandscapeRight;

        Screen.sleepTimeout = SleepTimeout.NeverSleep;


        spPoint = GameObject.Find("spPoint").transform;


        // Tile 만들기 

        newTile = Instantiate(tile, spPoint.position, spPoint.rotation) as Transform;


        //Cursor.visible = false;     // 커서 감추기 

        retryGame.gameObject.SetActive(false);

    }


    //-------------------

    // 게임 루프

    //-------------------

    void Update()

    {

        if (isDead) return;

        JumpPlayer();          // Player 점프

        MovePlayer();          // Player 이동 

        MoveCamera();          // 카메라 이동 

      

    }



    //-------------------

    // Player 점프

    //-------------------

    void JumpPlayer()

    {

        RaycastHit2D hit;

        hit = Physics2D.Linecast(startPos.position, endPos.position, 1 << LayerMask.NameToLayer("Tile"));


        if (hit)

        {

            moveDir.y = speedJump;

            AudioSource.PlayClipAtPoint(sndJump, transform.position);

        }

    }


    //-------------------

    //  Player 이동

    //-------------------

    void MovePlayer()

    {

        Vector3 view = Camera.main.WorldToScreenPoint(transform.position);


        if (view.y < -50)

        {       // 화면 아래를 벗어나면  

            isDead = true;      // 게임 오버 

            GameOver();

            return;

        }


        moveDir.x = 0;      // Player의 좌우 이동 방향


        // Mobile 처리 

        if (Application.platform == RuntimePlatform.Android ||

            Application.platform == RuntimePlatform.IPhonePlayer)

        {

            // 중력 가속도 센서 읽기 

            float x = Input.acceleration.x;


            // 왼쪽으로 기울였나?

            if (x < -0.2f && view.x > 35)

            {

                moveDir.x = 2 * x * speedSide;

            }


            if (x > 0.2f && view.x < Screen.width - 35)

            {

                moveDir.x = 2 * x * speedSide;

            }


        }

        else

        {   // Keyboard 읽기 

            float key = Input.GetAxis("Horizontal");

            if ((key < 0 && view.x > 35) ||

                (key > 0 && view.x < Screen.width - 35))

            {

                moveDir.x = key * speedSide;

            }

        }


        // 매 프레임마다 점프 속도 감소

        moveDir.y -= gravity * Time.deltaTime;

        transform.Translate(moveDir * Time.smoothDeltaTime);



        //애니메이션 설정

        if (moveDir.y > 0)

        {

            anim.Play("PlayerJump");

        }

        else

        {

            anim.Play("PlayerIdle");

        }

    }


    void MoveCamera()

    {

        // Player 최대 높이 구하기 

        if (transform.position.y > maxY)

        {

            maxY = transform.position.y;


            // 카메라 위치 이동 

            Camera.main.transform.position = new Vector3(0, maxY - 2.5f, -10);

           // score = (int)maxY * 1000;

        }


        // 가장 최근의 Tile과 spPoint와의 거리 구하기

        if (spPoint.position.y - newTile.position.y >= 4)

        {

            float x = Random.Range(-10f, 10f) * 0.5f;

            Vector3 pos = new Vector3(x, spPoint.position.y, 0.3f);

            newTile = Instantiate(tile, pos, Quaternion.identity) as Transform;


            // 나뭇가지의 회전방향 설정 

            int mx = (Random.Range(0, 2) == 0) ? -1 : 1;

            int my = (Random.Range(0, 2) == 0) ? -1 : 1;

            newTile.GetComponent<SpriteRenderer>().material.mainTextureScale = new Vector2(mx, my);

        }

    }


    //게임 오버 설정

    void GameOver()

    {

         retryGame.gameObject.SetActive(true);

        if (GetComponent<AudioSource>().clip != sndOver)

        {

            GetComponent<AudioSource>().clip = sndOver;

            GetComponent<AudioSource>().loop = false;

            GetComponent<AudioSource>().Play();

        }

    }

    // 재시작 

    public void RetryGame()

    {


        Application.LoadLevel("MainGame");

    }


}





Player를 선택하고 Animation을 만든다


Create New Clip 으로 들어가 PlayerIdle 만들고 저장하고 그림과 같이 rabbit_jump2 텍스쳐를 드래그하여 애니메이션을 만든다



Create New Clip 으로 들어가 PlayerJump 이름을 만들고 저장하고 그림과 같이 rabbit_jump 텍스쳐를 드래그하여 애니메이션을 만든다


Main Camera 자식으로 한 Canvas 를 만들어 

Button 을 만듭니다



Canvas 의 설정을 그림과 같이 합니다


Button 밑에 있는  Text 도 그림과 같이 속성을 합니다



버튼 크기를 적당히 그림과 같이 배치하고 크기를 조절합니다


Player 오브젝트에 PlayerCtrl스크립트를 연결하고 그림과 같이 오브젝트들을 연결합니다



Tile은 프리팹으로 연결합니다 


게임을 실행합니다

영상과 같이 캐릭터가 올라가면 됩니다



bird 스프라이트 하나를 드래그 하여 Hierarchy 창에 올리고 이름을 Bird하 합니다


만든  Bird 오브젝트에 Animation 을 열고  BirdFly  애니메이션 이름을 만들고 bird_107*92*0 ~ 5 스프라이트를 드래그 하여 Animation창에 올려 놓습니다



Bird 오브젝트를 선택하여 Tag를 Bird 를 만들어 선택하고 Sorting Layer를 bird 를 만들어 선택합니다

그리고 Box Coillder2D를 붙힙니다


BirdCtrl 스크립트를 만들어 붙힙니다


BirdCtrl 스크립트 작성



using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class BirdCtrl : MonoBehaviour {


    public Transform txtScore;  // 프리팹 

    int speed;                  // 이동 속도 

    bool isDrop = false; // Use this for initialization


    Animator anim;


    private void Awake()

    {

        anim = transform.GetComponent<Animator>();

    }


    void Update()

    {

        speed = Random.Range(3, 7);


        float amtMove = speed * Time.smoothDeltaTime;


        if (!isDrop)

        {

            AnimateBird();

            transform.Translate(Vector3.right * amtMove);

        }

        else

        {           // 아래로 이동 

            transform.Translate(Vector3.down * amtMove, Space.World);

        }


        // 화면을 벗어난 오브젝트 제거 

        Vector3 view = Camera.main.WorldToScreenPoint(transform.position);

        if (view.y < -50 || view.x > Screen.width + 50)

        {

            Destroy(gameObject);

        }


    }


    void AnimateBird()

    {

        anim.Play("BirdFly");

    }


    void DropBird()

    {

        isDrop = true;


        // 참새 회전 

        transform.eulerAngles = new Vector3(0, 0, 180);


        // 감점 표시 

        Transform obj = Instantiate(txtScore) as Transform;

        obj.GetComponent<Text>().text = "<color=red><size=22>-1000</size></color>";


        // World 좌표를 Viewport 좌표로 변환 

        var pos = Camera.main.WorldToViewportPoint(transform.position);

        obj.position = transform.position;

    }

    // Update is called once per frame

}




스크립트를 작성하고 Bird 프리팹을 만들어 저장합니다 Hierachy 창에 Bird는 삭제합니다


Canvas 자식으로 Text 를 만들고 이름을 TextCtrl이라 하고속성을 그림과 같이 합니다



TextCtrl 스크립트를 만들고 TextCtrl 오브젝트에 붙힙니다


TextCtrl 오브젝트를 Canvas 자식에서 빼냅니다



TextCtrl 오브젝트의 Scale을 변경 하지마시고 TextCtrl 오브젝트를 프리팹을 만들고 Hierarchy창에 있는 TextCtrl을 삭제합니다


Bird 오브젝트를 선택하고 TextCtrl 프리팹을 txtScore에 붙힙니다



gift1 스트라이트를 선택 드래그 하여 Item1오브젝트를 만듭니다


Item1오브젝트를 선택하여 Tag를 Item1 정하고 Sorting Layer를 Item을 만들어 선택합니다

그리고 BoxCoillder2D를 붙힙니다


ItemCtrl 스크립트 작성



using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class ItemCtrl : MonoBehaviour {


    public Transform txtScore;      // 프리팹 


    //-------------------

    // 화면을 벗어난 Gift 제거

    //-------------------

    void Update()

    {

        // World 좌표를 Screen 좌표로 변환 

        Vector3 view = Camera.main.WorldToScreenPoint(transform.position);

        if (view.y < -50)

        {

            Destroy(gameObject);

        }

    }

    //-------------------

    // 점수 표시 - 외부 호출 

    //-------------------

    void DisplayScore(int n)

    {

       

        // 점수 표시용 UIText 만들기 

        Transform obj = Instantiate(txtScore, transform.position, Quaternion.identity) as Transform;

        obj.GetComponent<Text>().text = "+" + n * 500;


        // World 좌표를 Viewport 좌표로 변환 

        var pos = Camera.main.WorldToViewportPoint(transform.position);

        // obj.position = pos;


        // Gift 제거 

        Destroy(gameObject);

    }

}




작성한 스크립트를 Item1에 붙히고 TextCtrl 프리팹을 연결합니다


Item2를 만들어 그림과 같이 합니다



Item3를 만들어 그림과 같이 합니다


Canvas에 Text 3개를 만들어 이름을 그림과 같이 하고 속성을 그림과 같이 합니다



Scene 창에 그림과 같이 대략 적절한 크기와 위치 색깔로 맞춤니다 




PlayerCtrl 스크립트를 그립과 같이 수정합니다


PlayerCtrl 스크립트 수정



using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class PlayerCtrl : MonoBehaviour

{

    public Text tScore;          //점수,높이 ,아이템갯수 

    public Text tHeight;

    public Text tItem;


    public Transform tile;        // 프리팹 

    public Transform[] item;

    public Transform bird;


  

    public Transform startPos;

    public Transform endPos;


    public AudioClip sndJump;       // 효과음 및 배경음악 

    public AudioClip sndGift;

    public AudioClip sndBird;

    public AudioClip sndStage;

    public AudioClip sndOver;


    Transform spPoint;

    Transform newTile;

    float maxY = 0;

    int giftCnt = 0;                // 획득한 선물 수 

    int score = 0;                // 득점 


    int speedSide = 10;             // 좌우 이동 속도 

    int speedJump = 16;             // 점프 속도 

    int gravity = 25;               // 추락 속도 


    Vector3 moveDir = Vector3.zero;

 

    bool isDead = false;

    Animator anim;

    public Button retryGame;



    void Start()

    {

        anim = GetComponent<Animator>();


        // 모바일 단말기 설정

        Screen.orientation = ScreenOrientation.LandscapeRight;

        Screen.sleepTimeout = SleepTimeout.NeverSleep;


        spPoint = GameObject.Find("spPoint").transform;


        // Tile 만들기 

        newTile = Instantiate(tile, spPoint.position, spPoint.rotation) as Transform;


        //Cursor.visible = false;     // 커서 감추기 

        retryGame.gameObject.SetActive(false);

    }


    //-------------------

    // 게임 루프

    //-------------------

    void Update()

    {

        if (isDead) return;

        JumpPlayer();          // Player 점프

        MovePlayer();          // Player 이동 

        MoveCamera();          // 카메라 이동 

        MakeItem();

        UIText();

    }



    //-------------------

    // Player 점프

    //-------------------

    void JumpPlayer()

    {

        RaycastHit2D hit;

        hit = Physics2D.Linecast(startPos.position, endPos.position, 1 << LayerMask.NameToLayer("Tile"));


        if (hit)

        {

            moveDir.y = speedJump;

            AudioSource.PlayClipAtPoint(sndJump, transform.position);

        }

    }


    //-------------------

    //  Player 이동

    //-------------------

    void MovePlayer()

    {

        Vector3 view = Camera.main.WorldToScreenPoint(transform.position);


        if (view.y < -50)

        {       // 화면 아래를 벗어나면  

            isDead = true;      // 게임 오버 

            GameOver();

            return;

        }


        moveDir.x = 0;      // Player의 좌우 이동 방향


        // Mobile 처리 

        if (Application.platform == RuntimePlatform.Android ||

            Application.platform == RuntimePlatform.IPhonePlayer)

        {

            // 중력 가속도 센서 읽기 

            float x = Input.acceleration.x;


            // 왼쪽으로 기울였나?

            if (x < -0.2f && view.x > 35)

            {

                moveDir.x = 2 * x * speedSide;

            }


            if (x > 0.2f && view.x < Screen.width - 35)

            {

                moveDir.x = 2 * x * speedSide;

            }


        }

        else

        {   // Keyboard 읽기 

            float key = Input.GetAxis("Horizontal");

            if ((key < 0 && view.x > 35) ||

                (key > 0 && view.x < Screen.width - 35))

            {

                moveDir.x = key * speedSide;

            }

        }


        // 매 프레임마다 점프 속도 감소

        moveDir.y -= gravity * Time.deltaTime;

        transform.Translate(moveDir * Time.smoothDeltaTime);



        //애니메이션 설정

        if (moveDir.y > 0)

        {

            anim.Play("PlayerJump");

        }

        else

        {

            anim.Play("PlayerIdle");

        }

    }


    void MoveCamera()

    {

        // Player 최대 높이 구하기 

        if (transform.position.y > maxY)

        {

            maxY = transform.position.y;


            // 카메라 위치 이동 

            Camera.main.transform.position = new Vector3(0, maxY - 2.5f, -10);

           // score = (int)maxY * 1000;

        }


        // 가장 최근의 Tile과 spPoint와의 거리 구하기

        if (spPoint.position.y - newTile.position.y >= 4)

        {

            float x = Random.Range(-10f, 10f) * 0.5f;

            Vector3 pos = new Vector3(x, spPoint.position.y, 0.3f);

            newTile = Instantiate(tile, pos, Quaternion.identity) as Transform;


            // 나뭇가지의 회전방향 설정 

            int mx = (Random.Range(0, 2) == 0) ? -1 : 1;

            int my = (Random.Range(0, 2) == 0) ? -1 : 1;

            newTile.GetComponent<SpriteRenderer>().material.mainTextureScale = new Vector2(mx, my);

        }

    }

    void MakeItem()

    {

        if (Random.Range(1, 1000) < 990) return;


        // 오브젝트 표시 위치 

        Vector3 pos = Vector3.zero;

        pos.y = maxY + Random.Range(4, 5.5f);


        if (Random.Range(0, 100) < 50)

        {

            // 참새 만들기 

            pos.x = -12f;

            Instantiate(bird, pos, Quaternion.identity);

        }

        else

        {

            for (int i = 1; i < item.Length; i++)

            {

                // 화면의 선물 수를 5개 이내로 제한 

                int n1 = GameObject.FindGameObjectsWithTag("Item1").Length;

                int n2 = GameObject.FindGameObjectsWithTag("Item2").Length;

                int n3 = GameObject.FindGameObjectsWithTag("Item3").Length;


                if (n1 + n2 + n3 >= 5) return;


                // Item 만들기 

                pos.x = Random.Range(-5f, 5f);


                int n = Random.Range(0, 3);

                Transform obj = Instantiate(item[n], pos, Quaternion.identity) as Transform;


            }

        }

    }


    void OnTriggerEnter2D(Collider2D coll)

    {

        switch (coll.transform.tag)

        {

            case "Item1":

                AudioSource.PlayClipAtPoint(sndGift, transform.position);

                // 득점 처리              

                score += 500;

                giftCnt++;


                // 선물 제거 

                coll.transform.SendMessage("DisplayScore", 1);

                break;


            case "Item2":

                AudioSource.PlayClipAtPoint(sndGift, transform.position);

                // 득점 처리 

                score += 1000;

                giftCnt++;


                // 선물 제거 

                coll.transform.SendMessage("DisplayScore", 2);

                break;


            case "Item3":

                AudioSource.PlayClipAtPoint(sndGift, transform.position);

                // 득점 처리 

                score += 1500;

                giftCnt++;


                // 선물 제거 

                coll.transform.SendMessage("DisplayScore", 3);

                break;


            case "Bird":

                if (coll.transform.eulerAngles.z != 0) return;


                AudioSource.PlayClipAtPoint(sndBird, transform.position);

                score -= 1000;


                // 참새 추락 처리 

                coll.transform.SendMessage("DropBird", SendMessageOptions.DontRequireReceiver);

                break;

        }

    }


    void UIText()

    {

        tScore.text = "Score :" + score;

        tHeight.text = "Height :" + (int)maxY;

        tItem.text = "Gift :" + giftCnt;

    }


    //게임 오버 설정

    void GameOver()

    {

         retryGame.gameObject.SetActive(true);

        if (GetComponent<AudioSource>().clip != sndOver)

        {

            GetComponent<AudioSource>().clip = sndOver;

            GetComponent<AudioSource>().loop = false;

            GetComponent<AudioSource>().Play();

        }

    }

    // 재시작 

    public void RetryGame()

    {


        Application.LoadLevel("MainGame");

    }


}





수정한 PlayerCtrl 스크립트에 그림과 같이 리소스를 연결합니다



제작게임 해보기  Google Play



게임을 실행하여 동영상과 같이 실행되는지 확인합니다


Audio_Texture.unitypackage


JumpRabbit.unitypackage



반응형
반응형



가위바위보.zip


유니티3D 엔진으로 간단한 가위 바위 보 게임을 만들어 보겠습니다


먼저 Scenes 폴더를 만들고 씬을 만들어 Game 이라하고 저장합니다



Resources 폴더를 만들어 예제 소스를 다운받아 드래그하여 넣습니다

여기서 Resources 철자를 틀려서는 안됩니다

 


Main Camera 를 선택하고 display를 16:9 로 맞추고 Scene 화면을 2D 로 합니다


hierachy창에 Canvas를 생성하고 속성을 그림 빨간 박스 와 같이 맞춤니다



Canvas 자식으로 Image 두개를 생성하고 그림과 같이 이름을 바꿉니다


Image에 그림과 같이 Resources에 있는 그림 아무거나 연결합니다



Canvas 자식으로 Button을 생성하고 Button 자식으로 있는 Text 속성을 그림 빨간 박스와 같이 바꿉니다



Button 그리과 같이 3개가 되도록 생성하고 Button 이름을 그림과 같이 바꿉니다


Scene 화면에 그림과 같이 Button 을 크기와 위치을 합니다


Text 를 생성하고 속성을 그림 빨간 박스와 같이 바꿉니다



Text 3개가 되도록 그림과 같이 이름을 바꿉니다


.Scene 화면에 그림과 같이 Text를 배열 합니다



Hierachy 창에 빈 게임 오브젝트를 만들어 GameManager라 이름 붙힙니다


새 스크립트를 만들어 이름을 GameManager 로 합니다



GameManager 스크립트 작성


using UnityEngine;

using System.Collections;

using System.Collections.Generic;

using UnityEngine.UI;


public class GameManager : MonoBehaviour

{


    public Image objMy; // 가위바위보 오브젝트 

    public Image objCom;


    public Text txtMy;

    public Text txtCom;

    public Text txtScore;


    public Text scissors;

    public Text rock;

    public Text paper;


    // int you = 0; // 가위바위보(가위:1, 바위:2, 보:3)

    int com = 0;

    int scMy = 0; // 승패 점수 

    int scCom = 0;


    //--------------------------------

    // 승패 처리

    //--------------------------------

    void CheckScore(int myHand)

    {

        com = Random.Range(1, 4);       // 1~3의 난수

        objCom.GetComponent<Image>().sprite = Resources.Load<Sprite>("Game_" + com) as Sprite;

        objMy.GetComponent<Image>().sprite = Resources.Load<Sprite>("Game_" + myHand) as Sprite;


        // 승패 판정

        int k = myHand - com;


        if (k == 0)

        {

            txtScore.text = "비겼습니다";

        }

        else if (k == 1 || k == -2)

        {

            txtScore.text = "당신이 이겼습니다";

            scMy++;

        }

        else

        {

            txtScore.text = "컴퓨터가 이겼습니다";

            scCom++;

        }


        txtMy.text = "당신 : " + scMy;

        txtCom.text = "컴퓨터 : " + scCom;


        myHand = 0;

    }


    //--------------------------------

    // 화면 처리 

    //--------------------------------



    public void ClickScissors()

    {

        scissors.text = "가위";


        CheckScore(1);

    }


    public void ClickRock()

    {

        rock.text = "바위";

        CheckScore(2);

    }



    public void ClickPaper()

    {

        paper.text = "보";

        CheckScore(3);

    }


}




승패 판정원리

가위 바위 보 게임의 핵심부분이라고 할수 있습니다.

가위 바위 보에 각 각 1~3을 할당해 두고 승패는 다음과 같이 정하면 됩니다. 두값이 같으면 비깁니다.

가위(1) < 바위(2) <보 (3) < 가위(1) <  ~

이긴 값에서 진 값을 빼면 다음과 같은 결과를 얻을 수 있습니다.


바위(2) > 가위(1) -> 2-1 = 1

보(3) > 바위(2) -> 3-2 = 1

가위(1) > 보 (3) -> 1-3 = -2


내 값에서 상대방의 값을 뺀 결과가 1이거나 -2 이면 이기고 그 외에는 진다. 입니다.

이 원리를 이용하여  CheckScore() 함수를 만들어 스크릡트를 짭니다



GameManager 오브젝트에 GameManager 스크립트를  붙히고  그림과 같이 Canvas에 있는 오브젝트 들을 연결합니다 



ButtonScissors 를 선택하고  OnClic()에 GameManager를 연결합니다

그리고 GameManager -> ClicScissors() 를 찾아 연결합니다



ButtonRock 를 선택하고  OnClic()에 GameManager를 연결합니다

그리고 GameManager -> ClickRock() 를 찾아 연결합니다


ButtonPaper 를 선택하고  OnClic()에 GameManager를 연결합니다

그리고 GameManager -> ClickPaper()를 찾아 연결합니다



게임을 실행 하여 버튼을 누르면 컴 퓨터와 가위 바위 보 대결을 하고 점수가 올라 가는지 확인 합니다



 

프로젝트 파일

Rock Paper Scissors.unitypackage


반응형
반응형

이번시간에는 게임을 빌드 시켜 안드로이드 게임앱을 만들고 Player 에 터치 드레그 프로그램을 짜 보겠습니다


먼저 안드로이드 앱을 빌드하기위해선 

안드로디드 스튜디오의 SDK Manager 와 자바 프로그램에 Jdk 가 필요 합니다.

두 프로그램을 다운받아 설치하고 그림과 같이 

Edit ->Preferences -> External Tools  로 가서 연결합니다

자바프로그램은 꼭 환경변수 설정을 하셔야 됩니다 

자바 환경변수 설정은 인터넷에 많이 나오니 참고 하시면 됩니다 



그리고 각각 Sprite Renderer가 있는 오브젝트에 Sorting Layer를 붙힙니다

PC에서는 텍스쳐는 만든 순서 대로 해서 보이겠지만  스마트 기기에 들어가면 Layer 지정이 안되 있어 화면에 텍스쳐가 가릴지도 모릅니다 



그림과 같이 Sorting Layers를 만듭니다

 아래로 내려갈수록 텍스쳐 Layer가 위로 보이게 하는 것입니다


Player오브젝트를 선택하고 Sorting Layer를 Player로 합니다





Enemy 프리팹을 선택하여 Sorting Layer 를 Enemy 로 합니다


Explosion 프리팹을 선택하여 Sorting Layer effect 로 합니다


rocket 프리팹을 선택하여 Sorting Layer 를 Rocket로 합니다



스크립트를 생성하고 이름을 DragScript 라고 합니다


Player 에 연결하여 스마트 폰에서 손가락으로 touch 하고 drag 할수 있는 스크립트를 짭니다 



DragScript


using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class DragScript : MonoBehaviour

{

    float deltaX, deltaY;


    Rigidbody2D rb;


    bool moveAllowed = false;


// Use this for initialization

void Start ()

    {

        rb = GetComponent<Rigidbody2D>();


        PhysicsMaterial2D mat = new PhysicsMaterial2D();

        mat.bounciness = 0.75f;

        mat.friction = 0.4f;

        GetComponent<CircleCollider2D>().sharedMaterial = mat;

}

// Update is called once per frame

void Update ()

    {

        if (Input.touchCount > 0)

        {

            Touch touch = Input.GetTouch(0);


            Vector2 touchPos = Camera.main.ScreenToWorldPoint(touch.position);


            switch (touch.phase)

            {

                case TouchPhase.Began:


                    if (GetComponent<Collider2D>() == Physics2D.OverlapPoint(touchPos))

                    {

                        deltaX = touchPos.x - transform.position.x;

                        deltaY = touchPos.y - transform.position.y;


                        moveAllowed = true;


                        rb.freezeRotation = true;

                        rb.velocity = new Vector2(0, 0);


                        rb.gravityScale = 0;


                        GetComponent<CircleCollider2D>().sharedMaterial = null;

                    }

                    break;

                case TouchPhase.Moved:

                    if (GetComponent<Collider2D>()== 

                        Physics2D.OverlapPoint(touchPos)&& moveAllowed)

                    {                                                    

                        rb.MovePosition(new Vector2(touchPos.x - deltaX, touchPos.y - deltaY));

                    }                                                                       //Player를 좌우로만 움직일경우  touchPos.y - deltaY 대신에  transform.position.y 을 넣는다

                    break;


                case TouchPhase.Ended:


                    moveAllowed = false;

                    rb.freezeRotation = false;

                    rb.gravityScale = 2;


                    PhysicsMaterial2D mat = new PhysicsMaterial2D();

                    mat.bounciness = 0.75f;

                    mat.friction = 0.4f;

                    GetComponent<CircleCollider2D>().sharedMaterial = mat;

                    break;                   

            }


        }

}

}







DragScript를 Player 전투기에 붙힙니다




File -> Build Settings 를 엽니다



Android를 선택하고 Switch Platform을 클릭합니다


Game 씬을 드래그하여 Snenes In Build 에 집어 넣습니다






Player Settings 를 눌러 인스팩터 값을 수정합니다


Default Icon을 게임에 적합한 그림을 찾아 넣습니다 되도록 정사각형의 그림을 찾아넣고 Resolution and Presentation 에 그림과 같이 Landscape Right 와 Landscape Left를 체크 해제합니다







Other Settings 로 들어가서 Package Name을 변경합니다 


설정이 끝나면 빌드를 눌러 압축 APK 파일이 생성되도록합니다


생성한 압축파일 아이콘이 각자에 압축프로그램에 따라 다를수 있습니다

저는 참고로 반디집을 깔아서 저렇게 나오네요 ㅎㅎ


각자 안드로이드 휴대폰에 파일을 넣고 실행시킵니다











프로젝트파일 다운로드 





반응형
반응형

이번시간은 재시작 버튼을 만들어 게임을 재시작하는 프로그램을 짜보겠습니다


Hierarchy 창에 오른쪽 마우스 버튼을 눌러 UI -> Button 을 생성합니다


생성된 Button을  GameOver 오브젝트  밑으로 자식으로 둡니다 그리고 속성을 그림과 같이 합니다



Button 밑에 Text Retry 라 하고 그림과 같이 속성 바꿉니다 




그리과 같이 Button 의 크기와 위치을 하게 합니다


ObjectManager 스크립트를 열어서 수정합니다


총알을 모두 비활성 시키는 함수 ClearBullets()를 만들어 저장합니다





SpawnManager 함수를 열고 수정합니다



적 리스트를 담을 리스트배열 변수를 만들고 생성되는 적오브젝트를 삭제 하는 함수를 만듭니다






TextContrl 스크립트를 열고 수정합니다


Restart()함수를 만들어 게임오버 텍스쳐를 비활성 시키고 코루틴함수 ShowReady()를 실행시켜 게임을 재시작 합니다



Player 스크립트를 열어 스크립트를 수정합니다


Player가 격파 되었을때 삭제 하지 않고 비활성으로 만듭니다

그리고 게임이 재시작 되었을때 Player 가 초기 지점에 올수 있도록 transform.position 을 playerPos에 맞춤니다




GameManager 스크립트를 열고 수정합니다 


게임 재시작 과 관련된 수정된 스크립트를 불러 들여 게임을 3초후 재시작 하고 Palyer를  활성 시킵니다 




Button 오브젝트를 선택하여 OnClick에 + 를 눌러 그림과 같이 합니다



GameManager 오브젝트를 드래그하여 OnClick()에 넣습니다



No Function에 GameManager -> ResetGame() 선택합니다 




게임을 실행 시킵니다 

Player가 격파되고  Retry 버튼이 생성되고 버튼 눌렸을때 3초후 게임이 실행되는지 확인 합니다




반응형
반응형

이번시간에는 총알이 계속 생성되고 삭제되면 메모리 누수현상이 일어나는 것을 방지 하기 위한 프로그램을 짜보겠습니다

그림과 같이 비행기 총알을 만들고 계속 삭제 하다 보면 기기에 메모리 누수 현상으로 기기에 무리를 가하게 됩니다

그래서 memory pool을 만들어 활용 하겠습니다

이것은 총알 뿐만 아니라 다른 이팩트나 적을 생성 할때도 같이 활용합니다 



빈 게임오브젝트를 만들어 ObjectManager라 이름 붙힘니다


ObjectManager 스크립트를 만듭니다




ObjectManager 스크립트를 오브젝트에 붙힘니다


ObjectManager 스크립트작성



using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class ObjectManager : MonoBehaviour

{

    public static ObjectManager instance;


    public GameObject rocketPrefab;


    List<GameObject> bullets = new List<GameObject>();// 총알을 담아둘 리스트를 만듬


    private void Awake()

    {

        if (ObjectManager.instance == null)

        {

            ObjectManager.instance = this;

        }

    }

    void Start ()

    {

        CreateBullets(5); //총알 5개를 생성

}

    void CreateBullets(int bulletCount)

    {

        for (int i = 0; i <  bulletCount; i++)

        {     

            // Instantiate()로 생성한 게임 오브젝트를 변수에 담고자 하면 ,"as + 데이터타임"을 명령어 뒤어 붙여 주어야 함

            GameObject bullet = Instantiate(rocketPrefab) as GameObject;


            bullet.transform.parent = transform;

            bullet.SetActive(false);


            bullets.Add(bullet);

        }

    }

    public GameObject GetBullet(Vector3 pos)

    {

        GameObject reqBullet = null;

        for (int i = 0; i < bullets.Count; i++) 

        {

            if (bullets[i].activeSelf == false)

            {

                reqBullet = bullets[i];// 비활성화 되어있는 총알을 찾아 reqBullet에 담아둠니다


                break;

            }

        }


        if (reqBullet == null)// 추가 총알 생성

        {

            GameObject newBullet = Instantiate(rocketPrefab) as GameObject;

            newBullet.transform.parent = transform;


            bullets.Add(newBullet);

            reqBullet = newBullet;

        }


        reqBullet.SetActive(true); //reqBullet활성


        reqBullet.transform.position = pos;


        return reqBullet;

    }

void Update () {

}

}







메모리 플 스크립트를 작성 하였습니다

메모리 플 스크립트를 총알에 활둉하기 위하여 총알과 연관되어 있는 스크립트를 수정하여야 합니다



Player 스크립트를 엽니다




그림과 같이 스크립트를 수정합니다


Rocket스크립트를 엽니다 




Rocket 스크립트를 수정합니다


Enemy 스크립트를 엽니다


그림과 같이 Enemy 스크립트를 수정합니다


게임을 실행하여 총알의 메모리 풀이 제대로 동작하는지 확인합니다

이번시간은 총알을 활용한 메모리 풀을 만들어 보았는데 같은 방법으로 적기나 이팩트를 메모리 풀로 활용하면 데이터 메모리 누수를 막을 수 있습니다 

 


반응형
반응형

이번시간은 

슈팅게임이 시작하기 전에 게임시작을 알리는 텍스쳐를 만들고 Player가 격파 되었을때 게임오버 텍스쳐를 나타내어 보겠습니다


먼저 Canvas 에  Text를 만들고 이름을 Ready 라고 합니다.

Ready 인스펙터를 그림과 같이 바꿉니다


새로운 스크립트를 생성하고 이름을  TextContrl 라고 합니다



TextContrl 스크립트를 Canvas 에 붙힙니다




TextContrl 스크립트를 작성합니다 코루틴 함수를 넣어 3초간 텍스쳐가 깜박이는 스크립트를 짭니다





Canvas 를 선택하여 Ready텍스쳐를 Text Contrl 스크립트에 연결합니다.



GameOver 텍스트를 만들고 그림과 같이 인스텍터 속성을 바꿉니다





TextContrl 스크립트를 수정합니다



TextContrl 스크립트를 수정합니다


GameOver 텍스트를 TextContrl스크립트에 연결합니다



GameManager 스크립트를 선택하고 수정합니다


그리과 같이 isPlayerAlive 변수와 KillPlayer() 함수를 추가 합니다 





Player 스크립트를 선택하고 스크립트를 수정합니다



OnTriggerEnter2D() 함수에  GameManager.instance.KillPlayer();

추가하여 저장합니다


게임을 실행합니다 

게임 시작하면 Ready 텍스쳐가 깜박거리고 Player가 격파 되었을때 GameOver텍스쳐가 뜨는지 확인합니다




반응형
반응형

이번에는 Game Manager를 만들어서 게임전체를 컨트롤 하고 점수를 올리는 프로그램을 짜보겠습니다

먼저 빈게임오브젝트를 만들고 GameManager라고 합니다



GameManager스크립트를 만듭니다


Player스크립트를 선택하고 수정합니다


canShoot = false로 수정합니다


can Shoot 체크 해제 합니다




SpawnManager스크립트를 수정합니다


SpawnManager스크립트를 SoundManager스크립트 처럼 인스턴스 스크립트를 추가 합니다


SpawnManager오브젝트를 선택하고 is Spawn 을 체크 해제한다


GameManager스크립트를 GameManager오브젝트에 붙힙니다



그리고  Hierarchy창에서 마우스 오른쪽 버튼을 눌러 UI -> Text 를 생성합니다





생성된 Canvas -> Canvas Scaler를 그림과 같이 Scale With Screen Size를 선택합니다


Scale With Screen Size를 선택 하고 Reference Resolution 을 200 320 으로 바끕니다


Text를 선택하여 그림과 같이 속성을 바꿉니다


Text가 화면에 잘보일수있게 그림과같이 설정되었습니다.Text와 위치는 사용자가 보기 좋은 방향으로 위치와 색깔을 수정하셔도 됩니다



그림과 같이 GameManager 스크립트를 작성합니다

3초후 게임이 실행되는 Invoke 함수를 넣고 AddScore 함수를 넣어 적을 격파 하였을때 점수를 올리는 스크립트를 짭니다







GameManager를 선택하고 Text를 붙힙니다



using System.Collections;

using System.Collections.Generic;

using UnityEngine;

using UnityEngine.UI;


public class GameManager : MonoBehaviour {


    GameObject player;


    int score = 0;


    public Text scoreText;


    public static GameManager instance;


    private void Awake()

    {

        if (GameManager.instance == null)

        {

            GameManager.instance = this;

        }

    }

    void Start ()

    {

        player = GameObject.FindGameObjectWithTag("Player");


        Invoke("StartGame", 3f);//3초후 StarGame실행

}


    void StartGame()

    {

        player.GetComponent<Player>().canShoot = true;


        SpawnManager.instance.isSpawn = true;

        

    }


    public void AddScore(int enemyScore)//적을 격파할때마다 점수가 올라가는 함수

    {

        score += enemyScore;

        scoreText.text = "Score:" + score;

    }

void Update () {

}

}





Enemy 스크립트를 선택하고 수정합니다


그림과 같이 Enemy스크립트를 수정합니다




게임을 실행하여 3초후 총알이 발사되고 적이나오는것을 확인하고 적을 격파하였을때 점수가 올라가는 것을 확인합니다





반응형
반응형


이번 시간에는 Player 전투기가 화면 밖으로 나가지 않도록 이동 제한을 주겠습니다


Player 스크립트를 선택하고 수정합니다


 

  



MoveContro()함수를 수정합니다 /현재 플레이어의 월드좌표(transform.position)을 뷰포트 기준 좌표로 변환시키는 명령을 추가 하고 

//Mathf.Clamp01(값)- 입력된 값이 0~1 사이를 벗어나지 못하게 강제로 조정해 주는 함수를 넣어 줍니다

그리고 다시 뷰포트를 월드좌표를 변환한 벡터값을 Player  transform position에 넣어줍니다


게임을 실행하여 Player전투기가 화면의 이동제한이 되었는지 확인합니다


반응형
반응형

이번시간에는 적기가 계속 나오는 것을 화면상 안보이면 삭제하여 데

이터 메모리를 절약하는 프로그램을 짜보이겠습니다

먼저 빈게임오브젝트를 만들어 RemoveZone 이라고 이름 붙힙니다


RemoveZone 읍젝트에 Box Collider2D를 붙힘니다



배경 스크롤 밑에다 화면 끝나는 지점에 RemoveZone을 크기와 맞추어 놓습니다


새로운 스크립트 이름을 RemoveZone 이라 하고 스크립트를 만듭니다




RemoveZone 오브젝트에  RemoveZone 스크립트를 붙힙니다



RemoveZone 스크립트에 OnTriggerEnter2D() 함수를 넣어 Enemy 태그가 있는 오브젝트를 삭제 시키는 스크립트를 짭니다



RemoveZone오브젝트에 isTrigger을 체크합니다


게임을 실행합니다




반응형
반응형

이번시간에는 적이 자동으로 생성되는 적 Spawn을 만들겠습니다

Enemy 오브젝트를 선택하고 프리팹을 만듭니다 Enemy 오브젝트를 폴더에 드래그하면 생성됩니다


그리고 Enemy프리팹에 좌표를 0으로 세팅합니다


게임화면에 있는 Enemy는 삭제합니다




빈 오브젝트를 만들어 이름을 SpawnManager라 합니다


스크립트를 생성하여 SpawnManager라 이름 붙힙니다


SpawnManager스크립트를 SpawnManager오브젝트에 붙힙니다




SpawnManager 스크립트를 작성합니다

적이 생성되는 지점을 카메라의 월드좌표를 정의 하는 함수 CreatePosition()을 만들고

시간에따라 자동으로 랜덤하게 적을 생성하게하는 SpawnEnemy()함수를 만들어 저장합니다



using System.Collections;

using System.Collections.Generic;

using UnityEngine;


public class SpawnManager : MonoBehaviour

{


    Vector3[] positions = new Vector3[5];


    public GameObject enemy;


    public bool isSpawn = false;


    public float spawnDelay = 1.5f;

    float spawnTimer = 0f;


// Use this for initialization

void Start ()

    {

        CreatePositions();

}

    void CreatePositions() // 적이 나오는 지점을 카메라의 월드좌표로 정의한다

    {

        float viewPosY = 1.2f;

        float gapX = 1f / 6f;

        float viewPosX = 0f;


        for(int i= 0; i < positions.Length; i++)

        {

            viewPosX = gapX + gapX * i;


            Vector3 viewPos = new Vector3(viewPosX, viewPosY, 0);


            Vector3 WorldPos = Camera.main.ViewportToWorldPoint(viewPos);


            WorldPos.z = 0f;


            positions[i] = WorldPos;

        }

    }


    void SpawnEnemy()//isSpawn이 true일때 적을 랜덤하게 생성

    {

        if (isSpawn == true)

        {

            if (spawnTimer > spawnDelay)

            {

                 int rand = Random.Range(0, positions.Length);

              

                 Instantiate(enemy, positions[rand], Quaternion.identity);

                           

                spawnTimer = 0f;

            }


            spawnTimer += Time.deltaTime;

        }


    }

// Update is called once per frame

void Update ()

    {

        SpawnEnemy();

    }

}






SpawnManager오브젝트를 선택하고 Enemy 프리팹을 연결하고 isSpawn을 체크합니다




반응형

+ Recent posts