기록 보관소

[Unity/유니티] 기초-물리 퍼즐게임: 멋진 이펙트 만들기[B57] 본문

유니티 프로젝트/물리 퍼즐게임

[Unity/유니티] 기초-물리 퍼즐게임: 멋진 이펙트 만들기[B57]

JongHoon 2022. 4. 22. 23:12

개요

유니티 입문과 독학을 위해서 아래 링크의 골드메탈님의 영상들을 보며 진행 상황 사진 또는 캡처를 올리고 배웠던 점을 요약해서 적는다.

현재는 영상들을 보고 따라하고 배우는 것에 집중할 것이며, 영상을 모두 보고 따라한 후에는 개인 프로젝트를 설계하고 직접 만드는 것이 목표다.

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


물리 퍼즐게임: 멋진 이펙트 만들기[B57]

1. 파티클꾸미기

Effects -> Particle System 생성
Particle System 설정(1)
Particle System 설정(2)
Particle System 설정(3)

  • Texture Sheet Animation : 입자를 스프라이트로 애니메이팅
  • Looping : 계속 반복시키는 옵션
  • Play On Awake : 파티클이 활성화될 때 자동 플레이 옵션
  • Trails : 입자에 꼬리 혹은 리본 효과 추가
    • 사용시 Renderer 항목에서 Trail Material을 설정 해줘야한다.

완성된 Effect는 프리펩으로 만들어준다


2. 이펙트 생성

//Dongle 스크립트 파일

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

public class Dongle : MonoBehaviour {
	public GameManager manager;
	public ParticleSystem effect;

	public int level;
	public bool isDrag;
	public bool isMerge;

	Rigidbody2D rigid;
	CircleCollider2D circle;
	Animator anim;

	void Awake() {
		rigid = GetComponent<Rigidbody2D>();
		circle = GetComponent<CircleCollider2D>();
		anim = GetComponent<Animator>();
	}

	void OnEnable() {
		anim.SetInteger("Level", level);
	}

	void Update() {
		if (isDrag) {
			Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
			float leftBorder = -4.2f + transform.localScale.x / 2f; //좌측 벽 경계 설정
			float rightBorder = 4.2f - transform.localScale.x / 2f; //우측 벽 경계 설정

			if (mousePos.x < leftBorder) {  //벽에 x축 접근 제한
				mousePos.x = leftBorder;
			}
			else if (mousePos.x > rightBorder) {
				mousePos.x = rightBorder;
			}

			mousePos.y = 8; //Y축 고정해서 경계선 밑으로 내려가지 않도록 설정
			mousePos.z = 0; //Z축 고정해서 맵 밖으로 나가지 않도록 설정
			transform.position = Vector3.Lerp(transform.position, mousePos, 0.2f);
		}

	}

	public void Drag() {
		isDrag = true;
	}

	public void Drop() {
		isDrag = false;
		rigid.simulated = true;
	}

	void OnCollisionStay2D(Collision2D collision) {
		if (collision.gameObject.tag == "Dongle") {
			Dongle other = collision.gameObject.GetComponent<Dongle>();

			if (level == other.level && !isMerge && !other.isMerge && level < 7) {
				//상대편 위치 가져오기
				float meX = transform.position.x;
				float meY = transform.position.y;
				float otherX = other.transform.position.x;
				float otherY = other.transform.position.y;

				//1. 내가 아래에 있을 때
				//2. 동일한 높이 or 내가 오른쪽에 있을 때
				if (meY < otherY || (meY == otherY && meX > otherX)) {
					//상대방 숨기기
					other.Hide(transform.position);
					//나는 레벨 업
					LevelUp();
				}
			}
		}
	}

	public void Hide(Vector3 targetPos) {
		isMerge = true;

		rigid.simulated = false;
		circle.enabled = false;

		StartCoroutine(HideRoutine(targetPos));
	}

	IEnumerator HideRoutine(Vector3 targetPos) {
		int frameCount = 0;

		while(frameCount < 20) {
			frameCount++;
			transform.position = Vector3.Lerp(transform.position, targetPos, 0.5f);
			yield return null;
		}

		isMerge = false;
		gameObject.SetActive(false);
	}

	void LevelUp() {
		isMerge = true;

		rigid.velocity = Vector2.zero;
		rigid.angularVelocity = 0;  //회전 속도

		StartCoroutine(LevelUpRoutine());
	}

	IEnumerator LevelUpRoutine() {
		yield return new WaitForSeconds(0.2f);

		anim.SetInteger("Level", level + 1);	//애니메이션 실행

		yield return new WaitForSeconds(0.3f);
		level++;    //변수 값 증가

		manager.maxLevel = Mathf.Max(level, manager.maxLevel);

		isMerge = false;
	}
}
//GameManager 스크립트 파일

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

public class GameManager : MonoBehaviour {
	public Dongle lastDongle;
	public GameObject donglePrefab;
	public Transform dongleGroup;
	public GameObject effectPrefab;
	public Transform effectGroup;

	public int maxLevel;

	void Awake() {
		Application.targetFrameRate = 60;	//60프레임 이하로 유지
	}

	void Start() {
		NextDongle();
	}

	Dongle GetDongle() {
		//이펙트 생성
		GameObject instantEffectObj = Instantiate(effectPrefab, effectGroup);
		ParticleSystem instantEffect = instantEffectObj.GetComponent<ParticleSystem>();

		//동글 생성
		GameObject instantDongleObj = Instantiate(donglePrefab, dongleGroup);
		Dongle instantDongle = instantDongleObj.GetComponent<Dongle>();

		instantDongle.effect = instantEffect;	//이펙트 전달

		return instantDongle;
	}

	void NextDongle() {
		Dongle newDongle = GetDongle();
		lastDongle = newDongle;
		lastDongle.manager = this;
		lastDongle.level = Random.Range(0, maxLevel);
		lastDongle.gameObject.SetActive(true);

		StartCoroutine(WaitNext());
	}

	IEnumerator WaitNext() {
		while (lastDongle != null) {
			yield return null;
		}

		yield return new WaitForSeconds(2.5f);

		NextDongle();
	}

	public void TouchDown() {
		if (lastDongle == null)
			return;

		lastDongle.Drag();
	}
	
	public void TouchUp() {
		if (lastDongle == null)
			return;

		lastDongle.Drop();
		lastDongle = null;	//드랍 후 조종 불가로 만들기
	}
}
  • Dongle과 GameManager에 앞에서 생성했던 파티클 이펙트 변수를 추가하고, 생성하도록 추가했다.

Dongle Group을 복사해 Effect Group으로 변경
Game Manager 변수 할당


3. 이펙트 실행

//Dongle 스크립트 파일

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

public class Dongle : MonoBehaviour {
	public GameManager manager;
	public ParticleSystem effect;

	public int level;
	public bool isDrag;
	public bool isMerge;

	Rigidbody2D rigid;
	CircleCollider2D circle;
	Animator anim;

	void Awake() {
		rigid = GetComponent<Rigidbody2D>();
		circle = GetComponent<CircleCollider2D>();
		anim = GetComponent<Animator>();
	}

	void OnEnable() {
		anim.SetInteger("Level", level);
	}

	void Update() {
		if (isDrag) {
			Vector3 mousePos = Camera.main.ScreenToWorldPoint(Input.mousePosition);
			float leftBorder = -4.2f + transform.localScale.x / 2f; //좌측 벽 경계 설정
			float rightBorder = 4.2f - transform.localScale.x / 2f; //우측 벽 경계 설정

			if (mousePos.x < leftBorder) {  //벽에 x축 접근 제한
				mousePos.x = leftBorder;
			}
			else if (mousePos.x > rightBorder) {
				mousePos.x = rightBorder;
			}

			mousePos.y = 8; //Y축 고정해서 경계선 밑으로 내려가지 않도록 설정
			mousePos.z = 0; //Z축 고정해서 맵 밖으로 나가지 않도록 설정
			transform.position = Vector3.Lerp(transform.position, mousePos, 0.2f);
		}

	}

	public void Drag() {
		isDrag = true;
	}

	public void Drop() {
		isDrag = false;
		rigid.simulated = true;
	}

	void OnCollisionStay2D(Collision2D collision) {
		if (collision.gameObject.tag == "Dongle") {
			Dongle other = collision.gameObject.GetComponent<Dongle>();

			if (level == other.level && !isMerge && !other.isMerge && level < 7) {
				//상대편 위치 가져오기
				float meX = transform.position.x;
				float meY = transform.position.y;
				float otherX = other.transform.position.x;
				float otherY = other.transform.position.y;

				//1. 내가 아래에 있을 때
				//2. 동일한 높이 or 내가 오른쪽에 있을 때
				if (meY < otherY || (meY == otherY && meX > otherX)) {
					//상대방 숨기기
					other.Hide(transform.position);
					//나는 레벨 업
					LevelUp();
				}
			}
		}
	}

	public void Hide(Vector3 targetPos) {
		isMerge = true;

		rigid.simulated = false;
		circle.enabled = false;

		StartCoroutine(HideRoutine(targetPos));
	}

	IEnumerator HideRoutine(Vector3 targetPos) {
		int frameCount = 0;

		while(frameCount < 20) {
			frameCount++;
			transform.position = Vector3.Lerp(transform.position, targetPos, 0.5f);
			yield return null;
		}

		isMerge = false;
		gameObject.SetActive(false);
	}

	void LevelUp() {
		isMerge = true;

		rigid.velocity = Vector2.zero;
		rigid.angularVelocity = 0;  //회전 속도

		StartCoroutine(LevelUpRoutine());
	}

	IEnumerator LevelUpRoutine() {
		yield return new WaitForSeconds(0.2f);

		anim.SetInteger("Level", level + 1);    //애니메이션 실행
		EffectPlay();	//이펙트 실행

		yield return new WaitForSeconds(0.3f);
		level++;    //변수 값 증가

		manager.maxLevel = Mathf.Max(level, manager.maxLevel);

		isMerge = false;
	}

	void EffectPlay() {
		effect.transform.position = transform.position;
		effect.transform.localScale = transform.localScale;
		effect.Play();
	}
}
  • 이펙트 실행시 동글의 위치와 크기를 맞춰 실행하도록 하는 함수를 만들고, 이를 레벨업 코루틴에 넣어서 합쳐질 때 이펙트가 실행되도록 했다.

1레벨 동글 2개가 합쳐지면
이펙트가 발동한다
2레벨끼리 합치면
커진 크기에 맞게 더 커진 이펙트가 발동한다
레벨에 따라 동글이 커지면서 그에따라 이펙트도 더더욱 커졌다