활과 화살의 이미지 에셋을 씬뷰에 끌어놔 게임오브젝트를 만들고 Sprite Renderer의 Order in Layer값을 3으로 설정합니다.
활 : 이름을 Bow로 변경하고 위치는 0으로 합니다.
화살 : 이름을 Arrow라는 태그와 레이어를 만들어 설정합니다. 접촉판정에 사용됩니다.
활과 화살을 Player폴더로 끌어서 프리팹으로 만들고 씬뷰의 활과 화살은 제거합니다.
화살을 발사하는 스크립트
ArrowShoot이라는 스크립트를 Player폴더에 만들고 Player에 어태치합니다.
변수
public float shootSpeed = 12.0f; //화살 속도
public float shootDelay = 0.25f; //발사 간격
public GameObject bowPrefab; //활의 프리펩
public GameObject arrowPrefab; //호살의 프리펩
bool inAttack = false; //공격 중 여부
GameObject bowObj; //활의 게임 오브젝트
Start()
bow프리팹을 이용해 오브젝트를 만들고 플레이어의 자식으로 만듭니다.
void Start(){
//활을 플레이어 캐릭터에 배치
Vector3 pos = transform.position;
bowObj = Instantiate(bowPrefab, pos, Quaternion.identity);
bowObj.transform.SetParent(transform);//활의 부모로 플레이어 캐릭터를 설정
}
Update()
활을 캐릭터에 맞게 회전시키고 위치시킵니다.
z값을 조정해 캐릭터보다 앞으로 놓습니다.
공격키가 눌리면 Attack()를 실행합니다.
void Update() {
if ((Input.GetButtonDown("Fire3"))) {
//공격 키가 눌림
Attack();
}
//활의 회전과 우선순위
float bowZ = -1; //활의 Z값(캐릭터보다 앞으로 설정)
PlayerController plmv = GetComponent<PlayerController>();
if (plmv.angleZ > 30 && plmv.angleZ < 150) {
//위 방향
bowZ = 1; //활의 Z값(캐릭터 보다 뒤로 설정)
}
//활의 회전
bowObj.transform.rotation = Quaternion.Euler(0, 0, plmv.angleZ);
//활의 우선순위
bowObj.transform.position = new Vector3(transform.position.x,
transform.position.y, bowZ);
}
Attack()
화살을 쏘는 처리를 합니다 . ItemKeepr.hasArrows를 하나 줄입니다. static이므로 참조없이 바로 접근이 가능합니다.
public void Attack()
{
//화살을 가지고 있음 & 공격중이 아님
if (ItemKeeper.hasArrows > 0 && inAttack == false)
{
ItemKeeper.hasArrows -= 1; //화살을 소모
inAttack = true; //공격 중으로 설정
//화살 발사
PlayerController playerCnt = GetComponent<PlayerController>();
float angleZ = playerCnt.angleZ; //회전 각도
//화살의 게임 오브젝트 만들기(진행 방향으로 회전)
Quaternion r = Quaternion.Euler(0, 0, angleZ);
GameObject arrowObj = Instantiate(arrowPrefab, transform.position, r);
//화살을 발사할 벡터 생성
float x = Mathf.Cos(angleZ * Mathf.Deg2Rad);
float y = Mathf.Sin(angleZ * Mathf.Deg2Rad);
Vector3 v = new Vector3(x, y) * shootSpeed;
//화살에 힘들 가하기
Rigidbody2D body = arrowObj.GetComponent<Rigidbody2D>();
body.AddForce(v, ForceMode2D.Impulse);
//공격 중이 아님으로 설정
Invoke("StopAttack", shootDelay);
}
}
화살을 제어하는 스크립트 만들기
발사된 화살을 제어하는 스크립트를 만들어봅니다. ArrowController라는 스크립트를 Player폴더에 만들고 Player에 어태치 합니다.(이미 어태치되어있으면 두번할 필요는 없습니다.) 발사된 화살은 접촉된 물체의 자식이 되어 붙어 있다가 deleteTime후 제거됩니다.
ItemKeeper 스크립트를 만들어 Player에 어태치 합니다. 전 player프리팹을 사용했기 때문에 이미 어태치되어 있습니다.
변수
코드는 static int 타입의 hgsKeys와 hasArrows 변수 두개 뿐입니다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ItemKeeper : MonoBehaviour
{
public static int hasKeys = 0; //열쇠 수
public static int hasArrows = 0; //화살 소지수
// Start is called before the first frame update
void Start() { }
// Update is called once per frame
void Update() { }
//아이템 저장하기
public static void SaveItem() { }
}
플레이어 캐릭터는 활을 겨누면서 360도 방향을 자유롭게 이동합니다. 활은 진행하는 방향을 향하게 되고 진행 방향으로 발사할수 있습니다. 발사된 활은 물체에 닿으면 그 자리에 박힌후 잠시 뒤 사라집니다.
멀티 스프라이트로 캐릭터 이미지 만들기
프로젝트뷰에 Player폴더를 만듭니다. 교재를 다운 받으면 이미 폴더가 만들어져 있습니다.
Player폴더의 PlayerImages를 엽니다. 패턴은 32x32 픽셀의 도트이미지 입니다. 타일맵과 동일하게 멀티 스프라이트로 만듭니다. Sprite Mode:Multiple, Pixels Per Unit: 32, Filter Mode: Point 아까 멀티맵처럼 설정합니다.
Sprite Editor를 클릭해 [Slice]탭을 선택하고 Pixel Size:32x32 Pivot:Custom, Custom Pivot x:0.5 Y:0.2로 약간 아래로 내립니다.
화살과 활은 아래와 같이 이동중심으로 옮겨 줍니다.
화살은 Center로 합니다.
설정이 완료되면 위쪽의 APPLY를 클릭합니다.
플레이어 캐릭터의 게임 오브젝트 만들기
플레이어의 상하좌우 이동 애니메이션을 만들어야 합니다. 우선 분할된 이미지의 앞 2개를 선택해서 씬뷰로 끌어줍니다.
이름을 PlayerDown 으로 Player 폴더에 저장합니다.
배경 타일맵 보다 위에 보이도록 Order in Layer값을 3으로 설정합니다.
여기까지 작업후 저장된 애니메이터 컨트롤러 이름을 PlayerAnime으로 합니다. 게임오브젝트의 이름도 Player로 합니다.
* 저는 그냥 Player폴더에 있는 player프리팹을 끌어다 놨습니다. 모든게 이미 설정되어져 있습니다.
플레이어 캐릭터에 Rigidbody2D를 어태치하고 Gravity를 0, Freeze Rotation Z를 체크,
Circle Collider2D를 어태치합니다. 범위를 전체의 반정도로 조정합니다. Player Tag를 설정하고 Player 레이어를 만들어 설정합니다.
PlayerDown 애니메이션을 열어 Samples를 4로 설정합니다.
같은 방법으로 PlayerUp(Image2~3), PlayerLeft(Image4~5), PlayerRight(Image6~7)를 만듭니다.
생성된 게임오브젝트는 애니메이션 클립을 만들기 위한 것이므로 실제 사용하지 않습니다. 애니메이션 클립을 저장한 후에는 PlayerUp, PlayerLeft, PlayerRight 애니메이션 컨트롤러와 게임오브젝트는 제거됩니다.
방금만든 PlayerUp, PlayerLeft, PlayerRight 애니메이션을 Animator에 끌어다 놓습니다.
게임오버 애니메이션 만들기
이제 게임오버 애니메이션을 만듭니다.
하이라키에서 Player를 선택하고 애니메이션 창을 엽니다. Crate NewClip을 선택해 PlayerDead라는 이름을 애니메이션 클립을 추가합니다.
[Add Propety] 버튼을 클릭하고 Sprite Renderer > Sprite를 선택해 Sprite를 추가합니다. 빨간색 기록버튼을 누르고 키 프레임으로 등록된 이미지 두개를 PlayerImage_8로 변경합니다.
Sprite Renderer.Color를 추가하고 마지막 프레임의 Color.a를 0으로 설정합니다.
플레이어 이동 스크립트 만들기
PlayerController라는 스크립트를 Player폴더에 만들고 씬뷰의 Player에 어태치합니다. 7장에서 만든 버추얼패드의 스크립트를 다시사용하고자 이름을 똑같이 했습니다.
변수
과거 애니메이션과 현재 애니메이션의 이름이 다르면 애니메이션을 변경하는 처리를 합니다.
public float speed = 3.0f; //이동 속도
//애니메이션 이름
public string upAnime = "PlayerUp"; // 위
public string downAnime = "PlayerDown"; // 아래
public string rightAnime = "PlayerRight"; // 오른쪽
public string leftAnime = "PlayerLeft"; // 왼쪽
public string deadAnime = "PlayerDead"; // 사망
string nowAnimation = ""; //현재 애니메이션
string oldAnimation = ""; //이전 애니메이션
float axisH; //가로 축 값(-1.0 〜 0.0 〜 1.0)
float axisV; //세로 축 값(-1.0 〜 0.0 〜 1.0)
public float angleZ = -90.0f; //회전 각
Rigidbody2D rbody; //Rigidbody 2D
bool isMoving = false; //이동 중인지 여부
//데미지 처리
public static int hp = 3; //플레이어 HP
public static string gameState; //게임 상태
bool inDamage = false; //데미지 받는 중인지 여부
Start()
Rigidbody2D 컴포넌트 참조를 저장하고, oldAnimation을 downAnime으로 하고 gameState를 "playing"으로 설정합니다.
// Use this for initialization
void Start()
{
//Rigidbody2D 가져오기
rbody = GetComponent<Rigidbody2D>();
//애니메이션
oldAnimation = downAnime;
//게임 상태를 플레이 중으로 변경
gameState = "playing";
}
Update()
키입력으로 방향을 정하고 애니메이션을 결정
// Update is called once per frame
void Update() {
//게임중이 아니거나 데미지 받는 중엔 아무것도 하지 않음
if (gameState != "playing" || inDamage) {
return;
}
if (isMoving == false) {
axisH = Input.GetAxisRaw("Horizontal"); //좌우 키 입력
axisV = Input.GetAxisRaw("Vertical"); //상하 키 입력
}
//키 입력으로 이동 각도 구하기
Vector2 fromPt = transform.position;
Vector2 toPt = new Vector2(fromPt.x + axisH, fromPt.y + axisV);
angleZ = GetAngle(fromPt, toPt);
//이동 각도에서 방향과 애니메이션 변경
if (angleZ >= -45 && angleZ < 45) {
//오른쪽
nowAnimation = rightAnime;
} else if (angleZ >= 45 && angleZ <= 135) {
//위쪽
nowAnimation = upAnime;
} else if (angleZ >= -135 && angleZ <= -45) {
//아래쪽
nowAnimation = downAnime;
} else {
//왼쪽
nowAnimation = leftAnime;
}
// 애니메이션 변경하
if (nowAnimation != oldAnimation) {
oldAnimation = nowAnimation;
GetComponent<Animator>().Play(nowAnimation);
}
}
하이라키에서 타일맵을 선택하면 인스펙터뷰에서 Timemap Renderer 컴포넌트에서 Order in Layer가 있습니다. 타일맵이 여러개일때 레이어를 조정해 우선순위를 변경할 수 있습니다.
타일 에셋을 타일맵에 배치하기
타일 팔레트를 사용해 맵에 타일을 배치해보겠습니다. 먼저 월드맵을 만들어 봅니다.
Tile Pallet 창에 나열된 타일의 가장 왼쪽에 있는 바다 타일을 선택하고 툴 바의 왼쪽에서 세번째브러시 툴을 선택합니다. 신뷰에서 마우스를 클릭할 상태에서 드래그하면 선택한 타일이 그리드에 배치됩니다.
Tile Palette 툴바
Tile Palette 창의 상단에는 타일맵에 타일 에셋을 배치하기 위한 툴 바가 있습니다. 이 툴들을 사용해 타일맵을 배치합니다.
타일 회전 배치
키보드의 중괄호 [ , ] 를 누르면 타일을 90도씩 회전시킬수 있습니다.
맵만들기
타일로 맵을 만들어 봅니다. 씬을 두개 만듭니다. 교재에서는 바다 위에 떠 있는 섬을 월드맵으로 해 던전 맵을 만들었습니다. 현재이름이 WorldMap입니다.
뒤에서 입구로 씬을 이동하는 기능을 추가할 것이니 적당한 위치에 입구를 하나 만듭니다. 이건 dungeon1으로 저장합니다.
타일맵에 충돌 판정 설정하기
캐릭터와 접촉했을때 충돌판정을 만들기 위해 하이라키에서 TileMap을 선택한후 Tilemap collider 2D 컴포넌트를 추가합니다. (첨부파일에는 이미 추가되어져 있습니다.)
타일맵에 충돌 판정이 추가됐습니다. 필요없는 지면 부분에도 충돌 판정이 생기니 다음과 같이 조정합니다.
프로젝트뷰에서 타일에셋을 선택하면 인스펙터뷰에서 Collider Type이라는 풀다운 메뉴를 선택할 수 있습니다.
이 값으로 충돌 타입을 선택할 수 있습니다.
None: 충돌판정이 없습니다.
Sprite: 스프라이트의 모양대로 충돌 판정을 합니다. 투명한 부분에는 충돌 판정이 없습니다.
Grid:타일의 사각형 모양으로 충돌 판정을 합니다.
예제에서 충돌 판정을 없앨 타일은 모래, 풀밭, 짙은 풀밭, 다리, 던전 바닥입니다. 해당 타일의 ColliderType을 None으로 설정합니다. 첨부된 파일에는 이미 설정이 되어져 있습니다.
자유로운 모양의 충돌 판정
Collider Type이 Sprite인 타일을 선택하고 [Sprite Editor]버튼을 누르면 Sprite Editor창이 열립니다. 이번에는 던전 출입구인 TipeMap_9을 선택했습니다.
Sprite Editor창의 왼족 위에 풀다운 메뉴에서 Custom Physics Shape를 선택하면 충돌 판정을 패스로 설절할 수 있어 플레이어가 출입구로 진입하는 방향을 제한할 수 있습니다. 스프라이트를 선택하고 Generate를 선택하면 해당 스프라이트의 주변에 사각형 포인터가 배치되고 흰선으로 테두리가 만들어집니다. 하얀테두리 영역이 충돌판정 범위가 됩니다.
네모서리의 사각형을 마우스로 드래그해 흰 테두리의 모양을 바꿀수 잇고 선을 클릭해 새로운 포인트를 만들고 delete키로 삭제가능합니다. 완료후 Apply로 저장합니다.
던전 입구 뿐 아니라 다섯 종류의 계단 타일도 충돌 판정을 똑같이 설정합니다. 캐릭터가 계단에 진입할 곳외는 블록킹 처리 해줍니다.