ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • Parrying Sword #8 : [기획][프로그래밍] 와이어 액션 구현
    유니티 게임 개발/메인 프로젝트: Parrying Sowrd 2023. 2. 17. 11:56

     

    벽 잡기를 만들고 나서도 y축을 활용하는 액션이 부족하다는 생각이 들어

    와이어 액션을 구현하였습니다

    마법을 쓴다는 이미지로 구상을 했고, 그 결과 위 이미지처럼 제작하게 되었습니다.

     

    // 와이어 액션 **************************************
            if ( Input.GetKey(KeyCode.S) && playerCtrl.actionActive && wire != 0 )
            {
                prevWire = wire;
                wire = playerCtrl.ActionWireJump();
    
                if( prevWire != -1 && wire == 0 || (prevWire == 1 && wire == -1) )
                {
                    playerCtrl.ActionWireInertia();
                }
            }
    
            if( Input.GetKeyUp(KeyCode.S) )
            {
                if( wire > 0 )
                {
                    wire = -1;
                    playerCtrl.ActionWireInertia();
                }
                else if( wire == 0 )
                {
                    wire = -1;
                }
            }
            
            if (wire > 0) return;
            // ************************************************

    PlayerMain 클래스에서의 와이어 액션 코드입니다.

    여기서 wire 변수가 1이면 잡기 성공, 0이면 너무 가까워서 실패, -1이면 잡을 벽이 없음 판정입니다.

     

    잡을 벽이 가까이에 없으면 아예 실패하고

    중간에 키 입력을 놓거나, 너무 가까워지면 와이어가 끊기도록 설정합니다.

     

    가능하면 리턴을 bool 변수로 다루고 싶었으나, 각각의 상황을 전부 제대로 다루어 놓지 않으면 의도대로 작동하지 않습니다.

    다른 클래스와 너무 깊게 엮이는 것 같아 그렇게 마음에 들지는 않지만, 그래도 버그가 나는 대로 내버려 둘 수는 없습니다.

     

        // 와이어액션
        public int ActionWireJump()
        {
            int success = wireAction.ActionWireJump();
    
            if( success > 0 )
            {
                usingWire = true;
                animator.SetTrigger("WireAction");
            }
    
            return success;
        }
    
        public void ActionWireInertia()
        {
            usingWire = false;
            wireAction.inactiveWire();
            rb.velocity = new Vector2(rb.velocity.x * 1.3f, 40.0f);
        }

    위에서 언급된 wire 변수의 값을 위 함수에서 제공합니다.

    WireAction 클래스에서 값을 받아오고, 애니메이션 상태를 변경 해 줍니다.

     

    중요한 것은, 와이어가 끊어졌을 때 반동으로 가해지는 힘을 억지로 바꿔주어야 합니다.

    그렇지 않으면 와이어액션 중 가해지는 힘에 의해 저 멀리 날아가는 문제가 생깁니다.

    그렇다고 아예 없으면 안되니 velocity를 바꾸어 반동을 조절합니다.

     

    using System.Collections;
    using System.Collections.Generic;
    using UnityEngine;
    
    public class WireAction : MonoBehaviour
    {
        private PlayerController playerCtrl;
        private GameObject player;
        private Transform wire;
        private Transform wireHook;
        private SpriteRenderer[] sprites;
        private Transform wirePosition;
        private float distance = 0;
        
    
        private void Awake()
        {
            player = GameObject.Find("Player");
            playerCtrl = player.GetComponent<PlayerController>();
            sprites = GetComponentsInChildren<SpriteRenderer>();
            wire = transform.Find("Wire");
            wireHook = transform.Find("WireHook");
            wirePosition = player.transform.Find("WirePosition");
        }
    
        // 템플릿 메소드 *********************************************************
        // -1 탐색 실패, 0 너무 가까움 1 가능
        public int ActionWireJump()
        {
            moveAtPlayer();
            int check = checkDistance();
            if (check < 1) return check;
            modifyTransform();
            activeWire();
            addForce();
    
            return check;
        }
        // ********************************************************************
    
    
        private void moveAtPlayer()
        {
            transform.position = wirePosition.position;
        }
    
        // -1 탐색 실패, 0 너무 가까움 1 가능
        private int checkDistance()
        {
            Collider2D[] colliders;
    
            for( int i = 0; i < 2; i++ )
            {
                colliders = Physics2D.OverlapPointAll(new Vector2(transform.position.x + i * playerCtrl.dir,
                                                                  transform.position.y + i));
                // Debug.Log(i);
                foreach (Collider2D collider in colliders)
                {
                    if (collider.tag == "Road")
                    {
                        return 0;
                    }
                }
            }
    
            
            for( int i = 1; i < 26; i++ )
            {
                colliders = Physics2D.OverlapPointAll(new Vector2(transform.position.x + i * playerCtrl.dir,
                                                                  transform.position.y + i));
                // Debug.Log(i);
                foreach( Collider2D collider in colliders )
                {
                    if( collider.tag == "Road" )
                    {
                        distance = i;
                        return 1;
                    }
                }
            }
    
            return -1;
        }
    
        private void modifyTransform()
        {
            wire.localScale = new Vector3(distance * 1.4f, wire.localScale.y,
                                                        wire.localScale.z);
    
            transform.localScale = new Vector3( Mathf.Abs( transform.localScale.x ),
                                                transform.localScale.y,
                                                transform.localScale.z);
    
            wireHook.transform.position = new Vector3(transform.position.x + ( distance + 1 ) * playerCtrl.dir,
                                                    transform.position.y + distance,
                                                    transform.position.z);
        }
    
        private void activeWire()
        {
            foreach( SpriteRenderer sprite in sprites )
            {
                sprite.enabled = true;
            }
        }
    
        private void addForce()
        {
            player.GetComponent<Rigidbody2D>().velocity = new Vector2(70.0f * playerCtrl.dir, 60.0f);
        }
    
        // 스프라이트 끄기
        public void inactiveWire()
        {
            foreach (SpriteRenderer sprite in sprites)
            {
                sprite.enabled = false;
            }
        }
    }

    WireAction 클래스입니다.

    순서가 중요한 관계로 템플릿 메소드 패턴을 적용하였습니다.

     

    시작 지점을 기준으로 x, y축을 대각선으로 1만큼 이동하며 잡을 벽이 있나를 체크하고, 있으면 벽을 잡고 쭉 진행합니다.

     

    추가로 와이어를 끊기 위해 스프라이트를 꺼주는 함수를 하나 더 만들어 줍니다.

Designed by Tistory.