턴제제작

카메라

아이고이아 2019. 1. 7. 20:01

 

 캐릭터를 추가했다. 일단 프로토 타입 단계에서는 붉은 쪽이 플레이어고 파란 쪽이 에너미로 테스트해봐야겠다. 이게 진도를 빼고 싶은데 카메라 각도에 따라서 플레이어가 움직이고 있을 때 플레이어가 사라져 버리는 버그가 있어서 그것부터 해결하고 가야 할 듯싶다.

 머가 문제인 건지...

 이게 정상적인 거고 카메라 돌리고 어떻게 된 건지 올려서 비교해 봐야겠다. 

 머가 문제인지 알 꺼도 같고 모르겠다. 카메라를 이동시키게 하는 코드를 봐야겠다. 이동할 때 줌이 플레이어에게 맞춰진 채로 돌아가야 할꺼같은데 그 부분이 안되고 있는 듯싶다. 

// camera move
while (player.playerActiveState == PlayerActiveState.Moving)
{
yield return null;
camera.transform.position = this.transform.position + cameraPos;
camera.orthographicSize = 3;
}

 

 이 부분이 문제다. 플레이어 포지션만 고려하고 로테이트를 고려하지 않아서 그런 건가 모르겠다. 해보면 알 수 있겠지. 지금은 자유롭게 돌아가는데 엑스컴처럼 90도씩 돌릴 수 있게 만들어야 할 거 같기도 하다.

public Transform cameraDot;

 

 이걸 만들어 본다. 

 dot을 45도 돌렸을 때 카메라가 45도에서 0으로 돌리면 플레이어를 잡는다. 공감각이 뛰어난 편이 아니라서 머리로는 이해가 가지 않지만 카메라 로테이션에서 dot의 로테이션을 빼주면 되지 않을까 싶다. 가설이 맞는지는 테스트를 하면 할 수가 있겠지.

 쿼터니언은 뺄셈이 안된다는 건가. 아 그냥 첫 번째 게임은 간단한 2D 게임부터 만드는 걸 추천한다.

var cameraRocation = new Quaternion
(
camera.transform.rotation.x,
camera.transform.rotation. y - cameraDot.rotation.y, camera.transform.rotation.z,
camera.transform.rotation.w
);

        // do
foreach (var destination in way)
{

MoveUnit (destination);

// camera move
while (player.playerActiveState == PlayerActiveState.Moving)
{
yield return null;
camera.transform.position = this.transform.position + cameraPos;
camera.transform.rotation = cameraRocation;
camera.orthographicSize = 3;
}
}

 

 이렇게 하면 문제가 해결될 줄 알았는데...

 엉덩이 성애자가 되어 버렸다. 

 끝나고 카메라가 완전히 돌아가 버리는 문제도 있는데 그건 해결이 쉽게 될 거 같긴 하다. 이게 왜 엑스컴에서 90도 각도로 끊어서 돌아가게 고정을 해두었는지 알듯 싶다. 일단 해보고 안되면 그냥 카메라각을 고정하는 쪽으로 가야 할 듯싶다. 

...

...

... 

 아 안 되겠다. 어떻게 해야 할지 모르겠다. 그냥 카메라 각을 고정해야겠다. 카메라 돌아가는 거는 ITween으로 만들고 줌아웃하는 것도 누르면 쭉 줌이 되는 게 아니라 한번 클릭하면 줌이 1 단계 되고 클릭을 해제하면 줌이 풀리는 걸로 바꿔야겠다. 그렇게 하려면 지금 카메라 스크립트 에셋을 해제를 하고 내가 첨부터 새로 만들어야 될 듯싶다. 

 아 그리고 게임 수익 모델을 짤 때 무료로 내고 구글 에드센스 광고로 해결하려고 하지 마라. 구글 애드센스 신청해봤는데 들어오는 사람이 엄청 많으면 모를까 하루에 100원 들어올까 말 까다. 차라리 광고는 다 때고 인엡 결제 쪽으로 가는 거나 유료 게임으로 내는 게 좋다

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

public class CameraController : MonoBehaviour {

    Camera mainCamera;

    void Awake()
    {
        mainCamera = this.transform.Find ("Camera").GetComponent<Camera>();
    }

    void Start () {
        
    }

    void Update () {
        
    }

    void FixedUpdate()
    {
        
    }
}

 

 일단 천천히 시작해보자. RTS카메라 에셋을 보니까 이동을 물리적인 거라 픽스드 업데이트를 쓰는 듯싶고 입력은 그냥 업데이로 받는 듯싶다. 카메라 이동하는 것은 Dot의 좌표를 그냥 이동시키면 되려나.

 
Vector2 MouseInput
{
get { return Input.mousePosition; }
}

 

 일단 카메라 이동하는 건 이걸로 만든다. 키 입력받는 건 어떻게 하는지 봐봐야겠다. 

private Vector2 KeyboardInput
{
get { return useKeyboardInput ? new Vector2(Input.GetAxis("Horizontal"), Input.GetAxis("Vertical")) : Vector2.zero; }
}

 

 이렇게 키보드를 움직이는 걸 만들 수 있는 듯싶다. 이제 실제로 카메라를 움직여 봐야겠다. 지금 방향이 틀어져 있어서 대각선으로 움직꺼 같긴 한데 해보면 알겠지.

    void FixedUpdate()
    {
        this.transform.position = this.transform.position + KeyboardInput;
    }

private Vector3 KeyboardInput
{
get { return useKeyboardInput ? new Vector3(Input.GetAxis("Horizontal"), 0f, Input.GetAxis("Vertical")) : Vector3.zero; }
}

    
Vector2 MouseInput
{
get { return Input.mousePosition; }

} 

 역시나 예상대로다.

    void Mover ()
    {
        if (useKeyboardInput)
{
Vector3 desiredMove = KeyboardInput;

desiredMove *= keyboardMovementSpeed;
desiredMove *= Time.deltaTime;
desiredMove = Quaternion.Euler(new Vector3(0f, transform.eulerAngles.y + 45f, 0f)) * desiredMove;
desiredMove = m_Transform.InverseTransformDirection(desiredMove);

m_Transform.Translate(desiredMove, Space.Self);
}

    }

 예전에도 이것 때문에 코드를 수정했던 거 같다. 한 가지 문제가 더 있다. 위아래는 묵직한데 좌우는 가볍다는 거다.  

    public float verticalWeight = 2;
    public float horizontalWeight = 1;

 

private Vector3 KeyboardInput
{
get { return useKeyboardInput ? new Vector3(Input.GetAxis("Horizontal")*horizontalWeight, 0f, Input.GetAxis("Vertical")*verticalWeight) : Vector3.zero; }
}

 

 이건 몰랐던 건데 이번에 수정하게 되는 듯싶다. 이제 마우스 이동하는 걸 만들어 보자. 

 아 그전에 문득 드는 생각이 머 선택할 때 그냥 나오는 게 아니라 프로필 같은 경우에는 쑥 앞으로 나오고 기술 클릭하면 위아래 진동한다는지 머 그런 게 있어야 타격하는 맛이 나고 그럴 거 같다. 카메라 부분만 좀 해결하고 해당 기능을 넣으면 좋을 듯싶다. 

    void Awake()
    {
        desiredVector = new Vector3(horizontalWeight, 0f, verticalWeight);
        leftRect = new Rect (-1, -1, screenEdgeBorder, Screen.height);
        rightRect = new Rect (Screen.width - screenEdgeBorder, -1, screenEdgeBorder, Screen.height);
        upRect = new Rect (-1, Screen.height - screenEdgeBorder +1, Screen.width, screenEdgeBorder);
        downRect = new Rect (-1, -1, Screen.width, screenEdgeBorder);

        m_Transform = transform;
        desiredQuaternion = Quaternion.Euler (new Vector3 (0f, m_Transform.eulerAngles.y + 45f, 0f));
        thisCamera = this.transform.Find ("Camera").GetComponent<Camera>();
    }

 

        if (useScreenEdgeInput)
{
Vector3 desiredMove = desiredVector;

desiredMove.x *= leftRect.Contains (MouseInput) ? -1 : rightRect.Contains (MouseInput) ? 1 : 0;
desiredMove.z *= upRect.Contains (MouseInput) ? 1 : downRect.Contains (MouseInput) ? -1 : 0;

desiredMove *= screenEdgeMovementSpeed * Time.deltaTime;
desiredMove = desiredQuaternion * desiredMove;
desiredMove = m_Transform.InverseTransformDirection (desiredMove);

m_Transform.Translate (desiredMove, Space.Self);
    }

 

 이런 식으로 만들어 주면 잘 작동한다. 이제 추가해 주어야 하는 건 로테이트다. 일단 그냥 45씩 바뀌는걸 먼저 만들어 본 다음에 잘 되면 45도 회전을 기능을 ITween을 이용해서 추가해야 할 듯싶다. 


    int KeyboardRotate ()
    {
        int clockwise = 0;
        if (Input.GetButtonDown ("Clockwise"))
        {
            clockwise = -1;
            print ("Q");
        }
        else if (Input.GetButtonDown ("AntiClockwise"))
        {
            clockwise = 1;
            print ("E");
        }
        return clockwise;
    }

 

 나중에 조이스틱에도 대응시키고 그러려면 이렇게 만들어 두어야 할 듯싶다. 새 인풋 시스템이 나왔다는데 귀찮아서 확인 안 해보고 있다. 

        if (useKeyboardInput)
        {
            m_Transform.Rotate (new Vector3 (0f, 90f *KeyboardRotate(), 0f), Space.Self);
            
        }

 

 이렇게 하면 돌아가는데 문제는 돌린 다음에 마우스 이동이 제멋대로라는 거다.  ITween의 경우 추가하니까 어지러워서 일단 화면 바꿈으로 해당 문제를 해결해야겠다. 

        if (useKeyboardInput)
        {
            var rotation = new Vector3 (0f, 90f *KeyboardRotate(), 0f);
            m_Transform.Rotate (rotation, Space.World);
            desiredQuaternion.eulerAngles += rotation;
        }

 

 이렇게 해주면 문제는 쉽게 해결된다. 줌인 줌아웃을 만들어야 하는데 일단 그게 급한 게 아니고 이동했을 때 카메라가 제대로 잡지 못하는 문제를 해결해야 할 듯싶다. 

cameraController.transform.rotation = Quaternion.Euler(0f, 0f, 0f);

 

 문제는 너무 간단하게 해결되었다. 플레이어 무버에 이거 한 줄 추가해주면 어느 방향에서도 문제없이 작동한다. 

...

...

...

 착각이었다. 이 방법에 문제는 늘 초기 방향에서만 바라보게 된다는 단점을 가진다. 또 하나 로테이트 기준점이 문제다. 

m_Transform.transform.Rotate (rotation, Space.Self);

 

 셀프로 해주어야 문제가 안 생긴다. 일단 졸려서 내일 해결해야겠다. 여하튼 이것저것 은근히 버그를 해결한 것도 있다. 

 자고 일어났더니 아이디어가 떠올랐다. 이런 식으로 점진적으로 돌려서 문제를 해결하는 게 아니라 4가지 상태를 정해놓고 버튼을 눌렸을 때 상태를 천이하도록 해야겠다. 각 상태를 배열로 놓는데 0 다음이 3이 되도록 원형의 배열을 만들어야겠다.

 문득 예전에 처음 프로그래밍을 배웠을 때가 생각난다. 그때도 수업 과제를 내야 되는데 손을 놓을 수가 없어서 3일 동안 다른 수업도 다 안 나가고 밥도 안 먹으로 나가고 간식 쌓아둔 거 먹으면서 존버 모드로 지냈는데 말이다. 잠깐 가서 식료품 일주일치 싸가지고 와야겠다. 이거 해결할 때까지 밖에 못 나가네 친구. 

 일단 갈아엎고 새로 만들어야겠다.  

public class Clockwise {

    public string name = "default";
    public int index = 0;

    public Quaternion clockwiseRotation;
    public Quaternion desiredQuaternion = Quaternion.Euler (new Vector3 (0f, 45f, 0f));
    
}

 

 이러한 클래스를 하나 만들어 준다. 

    List<Clockwise> clockwiseList;
    Clockwise zeroClock;
    Clockwise threeClock;
    Clockwise sixClock;
    Clockwise nineClock;

 

    Clockwise MakeClockwise (string name, int index, Vector3 rotation)
    {
        var newClockwise = new Clockwise();

        newClockwise.name = name;
        newClockwise.index = index;
        newClockwise.clockwiseRotation = Quaternion.Euler (rotation);
        newClockwise.desiredQuaternion = Quaternion.Euler (new Vector3 (0f, newClockwise.clockwiseRotation.eulerAngles.y + 45f, 0f));

        return newClockwise;
    }

 

 이런 것도 만들어 준다. 이제 리스트에 하나씩 넣어서 총 4개를 만들어 주어야겠다. 

        clockwiseList = new List<Clockwise>();
        zeroClock = MakeClockwise ("zeroClock", 0, new Vector3 (0f, 0f, 0f));
        threeClock = MakeClockwise ("threeClock", 1, new Vector3 (0f, 90f, 0f));
        sixClock = MakeClockwise ("sixClock", 2, new Vector3 (0f, 180f, 0f));
        nineClock = MakeClockwise ("nineClock", 3, new Vector3 (0f, 270f, 0f));
        clockwiseList.Add (zeroClock);
        clockwiseList.Add (threeClock);
        clockwiseList.Add (sixClock);
        clockwiseList.Add (nineClock);
        clockwiseList.ForEach (i => print(i.name +" [" + i.index + "]"));

 

 순항 중이다. 이제 기존에 돌리는 시스템을 대체를 해야겠다. 

        currentClock = clockwiseList[0];
        desiredQuaternion = currentClock.desiredQuaternion;

 

 현재 방향이 어디인지 정하는 시스템을 만들어야겠다. 일단은 여기에 0시를 집어넣는다. 

    void FixedUpdate()
    {
        if (cameraOn)
        {
            KeyboardMover();
            MouseMover();
        }
    }

    void KeyboardMover ()
    {
        if (!useKeyboardInput) return;
        RotateScreenbyKey();
MoveScreenbyKey();
    }

    void MoveScreenbyKey()
    {
        Vector3 desiredMove = KeyboardInput;
desiredMove *= keyboardMovementSpeed * Time.deltaTime;
    desiredMove = currentClock.desiredQuaternion * desiredMove;
desiredMove = m_Transform.InverseTransformDirection (desiredMove);
m_Transform.Translate (desiredMove, Space.Self);
    }

    void RotateScreenbyKey()
    {
        int newClockIndex = currentClock.index + KeyboardRotate();
        if (newClockIndex > 3 || newClockIndex < 0) newClockIndex = 3;
        currentClock = clockwiseList[newClockIndex];
        this.transform.rotation = currentClock.clockwiseRotation;
    }

 

 일단 한고비 넘겼다. 문제는 이동할 때 클로즈업이다. 일단 지금 PlayerMove클래스가 너무 지저분해서 이거 코드를 좀 정리를 하고 가야겠다. 

 그럴라 그랬는데 마우스로 이동하는 거 고치는걸 깜박한 듯싶다. 이것까지만 고치고 가자.

    void MouseMover()
    {
        if (!useScreenEdgeInput) return;

Vector3 desiredMove = desiredVector;

desiredMove.x *= leftRect.Contains (MouseInput) ? -1 : rightRect.Contains (MouseInput) ? 1 : 0;
desiredMove.z *= upRect.Contains (MouseInput) ? 1 : downRect.Contains (MouseInput) ? -1 : 0;

        if (desiredMove == Vector3.zero) return;

desiredMove *= screenEdgeMovementSpeed * Time.deltaTime;
desiredMove = currentClock.desiredQuaternion * desiredMove;
desiredMove = m_Transform.InverseTransformDirection (desiredMove);
m_Transform.Translate (desiredMove, Space.Self);
    }

 

void StartIndicator()

{
player.playerActiveState = PlayerActiveState.Moving;
player.GetComponent<CapsuleCollider>().enabled = false;
player.activeOff.Invoke();
canvas.enabled = false;
cameraController.useKeyboardInput = false;
cameraController.useScreenEdgeInput = false;
}

void EndIndicator()
{
player.playerActiveState = PlayerActiveState.NotAnything;
player.GetComponent<CapsuleCollider>().enabled = true;
canvas.enabled = true;
cameraController.useKeyboardInput = true;
cameraController.useScreenEdgeInput = true;
board.ResetBoard();
}

IEnumerator Indicator (List<Vector3> way)
{
        // start
StartIndicator();
var destinationFlag = Instantiate (destinationPoint, way[way.Count -1], Quaternion.identity);
var initCamera = cameraController.transform;
cameraController.transform.rotation = Quaternion.Euler(0f, 0f, 0f);

yield return null;

if (way == null)
{
print ("No Way");
DestroyImmediate (destinationFlag);
EndIndicator();
cameraController.transform.rotation = initCamera.rotation;
cameraController.transform.position = cameraController.initPos;
}
 
animator.SetBool ("Running", true);
var waysCount = way.Count;

        // do
foreach (var destination in way)
{

MoveUnit (destination);

// camera move
while (player.playerActiveState == PlayerActiveState.Moving)
{
yield return null;
cameraController.transform.position = this.transform.position + cameraPos;
cameraController.thisCamera.orthographicSize = CameraZoomIn;
}
}

animator.SetBool ("Running", false);

yield return null;

        // end
EndIndicator();
cameraController.transform.rotation = defaultRotation;
DestroyImmediate (destinationFlag);
// player.activeOff.Invoke();
if ( player.currentVigor > 0 )
        {
            gameManager.SelectPlayer (this.player);
        }
gameManager.CheckPlayerTurn();
 
cameraController.thisCamera.orthographicSize = CameraZoomOut;
cameraController.transform.rotation = initCamera.rotation;
cameraController.transform.position = cameraController.initPos;
}

 

 일단 코드는 이렇게 정리해 봤다. 머 여전히 복잡한 건 마찬가지니 말이다. 일단 플레이어 이동후에 위아래 좌우 이동이 안 맞아서 생기는 버그는 잡은 듯싶다. 문제는 카메라를 이리저리 회전해 놓아도 무조건 이동시 제로 방향에서 클로즈업한 것만 뜬다는 거다. 이걸 다양한 방향에서 쳐다보는 걸로 바꾸는 것이 숙제다. 물론 못 바꾸면 그냥 이대로 하고 진도를 빼야 할 듯싶다. 

 자고 일어났는데 문제점이 생각났다. 일단 회전 인덱스가 4일 때는 0으로 되도록 바꿔줘야 한다. -1일 때 3이 되는 것처럼 말이다. 그리고 400x230은 축소할 때 문제가 발생하기 때문에 400x220으로 바꿔줘야 할 듯싶다. 

    void RotateScreenbyKey()
    {
        int newClockIndex = currentClock.index + KeyboardRotate();
        if (newClockIndex < 0) newClockIndex = 3;
        else if (newClockIndex > 3) newClockIndex = 0;
        currentClock = clockwiseList[newClockIndex];
        this.transform.rotation = currentClock.clockwiseRotation;
    }

 

 이제 카메라 방향이 초기 방향으로 고정되지 않고 카메라 시점에 따라 달라지게 하는 걸 구현해야겠다. 그냥 해도 될 거 같은데 왜 이리 장인 정신을 투입하는지 나도 모르겠다. 

        zeroClock = MakeClockwise ("zeroClock", 0, zeroPos, zeroClockRot);
        threeClock = MakeClockwise ("threeClock", 1, threePos, threeClockRot);
        sixClock = MakeClockwise ("sixClock", 2, sixPos, sixClockRot);
        nineClock = MakeClockwise ("nineClock", 3, ninePos, nineClockRot);

 

일단 테스트...

cameraController.thisCamera.transform.localPosition = cameraController.currentClock.clockwisePosition;

 

 잘 안되길래 보니까 로컬 포지션을 바꿔줘야 하는 거더라. 

void StartIndicator()
{
player.playerActiveState = PlayerActiveState.Moving;
player.GetComponent<CapsuleCollider>().enabled = false;
player.activeOff.Invoke();
canvas.enabled = false;
cameraController.useKeyboardInput = false;
cameraController.useScreenEdgeInput = false;
cameraController.thisCamera.transform.localPosition = cameraController.currentClock.clockwisePosition;
}

void EndIndicator()
{
player.playerActiveState = PlayerActiveState.NotAnything;
player.GetComponent<CapsuleCollider>().enabled = true;
canvas.enabled = true;
cameraController.useKeyboardInput = true;
cameraController.useScreenEdgeInput = true;
cameraController.thisCamera.transform.localPosition = cameraController.zeroPos;
board.ResetBoard();
}

 

 줌아웃이 됐는데 카메라가 엉뚱한 곳에 가있는 문제만 해결하면 될 듯싶다. 

cameraController.transform.position = this.transform.position;
 

 해당 문제를 해결하는 과정에서 이 부분을 수정하고 카메라를 셀프로 돌리게 해면 이런 식으로 복잡하게 만들 필요가 없다는 걸 깨달았다. 지금까지 쓸 때 없이 삽질한 거다. 바보같이...

public class Clockwise {

    public string name = "default";
    public int index = 0;

    public Quaternion clockwiseRotation = Quaternion.Euler (new Vector3 (0f, 0f, 0f));
    public Quaternion desiredQuaternion = Quaternion.Euler (new Vector3 (0f, 45f, 0f));
    
}

 

 해당 내용은 삭제해 준다. 삽질 한셈 쳐야겠다. 여하튼 원하는 문제는 해결됐다.

 캐릭터들을 추가하고 있다. 주의할 점은 상위 오브젝트만 레이어를 설정해야 된다는 거다. 밑에꺼랑 다 설정하면 작동을 못하게 되는데 아마 하위에 무브 베이스 에어리어랑 무브 더블 에어리어가 설정이 되어 있기 때문에 그럴 거다. 

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

public class PlayerUI : MonoBehaviour {

    public Transform propile;

    void Awake()
    {
        propile = this.transform.Find ("Propile");
    }
}

 

 캐릭터 프로파일을 바꿔주는 걸 만들고 영상으로 정리를 해야겠다. 일단 트렌스폼으로 접근하는 패턴을 만들어 주어야겠다. 문득 고닷엔진의 경우 시그널을 주고받는 시스템을 활용하는데 유니티도 그런 방식을 구현하는 게 있을 듯싶다. 메시지 같은 게 있는지 확인해 봐야겠다. 보니까 게임 오브젝트에 SendMessage라는 게 있네 이걸 좀 천천히 연구를 해봐야겠다. 다수의 게임 오브젝트에게 메시지를 주려면 이걸 적절히 활용해야 할 테니 말이다. 여하튼 원래 하려던 작업을 해보자 

    void OnEnable()
    {
        print ("Propile On");
    }

 

 이런 함수가 있어서 게임 오브젝트가 켜질 때 발동이 된다. 여기다가 프로필을 바꾸는 걸 넣어야겠다. 

using UnityEngine.UI;

 

 이미지 컴포넌트가 없길래 검색해 보니 UI를 참조해 줘야 한다더라. 프로 파일을 바꾸려는데 지금 캐릭터 이름을 임시로 클레이모어 캐릭터 이름으로 지어 놓은 상태다. 이걸 좀 바꿔 줘야 하는데 일단 각각의 캐릭터 설정에 맞는 이름이 필요할 듯싶어서 일단은 이 문제는 천천히 해결해야겠다. 

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

public class PlayerUI : MonoBehaviour {

    GameManager gameManager;
    public Transform propile;
    public Image propileImage;

    public Sprite red;
    public Sprite blue;
    public Sprite pink;
    public Sprite green;
    public Sprite white;
    public Sprite purple;

    void Awake()
    {
        propile = this.transform.Find ("Propile");
        gameManager = FindObjectOfType<GameManager>();
        propileImage = propile.Find ("PropileImage").GetComponent<Image>();
    }

    void OnEnable()
    {
        print ( gameManager.currentPlayer + " Propile On");
        if (gameManager.currentPlayer.name == "Galateia") propileImage.sprite = red;
        else if (gameManager.currentPlayer.name == "Jean") propileImage.sprite = blue;
        else if (gameManager.currentPlayer.name == "Flora") propileImage.sprite = pink;
        else if (gameManager.currentPlayer.name == "Miria") propileImage.sprite = green;
        else if (gameManager.currentPlayer.name == "Clare") propileImage.sprite = white;
        else if (gameManager.currentPlayer.name == "Deneve") propileImage.sprite = purple;
    }

 

 그나저나 광고가 노출로는 거의 돈이 안 나오길래 광고를 글 읽는데 방해가 안되게 일부러 아래에 배치하는 작업을 했다. 근데 도 오히려 클릭을 하셨더라. 고맙다.