캐릭터를 추가했다. 일단 프로토 타입 단계에서는 붉은 쪽이 플레이어고 파란 쪽이 에너미로 테스트해봐야겠다. 이게 진도를 빼고 싶은데 카메라 각도에 따라서 플레이어가 움직이고 있을 때 플레이어가 사라져 버리는 버그가 있어서 그것부터 해결하고 가야 할 듯싶다.
머가 문제인 건지...
이게 정상적인 거고 카메라 돌리고 어떻게 된 건지 올려서 비교해 봐야겠다.
머가 문제인지 알 꺼도 같고 모르겠다. 카메라를 이동시키게 하는 코드를 봐야겠다. 이동할 때 줌이 플레이어에게 맞춰진 채로 돌아가야 할꺼같은데 그 부분이 안되고 있는 듯싶다.
// 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" ), 0 f, 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 + 45 f, 0 f)) * 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 , 0 f, Input .GetAxis("Vertical" )* verticalWeight ) : Vector3 .zero ; }
}
이건 몰랐던 건데 이번에 수정하게 되는 듯싶다. 이제 마우스 이동하는 걸 만들어 보자.
아 그전에 문득 드는 생각이 머 선택할 때 그냥 나오는 게 아니라 프로필 같은 경우에는 쑥 앞으로 나오고 기술 클릭하면 위아래 진동한다는지 머 그런 게 있어야 타격하는 맛이 나고 그럴 거 같다. 카메라 부분만 좀 해결하고 해당 기능을 넣으면 좋을 듯싶다.
void Awake ()
{
desiredVector = new Vector3 (horizontalWeight , 0 f, 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 + 45 f, 0 f));
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, 90 f * KeyboardRotate(), 0 f), Space .Self );
}
이렇게 하면 돌아가는데 문제는 돌린 다음에 마우스 이동이 제멋대로라는 거다. ITween의 경우 추가하니까 어지러워서 일단 화면 바꿈으로 해당 문제를 해결해야겠다.
if (useKeyboardInput )
{
var rotation = new Vector3 (0f, 90 f * KeyboardRotate(), 0 f);
m_Transform .Rotate (rotation , Space .World );
desiredQuaternion .eulerAngles += rotation ;
}
이렇게 해주면 문제는 쉽게 해결된다. 줌인 줌아웃을 만들어야 하는데 일단 그게 급한 게 아니고 이동했을 때 카메라가 제대로 잡지 못하는 문제를 해결해야 할 듯싶다.
cameraController .transform .rotation = Quaternion .Euler(0f, 0 f, 0 f);
문제는 너무 간단하게 해결되었다. 플레이어 무버에 이거 한 줄 추가해주면 어느 방향에서도 문제없이 작동한다.
...
...
...
착각이었다. 이 방법에 문제는 늘 초기 방향에서만 바라보게 된다는 단점을 가진다. 또 하나 로테이트 기준점이 문제다.
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, 45 f, 0 f));
}
이러한 클래스를 하나 만들어 준다.
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 + 45 f, 0 f));
return newClockwise ;
}
이런 것도 만들어 준다. 이제 리스트에 하나씩 넣어서 총 4개를 만들어 주어야겠다.
clockwiseList = new List <Clockwise >();
zeroClock = MakeClockwise ("zeroClock" , 0 , new Vector3 (0f, 0 f, 0 f));
threeClock = MakeClockwise ("threeClock" , 1 , new Vector3 (0f, 90 f, 0 f));
sixClock = MakeClockwise ("sixClock" , 2 , new Vector3 (0f, 180 f, 0 f));
nineClock = MakeClockwise ("nineClock" , 3 , new Vector3 (0f, 270 f, 0 f));
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, 0 f, 0 f);
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, 0 f, 0 f));
public Quaternion desiredQuaternion = Quaternion .Euler (new Vector3 (0f, 45 f, 0 f));
}
해당 내용은 삭제해 준다. 삽질 한셈 쳐야겠다. 여하튼 원하는 문제는 해결됐다.
캐릭터들을 추가하고 있다. 주의할 점은 상위 오브젝트만 레이어를 설정해야 된다는 거다. 밑에꺼랑 다 설정하면 작동을 못하게 되는데 아마 하위에 무브 베이스 에어리어랑 무브 더블 에어리어가 설정이 되어 있기 때문에 그럴 거다.
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 ;
}
그나저나 광고가 노출로는 거의 돈이 안 나오길래 광고를 글 읽는데 방해가 안되게 일부러 아래에 배치하는 작업을 했다. 근데 도 오히려 클릭을 하셨더라. 고맙다.