item 스크립트에 퍼블릭으로 string으로 선언하고 각 아이템의 이름을 직접 적어줄 것이다.
물체가 잘 내려오는지 확인
이제 애니메이션을 넣어줄것이다.
다른 오브젝트들도 똑같이 애니메이션을 만든다.
테스트
아이템 태그 부착
플레이어가 아이템을 먹어야하기에 onTriggerEnter2D를 사용할 것이다.
그런데 파워는 이미 선언되어 있고
파워 아이템을 먹으면 공격력이 증가되어야 하기에 변수를 따로 하나 더 선언
파워가 맥스 파워보다 증가되면 안되므로 조건을 걸어주고
맥스파워가 되면 보통 슈팅게임처럼 점수만 올려줄 것이다.
이제 Boom을 만들 것이다.
붐스킬 이미지를 가져오면 다른 오브젝트를 덮어버리는데
이는 order in layer로 층을 바꾸면 된다
현재 모두 0 이고 배경은 -1 이므로
배경은 -2로 붐스킬은 -1로 변경
그 후 붐스킬도 똑같이 애니메이션을 만들어 준다.
나중에 아이템 먹을 시 활성화 할것이므로 현재는 비활성화
그럼 플레이에게 따로 값을 주면된다.
플레이에게 퍼블릭으로 인스턴스를 만들어주고 어싸인
Boom을 먹으면 스킬을 활성화 하고 평상시에는 4초후 이펙트가 꺼지게 설정
적들은 배열로 관리하여 배열에서 찾아서 제거해준다.
퍼블릭으로 변경
플레이어의 파워와 맥스파워 설정
보통 슈팅게임에서 아이템을 먹으면 저장을 하고
원하는 시기에 폭탄을 쏠 수 있도록 조정해야 한다.
그러므로 폭탄 개수를 저장할 변수 선언
폭탄도 파워와 같이 한계를 정할 것이므로 maxBoom까지 두개를 만들 것이다.
이제 Boom메소드를 만들것인데
아까 구현해던
이것을 다 긁어서 붐 메소드 안에 넣어줄 것이다.
붐 메서드에 기능은 이제 따로 빼두었는데
붐도 파워처럼 먹었을때에 대한 설정을 다시 지정해준다.
초기화
붐 아이콘도 라이프 아이콘을 복사해서 앵커의 위치를 바꾸어 준다.
게임 매니저에서 라이프 아이콘을 관리했던 것 처럼 복사해서
붐으로 만든 메소드에 집어 넣는다.
플레이어에서 게임매니저를 가져오고 업데이트하기 위해 메소드를 넣어준다.
알베도의 값을 0으로 줄인다.
만들어진 아이템 프리팹화
에너미가 사라진 곳에 아이템이 생성될 것이다.
어싸인
플레이 해보면 하나에서 아이템 2개가 나오는 오류?가 생긴다.
따라서 예외처리해야하는데
코드를 잘 살펴보면 디스트로이 되기 전에 온히트가 2번이라는 것을 알 수 있다.
이 코드만 추가해주면 예외처리가 된다.
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.Net.Http.Headers;
using UnityEngine;
public class Player : MonoBehaviour
{
public bool isTouchTop;
public bool isTouchBottom;
public bool isTouchRight;
public bool isTouchLeft;
public int life;
public int score;
public int speed;
public int power;
public int boom;
public int maxBoom;
public float maxPower;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
public GameObject boomSkill;
public GameManager manager;
public bool isHit;
public bool isBoomTime;
private Animator anim;
private void Awake()
{
anim = GetComponent<Animator>();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
h = 0;
float v = Input.GetAxisRaw("Vertical");
if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
v = 0;
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
if (Input.GetButtonDown("Horizontal") ||
Input.GetButtonDown("Vertical"))
{
anim.SetInteger("Input", (int)h);
}
}
private void Update()
{
Move();
Fire();
Boom();
Reload();
}
void Fire()
{
if (!Input.GetButton("Fire1"))
return;
if (curShorDelay < maxShotDelay)
return;
switch (power)
{
case 1://Power One
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 2:
GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 3:
GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
}
curShorDelay = 0;
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
void Boom()
{
if (!Input.GetKeyDown(KeyCode.Space))
return;
if (boom == 0)
return;
boom--;
isBoomTime = true;
manager.UpdateBoomIcon(boom);
if (isBoomTime)
{
boomSkill.SetActive(true);
Invoke("OffBoomSkill", 4f);
GameObject[] enemies = GameObject.FindGameObjectsWithTag("Enemy");
for (int index = 0; index < enemies.Length; index++)
{
Enemy enemyLogic = enemies[index].GetComponent<Enemy>();
enemyLogic.OnHit(1000);
}
GameObject[] bullets = GameObject.FindGameObjectsWithTag("EnemyBullet");
for (int index = 0; index < bullets.Length; index++)
{
Destroy(bullets[index]);
}
}
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Border")
{
switch(collision.gameObject.name)
{
case "Top":
isTouchTop = true;
break;
case "Bottom":
isTouchBottom = true;
break;
case "Right":
isTouchRight = true;
break;
case "Left":
isTouchLeft = true;
break;
}
}
else if(collision.gameObject.tag == "Enemy" || collision.gameObject.tag == "EnemyBullet")
{
if (isHit)
{
return;
}
isHit = true;
life--;
manager.UpdateLifeIcon(life);
if(life == 0)
{
manager.GameOver();
}
else
{
manager.RespawnPlayer();
}
gameObject.SetActive(false);
}
else if(collision.gameObject.tag == "Item")
{
Item item = collision.gameObject.GetComponent<Item>();
switch(item.type)
{
case "Coin":
score += 1000;
break;
case "Power":
if(power == maxPower)
{
score += 500;
}
else
{
power++;
}
break;
case "Boom":
if (boom == maxBoom)
{
score += 500;
}
else
boom++;
manager.UpdateBoomIcon(boom);
break;
}
Destroy(collision.gameObject);
}
}
void OffBoomSkill()
{
boomSkill.SetActive(false);
isBoomTime = false;
}
private void OnTriggerExit2D(Collider2D collision)
{
switch (collision.gameObject.name)
{
case "Top":
isTouchTop = false;
break;
case "Bottom":
isTouchBottom = false;
break;
case "Right":
isTouchRight = false;
break;
case "Left":
isTouchLeft = false;
break;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int dmg;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "BorderBullet")
Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
}
}
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public string enemyName;
public int enemyScore;
public float speed;
public int health;
public Sprite[] sprites;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
public GameObject itemCoin;
public GameObject itemPower;
public GameObject itemBoom;
public GameObject player;
SpriteRenderer spriteRenderer;
Rigidbody2D rigid;
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
rigid = GetComponent<Rigidbody2D>();
rigid.velocity = Vector2.down * speed;
}
private void Update()
{
Fire();
Reload();
}
void Fire()
{
if (curShorDelay < maxShotDelay)
return;
if (enemyName == "S")
{
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
Vector3 dirVec = player.transform.position - transform.position;
rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
}
else if (enemyName == "L")
{
GameObject bulletR = Instantiate(bulletObjB, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjB, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
Vector3 dirVecR = player.transform.position - (transform.position + Vector3.right * 0.3f);
Vector3 dirVecL = player.transform.position - (transform.position + Vector3.left * 0.3f);
rigidR.AddForce(dirVecR.normalized * 4, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
rigidL.AddForce(dirVecL.normalized * 4, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
}
curShorDelay = 0; // 발사 후 딜레이 초기화는 발사 이후에 처리되도록 이동
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
public void OnHit(int dmg)
{
if (health <= 0)
return;
health -= dmg;
spriteRenderer.sprite = sprites[1]; //피격시 1번
Invoke("ReturnSprite", 0.1f);
if (health <= 0)
{
Player playerLogic = player.GetComponent<Player>();
playerLogic.score += enemyScore;
//#.Random.Ratio Item Drop
int ran = Random.Range(0, 10);
if (ran < 3)
{
Debug.Log("No Item");
}
else if (ran < 6) { //Coin 30%
Instantiate(itemCoin, transform.position, itemCoin.transform.rotation);
}else if (ran < 8) { //Power 20%
Instantiate(itemPower, transform.position, itemPower.transform.rotation);
}else if(ran < 10) { //Boom 20%
Instantiate(itemBoom, transform.position, itemBoom.transform.rotation);
}
Destroy(gameObject);
}
}
void ReturnSprite()
{
spriteRenderer.sprite = sprites[0];
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "BorderBullet")
{
Destroy(gameObject );
}
else if(collision.gameObject.tag == "PlayerBullet")
{
Bullet bullet = collision.gameObject.GetComponent<Bullet>();
OnHit(bullet.dmg);
Destroy(collision.gameObject);
}
}
}
using JetBrains.Annotations;
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public GameObject[] enemyObjs;
public Transform[] spawnPoints;
public float maxSpawnDelay;
public float curSpawnDelay;
public GameObject player;
public Text scoreText;
public Image[] lifeImage;
public Image[] boomImage;
public GameObject gameOverSet;
private void Update()
{
curSpawnDelay += Time.deltaTime;
if (curSpawnDelay > maxSpawnDelay)
{
SpawnEnemy();
maxSpawnDelay = Random.Range(0.5f, 3f);
curSpawnDelay = 0;
}
//UI Score
Player playerLogic = player.GetComponent<Player>();
scoreText.text = string.Format("{0:n0}", playerLogic.score);
}
void SpawnEnemy()
{
int ranEnemy = Random.Range(0, 3); //소환될 적
int ranPoint = Random.Range(0, 9); //소환될 위치
GameObject enemy = Instantiate(enemyObjs[ranEnemy],
spawnPoints[ranPoint].position,
spawnPoints[ranPoint].rotation);
Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
//스피드도 가지고 와야한다.
Enemy enemyLogic = enemy.GetComponent<Enemy>();
//적비행기 생성 직후 플레이에 변수를 넘겨준다.
enemyLogic.player = player;
if (ranPoint == 5 || ranPoint == 6)
{
enemy.transform.Rotate(Vector3.back * 90); //z축으로 90도
rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
}else if(ranPoint == 7 || ranPoint == 8)
{
enemy.transform.Rotate(Vector3.forward * 90); //z축으로 90도
rigid.velocity = new Vector2(enemyLogic.speed, -1);
}
else
{
rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
}
}
public void UpdateLifeIcon(int life)
{
//UI Init Disable
for (int index = 0; index < 3; index++)
{
lifeImage[index].color = new Color(1, 1, 1, 0);
}
//UI Life Active
for (int index = 0; index < life; index++)
{
lifeImage[index].color = new Color(1, 1, 1, 1);
}
}
public void UpdateBoomIcon(int boom)
{
//UI Init Disable
for (int index = 0; index < 3; index++)
{
boomImage[index].color = new Color(1, 1, 1, 0);
}
//UI Life Active
for (int index = 0; index < boom; index++)
{
boomImage[index].color = new Color(1, 1, 1, 1);
}
}
public void RespawnPlayer()
{
Invoke("RespawnPlayerExe", 2f);
}
private void RespawnPlayerExe()
{
player.transform.position = Vector3.down * 3.5f;
player.SetActive(true);
Player playerLogic = player.GetComponent<Player>();
playerLogic.isHit = false;
}
public void GameOver()
{
gameOverSet.SetActive(true);
}
public void GameRetry()
{
SceneManager.LoadScene(0);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Item : MonoBehaviour
{
public string type;
private Rigidbody2D rigid;
void Start()
{
rigid = GetComponent<Rigidbody2D>();
rigid.velocity = Vector3.down * 3f; //아이템이 떨어지는 속도
}
void Update()
{
}
}
1, 2, 3, 4포인트는 x축은 변함이 없을 것이고 y축은 -1로 고정하여 아래로 내려오게
5, 6 포인트는 왼쪽(-1), y축은 -1
7, 8 포인트는 오른쪽(1), y축은 -1
생성시 보더불릿에 닿으면 사라져서 생성되지 않으니 위치 재조정
그림과 같이 옆모양으로 나오게 하기 위해 회전을 추가
이제 적의 총알을 만들 것이다.
기존의 불렛b를 복사한후 프리팹모드에 들어가서 이름을 바꾸고 저장
스프라이트 변경후 박스콜라이더 크기 재설정
하나더 만들어준 후 태그 달기
플레이어에 있는 코드를 가져와서 부착
네임은 오브젝트네임 자체에 들어가 있으므로 이름 변경
적이 생성될때 플레이어를 알아야한다.
적 비행기 생성 직후 플레이어 변수를 넘겨준다.
목표물 방향 = 목표물위치 - 자신의 위치
큰 적은 두발을 쏜다.
플레이어는 인스턴스화가 되어있지 않기에 할당X
if문에 총알을 발사할 이름을 넣었기 때문에 이름을 할당
적들의 총알이 너무 빠르므로 속도를 조절
플레이어의 온트리거에 추가
게임매니저가 플레이러를 리스폰 시킬수 있다.
플레이어도 게임매니저를 알아야한다.
public GameManager manager;
2초 후에 플레이어가 부활하게 할 것이다.
플레이어 스크립트에 리스폰 추가
using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;
public class Player : MonoBehaviour
{
public bool isTouchTop;
public bool isTouchBottom;
public bool isTouchRight;
public bool isTouchLeft;
private Animator anim;
public float speed;
public float power;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
public GameManager manager;
private void Awake()
{
anim = GetComponent<Animator>();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
h = 0;
float v = Input.GetAxisRaw("Vertical");
if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
v = 0;
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
if (Input.GetButtonDown("Horizontal") ||
Input.GetButtonDown("Vertical"))
{
anim.SetInteger("Input", (int)h);
}
}
private void Update()
{
Move();
Fire();
Reload();
}
void Fire()
{
if (!Input.GetButton("Fire1"))
return;
if (curShorDelay < maxShotDelay)
return;
switch (power)
{
case 1://Power One
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 2:
GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 3:
GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
}
curShorDelay = 0;
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Border")
{
switch(collision.gameObject.name)
{
case "Top":
isTouchTop = true;
break;
case "Bottom":
isTouchBottom = true;
break;
case "Right":
isTouchRight = true;
break;
case "Left":
isTouchLeft = true;
break;
}
}
else if(collision.gameObject.tag == "Enemy" || collision.gameObject.tag == "EnemyBullet")
{
manager.RespawnPlayer();
gameObject.SetActive(false);
}
}
private void OnTriggerExit2D(Collider2D collision)
{
switch (collision.gameObject.name)
{
case "Top":
isTouchTop = false;
break;
case "Bottom":
isTouchBottom = false;
break;
case "Right":
isTouchRight = false;
break;
case "Left":
isTouchLeft = false;
break;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int dmg;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "BorderBullet")
Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
}
}
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public string enemyName;
public float speed;
public int health;
public Sprite[] sprites;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
public GameObject player;
SpriteRenderer spriteRenderer;
Rigidbody2D rigid;
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
rigid = GetComponent<Rigidbody2D>();
rigid.velocity = Vector2.down * speed;
}
private void Update()
{
Fire();
Reload();
}
void Fire()
{
if (curShorDelay < maxShotDelay)
return;
if (enemyName == "S")
{
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
Vector3 dirVec = player.transform.position - transform.position;
rigid.AddForce(dirVec.normalized * 3, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
}
else if (enemyName == "L")
{
GameObject bulletR = Instantiate(bulletObjB, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjB, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
Vector3 dirVecR = player.transform.position - (transform.position + Vector3.right * 0.3f);
Vector3 dirVecL = player.transform.position - (transform.position + Vector3.left * 0.3f);
rigidR.AddForce(dirVecR.normalized * 4, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
rigidL.AddForce(dirVecL.normalized * 4, ForceMode2D.Impulse); // 방향 벡터를 정규화하여 힘을 가하는 부분 수정
}
curShorDelay = 0; // 발사 후 딜레이 초기화는 발사 이후에 처리되도록 이동
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
private void OnHit(int dmg)
{
health -= dmg;
spriteRenderer.sprite = sprites[1]; //피격시 1번
Invoke("ReturnSprite", 0.1f);
if(health <= 0)
{
Destroy(gameObject);
}
}
void ReturnSprite()
{
spriteRenderer.sprite = sprites[0];
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "BorderBullet")
{
Destroy(gameObject );
}
else if(collision.gameObject.tag == "PlayerBullet")
{
Bullet bullet = collision.gameObject.GetComponent<Bullet>();
OnHit(bullet.dmg);
Destroy(collision.gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject[] enemyObjs;
public Transform[] spawnPoints;
public float maxSpawnDelay;
public float curSpawnDelay;
public GameObject player;
private void Update()
{
curSpawnDelay += Time.deltaTime;
if(curSpawnDelay > maxSpawnDelay)
{
SpawnEnemy();
maxSpawnDelay = Random.Range(0.5f, 3f);
curSpawnDelay = 0;
}
}
void SpawnEnemy()
{
int ranEnemy = Random.Range(0, 3); //소환될 적
int ranPoint = Random.Range(0, 9); //소환될 위치
GameObject enemy = Instantiate(enemyObjs[ranEnemy],
spawnPoints[ranPoint].position,
spawnPoints[ranPoint].rotation);
Rigidbody2D rigid = enemy.GetComponent<Rigidbody2D>();
//스피드도 가지고 와야한다.
Enemy enemyLogic = enemy.GetComponent<Enemy>();
//적비행기 생성 직후 플레이에 변수를 넘겨준다.
enemyLogic.player = player;
if (ranPoint == 5 || ranPoint == 6)
{
enemy.transform.Rotate(Vector3.back * 90); //z축으로 90도
rigid.velocity = new Vector2(enemyLogic.speed * (-1), -1);
}else if(ranPoint == 7 || ranPoint == 8)
{
enemy.transform.Rotate(Vector3.forward * 90); //z축으로 90도
rigid.velocity = new Vector2(enemyLogic.speed, -1);
}
else
{
rigid.velocity = new Vector2(0, enemyLogic.speed * (-1));
}
}
public void RespawnPlayer()
{
Invoke("RespawnPlayerExe", 2f);
}
private void RespawnPlayerExe()
{
player.transform.position = Vector3.down * 3.5f;
player.SetActive(true);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject[] enemyObjs;
public Transform[] spawnPoints;
public float maxSpawnDelay;
public float curSpawnDelay;
private void Update()
{
curSpawnDelay += Time.deltaTime;
if(curSpawnDelay > maxSpawnDelay)
{
SpawnEnemy();
maxSpawnDelay = Random.Range(0.5f, 3f);
curSpawnDelay = 0;
}
}
void SpawnEnemy()
{
int ranEnemy = Random.Range(0, 3); //소환될 적
int ranPoint = Random.Range(0, 5); //소환될 위치
Instantiate(enemyObjs[ranEnemy],
spawnPoints[ranPoint].position,
spawnPoints[ranPoint].rotation);
}
}
적들의 비행선이 보더에 걸리면사라지므로 씬보다 위 보더보다 아래에 에너미포인트 그룹을 위치시킨다.
각각의 포인트의 x좌표를 -1.8부터 1.8까지 위치
각각을 어싸인
using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;
public class Player : MonoBehaviour
{
public bool isTouchTop;
public bool isTouchBottom;
public bool isTouchRight;
public bool isTouchLeft;
private Animator anim;
public float speed;
public float power;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
private void Awake()
{
anim = GetComponent<Animator>();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
h = 0;
float v = Input.GetAxisRaw("Vertical");
if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
v = 0;
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
if (Input.GetButtonDown("Horizontal") ||
Input.GetButtonDown("Vertical"))
{
anim.SetInteger("Input", (int)h);
}
}
private void Update()
{
Move();
Fire();
Reload();
}
void Fire()
{
if (!Input.GetButton("Fire1"))
return;
if (curShorDelay < maxShotDelay)
return;
switch (power)
{
case 1://Power One
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 2:
GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 3:
GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
}
curShorDelay = 0;
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Border")
{
switch(collision.gameObject.name)
{
case "Top":
isTouchTop = true;
break;
case "Bottom":
isTouchBottom = true;
break;
case "Right":
isTouchRight = true;
break;
case "Left":
isTouchLeft = true;
break;
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
switch (collision.gameObject.name)
{
case "Top":
isTouchTop = false;
break;
case "Bottom":
isTouchBottom = false;
break;
case "Right":
isTouchRight = false;
break;
case "Left":
isTouchLeft = false;
break;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public int dmg;
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "BorderBullet")
Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
}
}
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel.Design;
using UnityEngine;
public class Enemy : MonoBehaviour
{
public float speed;
public int health;
public Sprite[] sprites;
SpriteRenderer spriteRenderer;
Rigidbody2D rigid;
private void Awake()
{
spriteRenderer = GetComponent<SpriteRenderer>();
rigid = GetComponent<Rigidbody2D>();
rigid.velocity = Vector2.down * speed;
}
private void OnHit(int dmg)
{
health -= dmg;
spriteRenderer.sprite = sprites[1]; //피격시 1번
Invoke("ReturnSprite", 0.1f);
if(health <= 0)
{
Destroy(gameObject);
}
}
void ReturnSprite()
{
spriteRenderer.sprite = sprites[0];
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "BorderBullet")
{
Destroy(gameObject );
}
else if(collision.gameObject.tag == "PlayerBullet")
{
Bullet bullet = collision.gameObject.GetComponent<Bullet>();
OnHit(bullet.dmg);
Destroy(collision.gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public GameObject[] enemyObjs;
public Transform[] spawnPoints;
public float maxSpawnDelay;
public float curSpawnDelay;
private void Update()
{
curSpawnDelay += Time.deltaTime;
if(curSpawnDelay > maxSpawnDelay)
{
SpawnEnemy();
maxSpawnDelay = Random.Range(0.5f, 3f);
curSpawnDelay = 0;
}
}
void SpawnEnemy()
{
int ranEnemy = Random.Range(0, 3); //소환될 적
int ranPoint = Random.Range(0, 5); //소환될 위치
Instantiate(enemyObjs[ranEnemy],
spawnPoints[ranPoint].position,
spawnPoints[ranPoint].rotation);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "BorderBullet")
Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
}
}
기존에 만들어 놓았던 Border를 복사해서 이름을 바꾸고
경계선을 재설정
태그를 새로 만든 후 할당
그럼 총알이 경계선에 닿으면 사라진다.
이제 총알을 만들 로직을 Player 스크립트에 작성할 것이다.
전 페이지에서 만들었던 업데이트문안에 있는 함수를 따로 캡슐화로 분리한 후 이름을 Move로 바꾸었고,
using System.Collections;
using System.Collections.Generic;
using System.Net.Http.Headers;
using UnityEngine;
public class Player : MonoBehaviour
{
public bool isTouchTop;
public bool isTouchBottom;
public bool isTouchRight;
public bool isTouchLeft;
private Animator anim;
public float speed;
public float power;
public float maxShotDelay; //실제 딜레이
public float curShorDelay; //한발 쏜 후 딜레이
public GameObject bulletObjA;
public GameObject bulletObjB;
private void Awake()
{
anim = GetComponent<Animator>();
}
void Move()
{
float h = Input.GetAxisRaw("Horizontal");
if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
h = 0;
float v = Input.GetAxisRaw("Vertical");
if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
v = 0;
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
if (Input.GetButtonDown("Horizontal") ||
Input.GetButtonDown("Vertical"))
{
anim.SetInteger("Input", (int)h);
}
}
private void Update()
{
Move();
Fire();
Reload();
}
void Fire()
{
if (!Input.GetButton("Fire1"))
return;
if (curShorDelay < maxShotDelay)
return;
switch (power)
{
case 1://Power One
GameObject bullet = Instantiate(bulletObjA, transform.position, transform.rotation);
Rigidbody2D rigid = bullet.GetComponent<Rigidbody2D>();
rigid.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 2:
GameObject bulletR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.1f, transform.rotation);
GameObject bulletL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.1f, transform.rotation);
Rigidbody2D rigidR = bulletR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidL = bulletL.GetComponent<Rigidbody2D>();
rigidR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
case 3:
GameObject bulletRR = Instantiate(bulletObjA, transform.position + Vector3.right * 0.3f, transform.rotation);
GameObject bulletCC = Instantiate(bulletObjB, transform.position, transform.rotation);
GameObject bulletLL = Instantiate(bulletObjA, transform.position + Vector3.left * 0.3f, transform.rotation);
Rigidbody2D rigidRR = bulletRR.GetComponent<Rigidbody2D>();
Rigidbody2D rigidCC = bulletCC.GetComponent<Rigidbody2D>();
Rigidbody2D rigidLL = bulletLL.GetComponent<Rigidbody2D>();
rigidRR.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidCC.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
rigidLL.AddForce(Vector2.up * 10, ForceMode2D.Impulse);
break;
}
curShorDelay = 0;
}
void Reload()
{
curShorDelay += Time.deltaTime;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Border")
{
switch(collision.gameObject.name)
{
case "Top":
isTouchTop = true;
break;
case "Bottom":
isTouchBottom = true;
break;
case "Right":
isTouchRight = true;
break;
case "Left":
isTouchLeft = true;
break;
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
switch (collision.gameObject.name)
{
case "Top":
isTouchTop = false;
break;
case "Bottom":
isTouchBottom = false;
break;
case "Right":
isTouchRight = false;
break;
case "Left":
isTouchLeft = false;
break;
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
private void OnTriggerEnter2D(Collider2D collision)
{
if (collision.gameObject.tag == "BorderBullet")
Destroy(gameObject); //모노비헤이어에 들어 있는 자체 함수 => 매개변수 오브젝트를 삭제하는 함수
}
}
그 후 Sprite Editor를 누르면 여러개의 이미지들이 묶여있는 것을 분할 할 수 있는데
크기로 24*24 여백을 1로 잘랐다.
우선 화면의 비율을 선택해주자
필자는 aspect로 하였고 비율을 입력하여 2가지 해상도를 만들었다.
비행기가 움직일 수 있게 코드 작성
transform은 time.deltatime이랑 세트로 생각하자!
현재의 위치에 키보드로 받을 위치를 더해준다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed;
void Update()
{
float h = Input.GetAxisRaw("Horizontal");
float v = Input.GetAxisRaw("Vertical");
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
}
}
움직여보면 원하지 않는 배경 밖으로 나가게 된다.
비행기에 Box Collider를 추가하여 부착해주면 초록색 경계가 생긴다.
피격 범위로도 사용할 것이므로 콜라이더의 크기를 줄여주었다.
맵의 바깥 부분에 Border라는 빈 오브젝트안에
방향 별로 box collider2D를 붙인 후 범위를 잡아주었다.
Player :
물리 연산이기에 모두 리지드바디2d적용
물리엔진을 많이 사용하지 않기에 키네틱을 사용
키넥틱이란? 다른 것이랑 충돌이 일어났을 때 영향을 받지 않겠다.
보더들은 고정이기때문에 static 사용
경계들을 각각 인지를 해야하기에 변수 선언
물리적인 힘은 넣을 필요 없으므로 collider대신에 온트리거 사용하였고,
각 경계마다 tag로 Border를 달아줄 것이다.
물체가 닿은 상태에서 h는 좌우 이동이므로 => 이동시 닿을 떄를 처리하는데 이때 각각 0으로 만들어서 이동을 멈춘다.
물체가 닿지 않았을 때 이동해야하므로 그 경우도 처리해야 한다.
태그를 달아준다.
현재 까지의 코드
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed;
public bool isTouchTop;
public bool isTouchBottom;
public bool isTouchRight;
public bool isTouchLeft;
void Update()
{
float h = Input.GetAxisRaw("Horizontal");
if ((isTouchRight && h == 1) || (isTouchLeft && h == -1))
h = 0;
float v = Input.GetAxisRaw("Vertical");
if ((isTouchTop && v == 1) || (isTouchBottom && v == -1))
v = 0;
Vector3 curPos = this.transform.position;
Vector3 nextPos = new Vector3(h, v, 0) * speed * Time.deltaTime;
transform.position = curPos + nextPos;
}
private void OnTriggerEnter2D(Collider2D collision)
{
if(collision.gameObject.tag == "Border")
{
switch(collision.gameObject.name)
{
case "Top":
isTouchTop = true;
break;
case "Bottom":
isTouchBottom = true;
break;
case "Right":
isTouchRight = true;
break;
case "Left":
isTouchLeft = true;
break;
}
}
}
private void OnTriggerExit2D(Collider2D collision)
{
switch (collision.gameObject.name)
{
case "Top":
isTouchTop = false;
break;
case "Bottom":
isTouchBottom = false;
break;
case "Right":
isTouchRight = false;
break;
case "Left":
isTouchLeft = false;
break;
}
}
}
이제 애니메이션을 넣어줄 것이다.
잘라놓은 애니메이션 각각 4개를 클릭해서
하이어라이키에 있는 Player에 드래그해서 넣어주고 애니메이션 폴더를 생성후
각각 Center, Left, Right로 이름을 만들어서 저
플레이어에 애니메이션 컴포넌트가 생성됨
그 후 플레이어(애니메이션 컨트롤러)를 눌러서 이 창을 띄어주고
Make Transition으로 서로의 관계를 만들어 준다.
트랜지션은 파라미터로 이동한다.
매개변수 Input.GetAxisRaw() 값 그대로 사
각각의 트랜지션 설정을 해줄것인다.
즉각 반응을 위해 Has Exit Time은 꺼야한다.
Transition Duration은 보통3D에서 모션을 부드럽게 변화시키기 위해 사용하는데
테스트를 해보려는 도중 점프가 되지 않아서 원인을 찾아보니 Simulated를 체크하지 않았다.
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TokoController : MonoBehaviour
{
public AudioClip deathClip;
public float jumpForce = 700f;
private int jumpCount = 0;
private bool isGrounded = false;
private bool isDead = false;
private Rigidbody2D playerRigidbody;
private Animator animator;
private AudioSource playerAudio;
void Start()
{
//게임 오브젝트로부터 사용할 컴포넌트들을 가져와 변수에 할당
playerRigidbody = GetComponent<Rigidbody2D>();
animator = GetComponent<Animator>();
playerAudio = GetComponent<AudioSource>();
}
void Update()
{
if (isDead)
{
//사망시 더 이상 처리를 하지 않고 종료
return;
}
if (Input.GetMouseButtonDown(0) && jumpCount < 2)
{
jumpCount++; //점프가 두번됨
//점프 직전 속도를 순간적으로 제로(0,0)으로 변경
playerRigidbody.velocity = Vector2.zero;
//리지드바디에 위쪽으로 힘 주기
playerRigidbody.AddForce(new Vector2(0, jumpForce));
//오디오 소스 재생
playerAudio.Play();
Debug.Log("마우스가 클릭됨");
}
else if(Input.GetMouseButtonUp(0) && playerRigidbody.velocity.y > 0) // 마우스에서 손을 떼는 순간 속도의 y값이 양수라면(위로 상승) => 현재 속도를 절반으로 변경
{
playerRigidbody.velocity = playerRigidbody.velocity * 0.5f;
}
//애니메이터의 Grounded 파라미터를 isGrounded 값으로 갱신
animator.SetBool("Grounded", isGrounded);
Debug.LogFormat("{0}", playerRigidbody.velocity.y);
}
private void Die()
{
}
private void OnTriggerEnter2D(Collider2D collision)
{
//트리거 콜라이더를 가진 물체와 충돌 감지
}
private void OnCollisionEnter2D(Collision2D collision)
{
//바닥에 닿았음을 감지
//어떤 콜라이더와 닿았으며, 충돌표면이 위쪽을 보고 있으면
if (collision.contacts[0].normal.y > 0.7f)
{
//isGrouded가 true로 변경하고, 누적 점프횟수를 0으로 초기화
isGrounded = true;
jumpCount = 0;
//Debug.Log("충돌 발생");
}
}
private void OnCollisionExit2D(Collision2D collision)
{
//바닥에서 벗어났음을 감지하는 처리
//어떤 콜라이더에서 떼어진 경우
isGrounded = false;
}
}
velocity.y의 값을 출력해 보았는데 음수가 나왔다.
Rigidbody의 velocity.y 값이 음수가 나오는 이유는 중력의 영향 때문이다.
Rigidbody의 velocity는 해당 객체의 현재 속도를 나타내며, 유니티에서는 아래쪽 방향을 음의 방향으로 간주한다..
따라서 캐릭터가 점프 후에 일정 높이에 도달하면 중력에 의해 아래로 가속됩니다. 이 때 velocity.y 값은 음수가 된다.
그리고 바닥에 닿으면 다시 상승하기 시작할 때 velocity.y 값은 양수가 될 것이다.
즉, velocity.y 값이 음수가 되는 것은 캐릭터가 점프 후 중력에 의해 아래로 가속되는 정상적인 동작이다.
private void Die()
{
animator.SetTrigger("Die");
playerAudio.clip = deathClip;
playerAudio.Play();
//속도를 제로로 변경
playerRigidbody.velocity = Vector2.zero;
//사망 상태를 true로 변경
isDead = true;
}
다른 Set계열 메서드와 달리 파라미터에 할당할 새로운 값은 입력하지 않는다.
SetTrigger()메서드는 '방아쇠''를 당길 뿐이다.
트리거 타입의 파라미터는 즉시 true가 되었다가 곧바로 false가 되기 때문에 별도의 값을 지정하지 않는다.
animator.SetTrigger("Die")가 실행되면 곧바로 Die상태러 전환되는 애니메이션 클립이 재생된다.
배경을 설정할 것인데 태그에다 이름을 지정하면 order in layer 처럼
레이어의 순서가 정해진다.
배경을 스크롤링 할것이므로 스크립트 추가
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ScrollingObject : MonoBehaviour
{
public float speed = 10f;
void Update()
{
this.transform.Translate(Vector3.left * speed * Time.deltaTime);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class BackgroudLoop : MonoBehaviour
{
private float width; //배경의 가로 길이
private void Awake()
{
BoxCollider2D col = GetComponent<BoxCollider2D>();
this.width = col.size.x;
Debug.Log(this.width);
}
void Update()
{
if(transform.position.x <= -this.width)
{
this.Reposition();
}
}
void Reposition()
{
//현재 위치에서 오른쪽으로 가로 길이 * 2 만큼 이동
Vector2 offset = new Vector2(this.width * 2f, 0);
transform.position = (Vector2)this.transform.position + offset;
}
}
sky를 복사해서 기존에 있는 스크린에 넣는다면
예를 들어 화면이 sky -> sky(1) -> sky -> sky(1)이런식으로 두배 간격으로 복사된다.
이제 발판을 더 만들 것인데 프리팹으로 만들었고
프리팹을 가져와서 생성된 게임오브젝트에 설정을 바꾸었다.
만들어진 발판 프리팹에 Obstacles를 추가할건데 이것을 추가할 때는
프리팹에서 만드는 것이 좋다.
그후 발판은 fore 방해물은 middle로 설정하였다.
발판 스크립트를 만들었고 방해물 3개가 각각 랜덤으로 나올수 있게 설정
그리고 방해물 회피 성공시 +1
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Platform : MonoBehaviour
{
public GameObject[] obstacles;
private bool stepped = false;
private void OnEnable()
{
stepped = false;
for (int i = 0; i < obstacles.Length; i++)
{
if (Random.Range(0, 3) == 0)
{
obstacles[i].SetActive(true);
}
else
{
obstacles[i].SetActive(false);
}
}
}
private void OnCollisionEnter2D(Collision2D collision)
{
if (collision.collider.CompareTag("Player") && !stepped)
{
stepped = true;
Debug.Log("점수 1점 추가");
}
}
}
그후 아까 했던 플랫폼의 모든 프리팹에 적용 되도록 Apply All
UI를 만들진 않았지만 UI라던지 게임을 관리할 게임 매니저를 만들었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.SceneManagement;
public class GameManager : MonoBehaviour
{
public static GameManager instance;
public bool isGameover = false; //게임오버 상태
public GameObject gameoverUI; //게임 오버 시 활성화 할 UI 게임 오브젝트
private int score = 0; //게임점수
void Awake()
{//싱글턴 변수 instance가 비어있는가?
if (instance == null)
{
//instance가 비어있다면 그곳에 자기 자신을 할당
instance = this;
}
else
{
Destroy(this.gameObject);
}
}
private void Update()
{
if (isGameover && Input.GetMouseButtonDown(0))
{
//게임오버 상태에서 마우스 왼쪽 버튼 클릭하면 현재 씬 재시작
SceneManager.LoadScene(SceneManager.GetActiveScene().name);
//현재 활성화된 씬의 정보를Scene 타입의 오브젝트로 가져오는 메서드 씬의 이름을 변수 name으로 제공
}
}
public void AddScore(int newScore)
{
//게임오버가 아니라면
if (!isGameover)
{
//점수를 증가
score += newScore;
//scoreTect.text = "Score : " + score;
}
}
public void OnPlayerDead()
{
isGameover = true;
// gameoverUI.SetActive(true);
}
}
이제 platform이 랜덤으로 생성될 것이니 하이어라이키에 있는 플랫폼프리팹을 지워준후
많아질 데이터를 관리하기 위해 오브젝트 풀링사용하여
플랫폼 스폰서를 만들어서 복제된 sky(1)에 할당
오브젝트 풀링
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlatformSpawner : MonoBehaviour
{
public GameObject platformPrefab;
public int count = 3;
private GameObject[] platforms;
private float lastSpawnTime = 0;
private float timeBetSpawn = 0;
private int currentIndex = 0;
private void Start()
{
platforms = new GameObject[count];
//미리만들자
for (int i = 0; i < count; i++)
{
platforms[i] = Instantiate(platformPrefab, new Vector3(0, -25, 0), Quaternion.identity);
}
}
private void Update()
{
if (Time.time >= lastSpawnTime + timeBetSpawn)
{
lastSpawnTime = Time.time;
timeBetSpawn = Random.Range(1.25f, 2.25f);
float yPos = Random.Range(-3.5f, 1.5f);
//OnEnable호출을 위해
this.platforms[currentIndex].SetActive(false);
this.platforms[currentIndex].SetActive(true);
//재배치
this.platforms[currentIndex].transform.position = new Vector2(20, yPos);
//인덱스 증가
currentIndex++;
//마지막 순번에 도달 했다면 순번 리셋
if (currentIndex >= count)
{
currentIndex = 0; //0, 1, 2, 0, 1, 2, 0, 1, 2....
}
}
}
}
오브젝트 풀링 사용하는 이유
유니티에서 오브젝트를 생성하기 위해서는 Instantiate를 사용하고 삭제할 때는 Destroy를 사용
하지만 Instantiate, Destroy 이 두 함수는 무게가 상당히 크다.
Instantiate(오브젝트 생성)은 메모리를 새로 할당하고 리소스를 로드하는 등의 초기화 과정이 필요하고, Destroy(오브젝트 파괴)는 파괴 이후에 발생하는 가비지 컬렉팅으로 인한 프레임 드랍이 발생할 수 있다.
쉽게 말해 "재사용" 이라고 볼 수 있다.
메모리를 할당 해두기 때문 메모리를 희생하여 성능을 높이는 것이지만, 실시간 작동하는 게임에서 프레임 때문에 선호된다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public float speed = 1f;
public Rigidbody playerRigidbody;
public GameObject bulletPrefab;
void Update()
{
PlayerMove();
}
private void PlayerMove()
{
float h = Input.GetAxis("Horizontal");
float v = Input.GetAxis("Vertical");
Vector3 movement = new Vector3(h, 0, v) * speed;
transform.Translate(movement * Time.deltaTime);
}
private void OnTriggerEnter(Collider other)
{
// 충돌한 오브젝트가 총알인지 확인
if (other.CompareTag("Bullet"))
{
// 플레이어를 파괴
Destroy(gameObject);
Debug.Log("Game over");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Enemy : MonoBehaviour
{
[SerializeField] GameObject player;
[SerializeField] public GameObject bulletPrefab;
private float spawnInterval = 3f; // 총알 생성 간격
private Vector3 spawnPosition; // 총알이 생성될 위치
void Start()
{
spawnPosition = this.gameObject.transform.position; // 총알이 생성될 위치 초기화
StartCoroutine(SpawnBullet()); // 코루틴 시작
}
IEnumerator SpawnBullet()
{
while (player != null)
{
// 총알 생성
Instantiate(bulletPrefab, spawnPosition, Quaternion.identity);
// 일정한 간격 기다리기
yield return new WaitForSeconds(spawnInterval);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Bullet : MonoBehaviour
{
public float speed = 2f;
public GameObject player;
void Start()
{
player = GameObject.FindGameObjectWithTag("Player");
// Rigidbody 컴포넌트 가져오기
Rigidbody bullets = GetComponent<Rigidbody>();
Vector3 direction = (player.transform.position - transform.position).normalized;
transform.forward = direction;
// 총알의 이동 방향 설정 및 속도 적용
bullets.velocity = transform.forward * speed;
// 5초 후에 총알 파괴
Destroy(gameObject, 4.5f);
}
private void OnTriggerEnter(Collider other)
{
// 충돌한 오브젝트가 총알인지 확인
if (other.CompareTag("Player"))
{
Destroy(gameObject);
}
if (other.CompareTag("Wall"))
{
Destroy(gameObject);
}
}
}
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
using UnityEngine.Assertions.Comparers;
using UnityEngine.SceneManagement;
public class CatController : MonoBehaviour
{
[SerializeField] private Rigidbody2D rbody;
[SerializeField] private float moveForce = 100f;
[SerializeField] private float jumpForce = 680f;
[SerializeField]
private ClimbCloudGameDirector gameDirector;
private Animator anim;
private bool hasSpace = false;
private void Start()
{
//this.gameObject.GetComponent<Animation>();
anim = GetComponent<Animator>();
//this.gameDirector = GameObject.Find("GameDirector").GetComponent<ClimbCloudGameDirector>();
//this.gameDirector = GameObject.FindAnyObjectByType<ClimbCloudGameDirector>();
}
void Update()
{
//스페이스바를 누르면
if (Mathf.Abs(rbody.velocity.y) < 0.01f)
{
if (Input.GetKeyDown(KeyCode.Space))
{
if (!hasSpace)
{
//힘을 가한다
this.rbody.AddForce(this.transform.up * this.jumpForce);
//this.rbody.AddForce(Vector3.up * this.force);
hasSpace = false; // 다시 점프할 수 있도록 허용
}
}
}
// -1, 0, 1 : 방향
int dirX = 0;
//왼쪽화살표키를 누르고 있는 동안에
if (Input.GetKey(KeyCode.LeftArrow))
{
dirX = -1;
}
if (Input.GetKey(KeyCode.RightArrow))
{
dirX = 1;
}
// Debug.Log(dirX); //방향 -1, 0, 1
//스케일 X를 변경 하는데 키가 눌렸을 때만
//키가 눌렸을때만 = (dirX != 0)
if (dirX != 0)
{
this.transform.localScale = new Vector3(dirX, 1, 1);
}
//벡터의 곱
//Debug.Log(this.transform.right * dirX); //벡터3
//도전 ! : 속도를 제한하자
//velocity.x 가 3정도가 넘어가니깐 빨라지는거 같드라구...
if (Mathf.Abs(this.rbody.velocity.x) < 3)
{
this.rbody.AddForce(this.transform.right * dirX * moveForce);
}
this.anim.speed = (Mathf.Abs(this.rbody.velocity.x) / 2f);
this.gameDirector.UpdateVelocityText(this.rbody.velocity);
// Debug.Log(this.transform.position);
float clampX = Mathf.Clamp(this.transform.position.x, -2.39f, 2.35f);
Vector3 pos = this.transform.position;
pos.x = clampX;
this.transform.position = pos;
}
// Trigger 모드 일경우 충돌 판정을 해주는 이벤트 함수
private bool hasEntered = false;
private void OnTriggerEnter2D(Collider2D collision)
{
if (!hasEntered)
{
Debug.LogFormat("OnTriggerEnter2D: {0}", collision);
SceneManager.LoadScene("ClimbCloudClear");
hasEntered = true; // 한 번 이벤트가 발생하면 이 변수를 true로 설정하여 두 번 이상 호출되지 않도록 함
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEditor.SearchService;
using UnityEngine;
using UnityEngine.SceneManagement;
public class ChangeScene : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
SceneManager.LoadScene("ClimbCloud");
Debug.Log("화면이 전환됨");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class ClimbCloudGameDirector : MonoBehaviour
{
[SerializeField] private Text velocityText;
public void UpdateVelocityText(Vector2 velocity)
{
float velocityX = Mathf.Abs(velocity.x);
this.velocityText.text = velocityX.ToString();
}
}