https://docs.unity3d.com/kr/530/Manual/OcclusionCulling.html

 

오클루전 컬링 - Unity 매뉴얼

오클루전 컬링(Occlusion Culling)은 다른 오브젝트에 가려(오클루전된) 카메라에 보이지 않는 오브젝트의 렌더링을 비활성화하는 기능입니다. 3D 컴퓨터 그래픽스에서는 대부분의 경우 카메라에서

docs.unity3d.com

 

큰 오브젝트에 가려질 오브젝트들도 만든다.

제일 큰 큐브와 플레인 설정

나머지 오브젝트들도 설정

 

오쿨루더와 오쿨루디 설정을 잘 해놓아야 한다.

 

윈도우를 누르면 오쿨루전 설정이 보인다.

인스펙터를 확인해보면 새로운 창이 추가되었다.

전체를 선택한후 베이크를 한다.

그러면 이렇게 뒤에 가려진 오브젝트들은 사라진다.

어떻게 빛이 나가는지 보고 싶을경우 여기를 체크하면 빛의 방향과 크기를 볼 수있다.

 

주의사항

 

베이크창을 보면 이렇게 크기를 조절할 수 있는데

제일 앞에 있는 오브젝트가 이 스케일보다 커야 한다.

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Sprite Shooter - Player and Monster Animation  (0) 2024.03.14
Sprite Shooter - Destroy Barrel and Layer  (0) 2024.03.13
Zombie - IK  (0) 2024.03.07
Zombie - Line Render  (0) 2024.03.07
Tank - Rigidbody.MovePosition  (0) 2024.03.06

게임오브젝트 3개를 생성해서 각각 배경을 넣어주는데

order in layer로 계층구조를 잡아줄것이다

a부터 c까지 각각 -3, -4, -5

게임 오브젝트의 좌표는 모두 0 0 0

복사를 해서 각각 3개로 만들어주고

0의 Y좌표는 0

1의 Y좌표는 -10

2의 Y좌표는 10

카메라는 그대로

배경은 뒤로 옮겨 눈속임을 할것이다.

 

스크립트 생성후 배경이 움직이는 로직 작성

using System.Collections;
using System.Collections.Generic;
using TreeEditor;
using UnityEditor;
using UnityEngine;

public class Background : MonoBehaviour
{
    public float speed;
    void Start()
    {
        
    }

    void Update()
    {
        Vector3 curPos = transform.position;
        Vector3 nextPos = Vector3.down * speed * Time.deltaTime;
        transform.position = curPos + nextPos;
    }
}

그런데 이렇게 하면 맨뒤에 아무것도 없는 배경이 보인다.

따라서 배경을 무한으로 만들어 주어야한다.

 

카메라의 시점

카메라를 잡을때 메인카메라의 시점 * 2를해서 화면이 짤리기전에 한번 더 보여주게  하였고

y좌표가 -1일때 시작점을 바꾸어 연속적으로 보이게 하였다.

using System.Collections;
using System.Collections.Generic;
using TreeEditor;
using UnityEditor;
using UnityEngine;

public class Background : MonoBehaviour
{
    public float speed;
    public int startIndex;
    public int endIndex;
    public Transform[] sprites;

    float viewHeight;
    
    void Start()
    {
        viewHeight = Camera.main.orthographicSize * 2; // 카메라
    }

    void Update()
    {
        Vector3 curPos = transform.position;
        Vector3 nextPos = Vector3.down * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

        if (sprites[endIndex].position.y < viewHeight * (-1))
        {
            //#.Sprite ReUse
            Vector3 backSpritePos = sprites[startIndex].localPosition;
            Vector3 frontSpritePos = sprites[endIndex].localPosition;

            sprites[endIndex].transform.localPosition = backSpritePos + Vector3.up * viewHeight;

            //이동이 완료되면 EndIndex, StartIndex 갱신
            //#.Cursor Index Change
            int startIndexSave = startIndex;
            startIndex = endIndex;
            endIndex = (startIndexSave -1 == -1) ? sprites.Length-1 : startIndexSave-1;
        }

    }
}

A, B, C를 나눈 이유는 원근감을 주기 위해서이다.

 

Parallax: 거리에 따른 상대적 속도를 활용한 기술

C는 속도1 B는2 A는4

 

using System.Collections;
using System.Collections.Generic;
using TreeEditor;
using UnityEditor;
using UnityEngine;

public class Background : MonoBehaviour
{
    public float speed;
    public int startIndex;
    public int endIndex;
    public Transform[] sprites;

    float viewHeight;
    
    void Start()
    {
        viewHeight = Camera.main.orthographicSize * 2; // 카메라
    }

    void Update()
    {
        

        if (sprites[endIndex].position.y < viewHeight * (-1))
        {
            Move();
            Scrolling();
        }

        void Move()
        {
            Vector3 curPos = transform.position;
            Vector3 nextPos = Vector3.down * speed * Time.deltaTime;
            transform.position = curPos + nextPos;
        }

        void Scrolling()
        {
            //#.Sprite ReUse
            Vector3 backSpritePos = sprites[startIndex].localPosition;
            Vector3 frontSpritePos = sprites[endIndex].localPosition;

            sprites[endIndex].transform.localPosition = backSpritePos + Vector3.up * viewHeight;

            //이동이 완료되면 EndIndex, StartIndex 갱신
            //#.Cursor Index Change
            int startIndexSave = startIndex;
            startIndex = endIndex;
            endIndex = (startIndexSave - 1 == -1) ? sprites.Length - 1 : startIndexSave - 1;
        }
    }
}

'산대특' 카테고리의 다른 글

2D Vertical Shooting Game 모작 최종  (0) 2024.04.08
팬텀로즈스칼렛 모작 최종  (0) 2024.04.08
2D Airplane - 5  (1) 2024.03.14
2D Airplane - 4  (0) 2024.03.12
2D Airplane - 3  (0) 2024.03.12

몬스터애니메이션 컨트롤러 초기

몬스터 추적범위와 스태이트 변경

몬스터애니메이션컨트롤러 Hit

몬스터 추적 후 어택

몬스터 피격 애니메이션

몬스터 피격 시 출혈

플레이어 사망시 몬스터 춤

적 사망시 죽는 애니메이션

6(1).적 사망시 죽는 애니메이션.mp4
3.94MB

 

using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;
    public float moveSpeed = 10.0f;
    public float turnSpeed = 80.0f;
    private Animation anim;
    private readonly float initHp = 100f;
    public float currentHp;

    public delegate void PlayerDieHandler(); //대리자 정의

    public static event PlayerDieHandler OnPlayerDie; //이벤트 변수 정의

    private void Awake()
    {
        //제일먼저 호출되는 함수
        //스크리브가 비활성화되있어도 호출되는 함수

    }

    private void OnEnable()
    {
        //두번째로 호출되는 함수
        //스크립트가 활성화 될때마다 호출되는 함수
    }

    IEnumerator Start()
    {
        this.currentHp = this.initHp;
        // 세번째로 호출되는 함수 
        //Update함수가 호출되기 전에 호출되는 함수 
        //코루틴으로 호출될수 있는 함수 
        //예) 
        //IEnumerator Start(){}

        tr = GetComponent<Transform>();
        this.anim = GetComponent<Animation>();


        //이름으로 실행 하거나 
        this.anim.Play("Idle");


        //이름으로 가져와서 플레이 하거나 
        //this.anim.clip = this.anim.GetClip("Idle");
        //this.anim.Play();

        this.turnSpeed = 0f;

        yield return new WaitForSeconds(0.3f);

        this.turnSpeed = 80f;

    }

    private void FixedUpdate()
    {
        //일정간격으로 호출되는 함수 (기본값은 0.02초)
        //물리엔진의 계산 주기와 일치

    }

    void Update()
    {
        //프레임마다 호출되는 함수
        //호출 간격이 불규칙하다
        //화면의 렌더링 주기와 일치 함
        float h = Input.GetAxis("Horizontal"); // -1.0 ~ 0 ~ 1
        float v = Input.GetAxis("Vertical"); // -1.0 ~ 0 ~ 1
        float r = Input.GetAxis("Mouse X");

        //Debug.Log("h=" + h);
        //Debug.Log("v=" + v);
        Debug.Log("r=" + r);

        //this.tr.Translate(Vector3.forward * 1 * 0.01f , Space.Self); //로컬좌표 기준으로 이동
        //this.tr.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World); //월드좌표 기준으로 이동

        //매 프레임마다 1유닛씩 이동 => 0.02초
        //this.tr.Translate(Vector3.forward * 1 * Time.deltaTime);


        //매 초마다 1유닛씩 이동
        //프레임 레이트가 서로다른 기기에서도 개발자가 지정한 일정한 속도로 이동하기 위함
        //this.tr.Translate(Vector3.forward * v * Time.deltaTime * moveSpeed); //방향 * 속도 * 시간 => forward = (0,0,1) // v => -1~ 1 사이의 값 => v를 사용해서 앞뿐만이 아닌 뒤로도 이동가능

        //전후좌우 이동방향 벡터 계산
        Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h); // right = (1,0,0)

        //방향 * 속도 * 시간 
        this.tr.Translate(moveDir.normalized * moveSpeed * Time.deltaTime);

        //Vector3.up 축으로 turnSpeed만큼
        this.tr.Rotate(Vector3.up * r * this.turnSpeed);

        //캐릭터 애니메이션 실행
        PlayerAnim(h, v);


    }

    private void LateUpdate()
    {
        //Update함수가 종료된 후 호출 되는 함수
    }

    private void PlayerAnim(float h, float v)
    {
        //키보드 입력값을 기준으로 동작할 애니메이션 실행
        if (v >= 0.1f)
        {
            //0.25f초간 이전 애니메이션과 실행할 애니메이션을 보간 
            this.anim.CrossFade("RunF", 0.25f); //전진 애니메이션 실행 
        }
        else if (v <= -0.1f)
        {
            this.anim.CrossFade("RunB", 0.25f);//후진 
        }
        else if (h >= 0.1f)
        {
            this.anim.CrossFade("RunR", 0.25f);//오른쪽 
        }
        else if (h <= -0.1f)
        {
            this.anim.CrossFade("RunL", 0.25f);//왼쪽 
        }
        else
        {
            this.anim.CrossFade("Idle", 0.25f);//기본 
        }

    }


    private void OnTriggerEnter(Collider other)
    {
        if(this.currentHp >= 0f && other.CompareTag("Punch"))
        {
            this.currentHp -= 10f;
            Debug.Log($"Player hp = {currentHp / initHp}");

            if (this.currentHp <= 0f)
            {
                this.PlayerDie();
            }

        }
    }
    private void PlayerDie()
    {
        Debug.Log("Player Die!");

        ////Monster 태그를 가진 오브젝트들을 검색
        //GameObject[] monsters = GameObject.FindGameObjectsWithTag("Monster");

        ////모든 몬스터의 OnplayerDie함수실행

        //foreach(GameObject monster in monsters)
        //{
        //    monster.SendMessage("OnPlayerDie", SendMessageOptions.RequireReceiver);
        //}
        PlayerCtrl.OnPlayerDie(); //ㄷ대리차 호출(이벤트 발생 Dispatch)

    }


}

 

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

public class BulletCtrl : MonoBehaviour
{
    public float damage = 20.0f;
    public float force = 1500f;
    private Rigidbody rb;

    void Start()
    {
        this.rb = this.GetComponent<Rigidbody>();
        this.rb.AddForce(this.transform.forward * force);

        Destroy(gameObject, 3f);
    }

}

 

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

public class FollowCam : MonoBehaviour
{
    public Transform targetTr;
    private Transform camTr;

    //대상으로 부터 떨어질 거리 
    [Range(2.0f, 20.0f)]
    public float distance = 10.0f;

    //Y축으로 이동할 높이 
    [Range(0.0f, 10.0f)]
    public float height = 2.0f;

    //반응속도 
    public float damping = 10f;

    //SmoothDamp에 사용할 속도 변수
    private Vector3 velocity = Vector3.zero;

    //카메라 LookAt OffSet
    public float targetOffset = 2.0f;


    void Start()
    {
        this.camTr = GetComponent<Transform>();     //Transform 객체 캐싱 
    }

    private void LateUpdate()
    {
        //추적할 대상의 뒤쪽 distance만큼 이동 
        //높이를 height만큼 이동 
        Vector3 pos = this.targetTr.position +
            (-targetTr.forward * distance) +
            (Vector3.up * height);

        //구면 선형 보간 함수를 사용해 부드럽게 위치를 변경 
        //시작위치, 목표 위치, 시간 
        //this.camTr.position = Vector3.Slerp(this.camTr.position, pos, Time.deltaTime * damping);

        //시작위치, 목표위치, 현재속도, 목표 위치까지 도달할 시간
        this.camTr.position = Vector3.SmoothDamp(this.camTr.position, pos, ref velocity, damping);


        //Camera를 피벗 좌표를 향해 회전 
        this.camTr.LookAt(this.targetTr.position + (targetTr.up * targetOffset));

    }
}

 

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

public class FireCtrl : MonoBehaviour
{
    public GameObject bulletPrefab;
    public Transform firePoint;
    public AudioClip fireSfx;
    private AudioSource audioSource;

    private MeshRenderer muzzleFlash;
    private Coroutine muzzleFlashCoroutine;

    private void Start()
    {
        audioSource = GetComponent<AudioSource>();
        muzzleFlash = this.firePoint.GetComponentInChildren<MeshRenderer>();
        muzzleFlash.enabled = false;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            this.Fire();
        }
    }

    private void Fire()
    {
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);

        audioSource.PlayOneShot(fireSfx, 1f);

        if (muzzleFlashCoroutine != null) StopCoroutine(muzzleFlashCoroutine);

        this.muzzleFlashCoroutine = StartCoroutine(this.ShowMuzzleFlash());
    }

    IEnumerator ShowMuzzleFlash()
    {
        Vector2 offset = new Vector2(Random.Range(0, 2), Random.Range(0, 2)) * 0.5f;
        muzzleFlash.material.mainTextureOffset = offset;



        float angle = Random.Range(0, 360);
        muzzleFlash.transform.localRotation = Quaternion.Euler(0, 0, angle);


        float scale = Random.Range(1, 2);
        muzzleFlash.transform.localScale = Vector3.one * scale;


        muzzleFlash.enabled = true; //보여주기 

        yield return new WaitForSeconds(0.2f);  //0.2초후 

        muzzleFlash.enabled = false;    //안보여주기 
    }
}

 

using UnityEngine;
using System.Collections;
using UnityEditor;


public enum ArrowType
{
    Default,
    Thin,
    Double,
    Triple,
    Solid,
    Fat,
    ThreeD,
}

public static class DrawArrow
{
    public static void ForGizmo(Vector3 pos, Vector3 direction, Color? color = null, bool doubled = false, float arrowHeadLength = 0.2f, float arrowHeadAngle = 20.0f)
    {
        Gizmos.color = color ?? Color.white;

        //arrow shaft
        Gizmos.DrawRay(pos, direction);

        if (direction != Vector3.zero)
        {
            //arrow head
            Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            Gizmos.DrawRay(pos + direction, right * arrowHeadLength);
            Gizmos.DrawRay(pos + direction, left * arrowHeadLength);
        }
    }

    public static void ForDebug(Vector3 pos, Vector3 direction, float duration = 0.5f, Color? color = null, ArrowType type = ArrowType.Default, float arrowHeadLength = 0.2f, float arrowHeadAngle = 30.0f, bool sceneCamFollows = false)
    {
        Color actualColor = color ?? Color.white;
        duration = duration / Time.timeScale;

        float width = 0.01f;

        Vector3 directlyRight = Vector3.zero;
        Vector3 directlyLeft = Vector3.zero;
        Vector3 directlyBack = Vector3.zero;
        Vector3 headRight = Vector3.zero;
        Vector3 headLeft = Vector3.zero;

        if (direction != Vector3.zero)
        {
            directlyRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + 90, 0) * new Vector3(0, 0, 1);
            directlyLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - 90, 0) * new Vector3(0, 0, 1);
            directlyBack = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180, 0) * new Vector3(0, 0, 1);
            headRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            headLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1);
        }

        //draw arrow head
        Debug.DrawRay(pos + direction, headRight * arrowHeadLength, actualColor, duration);
        Debug.DrawRay(pos + direction, headLeft * arrowHeadLength, actualColor, duration);

        switch (type)
        {
            case ArrowType.Default:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                break;
            case ArrowType.Double:
                Debug.DrawRay(pos + directlyRight * width, direction * (1 - width), actualColor, duration); //draw line slightly to right
                Debug.DrawRay(pos + directlyLeft * width, direction * (1 - width), actualColor, duration); //draw line slightly to left

                //draw second arrow head
                Debug.DrawRay(pos + directlyBack * width + direction, headRight * arrowHeadLength, actualColor, duration);
                Debug.DrawRay(pos + directlyBack * width + direction, headLeft * arrowHeadLength, actualColor, duration);

                break;
            case ArrowType.Triple:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                Debug.DrawRay(pos + directlyRight * width, direction * (1 - width), actualColor, duration); //draw line slightly to right
                Debug.DrawRay(pos + directlyLeft * width, direction * (1 - width), actualColor, duration); //draw line slightly to left
                break;
            case ArrowType.Fat:
                break;
            case ArrowType.Solid:
                int increments = 20;
                for (int i = 0; i < increments; i++)
                {
                    float displacement = Mathf.Lerp(-width, +width, i / (float)increments);
                    //draw arrow body
                    Debug.DrawRay(pos + directlyRight * displacement, direction, actualColor, duration); //draw line slightly to right
                    Debug.DrawRay(pos + directlyLeft * displacement, direction, actualColor, duration); //draw line slightly to left
                                                                                                        //draw arrow head
                    Debug.DrawRay((pos + direction) + directlyRight * displacement, headRight * arrowHeadLength, actualColor, duration);
                    Debug.DrawRay((pos + direction) + directlyRight * displacement, headLeft * arrowHeadLength, actualColor, duration);
                }
                break;
            case ArrowType.Thin:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                break;
            case ArrowType.ThreeD:
                break;
        }

        /*#if UNITY_EDITOR
            //snap the Scene view camera to a spot where it is looking directly at this arrow.
                if (sceneCamFollows)
                    SceneViewCameraFollower.activateAt(pos + direction, duration, "_arrow");
        #endif*/
    }

    public static void randomStar(Vector3 center, Color color)
    {
        //special: refuse to draw at 0,0.
        if (center == Vector3.zero) return;
        for (int i = 0; i < 2; i++)
            DrawArrow.ForGizmo(center, UnityEngine.Random.onUnitSphere * 1, color, false, 0.1f, 30.0f);
    }

    public static void comparePositions(Transform t1, Transform t2)
    {
        //direct from one to the other:
        ForDebug(t1.position, t2.position - t1.position);

        //direction
        //Vector3 moveDirection = (t2.position-t1.position).normalized;
    }
}

 

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

public class RemoveBullet : MonoBehaviour
{
    public GameObject sparkEffectPrefab;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            //첫번째 충돌 지점의 정보 추출 
            ContactPoint contactPoint = collision.contacts[0];

            DrawArrow.ForDebug(contactPoint.point, -contactPoint.normal, 10, Color.red);

            //충돌한 총알의 법선 벡터를 쿼터니언으로 변환 
            Quaternion rot = Quaternion.LookRotation(-contactPoint.normal);

            Instantiate(this.sparkEffectPrefab, contactPoint.point, rot);

            Destroy(collision.gameObject);
        }
    }
}

 

using UnityEngine;

public class BarrelCtrl : MonoBehaviour
{
    private int hitCount = 0;
    private Transform trans;
    private Rigidbody rb;
    public GameObject explosionPrefab;
    public Texture[] textures;
    private new MeshRenderer renderer;

    //폭발 반경 
    public float radius = 10f;


    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        this.rb = this.GetComponent<Rigidbody>();

        //자식에 있는 랜더러 컴포넌트를 찾아옴 
        this.renderer = this.GetComponentInChildren<MeshRenderer>();
        //난수 발생 
        int index = Random.Range(0, this.textures.Length);
        //텍스쳐 변경 
        this.renderer.material.mainTexture = textures[index];
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            if (++hitCount == 3)
            {
                ExplodeBarrel();
            }
        }
    }

    private void ExplodeBarrel()
    {
        //폭파 이펙트를 생성 
        GameObject exp = Instantiate(explosionPrefab, this.trans.position, Quaternion.identity);
        //이펙트 5초 있다가 제거 
        Destroy(exp, 5.0f);

        //힘을 위로 가함 
        //this.rb.mass = 1.0f;    //질량을 줄여주고 
        //this.rb.AddForce(Vector3.up * 1500);    //위로 힘을 가한다 

        //간접 폭발력 전달 
        IndirectDamage(this.trans.position);

        //3초후 드럼통 제거 
        Destroy(this.gameObject, 3);
    }

    private void IndirectDamage(Vector3 pos)
    {
        //주변에 있는 드럼통들을 모두 추출 
        Collider[] colls = Physics.OverlapSphere(pos, this.radius, 1 << 3);

        foreach (var coll in colls)
        {
            var rb = coll.GetComponent<Rigidbody>();
            rb.mass = 1.0f;
            rb.constraints = RigidbodyConstraints.None;
            rb.AddExplosionForce(1500, pos, radius, 1200);
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(this.transform.position, this.radius);
    }


}

 

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

public class MonsterCtrl : MonoBehaviour
{
    public enum State
    {
        IDLE, TRACE, ATTACK, DIE //ctrl+shift+u : 대문자로 변경
    }

    public State state = State.IDLE;
    public float traceDist = 10f; //추적 사정거리
    public float attackDist = 2.0f;//공격 사거리
    public bool isDie = false;//몬스터 사망여부
    private int hp = 100; //몬스터 체력
    
    private NavMeshAgent agent;
    private Transform playerTrans;
    private Animator anim;
    private GameObject bloodEffectPrefab;

    private readonly int hashTrace = Animator.StringToHash("IsTrace");
    private readonly int hashAttack = Animator.StringToHash("IsAttack");
    private readonly int hashHit = Animator.StringToHash("Hit");
    private readonly int hashPlayerDie = Animator.StringToHash("PlayerDie");
    private readonly int hashSpeed = Animator.StringToHash("Speed");
    private readonly int hashDie = Animator.StringToHash("Die");


    private void OnEnable()
    {
        PlayerCtrl.OnPlayerDie += this.OnPlayerDie;
    }



    void Start()
    {
        //플레이어를 찾아아서 이동한다.
        this.agent = GetComponent<NavMeshAgent>();

        //프리팹을 로드한다 
        this.bloodEffectPrefab = Resources.Load<GameObject>("BloodSprayEffect");

        this.playerTrans = GameObject.FindWithTag("Player").transform;

        this.anim = GetComponent<Animator>();

        

        //이동한다
        this.agent.SetDestination(this.playerTrans.position);

        //몬스터의 상태를 체크 하는 코루틴
        this.StartCoroutine(CheckMonsterState());

        //상태에 따라 몬스터의 행동을 수행하는 코루틴
        this.StartCoroutine(this.MonsterAction());
    }

    IEnumerator MonsterAction()
    {
        while(!this.isDie)
        {
            switch (this.state)
            {
                case State.IDLE:
                    agent.isStopped = true; //추적정지
                    this.anim.SetBool(hashTrace, false);
                    break;
                case State.TRACE:
                    agent.SetDestination(this.playerTrans.position);
                    agent.isStopped = false; //추적을 재개
                    this.anim.SetBool(hashTrace, true);
                    this.anim.SetBool(hashAttack, false);
                    break;
                case State.ATTACK:
                    this.anim.SetBool(hashAttack, true);
                    break;
                case State.DIE:
                    this.isDie = true;
                    agent.isStopped = true;
                    anim.SetTrigger(hashDie);
                    GetComponent<CapsuleCollider>().enabled = false;
                    break;
            
            }


            yield return new WaitForSeconds(0.3f);
        }
    }

    IEnumerator CheckMonsterState()
    {
        while(!this.isDie)
        {
            yield return new WaitForSeconds(0.3f);

            //몬스터 상태가 죽었을 때 코루틴을 즉시 종료
            if(state == State.DIE)
            {
                yield break; //코루틴을 즉시 종료
            }

            //거리 측정
            float distance = Vector3.Distance(this.transform.position, playerTrans.position);

            //공격 사거리 측정
            if(distance <= attackDist)
            {
                state = State.ATTACK;
            }
            else if(distance <= traceDist) //시야에 들어오면
            {
                state = State.TRACE;
            }
            else //공격 사거리에도 안들어오고 시야에도 없다
            {
                state = State.IDLE;
            }
        }
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            Destroy(collision.gameObject);
            anim.SetTrigger(this.hashHit);

            //충돌 지점을 가져온다.
            ContactPoint cp = collision.GetContact(0);
            Vector3 pos = cp.point;

            //충돌지점의 법선벡터(Noraml Vector)의 반대 방향으로 가져온다.
            //피격시 피가 튀기는 방향을 조절한다.
            Quaternion rot = Quaternion.LookRotation(-cp.normal);

            //이펙트를 생성
            this.ShowBloodEffect(pos, rot);

            hp -= 10;
            if(hp <= 0)
            {
                state = State.DIE;
            }
        }
    }


    void ShowBloodEffect(Vector3 pos, Quaternion rot)
    {
        //프리팹의 인스턴스화 게임인스턴스, 위치, 회전, 부모
        GameObject blood = Instantiate(this.bloodEffectPrefab, pos, rot, this.transform);

        Destroy(blood, 1.0f);
    }

    private void OnDrawGizmos()
    {
        if(state == State.TRACE)
        {
            Gizmos.color = Color.blue;
            Gizmos.DrawWireSphere(transform.position, traceDist);
        }
        if (state == State.ATTACK)
        {
            Gizmos.color = Color.red;
            Gizmos.DrawWireSphere(transform.position, attackDist);
        }
    }

    public void OnPlayerDie()
    {
        this.StopAllCoroutines();

        agent.isStopped = true;

        anim.SetFloat(hashSpeed, Random.Range(0.8f, 1.2f));

        anim.SetTrigger(hashPlayerDie);
    }

    private void OnDisable()
    {
        //이벤트 리스너를 제거
        PlayerCtrl.OnPlayerDie -= this.OnPlayerDie;
    }


}

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Occlusion Culling  (0) 2024.03.18
Sprite Shooter - Destroy Barrel and Layer  (0) 2024.03.13
Zombie - IK  (0) 2024.03.07
Zombie - Line Render  (0) 2024.03.07
Tank - Rigidbody.MovePosition  (0) 2024.03.06

 

 

녹화_2024_03_13_22_03_51_619.mp4
2.26MB

 

이제 목숨과 점수를 만들 것이다.

UI는 앵커를 잘 잡아야 한다.

이미지는 쉬프트 알트 왼쪽상단

텍스트는 쉬프트 알트 중앙 상단

캔버스 크기 고정

 

캔버스 안에 있는 것들 크기 및 위치 재조정

 

버튼 같이 늘어나는? 것들은 경계선을 설정 => apply

빈 오브젝트에 넣는다.

적을 처치하면 점수를 받는다. ( enemy가 파괴될때 player의 스코어를 더해준다.)

UI이미지들은 게임매니저에서 관리

게임매니저의 업데이트문에서 점수 처리

세자리마다 쉼표로 나눠주는 숫자 양식

체력을 잃는 부분은 플레이어 스크립트에 있으므로

체력 이미지를 배열로 받아서

색의 알파를 조절하여 보이지 않게 할 것이다.

목숨이 없으면 게임오버 창이 나오게 할것이고 아닐시 리스폰

게임매니저에서 활성화 여부 조절하는 메서드 작성

이제 퍼블릭으로 생성된 컴포넌트에 부착

프리팹 모드로 들어가서 각각 점수 할당 500, 200, 50

 

실행을 해서 죽으면 게임오버가 나오고 재시작 버튼로직을 이제 만들것이다.

화면 전환을 하는 방식으로 사용할 것이다.

https://docs.unity3d.com/ScriptReference/SceneManagement.SceneManager.LoadScene.html

 

Unity - Scripting API: SceneManagement.SceneManager.LoadScene

Note: In most cases, to avoid pauses or performance hiccups while loading, you should use the asynchronous version of this command which is: LoadSceneAsync. When using SceneManager.LoadScene, the scene loads in the next frame, that is it does not load imme

docs.unity3d.com

 

File - Bullid Settings => 기본값은 0

그런데 플레이를 하다가 한번에 두발을 맞으면 체력이 2가 줄어드는 곳을 볼 수 있는데 이것을 수정할 것이다.

bool 변수를 다시 초기화 하는 부분도 꼭 구현해야한다.

using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;

public class Player : MonoBehaviour
{
    public bool isTouchTop;
    public bool isTouchBottom;
    public bool isTouchRight;
    public bool isTouchLeft;

    public int life;
    public int score;
    public float speed;
    public float power;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이

    public GameObject bulletObjA;
    public GameObject bulletObjB;

    public GameManager manager;
    public bool isHit;
    private Animator anim;

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

    void Move()
    {
        float h = Input.GetAxisRaw("Horizontal");
        if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
            h = 0;
        float v = Input.GetAxisRaw("Vertical");
        if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
            v = 0;
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

        if (Input.GetButtonDown("Horizontal") ||
            Input.GetButtonDown("Vertical"))
        {
            anim.SetInteger("Input", (int)h);
        }
    }

    private void Update()
    {
        Move();
        Fire();
        Reload();
    }

    void Fire()
    {
        if (!Input.GetButton("Fire1"))
            return;
        if (curShorDelay < maxShotDelay)
            return;

        switch (power)
        {

            case 1://Power One
                GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
                Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
                rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                break;
            case 2:
                GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
                GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
                Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
                rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
            case 3:
                GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
                GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
                GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
                Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
                rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
        }
                curShorDelay = 0;
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Border")
        {
            switch(collision.gameObject.name)
            {
                case "Top":
                    isTouchTop = true;
                    break;
                case "Bottom":
                    isTouchBottom = true;
                    break;
                case "Right":
                    isTouchRight = true;
                    break;
                case "Left":
                    isTouchLeft = true;
                    break;
            }
        }
        else if(collision.gameObject.tag == "Enemy" || collision.gameObject.tag == "EnemyBullet")
        {
            if (isHit)
            {
                return;
            }
            isHit = true;
            life--;
            manager.UpdateLifeIcon(life);

            if(life == 0)
            {
                manager.GameOver();
            }
            else
            {
                manager.RespawnPlayer();
            }
            gameObject.SetActive(false);

        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        switch (collision.gameObject.name)
        {
            case "Top":
                isTouchTop = false;
                break;
            case "Bottom":
                isTouchBottom = false;
                break;
            case "Right":
                isTouchRight = false;
                break;
            case "Left":
                isTouchLeft = false;
                break;
        }
    }
}

 

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

public class Bullet : MonoBehaviour
{
    public int dmg;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet")
        Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
    }
}

 

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public string enemyName;
    public int enemyScore;
    public float speed;
    public int health;
    public Sprite[] sprites;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이
    public GameObject bulletObjA;
    public GameObject bulletObjB;
    public GameObject player;
    SpriteRenderer spriteRenderer;
    Rigidbody2D rigid;

    private void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        rigid = GetComponent<Rigidbody2D>();
        rigid.velocity = Vector2.down * speed;
    }
    private void Update()
    {
        Fire();
        Reload();
    }
    void Fire()
    {
        if (curShorDelay < maxShotDelay)
            return;

        if (enemyName == "S")
        {
            GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
            Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
            Vector3 dirVec = player.transform.position - transform.position;
            rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
        }
        else if (enemyName == "L")
        {
            GameObject bulletR = Instantiate(bulletObjB, transform.position + Vector3.right * 0.3f, transform.rotation);
            GameObject bulletL = Instantiate(bulletObjB, transform.position + Vector3.left * 0.3f, transform.rotation);
            Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
            Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
            Vector3 dirVecR = player.transform.position - (transform.position + Vector3.right * 0.3f);
            Vector3 dirVecL = player.transform.position - (transform.position + Vector3.left * 0.3f);
            rigidR.AddForce(dirVecR.normalized * 4, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
            rigidL.AddForce(dirVecL.normalized * 4, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
        }

        curShorDelay = 0;  // 발사 후 딜레이 초기화는 발사 이후에 처리되도록 이동
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }
    private void OnHit(int dmg)
    {
        health -= dmg;
        spriteRenderer.sprite = sprites[1]; //피격시 1번
        Invoke("ReturnSprite", 0.1f);
        if(health <= 0)
        {
            Player playerLogic = player.GetComponent<Player>();
            playerLogic.score += enemyScore;
            Destroy(gameObject);
        }
    }

    void ReturnSprite()
    {
        spriteRenderer.sprite = sprites[0];
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "BorderBullet")
        {
            Destroy(gameObject );
        }
        else if(collision.gameObject.tag == "PlayerBullet")
        {
            Bullet bullet = collision.gameObject.GetComponent<Bullet>();
            OnHit(bullet.dmg);

            Destroy(collision.gameObject);
        }
    }

}

 

using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;

public class GameManager : MonoBehaviour
{
    public GameObject[] enemyObjs;
    public Transform[] spawnPoints;

    public float maxSpawnDelay;
    public float curSpawnDelay;

    public GameObject player;
    public Text scoreText;
    public Image[] lifeImage;
    public GameObject gameOverSet;
    private void Update()
    {
        curSpawnDelay += Time.deltaTime;

        if (curSpawnDelay > maxSpawnDelay)
        {
            SpawnEnemy();
            maxSpawnDelay = Random.Range(0.5f, 3f);
            curSpawnDelay = 0;
        }

        //UI Score

        Player playerLogic = player.GetComponent<Player>();
        scoreText.text = string.Format("{0:n0}", playerLogic.score);



    }
    void SpawnEnemy()
    {
        int ranEnemy = Random.Range(0, 3); //소환될 적
        int ranPoint = Random.Range(0, 9); //소환될 위치
        GameObject enemy = Instantiate(enemyObjs[ranEnemy],
                                        spawnPoints[ranPoint].position,
                                        spawnPoints[ranPoint].rotation);
        Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
        //스피드도 가지고 와야한다.
        Enemy enemyLogic = enemy.GetComponent<Enemy>();
        //적비행기 생성 직후 플레이에 변수를 넘겨준다.
        enemyLogic.player = player;
        if (ranPoint == 5 || ranPoint == 6)
        {
            enemy.transform.Rotate(Vector3.back * 90); //z축으로 90도
            rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
        }else if(ranPoint == 7 || ranPoint == 8)
        {
            enemy.transform.Rotate(Vector3.forward * 90); //z축으로 90도
            rigid.velocity = new Vector2(enemyLogic.speed, -1);
        }
        else
        {
            rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
        }
    }

    public void UpdateLifeIcon(int life)
    {
        //UI Init Disable
        for (int index = 0; index < 3; index++)
        {
            lifeImage[index].color = new Color(1, 1, 1, 0);
        }
        //UI Life Active
        for (int index = 0; index < life; index++)
        {
            lifeImage[index].color = new Color(1, 1, 1, 1);
        }
    }


    public void RespawnPlayer()
    {
        Invoke("RespawnPlayerExe", 2f);
        
    }
    private void RespawnPlayerExe()
    {
        player.transform.position = Vector3.down * 3.5f;
        player.SetActive(true);

        Player playerLogic = player.GetComponent<Player>();
        playerLogic.isHit = false;
    }
    public void GameOver()
    {
        gameOverSet.SetActive(true);
    }

    public void GameRetry()
    {
        SceneManager.LoadScene(0);
    }
}

'산대특' 카테고리의 다른 글

팬텀로즈스칼렛 모작 최종  (0) 2024.04.08
2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17
2D Airplane - 4  (0) 2024.03.12
2D Airplane - 3  (0) 2024.03.12
2D Airplane - 2  (0) 2024.03.10
using System;
using System.Collections;
using System.Collections.Generic;
using UnityEditor.Experimental.GraphView;
using UnityEngine;

public class PlayerCtrl : MonoBehaviour
{
    private Transform tr;
    public float moveSpeed = 10.0f;
    public float turnSpeed = 80.0f;
    private Animation anim;

    void Awake()
    {
        //제일먼저 호출되는 함수
        //스크리브가 비활성화되있어도 호출되는 함수

    }

    private void OnEnable()
    {
        //두번째로 호출되는 함수
        //스크립트가 활성화 될때마다 호출되는 함수
    }

    private void Start()
    {
        //세번째로 호출되는 함수
        //Update함수가 호출되기 전에 호출되는 함수
        //코루틴으로 호출될 수 있는 함수
        //예)
        //IEnumerator Start(){}
        tr = GetComponent<Transform>();
        anim = GetComponent<Animation>();

        this.anim.Play("Idle");

        //this.anim.clip = this.anim.GetClip("Idle");
        //anim.Play();

    }

    private void FixedUpdate()
    {
        //일정간격으로 호출되는 함수 (기본값은 0.02초)
        //물리엔진의 계산 주기와 일치

    }

    void Update()
    {
        //프레임마다 호출되는 함수
        //호출 간격이 불규칙하다
        //화면의 렌더링 주기와 일치 함
        float h = Input.GetAxis("Horizontal"); // -1.0 ~ 0 ~ 1
        float v = Input.GetAxis("Vertical"); // -1.0 ~ 0 ~ 1
        float r = Input.GetAxis("Mouse X");

        //Debug.Log("h=" + h);
        //Debug.Log("v=" + v);
        Debug.Log("r=" + r);

        //this.tr.Translate(Vector3.forward * 1 * 0.01f , Space.Self); //로컬좌표 기준으로 이동
        //this.tr.Translate(Vector3.forward * 1 * Time.deltaTime, Space.World); //월드좌표 기준으로 이동

        //매 프레임마다 1유닛씩 이동 => 0.02초
        //this.tr.Translate(Vector3.forward * 1 * Time.deltaTime);


        //매 초마다 1유닛씩 이동
        //프레임 레이트가 서로다른 기기에서도 개발자가 지정한 일정한 속도로 이동하기 위함
        //this.tr.Translate(Vector3.forward * v * Time.deltaTime * moveSpeed); //방향 * 속도 * 시간 => forward = (0,0,1) // v => -1~ 1 사이의 값 => v를 사용해서 앞뿐만이 아닌 뒤로도 이동가능

        //전후좌우 이동방향 벡터 계산
        Vector3 moveDir = (Vector3.forward * v) + (Vector3.right * h); // right = (1,0,0)

        //방향 * 속도 * 시간 
        this.tr.Translate(moveDir.normalized * moveSpeed * Time.deltaTime);

        //Vector3.up 축으로 turnSpeed만큼
        this.tr.Rotate(Vector3.up * r * this.turnSpeed);

        //캐릭터 애니메이션 실행
        PlayerAnim(h, v);


    }

    private void LateUpdate()
    {
        //Update함수가 종료된 후 호출 되는 함수
    }

    private void PlayerAnim(float h, float v)
    {
        //키보드 입력값을 기준으로 동작할 애니메이션 실행
        if (v >= 0.1f)
        {
            //0.25f초간 이전 애니메이션과 실행할 애니메이션을 보간 
            this.anim.CrossFade("RunF", 0.25f); //전진 애니메이션 실행 
        }
        else if (v <= -0.1f)
        {
            this.anim.CrossFade("RunB", 0.25f);//후진 
        }
        else if (h >= 0.1f)
        {
            this.anim.CrossFade("RunR", 0.25f);//오른쪽 
        }
        else if (h <= -0.1f)
        {
            this.anim.CrossFade("RunL", 0.25f);//왼쪽 
        }
        else
        {
            this.anim.CrossFade("Idle", 0.25f);//기본 
        }

    }

}

 

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

public class BulletCtrl : MonoBehaviour
{
    public float damage = 20.0f;
    public float force = 1500f;
    private Rigidbody rb;

    void Start()
    {
        this.rb = this.GetComponent<Rigidbody>();
        this.rb.AddForce(this.transform.forward * force);

        Destroy(gameObject, 3f);
    }

}

 

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

public class FollowCam : MonoBehaviour
{
    public Transform targetTr;
    private Transform camTr;

    //대상으로 부터 떨어질 거리 
    [Range(2.0f, 20.0f)]
    public float distance = 10.0f;

    //Y축으로 이동할 높이 
    [Range(0.0f, 10.0f)]
    public float height = 2.0f;

    //반응속도 
    public float damping = 10f;

    //SmoothDamp에 사용할 속도 변수
    private Vector3 velocity = Vector3.zero;

    //카메라 LookAt OffSet
    public float targetOffset = 2.0f;


    void Start()
    {
        this.camTr = GetComponent<Transform>();     //Transform 객체 캐싱 
    }

    private void LateUpdate()
    {
        //추적할 대상의 뒤쪽 distance만큼 이동 
        //높이를 height만큼 이동 
        Vector3 pos = this.targetTr.position +
            (-targetTr.forward * distance) +
            (Vector3.up * height);

        //구면 선형 보간 함수를 사용해 부드럽게 위치를 변경 
        //시작위치, 목표 위치, 시간 
        //this.camTr.position = Vector3.Slerp(this.camTr.position, pos, Time.deltaTime * damping);

        //시작위치, 목표위치, 현재속도, 목표 위치까지 도달할 시간
        this.camTr.position = Vector3.SmoothDamp(this.camTr.position, pos, ref velocity, damping);


        //Camera를 피벗 좌표를 향해 회전 
        this.camTr.LookAt(this.targetTr.position + (targetTr.up * targetOffset));

    }
}

 

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

public class FireCtrl : MonoBehaviour
{
    public GameObject bulletPrefab;
    public Transform firePoint;
    public AudioClip fireSfx;
    private AudioSource audioSource;

    private MeshRenderer muzzleFlash;
    private Coroutine muzzleFlashCoroutine;

    private void Start()
    {
        audioSource = GetComponent<AudioSource>();
        muzzleFlash = this.firePoint.GetComponentInChildren<MeshRenderer>();
        muzzleFlash.enabled = false;
    }

    void Update()
    {
        if (Input.GetMouseButtonDown(0))
        {
            this.Fire();
        }
    }

    private void Fire()
    {
        Instantiate(bulletPrefab, firePoint.position, firePoint.rotation);

        audioSource.PlayOneShot(fireSfx, 1f);

        if (muzzleFlashCoroutine != null) StopCoroutine(muzzleFlashCoroutine);

        this.muzzleFlashCoroutine = StartCoroutine(this.ShowMuzzleFlash());
    }

    IEnumerator ShowMuzzleFlash()
    {
        Vector2 offset = new Vector2(Random.Range(0, 2), Random.Range(0, 2)) * 0.5f;
        muzzleFlash.material.mainTextureOffset = offset;



        float angle = Random.Range(0, 360);
        muzzleFlash.transform.localRotation = Quaternion.Euler(0, 0, angle);


        float scale = Random.Range(1, 2);
        muzzleFlash.transform.localScale = Vector3.one * scale;


        muzzleFlash.enabled = true; //보여주기 

        yield return new WaitForSeconds(0.2f);  //0.2초후 

        muzzleFlash.enabled = false;    //안보여주기 
    }
}

 

using UnityEngine;
using System.Collections;
using UnityEditor;


public enum ArrowType
{
    Default,
    Thin,
    Double,
    Triple,
    Solid,
    Fat,
    ThreeD,
}

public static class DrawArrow
{
    public static void ForGizmo(Vector3 pos, Vector3 direction, Color? color = null, bool doubled = false, float arrowHeadLength = 0.2f, float arrowHeadAngle = 20.0f)
    {
        Gizmos.color = color ?? Color.white;

        //arrow shaft
        Gizmos.DrawRay(pos, direction);

        if (direction != Vector3.zero)
        {
            //arrow head
            Vector3 right = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            Vector3 left = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            Gizmos.DrawRay(pos + direction, right * arrowHeadLength);
            Gizmos.DrawRay(pos + direction, left * arrowHeadLength);
        }
    }

    public static void ForDebug(Vector3 pos, Vector3 direction, float duration = 0.5f, Color? color = null, ArrowType type = ArrowType.Default, float arrowHeadLength = 0.2f, float arrowHeadAngle = 30.0f, bool sceneCamFollows = false)
    {
        Color actualColor = color ?? Color.white;
        duration = duration / Time.timeScale;

        float width = 0.01f;

        Vector3 directlyRight = Vector3.zero;
        Vector3 directlyLeft = Vector3.zero;
        Vector3 directlyBack = Vector3.zero;
        Vector3 headRight = Vector3.zero;
        Vector3 headLeft = Vector3.zero;

        if (direction != Vector3.zero)
        {
            directlyRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + 90, 0) * new Vector3(0, 0, 1);
            directlyLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - 90, 0) * new Vector3(0, 0, 1);
            directlyBack = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180, 0) * new Vector3(0, 0, 1);
            headRight = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 + arrowHeadAngle, 0) * new Vector3(0, 0, 1);
            headLeft = Quaternion.LookRotation(direction) * Quaternion.Euler(0, 180 - arrowHeadAngle, 0) * new Vector3(0, 0, 1);
        }

        //draw arrow head
        Debug.DrawRay(pos + direction, headRight * arrowHeadLength, actualColor, duration);
        Debug.DrawRay(pos + direction, headLeft * arrowHeadLength, actualColor, duration);

        switch (type)
        {
            case ArrowType.Default:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                break;
            case ArrowType.Double:
                Debug.DrawRay(pos + directlyRight * width, direction * (1 - width), actualColor, duration); //draw line slightly to right
                Debug.DrawRay(pos + directlyLeft * width, direction * (1 - width), actualColor, duration); //draw line slightly to left

                //draw second arrow head
                Debug.DrawRay(pos + directlyBack * width + direction, headRight * arrowHeadLength, actualColor, duration);
                Debug.DrawRay(pos + directlyBack * width + direction, headLeft * arrowHeadLength, actualColor, duration);

                break;
            case ArrowType.Triple:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                Debug.DrawRay(pos + directlyRight * width, direction * (1 - width), actualColor, duration); //draw line slightly to right
                Debug.DrawRay(pos + directlyLeft * width, direction * (1 - width), actualColor, duration); //draw line slightly to left
                break;
            case ArrowType.Fat:
                break;
            case ArrowType.Solid:
                int increments = 20;
                for (int i = 0; i < increments; i++)
                {
                    float displacement = Mathf.Lerp(-width, +width, i / (float)increments);
                    //draw arrow body
                    Debug.DrawRay(pos + directlyRight * displacement, direction, actualColor, duration); //draw line slightly to right
                    Debug.DrawRay(pos + directlyLeft * displacement, direction, actualColor, duration); //draw line slightly to left
                                                                                                        //draw arrow head
                    Debug.DrawRay((pos + direction) + directlyRight * displacement, headRight * arrowHeadLength, actualColor, duration);
                    Debug.DrawRay((pos + direction) + directlyRight * displacement, headLeft * arrowHeadLength, actualColor, duration);
                }
                break;
            case ArrowType.Thin:
                Debug.DrawRay(pos, direction, actualColor, duration); //draw center line
                break;
            case ArrowType.ThreeD:
                break;
        }

        /*#if UNITY_EDITOR
            //snap the Scene view camera to a spot where it is looking directly at this arrow.
                if (sceneCamFollows)
                    SceneViewCameraFollower.activateAt(pos + direction, duration, "_arrow");
        #endif*/
    }

    public static void randomStar(Vector3 center, Color color)
    {
        //special: refuse to draw at 0,0.
        if (center == Vector3.zero) return;
        for (int i = 0; i < 2; i++)
            DrawArrow.ForGizmo(center, UnityEngine.Random.onUnitSphere * 1, color, false, 0.1f, 30.0f);
    }

    public static void comparePositions(Transform t1, Transform t2)
    {
        //direct from one to the other:
        ForDebug(t1.position, t2.position - t1.position);

        //direction
        //Vector3 moveDirection = (t2.position-t1.position).normalized;
    }
}

 

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

public class RemoveBullet : MonoBehaviour
{
    public GameObject sparkEffectPrefab;

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            //첫번째 충돌 지점의 정보 추출 
            ContactPoint contactPoint = collision.contacts[0];

            DrawArrow.ForDebug(contactPoint.point, -contactPoint.normal, 10, Color.red);

            //충돌한 총알의 법선 벡터를 쿼터니언으로 변환 
            Quaternion rot = Quaternion.LookRotation(-contactPoint.normal);

            Instantiate(this.sparkEffectPrefab, contactPoint.point, rot);

            Destroy(collision.gameObject);
        }
    }
}

 

using UnityEngine;

public class BarrelCtrl : MonoBehaviour
{
    private int hitCount = 0;
    private Transform trans;
    private Rigidbody rb;
    public GameObject explosionPrefab;
    public Texture[] textures;
    private new MeshRenderer renderer;

    //폭발 반경 
    public float radius = 10f;


    void Start()
    {
        this.trans = this.GetComponent<Transform>();
        this.rb = this.GetComponent<Rigidbody>();

        //자식에 있는 랜더러 컴포넌트를 찾아옴 
        this.renderer = this.GetComponentInChildren<MeshRenderer>();
        //난수 발생 
        int index = Random.Range(0, this.textures.Length);
        //텍스쳐 변경 
        this.renderer.material.mainTexture = textures[index];
    }

    private void OnCollisionEnter(Collision collision)
    {
        if (collision.collider.CompareTag("Bullet"))
        {
            if (++hitCount == 3)
            {
                ExplodeBarrel();
            }
        }
    }

    private void ExplodeBarrel()
    {
        //폭파 이펙트를 생성 
        GameObject exp = Instantiate(explosionPrefab, this.trans.position, Quaternion.identity);
        //이펙트 5초 있다가 제거 
        Destroy(exp, 5.0f);

        //힘을 위로 가함 
        //this.rb.mass = 1.0f;    //질량을 줄여주고 
        //this.rb.AddForce(Vector3.up * 1500);    //위로 힘을 가한다 

        //간접 폭발력 전달 
        IndirectDamage(this.trans.position);

        //3초후 드럼통 제거 
        Destroy(this.gameObject, 3);
    }

    private void IndirectDamage(Vector3 pos)
    {
        //주변에 있는 드럼통들을 모두 추출 
        Collider[] colls = Physics.OverlapSphere(pos, this.radius, 1 << 3);

        foreach (var coll in colls)
        {
            var rb = coll.GetComponent<Rigidbody>();
            rb.mass = 1.0f;
            rb.constraints = RigidbodyConstraints.None;
            rb.AddExplosionForce(1500, pos, radius, 1200);
        }
    }

    private void OnDrawGizmos()
    {
        Gizmos.color = Color.red;
        Gizmos.DrawWireSphere(this.transform.position, this.radius);
    }


}

'산대특 > 게임 클라이언트 프로그래밍' 카테고리의 다른 글

Occlusion Culling  (0) 2024.03.18
Sprite Shooter - Player and Monster Animation  (0) 2024.03.14
Zombie - IK  (0) 2024.03.07
Zombie - Line Render  (0) 2024.03.07
Tank - Rigidbody.MovePosition  (0) 2024.03.06

 

 

리스폰 포인트 추가

추가후 어싸인

 

이제 적들이 옆에서도 등장하므로 위에서 아래로 떨어지는 부분 삭제

 

적 비행기 속도를 게임매니저가 관리할 것이다.

 

1, 2, 3, 4포인트는 x축은 변함이 없을 것이고 y축은 -1로 고정하여 아래로 내려오게

5, 6 포인트는 왼쪽(-1), y축은 -1

7, 8 포인트는 오른쪽(1), y축은 -1

생성시 보더불릿에 닿으면 사라져서 생성되지 않으니 위치 재조정

그림과 같이 옆모양으로 나오게 하기 위해 회전을 추가

 

이제 적의 총알을 만들 것이다.

기존의 불렛b를 복사한후 프리팹모드에 들어가서 이름을 바꾸고 저장

스프라이트 변경후 박스콜라이더 크기 재설정

 

하나더 만들어준 후 태그 달기

플레이어에 있는 코드를 가져와서 부착

네임은 오브젝트네임 자체에 들어가 있으므로 이름 변경

적이 생성될때 플레이어를 알아야한다.

적 비행기 생성 직후 플레이어 변수를 넘겨준다.

목표물 방향 = 목표물위치 - 자신의 위치

큰 적은 두발을 쏜다.

플레이어는 인스턴스화가 되어있지 않기에 할당X

 

 if문에 총알을 발사할 이름을 넣었기 때문에 이름을 할당

 

적들의 총알이 너무 빠르므로 속도를 조절

 

플레이어의 온트리거에 추가

게임매니저가 플레이러를 리스폰 시킬수 있다.

 

플레이어도 게임매니저를 알아야한다.

 public GameManager manager;

 

2초 후에 플레이어가 부활하게 할 것이다.

플레이어 스크립트에 리스폰 추가

 

using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;

public class Player : MonoBehaviour
{
    public bool isTouchTop;
    public bool isTouchBottom;
    public bool isTouchRight;
    public bool isTouchLeft;
    private Animator anim;

    public float speed;
    public float power;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이

    public GameObject bulletObjA;
    public GameObject bulletObjB;

    public GameManager manager;
    private void Awake()
    {
        anim = GetComponent<Animator>();
    }

    void Move()
    {
        float h = Input.GetAxisRaw("Horizontal");
        if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
            h = 0;
        float v = Input.GetAxisRaw("Vertical");
        if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
            v = 0;
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

        if (Input.GetButtonDown("Horizontal") ||
            Input.GetButtonDown("Vertical"))
        {
            anim.SetInteger("Input", (int)h);
        }
    }

    private void Update()
    {
        Move();
        Fire();
        Reload();
    }

    void Fire()
    {
        if (!Input.GetButton("Fire1"))
            return;
        if (curShorDelay < maxShotDelay)
            return;

        switch (power)
        {

            case 1://Power One
                GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
                Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
                rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                break;
            case 2:
                GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
                GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
                Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
                rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
            case 3:
                GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
                GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
                GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
                Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
                rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
        }
                curShorDelay = 0;
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Border")
        {
            switch(collision.gameObject.name)
            {
                case "Top":
                    isTouchTop = true;
                    break;
                case "Bottom":
                    isTouchBottom = true;
                    break;
                case "Right":
                    isTouchRight = true;
                    break;
                case "Left":
                    isTouchLeft = true;
                    break;
            }
        }
        else if(collision.gameObject.tag == "Enemy" || collision.gameObject.tag == "EnemyBullet")
        {
            manager.RespawnPlayer();
            gameObject.SetActive(false);

        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        switch (collision.gameObject.name)
        {
            case "Top":
                isTouchTop = false;
                break;
            case "Bottom":
                isTouchBottom = false;
                break;
            case "Right":
                isTouchRight = false;
                break;
            case "Left":
                isTouchLeft = false;
                break;
        }
    }
}

 

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

public class Bullet : MonoBehaviour
{
    public int dmg;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet")
        Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
    }
}

 

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public string enemyName;
    public float speed;
    public int health;
    public Sprite[] sprites;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이
    public GameObject bulletObjA;
    public GameObject bulletObjB;
    public GameObject player;
    SpriteRenderer spriteRenderer;
    Rigidbody2D rigid;

    private void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        rigid = GetComponent<Rigidbody2D>();
        rigid.velocity = Vector2.down * speed;
    }
    private void Update()
    {
        Fire();
        Reload();
    }
    void Fire()
    {
        if (curShorDelay < maxShotDelay)
            return;

        if (enemyName == "S")
        {
            GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
            Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
            Vector3 dirVec = player.transform.position - transform.position;
            rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
        }
        else if (enemyName == "L")
        {
            GameObject bulletR = Instantiate(bulletObjB, transform.position + Vector3.right * 0.3f, transform.rotation);
            GameObject bulletL = Instantiate(bulletObjB, transform.position + Vector3.left * 0.3f, transform.rotation);
            Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
            Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
            Vector3 dirVecR = player.transform.position - (transform.position + Vector3.right * 0.3f);
            Vector3 dirVecL = player.transform.position - (transform.position + Vector3.left * 0.3f);
            rigidR.AddForce(dirVecR.normalized * 4, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
            rigidL.AddForce(dirVecL.normalized * 4, ForceMode2D.Impulse);  // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
        }

        curShorDelay = 0;  // 발사 후 딜레이 초기화는 발사 이후에 처리되도록 이동
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }
    private void OnHit(int dmg)
    {
        health -= dmg;
        spriteRenderer.sprite = sprites[1]; //피격시 1번
        Invoke("ReturnSprite", 0.1f);
        if(health <= 0)
        {
            Destroy(gameObject);
        }
    }

    void ReturnSprite()
    {
        spriteRenderer.sprite = sprites[0];
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "BorderBullet")
        {
            Destroy(gameObject );
        }
        else if(collision.gameObject.tag == "PlayerBullet")
        {
            Bullet bullet = collision.gameObject.GetComponent<Bullet>();
            OnHit(bullet.dmg);

            Destroy(collision.gameObject);
        }
    }

}

 

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

public class GameManager : MonoBehaviour
{
    public GameObject[] enemyObjs;
    public Transform[] spawnPoints;

    public float maxSpawnDelay;
    public float curSpawnDelay;

    public GameObject player;

    private void Update()
    {
        curSpawnDelay += Time.deltaTime;

        if(curSpawnDelay > maxSpawnDelay)
        {
            SpawnEnemy();
            maxSpawnDelay = Random.Range(0.5f, 3f);
            curSpawnDelay = 0;
        }
    }
    void SpawnEnemy()
    {
        int ranEnemy = Random.Range(0, 3); //소환될 적
        int ranPoint = Random.Range(0, 9); //소환될 위치
        GameObject enemy = Instantiate(enemyObjs[ranEnemy],
                                        spawnPoints[ranPoint].position,
                                        spawnPoints[ranPoint].rotation);
        Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
        //스피드도 가지고 와야한다.
        Enemy enemyLogic = enemy.GetComponent<Enemy>();
        //적비행기 생성 직후 플레이에 변수를 넘겨준다.
        enemyLogic.player = player;
        if (ranPoint == 5 || ranPoint == 6)
        {
            enemy.transform.Rotate(Vector3.back * 90); //z축으로 90도
            rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
        }else if(ranPoint == 7 || ranPoint == 8)
        {
            enemy.transform.Rotate(Vector3.forward * 90); //z축으로 90도
            rigid.velocity = new Vector2(enemyLogic.speed, -1);
        }
        else
        {
            rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
        }
    }
    public void RespawnPlayer()
    {
        Invoke("RespawnPlayerExe", 2f);
        
    }
    private void RespawnPlayerExe()
    {
        player.transform.position = Vector3.down * 3.5f;
        player.SetActive(true);
    }
}

'산대특' 카테고리의 다른 글

2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17
2D Airplane - 5  (1) 2024.03.14
2D Airplane - 3  (0) 2024.03.12
2D Airplane - 2  (0) 2024.03.10
2D Airplane - 1  (0) 2024.03.09

녹화_2024_03_12_00_12_33_796.mp4
0.91MB

 

 

 

적들은 피해를 입어야하니 콜라이더  를 부착해야 되는데

이런 삼각형 물체는 박스콜라이더가 아닌 폴리건 콜라이더로 크기 조절을 할 수 있다.

 

그리고 이제 움직이는 속도가 있을 것이니 리지드바디 2D + 중력 0

 

그 후 태그 추가후 부착

적들이 필요한 요소추가와 지정한 속도로 내려올수 있도록 초기화

스프라이트 렌더러는 피격시 색이 흐려지므로 2개의 텍스쳐를 받기위하여 선언

피해를 받았을떄 체력은 데미지로 비례해서 감소하고

피격시 스프라이트 1번을 출력

그리고 0.1초후에 평상시 스프라이트인 0번을 출력할 것이고

체력이 0이하면 사라지고

또한 내려가다 벽과 부딫히거나 총알에 맞으면 파괴시킨다.

그런데 체력이 0이어야 사라져야 하므로 총알을 맞았을 경우와 그 데미지를 정해야한다.

 

각 총알에 파워 입력

적에게 체력과 스피드 부여

]

스프라이트도 각각 2개씩 넣어준다.

 

 

부딫히면 튕겨가는 것을 방지하기 위해 isTrigger체크

총알이 적과 부딫히면 관통하지 않고 사라지게 코드를 추가하였다.

피격시 스프라이트 변경과 Invoke함수를 통해 시간설정

 

만들어진 적들은 프리팹화 후 삭제

 

여기서 중요한건 프리팹화 할때 기존에 있던 자리가 저장되므로 모두 0 0 0 으로 초기화

게임을 관리할 게임매니저 생성

적을 정해진 시간동안 리스폰 할것이고

적의 수와 위치를 각각 지정

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

public class GameManager : MonoBehaviour
{
    public GameObject[] enemyObjs;
    public Transform[] spawnPoints;

    public float maxSpawnDelay;
    public float curSpawnDelay;

    private void Update()
    {
        curSpawnDelay += Time.deltaTime;

        if(curSpawnDelay > maxSpawnDelay)
        {
            SpawnEnemy();
            maxSpawnDelay = Random.Range(0.5f, 3f);
            curSpawnDelay = 0;
        }
    }
    void SpawnEnemy()
    {
        int ranEnemy = Random.Range(0, 3); //소환될 적
        int ranPoint = Random.Range(0, 5); //소환될 위치
        Instantiate(enemyObjs[ranEnemy],
            spawnPoints[ranPoint].position,
            spawnPoints[ranPoint].rotation);
    }
}

 

적들의 비행선이 보더에 걸리면사라지므로 씬보다 위 보더보다 아래에 에너미포인트 그룹을 위치시킨다.

각각의 포인트의 x좌표를 -1.8부터 1.8까지 위치

각각을 어싸인

 

using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;

public class Player : MonoBehaviour
{
    public bool isTouchTop;
    public bool isTouchBottom;
    public bool isTouchRight;
    public bool isTouchLeft;
    private Animator anim;

    public float speed;
    public float power;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이

    public GameObject bulletObjA;
    public GameObject bulletObjB;

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

    void Move()
    {
        float h = Input.GetAxisRaw("Horizontal");
        if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
            h = 0;
        float v = Input.GetAxisRaw("Vertical");
        if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
            v = 0;
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

        if (Input.GetButtonDown("Horizontal") ||
            Input.GetButtonDown("Vertical"))
        {
            anim.SetInteger("Input", (int)h);
        }
    }

    private void Update()
    {
        Move();
        Fire();
        Reload();
    }

    void Fire()
    {
        if (!Input.GetButton("Fire1"))
            return;
        if (curShorDelay < maxShotDelay)
            return;

        switch (power)
        {

            case 1://Power One
                GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
                Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
                rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                break;
            case 2:
                GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
                GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
                Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
                rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
            case 3:
                GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
                GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
                GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
                Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
                rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
        }
                curShorDelay = 0;
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Border")
        {
            switch(collision.gameObject.name)
            {
                case "Top":
                    isTouchTop = true;
                    break;
                case "Bottom":
                    isTouchBottom = true;
                    break;
                case "Right":
                    isTouchRight = true;
                    break;
                case "Left":
                    isTouchLeft = true;
                    break;
            }
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        switch (collision.gameObject.name)
        {
            case "Top":
                isTouchTop = false;
                break;
            case "Bottom":
                isTouchBottom = false;
                break;
            case "Right":
                isTouchRight = false;
                break;
            case "Left":
                isTouchLeft = false;
                break;
        }
    }
}

 

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

public class Bullet : MonoBehaviour
{
    public int dmg;
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet")
        Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
    }
}

 

using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;

public class Enemy : MonoBehaviour
{
    public float speed;
    public int health;
    public Sprite[] sprites;

    SpriteRenderer spriteRenderer;
    Rigidbody2D rigid;

    private void Awake()
    {
        spriteRenderer = GetComponent<SpriteRenderer>();
        rigid = GetComponent<Rigidbody2D>();
        rigid.velocity = Vector2.down * speed;
    }

    private void OnHit(int dmg)
    {
        health -= dmg;
        spriteRenderer.sprite = sprites[1]; //피격시 1번
        Invoke("ReturnSprite", 0.1f);
        if(health <= 0)
        {
            Destroy(gameObject);
        }
    }

    void ReturnSprite()
    {
        spriteRenderer.sprite = sprites[0];
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "BorderBullet")
        {
            Destroy(gameObject );
        }
        else if(collision.gameObject.tag == "PlayerBullet")
        {
            Bullet bullet = collision.gameObject.GetComponent<Bullet>();
            OnHit(bullet.dmg);

            Destroy(collision.gameObject);
        }
    }

}

 

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

public class GameManager : MonoBehaviour
{
    public GameObject[] enemyObjs;
    public Transform[] spawnPoints;

    public float maxSpawnDelay;
    public float curSpawnDelay;

    private void Update()
    {
        curSpawnDelay += Time.deltaTime;

        if(curSpawnDelay > maxSpawnDelay)
        {
            SpawnEnemy();
            maxSpawnDelay = Random.Range(0.5f, 3f);
            curSpawnDelay = 0;
        }
    }
    void SpawnEnemy()
    {
        int ranEnemy = Random.Range(0, 3); //소환될 적
        int ranPoint = Random.Range(0, 5); //소환될 위치
        Instantiate(enemyObjs[ranEnemy],
            spawnPoints[ranPoint].position,
            spawnPoints[ranPoint].rotation);
    }
}

 

'산대특' 카테고리의 다른 글

2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17
2D Airplane - 5  (1) 2024.03.14
2D Airplane - 4  (0) 2024.03.12
2D Airplane - 2  (0) 2024.03.10
2D Airplane - 1  (0) 2024.03.09

총알도 똑같이 잘라준다.

 

총알 충돌 시 이벤트가 생기므

총알에 박스콜라이더와 리지드바디 추가

에드포스로 날릴것이므로 리지드바디 -> 다이나믹

중력은 받지 않으므로 - 그래비티 스케일 0

재사용해서 사용할것이므로 프리팹화

총알 A를 복사해서 만든후 스프라이트에 다른 모양을 넣은 후 콜라이더 크기 조절

 

총알B를 저장하면 A를 복사해서 만든 것이므로 다시 프리팹화할때 알림 문구가 뜨는데

새로운 것이면 Original클릭

 

총알이 나중에 무수히 많아질 수 있으므로 지울 수 있는 또 다른 경계선 생성할 것인데

Bullet이라는 스크립트를 새로 생성

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

public class Bullet : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet")
        Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
    }
}

기존에 만들어 놓았던 Border를 복사해서 이름을 바꾸고

경계선을 재설정

 

태그를 새로 만든 후 할당

 

그럼 총알이 경계선에 닿으면 사라진다.

 

이제 총알을 만들 로직을 Player 스크립트에 작성할 것이다.

전 페이지에서 만들었던 업데이트문안에 있는 함수를 따로 캡슐화로 분리한 후 이름을 Move로 바꾸었고,

총알을 만들며 총알이 충돌을 일으켜야하므로 리지드바디를 사용

 

https://docs.unity3d.com/ScriptReference/ForceMode2D.html

 

Unity - Scripting API: ForceMode2D

Success! Thank you for helping us improve the quality of Unity Documentation. Although we cannot accept all submissions, we do read each suggested change from our users and will make updates where applicable. Close

docs.unity3d.com

퍼블릭으로 만들어둔 공간에 프리팹 할당

실행해보면 무수히 만들어진다.

 

그림과 같이 총알끼리 물리적 충돌로 인해 여러뱡항으로 나오는데

이것은 프리팹에서 isTrigger를 체크하면 해결된다.

 

총알이 눌렀을 때만 발사되게 조건을 넣어줄 것인데

!를 사용하는것이 아닌 조건일때만 실행되게 코드를 변경해줘도 된다.(코드 스타일)

이제 다음은 총알 딜레이를 추가할 것이다.

시간에 따라 현재 딜레이에 추가해줄것이고 

발사는 딜레이에 영향을 받아줄 것이므로 Fire문 안에 넣는다.

그리고 발사 후 딜레이를 없애주기 위해 마지막엔 딜레이를 0으로 초기화

총알을 쏠 때마다 딜레이를 변경해줄 수 있다.

 

총알의 파워를 입력해줄 것인데

케이스로 나눠서 각각 파워1, 2, 3일때 다르게 나오게 수치 변경

그리고 총알마다 간격을 조절하여

현재 위치해서 벡터값을 더해주면

총알의 생성위치를 원하는 간격으로 조절할 수 있다.

 

완성된 코드

using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;

public class Player : MonoBehaviour
{
    public bool isTouchTop;
    public bool isTouchBottom;
    public bool isTouchRight;
    public bool isTouchLeft;
    private Animator anim;

    public float speed;
    public float power;
    public float maxShotDelay; //실제 딜레이
    public float curShorDelay; //한발 쏜 후 딜레이

    public GameObject bulletObjA;
    public GameObject bulletObjB;

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

    void Move()
    {
        float h = Input.GetAxisRaw("Horizontal");
        if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
            h = 0;
        float v = Input.GetAxisRaw("Vertical");
        if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
            v = 0;
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

        if (Input.GetButtonDown("Horizontal") ||
            Input.GetButtonDown("Vertical"))
        {
            anim.SetInteger("Input", (int)h);
        }
    }

    private void Update()
    {
        Move();
        Fire();
        Reload();
    }

    void Fire()
    {
        if (!Input.GetButton("Fire1"))
            return;
        if (curShorDelay < maxShotDelay)
            return;

        switch (power)
        {

            case 1://Power One
                GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
                Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
                rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                break;
            case 2:
                GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
                GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
                Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
                rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
            case 3:
                GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
                GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
                GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
                Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
                Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
                rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
                rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);

                break;
        }
                curShorDelay = 0;
    }

    void Reload()
    {
        curShorDelay += Time.deltaTime;

    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Border")
        {
            switch(collision.gameObject.name)
            {
                case "Top":
                    isTouchTop = true;
                    break;
                case "Bottom":
                    isTouchBottom = true;
                    break;
                case "Right":
                    isTouchRight = true;
                    break;
                case "Left":
                    isTouchLeft = true;
                    break;
            }
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        switch (collision.gameObject.name)
        {
            case "Top":
                isTouchTop = false;
                break;
            case "Bottom":
                isTouchBottom = false;
                break;
            case "Right":
                isTouchRight = false;
                break;
            case "Left":
                isTouchLeft = false;
                break;
        }
    }
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;

public class Bullet : MonoBehaviour
{
    private void OnTriggerEnter2D(Collider2D collision)
    {
        if (collision.gameObject.tag == "BorderBullet")
        Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
    }
}

 

'산대특' 카테고리의 다른 글

2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17
2D Airplane - 5  (1) 2024.03.14
2D Airplane - 4  (0) 2024.03.12
2D Airplane - 3  (0) 2024.03.12
2D Airplane - 1  (0) 2024.03.09

 

 

 

 

스프라이트 게임을 자르기 위해 설정들을 이렇게 바꾸어 주었다.

그 후 Sprite Editor를 누르면 여러개의 이미지들이 묶여있는 것을 분할 할 수 있는데

크기로 24*24 여백을 1로 잘랐다.

 

우선 화면의 비율을 선택해주자

필자는 aspect로 하였고 비율을 입력하여 2가지 해상도를 만들었다.

 

비행기가 움직일 수 있게 코드 작성

transform은 time.deltatime이랑 세트로 생각하자!

현재의 위치에 키보드로 받을 위치를 더해준다.

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

public class Player : MonoBehaviour
{
    public float speed;
    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        float v = Input.GetAxisRaw("Vertical");
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;

    }
}

 

움직여보면 원하지 않는 배경 밖으로 나가게 된다.

 

비행기에 Box Collider를 추가하여 부착해주면 초록색 경계가 생긴다.

 

피격 범위로도 사용할 것이므로 콜라이더의 크기를 줄여주었다.

맵의 바깥 부분에 Border라는 빈 오브젝트안에

방향 별로 box collider2D를 붙인 후 범위를 잡아주었다.

 

 

Player :

물리 연산이기에 모두 리지드바디2d적용

물리엔진을 많이 사용하지 않기에 키네틱을 사용

키넥틱이란? 다른 것이랑 충돌이 일어났을 때 영향을 받지 않겠다.

 

보더들은 고정이기때문에 static 사용

 

경계들을 각각 인지를 해야하기에 변수 선언

물리적인 힘은 넣을 필요 없으므로 collider대신에 온트리거 사용하였고,

각 경계마다 tag로 Border를 달아줄 것이다.

물체가 닿은 상태에서 h는 좌우 이동이므로 => 이동시 닿을 떄를 처리하는데 이때 각각 0으로 만들어서 이동을 멈춘다.

물체가 닿지 않았을 때 이동해야하므로 그 경우도 처리해야 한다.

태그를 달아준다.

 

현재 까지의 코드

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

public class Player : MonoBehaviour
{
    public float speed;
    public bool isTouchTop;
    public bool isTouchBottom;
    public bool isTouchRight;
    public bool isTouchLeft;

    void Update()
    {
        float h = Input.GetAxisRaw("Horizontal");
        if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
            h = 0;
        float v = Input.GetAxisRaw("Vertical");
        if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
            v = 0;
        Vector3 curPos = this.transform.position;
        Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
        transform.position = curPos + nextPos;
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        if(collision.gameObject.tag == "Border")
        {
            switch(collision.gameObject.name)
            {
                case "Top":
                    isTouchTop = true;
                    break;
                case "Bottom":
                    isTouchBottom = true;
                    break;
                case "Right":
                    isTouchRight = true;
                    break;
                case "Left":
                    isTouchLeft = true;
                    break;
            }
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        switch (collision.gameObject.name)
        {
            case "Top":
                isTouchTop = false;
                break;
            case "Bottom":
                isTouchBottom = false;
                break;
            case "Right":
                isTouchRight = false;
                break;
            case "Left":
                isTouchLeft = false;
                break;
        }
    }
}

 

 

이제 애니메이션을 넣어줄 것이다.

잘라놓은 애니메이션 각각 4개를 클릭해서

하이어라이키에 있는 Player에 드래그해서 넣어주고 애니메이션 폴더를 생성후

각각 Center, Left, Right로 이름을 만들어서 저

플레이어에 애니메이션 컴포넌트가 생성됨

그 후 플레이어(애니메이션 컨트롤러)를 눌러서 이 창을 띄어주고

Make Transition으로 서로의 관계를 만들어 준다.

 

트랜지션은 파라미터로 이동한다.

매개변수 Input.GetAxisRaw() 값 그대로 사

각각의 트랜지션 설정을 해줄것인다.

즉각 반응을 위해 Has Exit Time은 꺼야한다.

Transition Duration은 보통3D에서 모션을 부드럽게 변화시키기 위해 사용하는데

필요 없으므로 0으로 변경

 

이제 인풋 파라미터를 받을 코드를 작성할 것이다.

그전에 애니메이터 변수 생성 후 초기화 진행

 

업데이트 문 안에 작성하고 좌우 방향은 h이므로 h를 넣고 float형이므로

(int)로 강제 형변환

\

배경을 집어 넣을 것인데 넣으면 플레이어가 보이지 않을 수 있다.

그러면 플레이어를 누르고 Order In Layer를 0에서 1로 변경

 

그러면 플레이어가 이제 잘보일 것이다.

마지막을 플레이를 해볼것인데 애니메이터 컨트롤러를 클릭하고 실행하면

실시간 애니메이션이 변경되며 사용됨을 눈으로 확인 할 수 있다.

애니메이터 실시간.mp4
0.71MB

'산대특' 카테고리의 다른 글

2D Airplane - 원근감있는 무한 배경 만들  (0) 2024.03.17
2D Airplane - 5  (1) 2024.03.14
2D Airplane - 4  (0) 2024.03.12
2D Airplane - 3  (0) 2024.03.12
2D Airplane - 2  (0) 2024.03.10

+ Recent posts