플레이어 캐릭터가 점프를 할 수 있는데 벽이나 블록에 닿으면 붙어서 떨어지지 않습니다. 이는 캐릭터의 마찰력 때문입니다.  마찰을 0으로 바꾸어 보겠습니다.

 프로젝트뷰의 +를 눌러 2D> physics Material 2D를 클릭합니다. 이름을 PM2D라고 변경합니다.

Fricsion을 0으로 바꿉니다.

이 매터리얼을 Player에 적용합니다. 

Player게임오브젝트의 인스펙터뷰> Rigidbody2D > Material 검색브라우저를 눌러 방금만든 PM2D를 적용시킵니다. 파일을 끌어다 여기다 놔도 됩니다.

 

Vector2 jumpPw = new Vector2(0, jump);  // 점프벡터 생성
rbody.AddForce(jumpPw, ForceMode2D.Impulse); //순간적인 힘 가하기
goJump = false; // 점프플래그 끄기

플레이어 Tag지정하기

플레이어를 선택하고 Tag를 player를 선택합니다. player Tag는 기본적으로 있습니다.

 

점프 구현하기

스페이스바를 눌렀을때 캐릭터를 점프하게 만들어 보겠습니다.

Update()함수에 스페이스키를 체크되면 Jump()함수를 호출하는 코드가 추가되었습니다. 

Jump() 함수는 goJump 플래그를 true로 할 뿐입니다.

public void Jump() {
    goJump = true;
}

실제 점프 동작은 FixedUpdate()에서 if(onGround && goJump) 때 이루어 집니다. 

Vector2 jumpPw = new Vector2(0, jump);  // 점프벡터 생성
rbody.AddForce(jumpPw, ForceMode2D.Impulse); //순간적인 힘 가하기
goJump = false; // 점프플래그 끄기

플레이 해보면 스페이스바를 눌러도 캐릭터가 점프를 안하는데 LayerMask 변수인 groundLayer를 인스펙터뷰 playerController 스크립트 창에서 ground로 설정해야 합니다.

public LayerMask groundLayer; //착지할 수 있는 레이어
bool onGround = false;  //지면에 서 있는 플래그

Linecast가 캐릭터위치에서 0.1f 아래로 광선을 쏴서 goundLayer가 감지될경우만 onGround를 true시킵니다.

onGround = Physics2D.Linecast(transform.position, 
					transform.position - (transform.up * 0.1f), groundLayer);

Linecast(시작점, 종료점, 마스크)

 

아래는 playerController.cs 전체코드입니다.

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

public class PlayerController : MonoBehaviour{
    // Start is called before the first frame update
    Rigidbody2D rbody;  // Rigidbody 참조
    float axisH = 0.0f; //입력
    public float speed = 3.0f;  //이동속도

    public float jump = 9.0f; //점프력
    public LayerMask groundLayer; //착지할 수 있는 레이어
    bool goJump = false; //점프개시 플래그
    bool onGround = false;  //지면에 서 있는 플래그
    void Start(){
        rbody = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update(){
        axisH = Input.GetAxisRaw("Horizontal");
        if(axisH > 0.0f) {
            transform.localScale = new Vector2(1, 1);
        } else if (axisH <0.0f) {
            transform.localScale = new Vector2(-1, 1);
        }
        if (Input.GetButtonDown("Jump")) {
            Jump();
        }
    }
    private void FixedUpdate() {
        //착지판정
        onGround = Physics2D.Linecast(transform.position, transform.position - (transform.up * 0.1f), groundLayer);
        if(onGround || axisH !=0) {  //점프상태일 때는 전진 안함.
            rbody.velocity = new Vector2(speed * axisH, rbody.velocity.y);
        }
        if(onGround && goJump) {
            Vector2 jumpPw = new Vector2(0, jump);  // 점프벡터 생성
            rbody.AddForce(jumpPw, ForceMode2D.Impulse); //순간적인 힘 가하기
            goJump = false; // 점프플래그 끄기
        }
    }
    public void Jump() {
        goJump = true;
    }
}

 

이번에 만들 게임요소를 정리해 보겠습니다.

  • 규칙 : 오른쪽 끝의 골인 지점에 도착하면 게임 클리어
  • 적과 장애물 : 함정에 떨어지면 게임 오버
  • 간섭과 변화 : 점프로 함정 뛰어넘기
  • 보상 : 함정을 피해서 목표 지점에  도착

 

4.1 사이드뷰게임

사이드뷰는 게임 속 세상을 옆에서 바라본 시점의 게임 시스템입니다. 이동은 좌우나 점프를 위해 상하로 움직일 수 있습니다. 

 

사용할 게임 오브젝트와 스크립트 생각해보기

  • 플레이어 캐릭터 : 좌우이동과 점프를 할 수 있습니다. 이동이나 점프를 묘사할 애니메이션을 적용합니다.
  • 지면과 블록 :  플레이어가 올라가서 이동할 수 있는 지면과 점프해 올라갈 수 있는 발판이 되는 블록입니다.
  • 골인 지점과 게임 오버 : 게임 스테이지의 오른쪽 끝에 게임 오브젝트를 배치해 이곳에 접촉하면 스테이지 클리어되도록 목표지점을 만듭니다. 지면 아래로 떨어지면 게임 오버되는 처리도 추가합니다.
  • 상태 표시와 재시작 : 게임으 시작및 게임오버 클리어했을 때의 화면을 표시하고 다시 처음부터 시작할 수 있게 합니다. 

4.2 샘플 게임 실행해보기  

Jewlry Hunter폴더내의 프로젝트를 플레이 해보시면 됩니다.

4.3 게임 스테이지 만들기

지면 블록 만들기

이전에 연습했던 프로젝트를 사용합니다.

프로젝트뷰의 Block들을 끌어다 씬뷰에 배치합니다.

위와 같이 보이지 않을겁니다. 왜냐하면 Order in Layer가 밑에 있기 때문에 가려져서 그렇습니다.

하이라키에서 방금 끌어온 block들을 선택해고 인스펙터뷰의 Additional Settings의 Order in Layer를 2로 하면 위와 같이 블록들이 보입니다.

레이어로 게임 오브젝트를 그룹으로 분류하기 (레이어)

유니티는 게임오브젝트를 이름으로 식별할 수 있지만 "레이어"를 사용해 그룹으로 묶을 수도 있습니다. 바닥과 블록을 Ground라는 이름으로 그룹화해 플레이어 캐릭터가 레이어에 접촉할 때만 점프할 수 있도록 합니다.

인스펙터뷰의 레이어 Item을 눌러 Add Layer로 Ground 레이어를 추가합니다.  보통 0~5까지는 유니티가 사용중이므로 레이어6에 새로 만듭니다.

레이어 추가 메뉴는 게임오브젝트 메뉴와 독립적이라 돌아갈 수 없고 다시 하이라키의 게임오브젝트를 선택해야 합니다.

한꺼번에 레이어를 바꾸기 위해 하이라키에서 ground 와 블록3가지를 선택하고 인스펙트뷰에서 레이어를 6으로 바꿔줍니다.

씬뷰에 goal팻말을 배치하고 인스펙터뷰에서 Order in Layer를 2로 바꿔줍니다.

골인 지점 도달 판별하기

목표에 도달한것을 판별하기 위해 goal에 Box Collider2D 컴포넌트를 추가하고 Is Trigger를 체크합니다. Is Trigger는 물리적인 충돌없이 충돌 이벤트만 만듭니다.

 

게임오브젝트를 구별하는 방법(태그)

골인 지점에 도달했는지 판별하는 방법중 태그(Tag)도 있습니다. 

하이라키에서 goal을 선택후 인스펙트뷰에서 Tag를 눌러 Add Tag에서 Goal을 만들고 저장합니다. 이후 Goal에서 지정합니다.

게임오브젝트 재사용하기

유니티에서 프리팹은 재사용가능한 "클래스"같은 개념입니다. 하이라키의 ground,block1,2,3,goal등을 끌어다 프로젝트뷰에 끌어다 놓으면 자동으로 프리팹이 만들어 집니다. 하이라키의  ground,block1,2,3,goal등은 이미 파란색으로 바뀌었는데 이건 프리팹의 인스턴스라는 이야기 입니다. 프리팹을 사용하고 싶으면 아이콘을 끌어다 하이라키나 씬뷰에 놓으면 인스턴스가 만들어집니다.

프로젝트뷰의 프리팹은 런타임시 존재하지 않습니다. 씬뷰에서 인스턴스화 되었을때 비로서 런타임에서 존재하고 접근할 수 있습니다. 이점이 중요합니다. 클래스나 타입도 변수화 하지 않으면 아무것도 아닌것 처럼요.

프리팹을 변경하면 기본에 만들었던 모든 인스턴스에 영향을 미칩니다. 반대로 인스턴스를 변경해면 유니티에서 프리팹을 변경할건지 물어보는 개념도 있습니다. 이걸 오버라이드라고 합니다.

프리팹을 정리하기 위해 프로젝트뷰 Assets 폴더안에 Prefabs폴더를 만들고 만들어진 프리팹들을 끌어다 놓습니다.

 

프리팹을 배치해 지면 만들기

프리팹 폴더안의 프리팹들을 이용해 다음과 같이 지면과 블록을 더 배치 합니다. (이미지 폴더가 아닙니다.)

백그라운드는 back_title에서 back으로 바꾸었습니다. 길게보이는 그라운드는 확대한게 아니라 같은 그라운드를 2개 연결한것입니다.

 

게임 오브젝트 정렬하기

옮길 오브젝트를 살짝 클릭해서 선택해줍니다. 마우스클릭하지 않고 키보드 V를 눌러 맞추고 싶은 중앙이나 모서리를 클릭하면서 맞출곳에 가져가면 자석같이 붙습니다. 화면을 좀 크게하시는게 잘되고 한번에 안되면 옮긴후 다시 시도 하면 됩니다.

 

보이지 않는 충돌 판정 만들기

하이라키의 +를 눌러 Create Empty를 클릭합니다. 이름을 Wall로 합니다. Box Collider 2D를 2개 추가합니다.

Edit  Collider를 눌러 충돌영역의 위치를 화면 양옆으로 이동시킵니다. BoxCollider2D를 2개 추가했기 때문에 영역이 2개 입니다. 

플레이해보면 캐릭터가 왼쪽벽에 부딪쳐 가지 못합니다. 

 

게임오버 판별하기

현재는 구덩이쪽으로 가면 캐릭터가 빠져 계속 떨어지기 때문에 게임진행이 더 이상 불가능 합니다.

DeadZone이라는 빈게임오브젝트를 만들고 BoxCollider2D컴포넌트를 추가하고 Is Trigger를 체크합니다.  Edit Collider를 누루고 다음과 같이 화면 아래 배치합니다. 

Dead라는 Tag를 만들어 지정해 줍니다.

 

 

 

 

 

스크립트로 게임 오브젝트 조작하기

Rigidbody2D나 Box Collider등과 같은 컴포넌트들을 게임오브젝트에 추가했던것 처럼 스크립트를 컴포넌트처럼 추가할 수 있습니다.

 

스크립트 파일 만들기

게임오브젝트에서 Add Component에서 스크립트를 추가할 수도 있고 프로젝트뷰에서 스크립트를 만들어 나중에 추가할 수도 있습니다. 프로젝트뷰에서 Scripts폴더를 만든후 폴더안에서 우클릭후 Create>Script를 클릭후 이름을 PlayerController로 바꿔줍니다.

키 입력으로 게임 오브젝트를 조작하는 스크립트

Rigidbody2D rbody;  // Rigidbody2D의 참조입니다.

게임오브젝트 생성시 불리워지는 Start() 함수에서 rbody변수에 Rigidbody2D 참조를 연결합니다.

매 프레임 불리워지는 Update()에서 키보드입력을 받습니다. axisH = Input.GetAxisRaw("Horizontal");

일정한 간격으로 호출되는 FixedUpdate()에서 Rigidbody 2D컴포넌트를 사용중이므로  Rigidbody를 물리를 이용 객체의 위치를 변경시킵니다. rbody.velocity = new Vector2 (axisH * 3.0f, rbody.velocity.y);

Rigidbody를 사용하지 않을경우 Transform컴포넌트를 이용 객체를 이동시키나 Rigidbody를 사용시는 되도록 Rigidbody를 사용해야 합니다. 이는 Rigidbody가 Trnasform컴포넌트 보다 나중에 실행되어지기 때문입니다.

저장후 Pplayer_stop에 적용시켜줍니다. 플레이후 AD키를 누르면 좌우로 움직입니다.

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

public class PlayerController : MonoBehaviour
{
    // Start is called before the first frame update
    Rigidbody2D rbody;
    float axisH = 0.0f;
    void Start()
    {
        rbody = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update()
    {
        axisH = Input.GetAxisRaw("Horizontal");
    }
    private void FixedUpdate() {
        rbody.velocity = new Vector2 (axisH * 3.0f, rbody.velocity.y);
    }
}

변수부분에 public float speed = 3.0f; 를 추가하고 상수 3.0f대신 speed를 사용합니다.

캐릭터가 좌우로 움직일 경우 진행 방향을 바라보기 위해 transform.localScale을 이용합니다. 물리상 - Scale은 말이 안되지만 유니티에서는 반전을 뜻합니다.  (-1,1)은 x축만 반전 시키겠다는 의미입니다.

if(axisH > 0.0f) { //오른쪽 처리
    transform.localScale = new Vector2(1, 1);
} else if (axisH <0.0f) {  //왼쪽 처리
    transform.localScale = new Vector2(-1, 1);
}

저장하고 실행해보면  캐릭터가 진행방향에 따라 반전하는걸 알수 있습니다. 

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

public class PlayerController : MonoBehaviour{
    // Start is called before the first frame update
    Rigidbody2D rbody;
    float axisH = 0.0f;
    public float speed = 3.0f;
    void Start(){
        rbody = GetComponent<Rigidbody2D>();
    }

    // Update is called once per frame
    void Update(){
        axisH = Input.GetAxisRaw("Horizontal");
        if(axisH > 0.0f) {
            transform.localScale = new Vector2(1, 1);
        } else if (axisH <0.0f) {
            transform.localScale = new Vector2(-1, 1);
        }
    }
    private void FixedUpdate() {
        rbody.velocity = new Vector2 (speed * axisH, rbody.velocity.y);
    }
}

실행해보면 좌우방향에 따라 캐릭터가 반전한다.

2.1 새프로젝트 만들기

2D 프로젝트 만들기

유니티 허브에서 우상 New project를 클릭후  2D템플릿을 선택후 이름을 UniSideGame으로 하겠습니다.

샘플 게임과 샘플 에셋 다운로드

아래 사이트에서 예제를 다운 받아  풀어보니까너무 커서 컴퓨터 멈출정도로 느리네요 ㅠㅠ  너무 커서 그냥 풀지 마시고 아래에서 그때  그때 필요한 파일을 올려드릴께요

https://bit.ly/unity2d_jpub

 

unity2d_jpub.zip

 

drive.google.com

 

2.2 게임화면 만들기

이미지 에셋을 프로젝트에 등록하기

압축파일을 풀어서 Playes, Images 폴더 통채로 Assets폴더로 복사합니다. 

Images.zip
2.11MB
Player.zip
0.36MB

이미지 에셋으로 게임화면 만들기

하이라키에 다음과 같이 Images/back, ground, Player폴더의 player_stop이미지를 끌어다 놓는다. 플레이해 보면 예상과 다르게 배경만 보인다. player_stop의 이름은 player로 바꿉니다.

트랜스폼

트랜스폼:  위치, 화면 크기를 조절하는 컴포넌트 입니다. 

스프라이트랜더러

스프라이트랜더러 :  이미지를 표시하는 컴포넌트입니다. 다음과 같은 값을 가지고 있습니다.

Sprite : 표시할 이미지에셋을 나타냅니다.

Color : 색감변경

Flip : 상하좌우로 반전시킬수 있습니다.

Sorting Layer: 게임오브젝트를 레이어라는 그룹으로 묶고 표시의 우성순위를 그룹별로 지정할 수 있습니다.

Order in Layer : Sorting Layer로 나눈 레이어 중 게임 오브젝트가 씬에 표시되는 순서를 정합니다. 값이 클수록 앞에 표시됩니다.

 

게임 오브젝트의 이미지 변경하기

스프라이트렌더러에는 이미지의 파일명이 표시된  Sprite라는 항목이 있습니다. Sprite를 클릭하면 프로젝트뷰에서 해당 이미지 에셋이 강조돼 어느 에셋이 사용되는지 확인할 수 있습니다.

이미지를 선택한후 Delete키를 클릭해 제거해 보면 씬뷰에서보이지 않게 되지만 하이라키뷰에서는 게임오브젝트가 남아 있습니다.

다시 이미지를 설정해 봅시다. 이미지가 제거 되었으니 계층뷰에서 게임오브젝트를 선택합니다. 프로젝트 뷰의 이미지를 선택해 Sprite의 오른쪽 텍스트박스에 드래그앤드롭합니다.

 

표시우선순위를 알아보기

지면이나 캐릭터 등은 항상 배경보다 앞에 표시돼야 해 표시 우선순위를 의도적으로 변경해야 합니다. 우선순위를 높이려면 Sprite Renderer 컴포넌트의 Additional Settings에서 Order in Layer의 값을 크게 합니다. 여기서는 배경 0, 플레이어는 3, 지면은 2로 설정했습니다. 드디어 보이네요

게임 오브젝트 재배치하기

title_back: (4,0,0), ground:(-5,-2,0), player_stop:(-5,0,0)

 

게임 오브젝트에 중력(Rigidbody 2D)추가하기

 하이라키에서 player_stop을 선택하고 오른쪽 인스펙터부에서 Rigidbody 2D 컴포넌트를 추가합니다.

플레이해보면 플레이어가 아래로 떨어집니다.

 

충돌판정, ground에 Box Collider 2D 추가하기

하이라키에서 ground를 선택하고 Box Collider 2D를 선택 추가합니다. 

플레이 해보면 아직도 플레이어가 떨어집니다. 이제 플레이어에도 Box Collider 2D를 추가해보면  다음과 같이 사각형이 생기는데 Edit Collider를 누르면 사각형 색과 모양이 다음과 같이 바끼고 크기를 조정할 수 있습니다. Capsule Collider 2D를 선택하면 좀더 타이트하게 설정도 가능합니다. 가벼운 처리를 위해서는 Circle Collider 2D가 있습니다. 대량의 적 캐릭터의 충돌 판정에 적합합니다.

 

플레이 해보면 캐릭터가 그라운드 위에서 멈춥니다.

 

+ Recent posts