SpriteRenderer : 스트라이트를 보여주는 컴포넌트
Camera > Orthographic : 원근법이 없는 정사영 투시
2D에서 스프라이트가 겹쳐있을때 보이게 하는방법은
1.2D뷰에서 3D뷰로 바꾼 후 Z축을 변경한다.
2.Order in Layer을 사용한다.
이 값이 높을수록 앞으로간다.
픽셀아트 세팅 A : 필터 모드를 Point로 설정
픽셀아트 세팅 B : 압축률을 None으로 설정
픽셀아트 세팅 C : 이미지 크기로 Pixel Per Unit 설정
이미지를 불러오고나서 스프라이트 타입을 멀티플로 설정.
(가만히있는모습 , 움직이는모습 , 점프하는모습 등)
Automatic : 이미지를 분석해서 자동으로 잡아주는 모드
Cell By Count : 입력한 갯수대로 균등하게 자르는 모드
애니메이션 만들때
해당하는 애니메이션(예: 걷는모션 1,2,3)을 전체 선택해서 만들고자하는 목표캐릭터에 넣는다.
Animator : 애니메이션을 관리하는 컴포넌트
Key Frame : 애니메이션 값을 가진 프레임
State : 애니메이션 상태를 관리하는 애니메이터 단위
velocity : 리지드바디의 현재 속도
리지드바디2D를 사용 중이라면, 물리 재질도 2D로 사용해야함
Linear Drag : 공기 저항, 이동 시 속도를 느리게 해줌. <- 리지드바디2D에 있음
normalized : 벡터 크기를 1로 만든 상태 (단위벡터)
캐릭터가 자꾸 넘어지는이유
리지드바디 2D에서 Constraints 들어가서 Freeze Rotation z축을 체크를 해야한다.
캐릭터 문워크 방지하는법
Sprite Renderer에 들어가서
Flip : 스프라이트를 뒤집는 옵션
애니메이터 매개변수 : 상태를 바꿀 때 필요한 변수
Has Exit Time : 애니메이션이 끝날 때까지 상태를 유지
Mathf : 수학 관련 함수를 제공하는 클래스
점프 구현하기.
단발적인 행동은 Update에서 구현 하는게 좋다.
Project setting > Physics 2D 에서 중력 값 설정 가능
RayCast : 오브젝트 검색을 위해 Ray를 쏘는 방식
DrawRay() : 에디터 상에서만 Ray를 그려주는 함수
RaycastHit : Ray에 닿은 오브젝트
RaycastHit변수의 콜라이더로 검색 확인 가능
LayerMask : 물리 효과를 구분하는 정수값
플레이어 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMove : MonoBehaviour
{
public GameManager gameManager;
public AudioClip audioJump;
public AudioClip audioAttack;
public AudioClip audioDamaged;
public AudioClip audioItem;
public AudioClip audioDie;
public AudioClip audioFinish;
public float maxSpeed;
public float JumpPower;
Rigidbody2D rigid;
SpriteRenderer spriteRenderer;
CapsuleCollider2D capsuleCollider;
Animator anim;
AudioSource audioSource;
void Awake()
{
rigid = GetComponent<Rigidbody2D>();
spriteRenderer = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
capsuleCollider = GetComponent<CapsuleCollider2D>();
audioSource = GetComponent<AudioSource>();
}
void Update()
{
//Jump
if (Input.GetButtonUp("Jump") && !anim.GetBool("isJumping"))
{
PlaySound("JUMP");
rigid.AddForce(Vector2.up * JumpPower, ForceMode2D.Impulse);
anim.SetBool("isJumping", true);
}
//Stop Speed
if (Input.GetButtonUp("Horizontal"))
{
rigid.velocity = new Vector2(rigid.velocity.normalized.x * 0.5f, rigid.velocity.y);
}
//Direction Sprite
if (Input.GetButton("Horizontal"))
spriteRenderer.flipX = Input.GetAxisRaw("Horizontal") == -1;
if (Mathf.Abs(rigid.velocity.x) < 0.5)
anim.SetBool("isWalking", false);
else
anim.SetBool("isWalking", true);
}
void FixedUpdate()
{
//Move Speed
float h = Input.GetAxisRaw("Horizontal");
rigid.AddForce(Vector2.right * h,ForceMode2D.Impulse);
//Max Speed
if (rigid.velocity.x > maxSpeed) //오른쪽 최대속력
rigid.velocity = new Vector2(maxSpeed, rigid.velocity.y);
else if (rigid.velocity.x < maxSpeed*(-1)) //왼쪽 최대속력
rigid.velocity = new Vector2(maxSpeed * (-1), rigid.velocity.y);
//Landing Platform
if(rigid.velocity.y < 0)
{
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")
{
//Attack 몬스터보다 위에 있음 && 아래로 낙하 중
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")
{
// Point
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 += 10;
else if(isSilver)
gameManager.stagePoint += 50;
else if (isGold)
gameManager.stagePoint += 100;
// Deactive Item
collision.gameObject.SetActive(false);
PlaySound("ITEM");
}
else if (collision.gameObject.tag == "Finish")
{
PlaySound("FINISH");
//Next stage
gameManager.NextStage();
}
}
void OnDamaged(Vector2 targetPos)
{
//체력 다운
gameManager.HealthDown();
//레이어 변경 (플레이어데미지드로)
gameObject.layer = 9;
//맞았으니까 투명해짐
spriteRenderer.color = new Color(1,1,1,0.4f);
//맞고 나서 튕겨져 나간다.
int dirc = transform.position.x - targetPos.x > 0 ? 1 : -1;
rigid.AddForce(new Vector2(dirc, 1)*7, ForceMode2D.Impulse);
//맞았을때 애니매이션 실행
anim.SetTrigger("doDamaged");
//무적시간 설정
Invoke("OffDamaged", 1.5f);
PlaySound("DAMAGED");
}
void OnAttack(Transform enemy)
{
//Point
gameManager.stagePoint += 100;
//Reaction Force
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
//Enemy Die
EnemyMove enemyMove = enemy.GetComponent<EnemyMove>();
enemyMove.OnDamaged();
PlaySound("ATTACK");
}
void OffDamaged()
{
gameObject.layer = 8;
spriteRenderer.color = new Color(1, 1, 1, 1);
}
public void OnDie()
{
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
spriteRenderer.flipY = true;
capsuleCollider.enabled = false;
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
PlaySound("DIE");
}
public void VelocityZero()
{
rigid.velocity = Vector2.zero;
}
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();
}
}
타일맵으로 플랫폼 만들기
Tile Palette : 타일을 사용하기 위해 모아둔 프리펩
TileMap : 타일을 일정하게 깔아두는 컴포넌트
팔레트를 수정할때는 에디트를 키고
타일을 그릴때는 에디트를 꺼야한다.
타일팔레트에서 먼저 삭제 후, 물리 모양 편집이 훨씬 안전하다.
TileMap Collider 2D : 타일맵에 맞춰 생성되는 콜라이더
2D에서 코딩없이 카메라가 플레이어를 따라가게 만드는법
메인 카메라를 플레이어에 집어넣으면 된다. Z축은 그대로. X,Y축은 0으로 초기화
몬스터 AI 구현하기
행동지표를 결정할 변수 하나를 생성
public int nextMove;
행동지표를 바꿔줄 함수 하나를 생성
void Think()
Random : 랜덤 수를 생성하는 로직 관련 클래스
Range(): 최소 ~ 최대 범위의 랜덤 수 생성
재귀함수 : 자신을 스스로 호출하는 함수
딜레이 없이 재귀함수를 사용하는것은 아주 위험하다. 스택오버플로우 에러 발생
Invoke() : 주어진 시간이 지난 뒤, 지정된 함수를 실행하는 함수 -> Invoke("Think", 5); 5초 뒤에 호출
CancelInvoke() : 현재 작동 중인 모든 Invoke함수를 멈추는 함수
그리고 박스콜라이더 보단 캡슐 콜라이더로 해야 잘움직인다.
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>();
Think();
Invoke("Think", 5);
}
void FixedUpdate()
{
//기본 이동 Move
rigid.velocity = new Vector2(nextMove, rigid.velocity.y);
//Platform Check
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()
{
//Set Next Active
nextMove = Random.Range(-1, 2);
//Sprite Animation
anim.SetInteger("WalkSpeed", nextMove);
//Flip Sprite
if(nextMove != 0)
spriteRenderer.flipX = nextMove == 1;
//Recursive (재귀함수)
float nextThinkTime = Random.Range(2f, 5f);
Invoke("Think", nextThinkTime);
}
void Turn()
{
nextMove *= -1;
spriteRenderer.flipX = nextMove == 1;
CancelInvoke();
Invoke("Think", 2);
}
//몬스터가 죽었을때 하는 행동
public void OnDamaged()
{
//Sprite Alpha 스프라이트 알파값 조정 (반투명)
spriteRenderer.color = new Color(1, 1, 1, 0.4f);
//Sprite Filp Y 밟혔으니까 적을 뒤집는다
spriteRenderer.flipY = true;
//Collider Disable 그리고 사라지게 만들고
capsuleCollider.enabled = false;
//Die Effect Jump 그리고 점프
rigid.AddForce(Vector2.up * 5, ForceMode2D.Impulse);
//Destroy 삭제
Invoke("DeActive", 5);
}
void DeActive()
{
gameObject.SetActive(false);
}
}
플레이어 피격 이벤트 구현하기
가시랑 몬스터끼리 물리작용을 안되게 하려면
Edit 에 들어가서 Project Settings에 들어가면
맨 밑에 길쭉하게 내가 만든 태그와 레이어가 겹쳐서 표가 있는데
거기에서 체크를 해제하면 물리작용이 서로 작동이 안된다.
또 무적 효과를 위해
플레이어 데미지드와 적 레이어와 충돌 해제
Invoke로 무적시간 설정
애니메이터 Parameters
Triggor : 방아쇠 역할의 매개변수, 값이 없다는 것이 특징 (예:맞았는지 확인)
점수,스테이지는 게임매니저에서 관리
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
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 UIRestarBtn;
void Update()
{
UIPoint.text = (totalPoint + stagePoint).ToString();
}
public void NextStage()
{
//Change Stage
if(stageIndex < Stages.Length-1)
{
Stages[stageIndex].SetActive(false);
stageIndex++;
Stages[stageIndex].SetActive(true);
PlayerReposition();
UIStage.text = "STAGE " + (stageIndex + 1);
}
else
{ //Game Clear
//Player Contol Lock
Time.timeScale = 0;
UIRestarBtn.SetActive(true);
Text btnText = UIRestarBtn.GetComponentInChildren<Text>();
btnText.text = "Clear!";
}
totalPoint += stagePoint;
stagePoint = 0;
}
public void HealthDown()
{
if (health > 1)
{
health--;
UIhealth[health].color = new Color(1, 0, 0, 0.4f);
}
else
{
UIhealth[0].color = new Color(1, 0, 0, 0.4f);
player.OnDie();
UIRestarBtn.SetActive(true);
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "Player")
//Health Down 체력 다운
HealthDown();
PlayerReposition();
//Player Reposition
if (health > 1)
PlayerReposition();
}
void PlayerReposition()
{
player.transform.position = new Vector3(0, 0, -1);
player.VelocityZero();
}
void ViewBtn()
{
UIRestarBtn.SetActive(true);
}
public void Restart()
{
Time.timeScale = 1;
SceneManager.LoadScene(0);
}
}
플레이어에서 매니져 변수를 만들어 점수 변수에 접근
Contains(비교문) : 대상 문자열에 비교문이 있으면 true
stagelndex에 따라 스테이지 활성화/비활성화
스테이지 갯수를 확인하여 다음 스테이지 이동 / 종료 구현
완주하면 Time.timeScale = 0; 로 멈추게 만듬
점수는 Update문으로 표시
버튼 텍스트는 자식오브젝트이므로 InChildron을 더 붙여야 한다.
using UnityEngine.UI;
using UnityEngine.SceneManagement;
UI와 씬을 옮겨갈려면 위의 2개를 맨위에 써줘야한다....
재시작하게 되면 timeScale = 1; 로 시간을 복구
그리고 오타좀 제발 신경쓰자...
'Unity' 카테고리의 다른 글
Unity 기초 눌러담은 3D게임 (0) | 2024.11.23 |
---|---|
Unity(2) 기초2 (0) | 2024.11.16 |
Unity(1) 기초 (0) | 2024.11.07 |