Notice
Recent Posts
Recent Comments
Link
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
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 |
Tags
- 유니티
- todolist
- 4월
- 기초
- 백준
- 개인 프로젝트 - 런앤건
- 2025년
- 3월
- 단계별로 풀어보기
- 게임 엔진 공부
- 2월
- 코딩 기초 트레이닝
- 입문
- 다이나믹 프로그래밍
- 유니티 심화과정
- 2022년
- C/C++
- 개인 프로젝트
- 수학
- 2024년
- 5월
- 6월
- c++
- 1월
- 10월
- 프로그래머스
- 골드메탈
- 2023년
- 코딩 테스트
- 자료 구조
Archives
- Today
- Total
기록 보관소
[Unity/유니티] 기초-뱀서라이크: 로직 보완하기[16] 본문
개요
유니티 독학을 위해 아래 링크의 골드메탈님의 영상들을 보고 직접 따라 해보면서 진행 상황을 쓰고 배웠던 점을 요약한다.
https://youtube.com/playlist?list=PLO-mt5Iu5TeYI4dbYwWP8JqZMC9iuUIW2
📚유니티 기초 강좌
유니티 게임 개발을 배우고 싶은 분들을 위한 기초 강좌
www.youtube.com
뱀서라이크: 로직 보완하기[16]
1. 무한 맵 재배치 보완
//Reposition Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Reposition : MonoBehaviour {
Collider2D coll; //모든 모양의 Collider를 포함
void Awake() {
coll = GetComponent<Collider2D>();
}
void OnTriggerExit2D(Collider2D collision) { //IsTrigger가 체크된 Collider에서 나갔을 때 동작
if (!collision.CompareTag("Area"))
return;
//거리를 구하기 위해 플레이어 위치와 타일맵 위치 미리 저장
Vector3 playerPos = GameManager.instance.player.transform.position;
Vector3 myPos = transform.position;
switch (transform.tag) {
case "Ground":
float diffX = playerPos.x - myPos.x;
float diffY = playerPos.y - myPos.y;
float dirX = diffX < 0 ? -1 : 1;
float dirY = diffY < 0 ? -1 : 1;
diffX = Mathf.Abs(diffX);
diffY = Mathf.Abs(diffY);
if (diffX > diffY) { //X축 이동시
transform.Translate(Vector3.right * dirX * 40); //X축으로 2칸 이동
}
else if (diffX < diffY) { //Y축 이동시
transform.Translate(Vector3.up * dirY * 40); //Y축으로 2칸 이동
}
break;
case "Enemy":
if (coll.enabled) { //몬스터가 죽으면 collider2D 컴포넌트를 disable 시키도록 구현할 예정
transform.Translate(playerDir * 20 + new Vector3(Random.Range(-3f, 3f), Random.Range(-3f, 3f), 0f)); //플레이어 이동 방향에 따라 맞은 편에서 등장하도록 이동
}
break;
}
}
}
- 플레이어 입력 방향에 따라 맵이 재배치 되는 방식에서 플레이어와 타일맵의 거리 차이를 이용해 재배치하는 방식으로 변경
- 코드 아랫 부분에 몬스터를 재배치하는 부분에서 이전 방식에 사용하는 playerDir 변수가 아직 있다. 이는 다음 파트에서 고친다.
2. 몬스터 재배치 보완
//Reposition Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Reposition : MonoBehaviour {
Collider2D coll; //모든 모양의 Collider를 포함
void Awake() {
coll = GetComponent<Collider2D>();
}
void OnTriggerExit2D(Collider2D collision) { //IsTrigger가 체크된 Collider에서 나갔을 때 동작
if (!collision.CompareTag("Area"))
return;
//거리를 구하기 위해 플레이어 위치와 타일맵 위치 미리 저장
Vector3 playerPos = GameManager.instance.player.transform.position;
Vector3 myPos = transform.position;
switch (transform.tag) {
case "Ground":
float diffX = playerPos.x - myPos.x;
float diffY = playerPos.y - myPos.y;
float dirX = diffX < 0 ? -1 : 1;
float dirY = diffY < 0 ? -1 : 1;
diffX = Mathf.Abs(diffX);
diffY = Mathf.Abs(diffY);
if (diffX > diffY) { //X축 이동시
transform.Translate(Vector3.right * dirX * 40); //X축으로 2칸 이동
}
else if (diffX < diffY) { //Y축 이동시
transform.Translate(Vector3.up * dirY * 40); //Y축으로 2칸 이동
}
break;
case "Enemy":
if (coll.enabled) {
Vector3 dist = playerPos - myPos;
Vector3 ran = new Vector3(Random.Range(-3, 3), Random.Range(-3, 3));
transform.Translate(ran + dist * 2);
}
break;
}
}
}
- 앞의 맵 재배치와 같이 거리를 이용하는 방식으로 변경
3. 투사체 멈춤 보완
//Weapon Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Weapon : MonoBehaviour {
public int id;
public int prefabId;
public float damage;
public int count;
public float speed;
float timer;
Player player;
void Awake() {
player = GameManager.instance.player;
}
void Update() {
if (!GameManager.instance.isLive)
return;
switch (id) {
case 0:
transform.Rotate(Vector3.back * speed * Time.deltaTime);
break;
default:
timer += Time.deltaTime;
if (timer > speed) {
timer = 0f;
Fire();
}
break;
}
}
public void LevelUp(float damage, int count) {
this.damage = damage * Character.Damage;
this.count += count;
if (id == 0)
Batch();
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
public void Init(ItemData data) {
// Basic Set
name = "Weapon " + data.itemId;
transform.parent = player.transform;
transform.localPosition = Vector3.zero;
// Property Set
id = data.itemId;
damage = data.baseDamage * Character.Damage;
count = data.baseCount + Character.Count;
for (int index = 0; index < GameManager.instance.pool.prefabs.Length; index++) {
if (data.projectile == GameManager.instance.pool.prefabs[index]) { //프리펩이 같은건지 확인
prefabId = index;
break;
}
}
switch (id) {
case 0:
speed = 150 * Character.WeaponSpeed;
Batch();
break;
default:
speed = 0.5f * Character.WeaponRate;
break;
}
// Hand Set
Hand hand = player.hands[(int)data.itemType];
hand.spriter.sprite = data.hand;
hand.gameObject.SetActive(true);
//플레이어가 가지고 있는 모든 Gear에 대해 ApplyGear를 실행하게 함. 나중에 추가된 무기에도 영향을 주기 위함.
player.BroadcastMessage("ApplyGear", SendMessageOptions.DontRequireReceiver);
}
void Batch() { //생성된 무기를 배치하는 함수
for (int index = 0; index < count; index++) {
Transform bullet;
if (index < transform.childCount) {
bullet = transform.GetChild(index); //기존 오브젝트가 있으면 먼저 활용
}
else {
bullet = GameManager.instance.pool.Get(prefabId).transform; //모자라면 풀링에서 가져옴
bullet.parent = transform;
}
bullet.localPosition = Vector3.zero; //무기 위치 초기화
bullet.localRotation = Quaternion.identity; //무기 회전값 초기화
Vector3 rotVec = Vector3.forward * 360 * index / count; //개수에 따라 360도 나누기
bullet.Rotate(rotVec);
bullet.Translate(bullet.up * 1.5f, Space.World); //무기 위쪽으로 이동
bullet.GetComponent<Bullet>().Init(damage, -100, Vector3.zero); //-100 is Infinity Per. 무한 관통.
}
}
void Fire() {
if (!player.scanner.nearestTarget)
return;
Vector3 targetPos = player.scanner.nearestTarget.position;
Vector3 dir = targetPos - transform.position;
dir = dir.normalized;
Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
bullet.position = transform.position;
bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir); //FromToRotation(지정된 축을 중심으로 목표를 향해 회전하는 함수
bullet.GetComponent<Bullet>().Init(damage, count, dir);
AudioManager.instance.PlaySfx(AudioManager.Sfx.Range);
}
}
//Bullet Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour {
public float damage; //피해량 변수
public int per; //관통 변수
Rigidbody2D rigid;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
}
public void Init(float damage, int per, Vector3 dir) {
this.damage = damage;
this.per = per;
if (per >= 0) {
rigid.velocity = dir * 15f;
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (!collision.CompareTag("Enemy") || per == -100)
return;
per--;
if (per < 0) {
rigid.velocity = Vector2.zero;
gameObject.SetActive(false);
}
}
}
- Weapon 스크립트에서 근거리 무기 관통력은 -1로 설정했는데, Bullet 스크립트에서 몬스터에게 닿으면 관통력을 1씩 깎아 0으로 만들어 파괴하는 방식으로 구현한 것이 가끔 -1이 되어 계속 남아있는 경우가 있다고 한다.
- 그래서 근접 무기의 관통력을 -1에서 -100으로 변경하고 관련 스크립트를 수정.

4. 투사제 삭제 추가
//Bullet Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour {
public float damage; //피해량 변수
public int per; //관통 변수
Rigidbody2D rigid;
void Awake() {
rigid = GetComponent<Rigidbody2D>();
}
public void Init(float damage, int per, Vector3 dir) {
this.damage = damage;
this.per = per;
if (per >= 0) {
rigid.velocity = dir * 15f;
}
}
void OnTriggerEnter2D(Collider2D collision) {
if (!collision.CompareTag("Enemy") || per == -100)
return;
per--;
if (per < 0) {
rigid.velocity = Vector2.zero;
gameObject.SetActive(false);
}
}
void OnTriggerExit2D(Collider2D collision) {
if (!collision.CompareTag("Area") || per == -100)
return;
gameObject.SetActive(false);
}
}
- 범위를 벗어난 투사체를 비활성화하는 함수 추가
5. 레벨 디자인
// Spawner Script
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Spawner : MonoBehaviour {
public Transform[] spawnPoint;
public SpawnData[] spawnData;
public float levelTime;
int level; //소환 레벨
float timer;
void Awake() {
spawnPoint = GetComponentsInChildren<Transform>();
levelTime = GameManager.instance.maxGameTime / spawnData.Length; //최대 시간에 몬스터 데이터 크기로 나눠 자동으로 구간 시간 계산
}
void Update() {
if (!GameManager.instance.isLive)
return;
timer += Time.deltaTime;
//FloorToInt : 소수점 아래는 버리고 Int형으로 바꾸는 함수. CeilToInt : 소수점 아래를 올리고 Int형으로 바꾸는 함수.
level = Mathf.Min(Mathf.FloorToInt(GameManager.instance.gameTime / levelTime), spawnData.Length - 1);
if (timer > spawnData[level].spawnTime) {
timer = 0;
Spawn();
}
}
void Spawn() {
GameObject enemy = GameManager.instance.pool.Get(0); //프리펩이 하나가 되었으므로 0으로 변경
enemy.transform.position = spawnPoint[Random.Range(1, spawnPoint.Length)].position; // Random Range가 1부터 시작하는 이유는 spawnPoint 초기화 함수 GetComponentsInChildren에 자기 자신(Spawner)도 포함되기 때문에.
enemy.GetComponent<Enemy>().Init(spawnData[level]);
}
}
//직렬화(Serialization) : 개체를 저장/전송하기 위해 변환
[System.Serializable]
public class SpawnData {
public float spawnTime; //몬스터 소환 시간
public int spriteType; //몬스터 스프라이트 타입
public int health; //몬스터 체력
public float speed; //몬스터 스피드
}
- 단순 10으로 나눴던 레벨을 최대 시간에 몬스터 데이터 크기로 나눠 자동으로 구간 시간을 계산하도록 변경


'유니티 프로젝트 > 뱀서라이크' 카테고리의 다른 글
[Unity/유니티] 기초-뱀서라이크: 모바일 빌드하기[17] (0) | 2023.09.02 |
---|---|
[Unity/유니티] 기초-뱀서라이크: 편리한 오디오 시스템 구축[15] (0) | 2023.07.28 |
[Unity/유니티] 기초-뱀서라이크: 캐릭터 해금 시스템[14+] (0) | 2023.07.25 |
[Unity/유니티] 기초-뱀서라이크: 플레이 캐릭터 선택[14] (0) | 2023.07.21 |
[Unity/유니티] 기초-뱀서라이크: 게임 시작과 종료[13] (0) | 2023.07.18 |