일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | ||||
4 | 5 | 6 | 7 | 8 | 9 | 10 |
11 | 12 | 13 | 14 | 15 | 16 | 17 |
18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 |
- 코딩 기초 트레이닝
- 10월
- 유니티
- c++
- 2022년
- 개인 프로젝트
- 게임 엔진 공부
- 2023년
- 다이나믹 프로그래밍
- 7월
- 골드메탈
- 5월
- 3월
- 수학
- C/C++
- 개인 프로젝트 - 런앤건
- 기초
- 자료 구조
- 2025년
- 프로그래머스
- 1월
- 단계별로 풀어보기
- 백준
- 2024년
- todolist
- 코딩 테스트
- 입문
- 유니티 심화과정
- 4월
- 2월
- Today
- Total
기록 보관소
[Unity/유니티] 기초-2D 플랫포머: 스테이지를 넘나드는 게임 완성하기[BE2] 본문
개요
유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.
현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.
https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2
유니티 강좌 기초 채널 Basic
유니티 개발을 처음 시작하시는 입문자 분들을 위한 기초 채널. [ 프로젝트 ] B00 ~ B12 (BE1) : 유니티 필수 기초 B13 ~ B19 (BE2) : 2D 플랫포머 B20 ~ B26 (BE3) : 2D 탑다운 대화형 RPG B27 ~ B37 (BE4) : 2D 종스크롤
www.youtube.com
2D 플랫포머: 스테이지를 넘나드는 게임 완성하기[BE2]
1. 플레이어 이동 로직 수정
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed*(-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //충돌 발생시
if (collision.gameObject.tag == "Enemy") {
OnDamaged(collision.transform.position);
}
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
}
- 플레이어 방향 전환시 GetButtonDown은 키 입력이 겹치는 구간에서 문제가 발생할 수 있어서 GetButton을 사용한다. GetButtonDown을 사용하면 방향 전환 키를 급작스럽게 여러번 누르면 스프라이트가 Flip되지 않아서 이동 방향과 스프라이트가 반대되어 문워크처럼 이동할 수 있다.
- 이 부분은 이전에 플레이어 이동을 구현할 때부터 GetButton을 사용해서 진행했으므로 넘어가도록 한다.
2. 몬스터 밟아서 잡기
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed*(-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //충돌 발생시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면 피해 받기
else
OnDamaged(collision.transform.position);
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//몬스터 밟았을때 반발력. 타격감?
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
}
- PlayerMove 스크립트 파일. 몬스터를 밟았을때 처치하기 위한 함수 OnAttack()을 추가하고, 물리 충돌 인지 함수인 OnCollisionEnter2D를 수정했다. EnemyMove의 함수 OnDamaged()는 아래 스크립트에 작성되어있다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EnemyMove : MonoBehaviour {
Rigidbody2D rigid;
Animator anim;
SpriteRenderer spriteRenderer;
CapsuleCollider2D capsulecollider;
public int nextMove;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
anim = GetComponent<Animator>();
spriteRenderer = GetComponent<SpriteRenderer>();
capsulecollider = GetComponent<CapsuleCollider2D>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
Invoke("Think", 5);
}
void FixedUpdate() {
//이동
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
//Ray를 사용한 지형 체크
Vector2 frontVec = new Vector2(rigid.position.x + nextMove * 0.2f, rigid.position.y);
Debug.DrawRay(frontVec, Vector3.down, new Color(0, 1, 0));
RaycastHit2D rayHit = Physics2D.Raycast(frontVec, Vector3.down, 1, LayerMask.GetMask("Platform"));
if (rayHit.collider == null)
Turn();
}
void Think() {
//행동 지표 결정
nextMove = Random.Range(-1, 2);
//애니메이션 전환
anim.SetInteger("WalkSpeed", nextMove);
//애니메이션 방향 전환
if (nextMove != 0)
spriteRenderer.flipX = nextMove == 1;
//재귀
float nextThinkTime = Random.Range(2f, 5f);
Invoke("Think", nextThinkTime);
}
void Turn() {
//방향 전환
nextMove *= -1;
spriteRenderer.flipX = nextMove == 1;
CancelInvoke();
Invoke("Think", 2);
}
public void OnDamaged() { //몬스터 공격 받았을때 함수
//스프라이트 반투명화
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
//스프라이트 Y축 뒤집기
spriteRenderer.flipY = true;
//물리 충돌 제거
capsulecollider.enabled = false;
//사망 모션(점프)
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
//몬스터 제거
Invoke("DeActive", 5);
}
void DeActive() { //비활성화 함수
gameObject.SetActive(false);
}
}
- MonsterMove 스크립트 파일. 플레이어에게 밟혀서 죽는 모션을 구현하기위해서 Collider 변수를 추가하여 OnDamaged() 함수와 DeActive() 함수를 만들었다.
3. 아이템
- Silver와 Gold 스프라이트를 생성해서 Circle Collider 2D 컴포넌트를 추가해 Redius 크기를 0.3으로 줄이고, 애니메이션 Silver_Coin과 Gold_Coin을 추가해서 Speed를 0.5로 모두 맞춰 주었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
void Awake()
{
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update()
{
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping"))
{ //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal"))
{
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate()
{
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0)
{ //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision)
{ //충돌 발생시
if (collision.gameObject.tag == "Enemy")
{
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y)
{
OnAttack(collision.transform);
}
//아니라면 피해 받기
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//아이템 제거
collision.gameObject.SetActive(false);
}
}
void OnAttack(Transform enemy)
{ //몬스터 공격 함수
//몬스터 밟았을때 반발력. 타격감?
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos)
{ //충돌 이벤트 무적 효과 함수
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged()
{ //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
}
- 아이템의 경우 트리거로 처리해서 각 아이템 Collider에 is Trigger를 체크해두어야하며, 스크립트에서도 OnTriggerEnter2D() 함수를 사용한다. 그리고 Tag를 사용해서 Item 태그에 닿으면 모두 비활성화되도록 하면 Player가 각 코인들에 닿을 때 게임상에서 보이지 않게 된다.
4. 결승점
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //적과 충돌시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면, 피해를 받음
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//점수 얻기
//아이템 제거
collision.gameObject.SetActive(false);
}
else if (collision.gameObject.tag == "Finish") {
//다음 스테이지로 이동
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//점수 얻기
//몬스터 밟았을때 반발력
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
}
- 결승점에 닿아 다음 스테이지로 가는 것과 몬스터 처치와 코인을 얻고 점수를 얻는 것은 이전 입문 프로젝트에서처럼 게임의 세부 사항들을 저장하고 처리하는 게임 매니저를 만들어서 해야한다. 따라서 일단은 OnTriggerEnter2D()등에 조건문 일부와 주석만 만들어 두고, 게임 매니저를 추가한 뒤에 완성하도록 한다.
5. 게임 매니저 추가
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour {
public int totalPoint; //총 점수
public int stagePoint; //스테이지 점수
public int stageIndex; //스테이지 번호
public void NextStage() {
stageIndex++;
totalPoint += stagePoint;
stagePoint = 0;
}
void Update () {
}
}
//PlayerMove 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public GameManager gameManager;
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //적과 충돌시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면, 피해를 받음
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//점수 얻기
bool isBronze = collision.gameObject.name.Contains("Bronze");
bool isSilver = collision.gameObject.name.Contains("Silver");
bool isGold = collision.gameObject.name.Contains("Gold");
if (isBronze)
gameManager.stagePoint += 50;
else if (isSilver)
gameManager.stagePoint += 100;
else if (isGold)
gameManager.stagePoint += 300;
//아이템 비활성화
collision.gameObject.SetActive(false);
}
else if (collision.gameObject.tag == "Finish") {
//다음 스테이지로 이동
gameManager.NextStage();
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//점수 얻기
gameManager.stagePoint += 100;
//몬스터 밟았을때 반발력
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
}
- PlayerMove 스크립트 파일의 변수창에 GameManager를 끌어다놓으면, 해당 스크립트 파일에 있는 변수들을 모두 사용할 수 있다.
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour {
public int totalPoint; //총 점수
public int stagePoint; //스테이지 점수
public int stageIndex; //스테이지 번호
public int health; //플레이어 체력
public PlayerMove player;
public void NextStage() { //다음 스테이지 이동 함수
stageIndex++;
totalPoint += stagePoint;
stagePoint = 0;
}
public void HealthDown() {
if (health > 1)
health--;
else {
//플레이어 사망 이펙트
player.OnDie();
//결과 UI
Debug.Log("죽었습니다!");
//재시작 버튼 UI
}
}
void OnTriggerEnter2D(Collider2D collision) {
//낙사할 경우
if (collision.gameObject.tag == "Player") {
//플레이어 위치 되돌리기
if (health > 1) {
collision.attachedRigidbody.velocity = Vector2.zero; //낙하 속도 0으로 만들기
collision.transform.position = new Vector3(0, 0, -1); //플레이어 위치 이동
}
//체력 감소
HealthDown();
}
}
}
//PlayerMove 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public GameManager gameManager;
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
CapsuleCollider2D capsulecollider;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
capsulecollider = GetComponent<CapsuleCollider2D>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //적과 충돌시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면, 피해를 받음
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//점수 얻기
bool isBronze = collision.gameObject.name.Contains("Bronze");
bool isSilver = collision.gameObject.name.Contains("Silver");
bool isGold = collision.gameObject.name.Contains("Gold");
if (isBronze)
gameManager.stagePoint += 50;
else if (isSilver)
gameManager.stagePoint += 100;
else if (isGold)
gameManager.stagePoint += 300;
//아이템 비활성화
collision.gameObject.SetActive(false);
}
else if (collision.gameObject.tag == "Finish") {
//다음 스테이지로 이동
gameManager.NextStage();
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//점수 얻기
gameManager.stagePoint += 100;
//몬스터 밟았을때 반발력
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//체력 감소
gameManager.HealthDown();
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
public void OnDie() { //플레이어 사망
//스프라이트 반투명화
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
//스프라이트 Y축 뒤집기
spriteRenderer.flipY = true;
//물리 충돌 제거
capsulecollider.enabled = false;
//사망 모션(점프)
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
}
}
- 몬스터에게 피해 받는 것을 구현하기 위해서 Health를 추가해서 값(3)을 주고, 낙사를 처리하기위해서 Box Collider 2D를 추가해 Offset과 Size를 조절한다. 또한 Is Trigger를 체크해둔다.
- 낙사할 경우 체력을 1깎고 플레이어를 좌표 (0, 0, -1)로 이동시킨다. 부활 지점 시작 지점이 똑같아야 게임 진행이 자연스럽기때문에 플레이어 좌표를 (0, 0)으로 설정하고 맵도 함께 이동했다. 물론, 부활 지점을 그냥 플레이어와 같이 만들어도 되지만 보고 따라하는 입장에서 영상과 맞추는 것이 편하고 문제 발생률이 적다고 생각해서 바꿨다.
6. 스테이지 추가
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour {
public int totalPoint; //총 점수
public int stagePoint; //스테이지 점수
public int stageIndex; //스테이지 번호
public int health; //플레이어 체력
public PlayerMove player;
public GameObject[] Stages;
public void NextStage() {
//다음 스테이지로 이동
if (stageIndex < Stages.Length - 1) {
Stages[stageIndex].SetActive(false); //현재 스테이지 비활성화
stageIndex++;
Stages[stageIndex].SetActive(true); //다음 스테이지 활성화
PlayerReposition();
}
else { //게임 클리어시
//플레이어 멈추기
Time.timeScale = 0;
//게임 결과 UI
Debug.Log("게임 클리어!");
//재시작 버튼 UI
}
//점수 계산
totalPoint += stagePoint;
stagePoint = 0;
}
public void HealthDown() {
if (health > 1)
health--;
else {
//플레이어 사망 이펙트
player.OnDie();
//결과 UI
Debug.Log("죽었습니다!");
//재시작 버튼 UI
}
}
void OnTriggerEnter2D(Collider2D collision) {
//낙사할 경우
if (collision.gameObject.tag == "Player") {
//플레이어 위치 되돌리기
if (health > 1) {
PlayerReposition();
}
//체력 감소
HealthDown();
}
}
void PlayerReposition() { //플레이어 위치 되돌리기 함수
player.transform.position = new Vector3(0, 0, -1); //플레이어 위치 이동
player.VelocityZero(); //플레이어 낙하 속도 0으로 만들기
}
}
//PlayerMove 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public GameManager gameManager;
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
CapsuleCollider2D capsulecollider;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
capsulecollider = GetComponent<CapsuleCollider2D>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //적과 충돌시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면, 피해를 받음
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//점수 얻기
bool isBronze = collision.gameObject.name.Contains("Bronze");
bool isSilver = collision.gameObject.name.Contains("Silver");
bool isGold = collision.gameObject.name.Contains("Gold");
if (isBronze)
gameManager.stagePoint += 50;
else if (isSilver)
gameManager.stagePoint += 100;
else if (isGold)
gameManager.stagePoint += 300;
//아이템 비활성화
collision.gameObject.SetActive(false);
}
else if (collision.gameObject.tag == "Finish") {
//다음 스테이지로 이동
gameManager.NextStage();
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//점수 얻기
gameManager.stagePoint += 100;
//몬스터 밟았을때 반발력
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//체력 감소
gameManager.HealthDown();
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
public void OnDie() { //플레이어 사망
//스프라이트 반투명화
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
//스프라이트 Y축 뒤집기
spriteRenderer.flipY = true;
//물리 충돌 제거
capsulecollider.enabled = false;
//사망 모션(점프)
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
}
public void VelocityZero() {
rigid.velocity = Vector2.zero;
}
}
- 스테이지를 만드는데 대부분의 시간을 쓴것같다. 직접 해보니 컨트롤에 문제도 조금 있고해서 약간 스테이지들을 수정하고, 게임 클리어나 사망시 콘솔창에 출력하는 것이 아닌 직접적으로 보여주는 UI를 추가할 차례다.
7. 유저 인터페이스
//GameManager 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI; //UI 사용을 위해서 추가
using UnityEngine.SceneManagement; //Scene 전환을 위해서 추가
public class GameManager : MonoBehaviour {
public int totalPoint; //총 점수
public int stagePoint; //스테이지 점수
public int stageIndex; //스테이지 번호
public int health; //플레이어 체력
public PlayerMove player;
public GameObject[] Stages;
public Image[] UIhealth;
public Text UIPoint;
public Text UIStage;
public GameObject UIRestartBtn;
void Update() {
UIPoint.text = (totalPoint + stagePoint).ToString();
}
public void NextStage() {
//다음 스테이지로 이동
if (stageIndex < Stages.Length - 1) {
Stages[stageIndex].SetActive(false); //현재 스테이지 비활성화
stageIndex++;
Stages[stageIndex].SetActive(true); //다음 스테이지 활성화
PlayerReposition();
UIStage.text = "STAGE " + (stageIndex + 1);
}
else { //게임 클리어시
//멈추기
Time.timeScale = 0;
//재시작 버튼 UI
UIRestartBtn.SetActive(true);
Text btnText = UIRestartBtn.GetComponentInChildren<Text>(); //버튼 텍스트는 자식 오브젝트이므로 InChildren을 붙여야함
btnText.text = "Clear!";
UIRestartBtn.SetActive(true);
}
//점수 계산
totalPoint += stagePoint;
stagePoint = 0;
}
public void HealthDown() {
if (health > 1) {
health--;
UIhealth[health].color = new Color(1, 0, 0, 0.4f); //체력 UI 색상 변화
}
else {
//체력 UI 끄기
UIhealth[0].color = new Color(1, 0, 0, 0.4f);
//플레이어 사망 이펙트
player.OnDie();
//재시작 버튼 UI
UIRestartBtn.SetActive(true);
}
}
void OnTriggerEnter2D(Collider2D collision) {
//낙사할 경우
if (collision.gameObject.tag == "Player") {
//플레이어 위치 되돌리기
if (health > 1) {
PlayerReposition();
}
//체력 감소
HealthDown();
}
}
void PlayerReposition() { //플레이어 위치 되돌리기 함수
player.transform.position = new Vector3(0, 0, -1); //플레이어 위치 이동
player.VelocityZero(); //플레이어 낙하 속도 0으로 만들기
}
public void Restart() {
Time.timeScale = 1; //재시작시 시간 복구
SceneManager.LoadScene(0);
}
}
- GameManager의 변수창에 이미지, 텍스트, 오브젝트(버튼)을 넣어줘야 작동한다.
- Retry 버튼은 게임 시작 때는 필요 없으므로 비활성화한채로 시작하고, 아래 On Click ()에 GameManager를 넣어 버튼 클릭시 만들어두었던 Restart() 함수를 실행시키도록 한다.
8. 사운드
//PlayerMove 스크립트 파일
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour {
public GameManager gameManager;
public float maxSpeed;
public float jumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
Animator anim;
CapsuleCollider2D capsulecollider;
AudioSource audioSource;
//Audio Clip 변수들
public AudioClip audioJump;
public AudioClip audioAttack;
public AudioClip audioDamaged;
public AudioClip audioItem;
public AudioClip audioDie;
public AudioClip audioFinish;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
capsulecollider = GetComponent<CapsuleCollider2D>();
audioSource = GetComponent<AudioSource>();
rigid.freezeRotation = true; //이동시 굴러가기 방지
}
void PlaySound(string action) {
switch(action) {
case "JUMP" :
audioSource.clip = audioJump;
break;
case "ATTACK":
audioSource.clip = audioAttack;
break;
case "DAMAGED":
audioSource.clip = audioDamaged;
break;
case "ITEM":
audioSource.clip = audioItem;
break;
case "DIE":
audioSource.clip = audioDie;
break;
case "FINISH":
audioSource.clip = audioFinish;
break;
}
audioSource.Play();
}
void Update() {
//점프
if (Input.GetButtonDown("Jump") && !anim.GetBool("isJumping")) { //스페이스바, 점프 애니메이션이 작동중이지 않다면
rigid.AddForce(Vector2.up * jumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true); //Jump 애니메이션 추가
PlaySound("JUMP"); //사운드 재생
}
//멈출 때 속도
if (Input.GetButtonUp("Horizontal")) {
//normalized : 벡터 크기를 1로 만든 상태
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//방향 전환
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1; //-1은 왼쪽 방향
//애니메이션 전환
if (Mathf.Abs(rigid.velocity.x) < 0.3) //정지 상태, Mathf : 수학관련 함수를 제공하는 클래스
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate() {
float h = Input.GetAxisRaw("Horizontal"); //좌,우 A, D
rigid.AddForce(Vector2.right * h, ForceMode2D.Impulse);
//오른쪽 속도 조절
if (rigid.velocity.x > maxSpeed) //velocity : 리지드바디의 현재 속도
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y); //y축 값을 0으로 하면 멈춤
//왼쪽 속도 조절
else if (rigid.velocity.x < maxSpeed * (-1))
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//점프 후 착지시 애니메이션 전환(레이캐스트)
if (rigid.velocity.y < 0) { //y축 속도 값이 0보다 클때만. 땅에 있으면 레이 표시 X
Debug.DrawRay(rigid.position, Vector3.down, new Color(0, 1, 0)); //레이 표시
RaycastHit2D rayHit = Physics2D.Raycast(rigid.position, Vector3.down, 1, LayerMask.GetMask("Platform")); //레이에 닿는 물체
if (rayHit.collider != null) //레이에 닿는 물체가 있다면
if (rayHit.distance < 0.5f)
anim.SetBool("isJumping", false);
}
}
void OnCollisionEnter2D(Collision2D collision) { //적과 충돌시
if (collision.gameObject.tag == "Enemy") {
//몬스터보다 위에 있고, 낙하 중이라면 밟음(공격)
if (rigid.velocity.y < 0 && transform.position.y > collision.transform.position.y) {
OnAttack(collision.transform);
}
//아니라면, 피해를 받음
else
OnDamaged(collision.transform.position);
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (collision.gameObject.tag == "Item") {
//점수 얻기
bool isBronze = collision.gameObject.name.Contains("Bronze");
bool isSilver = collision.gameObject.name.Contains("Silver");
bool isGold = collision.gameObject.name.Contains("Gold");
if (isBronze)
gameManager.stagePoint += 50;
else if (isSilver)
gameManager.stagePoint += 100;
else if (isGold)
gameManager.stagePoint += 300;
//아이템 비활성화
collision.gameObject.SetActive(false);
//사운드 재생
PlaySound("ITEM");
}
else if (collision.gameObject.tag == "Finish") {
//사운드 재생
PlaySound("FINISH");
//다음 스테이지로 이동
gameManager.NextStage();
}
}
void OnAttack(Transform enemy) { //몬스터 공격 함수
//점수 얻기
gameManager.stagePoint += 100;
//사운드 재생
PlaySound("ATTACK");
//몬스터 밟았을때 반발력
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//몬스터 사망
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
}
void OnDamaged(Vector2 targetPos) { //충돌 이벤트 무적 효과 함수
//체력 감소
gameManager.HealthDown();
//사운드 재생
PlaySound("DAMAGED");
//레이어 변경(무적)
gameObject.layer = 11; //11번 레이어, PlayerDamaged
//충돌시 스프라이트 색상 변화
spriteRenderer.color = new Color(1, 1, 1, 0.4f); //Color(R,G,B,투명도)
//충돌시 튕겨짐
//목표물 기준 왼쪽에서 닿으면 왼쪽으로, 오른쪽에서 닿으면 오른쪽으로
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1) * 7, ForceMode2D.Impulse);
//애니메이션 변경
anim.SetTrigger("doDamaged");
Invoke("OffDamaged", 3); //3초 뒤 OffDamaged 함수 실행
}
void OffDamaged() { //충돌 이벤트 무적 해제 함수
//레이어 변경(원래대로)
gameObject.layer = 10; //10번 레이어, Player
spriteRenderer.color = new Color(1, 1, 1, 1);
}
public void OnDie() { //플레이어 사망
//스프라이트 반투명화
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
//스프라이트 Y축 뒤집기
spriteRenderer.flipY = true;
//물리 충돌 제거
capsulecollider.enabled = false;
//사운드 재생
PlaySound("DIE");
//사망 모션(점프)
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
}
public void VelocityZero() {
rigid.velocity = Vector2.zero;
}
}
- Player에 AudioSource 컴포넌트를 추가하고, 기본적으로 체크되어 있는 Play On Awake를 체크 해제한다. Play On Awake는 이름대로 사운드를 시작하자마자 실행하는 것인데, 여기서 추가하는 사운드는 배경 음악이 아닌 효과음이므로 필요가 없다.
- 사운드는 점프 7번, 공격 22번, 피해 41번, 아이템 28번, 죽음 11번, 클리어 12번을 사용한다. 나머지 사운드는 사용하지 않으므로 Import할 때 이것만 해도 괜찮을듯 하다.
'유니티 프로젝트 > 2D 플랫포머' 카테고리의 다른 글
[Unity/유니티] 기초-2D 플랫포머: 플레이어 피격 이벤트 구현하기[B19] (0) | 2022.02.14 |
---|---|
[Unity/유니티] 기초-2D 플랫포머: 몬스터 AI 구현하기[B18] (0) | 2022.02.13 |
[Unity/유니티] 기초-2D 플랫포머: 타일맵으로 플랫폼 만들기[B17] (0) | 2022.02.13 |
[Unity/유니티] 기초-2D 플랫포머: 플레이어 점프 구현하기[B16] (0) | 2022.02.11 |
[Unity/유니티] 기초-2D 플랫포머: 플레이어 이동 구현하기[B15] (0) | 2022.02.10 |