[자동 원거리 공격 구현] --> 주변의 몬스터를 자동으로 타게팅하여 공격하는 무기 구현
- Add Layer --> User Layer 6을 Enemy로 설정
- Prefabs안의 Enemy의 Layer를 Enemy로 설정
- C# 스크립트 생성(Scanner)
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Scanner : MonoBehaviour
{
public float scanRange; // 범위
public LayerMask targetLayer; // 레이어
public RaycastHit2D[] targets; // 스캔 결과 배열
public Transform nearestTarget; // 가장 가까운 목표
void FixedUpdate(){
targets = Physics2D.CircleCastAll(transform.position, scanRange, Vector2.zero, 0, targetLayer); // 원형 범위로 오브젝트를 검색
nearestTarget = GetNearest();
}
Transform GetNearest(){ // 가장 가까운 오브젝트를 찾는 함수
Transform result = null;
float diff = 100;
foreach(RaycastHit2D target in targets){ // 범위 안에 인식 된 타겟 중, 가장 가까운 적을 찾아내는 함수
Vector3 myPos = transform.position;
Vector3 targetPos = target.transform.position;
float curDiff = Vector3.Distance(myPos, targetPos);
if(curDiff < diff) { // 작은 수를 점점 대입해 가며
diff = curDiff;
result = target.transform;
}
}
return result;
}
}
- Scanner 스크립트를 Player 오브젝트에 드래그&드랍
- 근접무기 프리펩 하나를 Scene에 드래그&드랍
- Sprite Renderer에 Bullet 3 sprite로 바꿔주기
- Box Collider 2D를 Remove Component --> Capsule Collider 2D 추가
- Hierachy의 새로운 Bullet 이름 변경 후 Prefabs 폴더에 드래그&드랍
- Capsule Collider 2D에 Is Trigger 체크
- Hierachy 안의 Weapon을 Ctrl + D로 복사
- Id | 1
- Prefab Id | 2
- Damage | 3
- Count | 0
- Speed | 0 - 기존의 Player 스크립트에 코드 추가
ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public Vector2 inputVec;
public float speed;
public Scanner scanner; // 스캐너 변수 추가
Rigidbody2D rigid;
SpriteRenderer spriter;
Animator anim;
void Awake()
{
rigid = GetComponent<Rigidbody2D>();
spriter = GetComponent<SpriteRenderer>();
anim = GetComponent<Animator>();
scanner = GetComponent<Scanner>(); // 변수 초기화
}
void Update()
{
inputVec.x = Input.GetAxisRaw("Horizontal");
inputVec.y = Input.GetAxisRaw("Vertical");
}
private void FixedUpdate()
{
Vector2 nextVec = inputVec.normalized*speed* Time.fixedDeltaTime;
rigid.MovePosition(rigid.position + nextVec);
}
private void LateUpdate()
{
anim.SetFloat("Speed", inputVec.magnitude);
if (inputVec.x != 0)
{
spriter.flipX = inputVec.x < 0;
}
}
}
- 기존의 Weapon 스크립트에 코드 추가
ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
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 = GetComponentInParent<Player>();
}
void Start(){
Init();
}
void Update(){
switch (id){
case 0:
transform.Rotate(Vector3.back * speed * Time.deltaTime);
break;
default:
timer += Time.deltaTime; // 프레임이 소비한 시간
if(timer > speed){
timer = 0f;
Fire();
}
break;
}
// .. Test Code ..
if (Input.GetButtonDown("Jump")){
LevelUp(10, 1);
}
}
public void LevelUp(float damage, int count)
{
this.damage += damage;
this.count += count;
if(id == 0){
Batch();
}
}
public void Init(){
switch(id){
case 0:
speed = 150; // (+) --> 반시계방향
Batch();
break;
default:
speed = 0.3f; // 발사속도 0.3f
break;
}
}
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 * 350 * index / count;
bullet.Rotate(rotVec);
bullet.Translate(bullet.up * 1.5f, Space.World);
bullet.GetComponent<Bullet>().Init(damage, -1); // -1 is Infinity Per.
}
}
void Fire()
{
if (!player.scanner.nearestTarget){ // 지정된 목표가 없으면 호출되지 않음
return;
}
Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
bullet.position = transform.position;
}
}
- Prefabs의 총탄에 Add Component
- RigidBody 2D --> gravity Scale | 0 - 기존의 Bullet 스크립트에 코드 추가
ㅡ아래는 완성된 코드ㅡ
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) // Vector3 방향 추가
{
this.damage = damage;
this.per = per;
if(per > -1){
rigid.velocity = dir;
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if(!collision.CompareTag("Enemy") || per == -1){
return;
}
per--;
if (per == -1){ // 관통력이 -1이 됨
rigid.velocity = Vector2.zero; // 비활성화 이전 물리 속도 초기화
gameObject.SetActive(false); // 스스로 사라짐
}
}
}
- 기존의 Weapon 스크립트에 코드 추가
ㅡ아래는 완성된 코드ㅡ
using System.Collections;
using System.Collections.Generic;
using System.Runtime.ConstrainedExecution;
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 = GetComponentInParent<Player>();
}
void Start(){
Init();
}
void Update(){
switch (id){
case 0:
transform.Rotate(Vector3.back * speed * Time.deltaTime);
break;
default:
timer += Time.deltaTime;
if(timer > speed){
timer = 0f;
Fire();
}
break;
}
// .. Test Code ..
if (Input.GetButtonDown("Jump")){
LevelUp(10, 1);
}
}
public void LevelUp(float damage, int count)
{
this.damage += damage;
this.count += count;
if(id == 0){
Batch();
}
}
public void Init(){
switch(id){
case 0:
speed = 150; // (+) --> 반시계방향
Batch();
break;
default:
speed = 0.3f;
break;
}
}
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 * 350 * index / count;
bullet.Rotate(rotVec);
bullet.Translate(bullet.up * 1.5f, Space.World);
bullet.GetComponent<Bullet>().Init(damage, -1, Vector3.zero); // 인자값 추가
}
}
void Fire()
{
if (!player.scanner.nearestTarget){
return;
}
Vector3 targetPos = player.scanner.nearestTarget.position; // 타겟의 위치
Vector3 dir = targetPos - transform.position; // 타겟의 방향
dir = dir.normalized; // 현재 벡터의 방향은 유지하고 크기를 1로 변환
Transform bullet = GameManager.instance.pool.Get(prefabId).transform;
bullet.position = transform.position;
bullet.rotation = Quaternion.FromToRotation(Vector3.up, dir); // 지정된 축을 중심으로 목표를 향해 회전
bullet.GetComponent<Bullet>().Init(damage, count, dir); // 초기화 함수 호출
}
}
- 기존의 Bullet 스크립트에 코드 추가
ㅡ아래는 완성된 코드ㅡ
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 > -1){
rigid.velocity = dir * 15f; // 속력을 곱해주어 총알이 날아가는 속도 증가
}
}
void OnTriggerEnter2D(Collider2D collision)
{
if(!collision.CompareTag("Enemy") || per == -1){
return;
}
per--;
if (per == -1){
rigid.velocity = Vector2.zero;
gameObject.SetActive(false);
}
}
}
ㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡㅡ
★Slow and Steady★
'Unity 공부 > Unity_2D' 카테고리의 다른 글
| [Day 8] HUD 제작 (0) | 2023.07.14 |
|---|---|
| [Day 7] 타격감 있는 몬스터 처치 구현 (0) | 2023.07.13 |
| [Day 5] 회전하는 근접무기 구현 (0) | 2023.07.11 |
| [Day 4] 소환 레벨 적용 (2) | 2023.07.10 |
| [Day 3] 오브젝트 풀링으로 소환 (0) | 2023.07.09 |