[마블 VR] 반경 안에 있는 적 공격하기 R&D
간단하게, 마우스 클릭하면 overlapshpere를 이용해서 반경 이내에 있는 적을 모두 공격하는걸 구현해보려고 한다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
[SerializeField] private int radius = 10;
void Start()
{
Collider[] colls = Physics.OverlapSphere(this.transform.position, radius, 1 << 6);
foreach(Collider coll in colls)
{
Debug.Log(coll.name);
}
}
}
Radius에 따라서 검출되는 적 log에 찍어보기
=> 결과
radius가 5일때는 Enemy만 검출되었다가 , 10으로 변경하니 둘 다 잘 검출된다.
카메라는 헤드셋, 마우스 클릭은 스윙하는거
1. 카메라부터 마우스 클릭 위치로 ray쏘기(방향) -main
2. 마우스 클릭했는데 ray와 부딪히는게 있으면 그쪽으로 방패가 이동하기 - main, main 에서 방패한테 이동하라고 알려줌
3. 이동해서 방패와 부딪히면 사라지기
4. overlapshere 안에 collider가 남아있으면 그쪽으로 이동하기
남아있지 않으면 처음 위치로 돌아오기
5. 부딪히는게 없어도 그 쪽으로 갔다가 돌아오기
이 순서대로 차근차근 실행해보려고 한다.
1. 카메라부터 마우스 클릭 위치로 ray쏘기(방향) -main
using Meta.WitAi;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
private Vector3 destPos;
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
//Ray ray = new Ray(Camera.main.transform.position, Input.mousePosition);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
}
}
2. 마우스 클릭했는데 ray와 부딪히는게 있으면 그쪽으로 방패가 이동하기 - main, main 에서 방패한테 이동하라고 알려줌
using Meta.WitAi;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
[SerializeField] private GameObject shieldGo;
private Vector3 destPos;
private float moveSpeed = 1f;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
//Ray ray = new Ray(Camera.main.transform.position, Input.mousePosition);
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
}
}
this.shieldGo.transform.position = Vector3.MoveTowards(this.shieldGo.transform.position, destPos, moveSpeed * Time.deltaTime);
}
}
이렇게 하면 시작하자마자 무조건 방패로 이동한다.
=> moveSpeed는 0으로 초기화 하고
if (Physics.Raycast(ray, out hit, 100f))
{
destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
this.moveSpeed = 2f;
}
부딪히면 스피드를 2로 변경한다.
처음엔 가만히 있다가 클릭하면 이동한다. 부딪히는 물체가 없으면 이동하지 않음
+++++++++++++++++코드 수정++++++++++++++++++++
방패 움직이는건 shieldController에서 하도록 수정
using Meta.WitAi;
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
private Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
// Update is called once per frame
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
}
}
shieldController.Move(this.destPos);
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
public float moveSpeed = 0;
private void Start()
{
}
public void Move(Vector3 destPos)
{
this.transform.position = Vector3.MoveTowards(this.transform.position, destPos, moveSpeed * Time.deltaTime);
}
}
똑같이 잘 동작한다.
3. 이동해서 방패와 부딪히면 사라지기
방패에 rigidbody 랑 sphere collider를 추가하고
enemy, enemy(1)에 box collider를 추가한다( 기본으로 붙어있음)
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
}
ShieldController 스크립트에 onCollisionEnter추가한다.
결과
4. overlapshere 안에 collider가 남아있으면 그쪽으로 이동하기 ,남아있지 않으면 처음 위치로 돌아오기
굳이 overlapshere 로 하지 않아도 될거같다는 생각이 듬 ..
그냥 현재 씬 안에 enemy가 있으면 .. 이런식으로 하면 될거같음
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
private Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
[SerializeField]
private GameObject shieldGo;
private GameObject enemyGo;
private void Start()
{
}
void Update()
{
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
}
}
var dis = Vector3.Distance(this.destPos, this.shieldGo.transform.position);
if(dis<=0.2f)
{
Debug.Log("도착했습니다.");
Debug.LogFormat("<color=red>destPos{0}</color>", destPos);
Debug.LogFormat("<color=lime>Dis:{0}</color>", dis);
}
shieldController.Move(this.destPos);
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
public float moveSpeed = 0f;
private void Start()
{
}
public void Move(Vector3 destPos)
{
this.transform.position = Vector3.MoveTowards(this.transform.position, destPos, moveSpeed * Time.deltaTime);
}
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
}
}
일단 처음엔 클릭해서 ray랑 닿는 위치로 이동을 하고 -> 그 다음에는 거리를 측정해서 다음 적으로 이동하게 하고싶다.
그래서 ray랑 닿는 위치에 이동 후 도착하면 log에 도착했습니다를 출력하도록 하려고 했으나
실행하자마자 계속 도착했습니다를 출력한다, 그래서 destpos와 distance를 log에 출력해보니 처음에 destpos가 0,0,0으로 위치가 초기화 되어있어서 계속 출력하는 것이었다. 따라서 처음에 그냥 dis가 0이 되지 않도록
this.destPos = new Vector3(2, 0, 0); 로 초기화 하였다.
나중에 오큘러스에서 이동하니까 문제가 될까 고민도 했지만 말 그대로 처음 위치만 설정하는것이기 때문에 괜찮을 거같다는 판단이 들어 근야 이대로 하기로 했다.
이렇게 하면 처음엔 로그에 아무것도 찍히지 않다가 도착하면 도착했습니다가 출력된다.
this.enemyGo=GameObject.FindGameObjectWithTag("Enemy");
if(dis<=0.2f)
{
Debug.Log("도착했습니다.");
if (enemyGo != null)
{
this.destPos = enemyGo.transform.position;
}
}
이렇게 해서 다음 적으로 이동하려고 했으나
이러면
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
private Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
[SerializeField]
private GameObject shieldGo;
private GameObject enemyGo;
private void Start()
{
this.destPos = new Vector3(2, 0, 0);
this.enemyGo=GameObject.FindGameObjectWithTag("Enemy");
}
// Update is called once per frame
void Update()
{
// Debug.Log(dis);
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
}
}
var dis = Vector3.Distance(this.destPos, this.shieldGo.transform.position);
if(dis<=0.2f)
{
Debug.Log("도착했습니다.");
if (enemyGo != null)
{
this.destPos = enemyGo.transform.position;
}
}
shieldController.Move(this.destPos);
}
}
다시 dis가 0.2보다 커지게 되므로 이동하지 않음
그러면 거리로 말고 collisionEnter하면으로 변경해볼까
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
public Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
[SerializeField]
private GameObject shieldGo;
private void Start()
{
this.destPos = new Vector3(2, 0, 0);
}
// Update is called once per frame
void Update()
{
// Debug.Log(dis);
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
shieldController.Move(this.destPos);
}
}
}
}
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
public float moveSpeed = 0f;
private GameObject enemyGo;
[SerializeField] private TestMain testMain;
private void Start()
{
}
public void Move(Vector3 destPos)
{
StartCoroutine(this.CoMove(destPos));
}
IEnumerator CoMove(Vector3 destPos)
{
while (true)
{
this.transform.position = Vector3.MoveTowards(this.transform.position, destPos, moveSpeed * Time.deltaTime);
yield return null;
}
}
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
this.testMain.destPos = this.enemyGo.transform.position;
Debug.Log(this.testMain.destPos);//계속 사라진애의 pos가 destpos임
Debug.Log(this.enemyGo.transform.position);
this.Move(this.testMain.destPos);
}
}
이렇게 하면 destPos가 다음 적의 위치로 잘 나오는데 움직이지 않음.. 논리적으로 문제가 없는데 대체 뭘까 하고 고민하던 도중에 갑자기 이미 실행중인 코루틴이 있다면 중지하고 해야한다는 생각이 듬
그래서
public void Move(Vector3 destPos)
{
if (this.coroutine != null) StopCoroutine(this.coroutine);
this.coroutine=StartCoroutine(this.CoMove(destPos));
}
코드를 이렇게 수정했더니 잘 실행된다,
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
public float moveSpeed = 0f;
private GameObject enemyGo;
private Coroutine coroutine;
[SerializeField] private TestMain testMain;
private void Start()
{
//for(int i = 1; i < enemyGo.Length; i++)
//{
// Transform enemyPos = enemyGo[i].transform;
// this.queue.Enqueue(enemyPos);
// Debug.Log(enemyGo);
//}
}
public void Move(Vector3 destPos)
{
if (this.coroutine != null) StopCoroutine(this.coroutine);
this.coroutine=StartCoroutine(this.CoMove(destPos));
}
IEnumerator CoMove(Vector3 destPos)
{
while (true)
{
this.transform.position = Vector3.MoveTowards(this.transform.position, destPos, moveSpeed * Time.deltaTime);
yield return null;
}
}
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
this.testMain.destPos = this.enemyGo.transform.position;
Debug.Log(this.testMain.destPos);//계속 사라진애의 pos가 destpos임
Debug.Log(this.enemyGo.transform.position);
this.Move(this.testMain.destPos);
}
}
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
public Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
[SerializeField]
private GameObject shieldGo;
// private GameObject enemyGo;
private void Start()
{
this.destPos = new Vector3(2, 0, 0);
//this.enemyGo=GameObject.FindGameObjectWithTag("Enemy");
}
// Update is called once per frame
void Update()
{
// Debug.Log(dis);
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
shieldController.Move(this.destPos);
}
}
//var dis = Vector3.Distance(this.destPos, this.shieldGo.transform.position);
//if (dis > 1f)
//{
// this.destPos = this.enemyGo.transform.position;
// //shieldController.moveSpeed = 3f;
// Debug.Log("가는중");
//}
//if(dis<=0.2f)
//{
// Debug.Log("도착했습니다.");
// if (enemyGo != null)
// {
// this.destPos = enemyGo.transform.position;
// }
//}
}
}
왜인지 모르겠는데
오른쪽 큐브로 먼저 이동하면 다음 큐브를 잘 찾아서 이동하지만 , 왼쪽 큐브를 먼저 이동하면 다음 큐브를 찾지 못하고 destroy 됐음에도 불구하고 태그를 이용해서 찾았을 때 왼쪽 큐브를 찾는다.
destroy 이후에 태그로 찾는 사이의 간격이 너무 짧아서 그런가 싶어서 태그로 찾는 부분을 코루틴으로 만들어서 시간을 좀 연장시켰다.
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
StartCoroutine(this.CoFindAndMove());
}
private IEnumerator CoFindAndMove()
{
yield return new WaitForSeconds(0.2f);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
this.testMain.destPos = this.enemyGo.transform.position;
this.Move(this.testMain.destPos);
}
}
이렇게 하니 정상적으로 돌아간다.
하지만 마지막 큐브까지 사라지고 나서 null refernce exception오류가 나서
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
StartCoroutine(this.CoFindAndMove());
}
private IEnumerator CoFindAndMove()
{
yield return new WaitForSeconds(0.2f);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
if (enemyGo != null)
{
this.testMain.destPos = this.enemyGo.transform.position;
this.Move(this.testMain.destPos);
}
}
null이 아니라면 이라는 조건을 추가 해주었다.
이제 모두 적이 사라지면 처음 위치로 돌아오는 것을 해줘야한다.
Start에서 방패의 처음 위치를 저장하고 enemyGo==null이면 처음 위치로 이동하도록한다 .
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using Unity.VisualScripting;
using UnityEngine;
public class ShieldController : MonoBehaviour
{
public float moveSpeed = 0f;
private GameObject enemyGo=null;
private Coroutine coroutine;
private Vector3 startPos;
[SerializeField] private TestMain testMain;
private void Start()
{
this.startPos = this.transform.position;
//for(int i = 1; i < enemyGo.Length; i++)
//{
// Transform enemyPos = enemyGo[i].transform;
// this.queue.Enqueue(enemyPos);
// Debug.Log(enemyGo);
//}
}
public void Move(Vector3 destPos)
{
if (this.coroutine != null) StopCoroutine(this.coroutine);
this.coroutine=StartCoroutine(this.CoMove(destPos));
}
IEnumerator CoMove(Vector3 destPos)
{
while (true)
{
this.transform.position = Vector3.MoveTowards(this.transform.position, destPos, moveSpeed * Time.deltaTime);
yield return null;
}
}
private void OnCollisionEnter(Collision collision)
{
Destroy(collision.gameObject);
StartCoroutine(this.CoFindAndMove());
}
private IEnumerator CoFindAndMove()
{
yield return new WaitForSeconds(0.2f);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
if (enemyGo != null)
{
this.testMain.destPos = this.enemyGo.transform.position;
//Debug.Log(this.testMain.destPos);//계속 사라진애의 pos가 destpos임
//Debug.Log(this.enemyGo.transform.position);
this.Move(this.testMain.destPos);
}
else if (enemyGo == null)
{
this.Move(this.startPos);
}
}
}
using System.Collections;
using System.Collections.Generic;
using Unity.VisualScripting;
using UnityEngine;
public class TestMain : MonoBehaviour
{
public Vector3 destPos;
[SerializeField]
private ShieldController shieldController;
[SerializeField]
private GameObject shieldGo;
// private GameObject enemyGo;
private void Start()
{
this.destPos = new Vector3(2, 0, 0);
//this.enemyGo=GameObject.FindGameObjectWithTag("Enemy");
}
// Update is called once per frame
void Update()
{
// Debug.Log(dis);
if (Input.GetMouseButtonDown(0))
{
RaycastHit hit;
//카메라부터 마우스 클릭 위치로 ray 그리기
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
Debug.DrawRay(ray.origin, ray.direction * 100f, Color.red, 3f);
if (Physics.Raycast(ray, out hit, 100f))
{
this.destPos = new Vector3(hit.point.x, hit.point.y, hit.point.z);
shieldController.moveSpeed = 2f;
shieldController.Move(this.destPos);
}
}
//var dis = Vector3.Distance(this.destPos, this.shieldGo.transform.position);
//if (dis > 1f)
//{
// this.destPos = this.enemyGo.transform.position;
// //shieldController.moveSpeed = 3f;
// Debug.Log("가는중");
//}
//if(dis<=0.2f)
//{
// Debug.Log("도착했습니다.");
// if (enemyGo != null)
// {
// this.destPos = enemyGo.transform.position;
// }
//}
}
}
어느쪽 큐브를 먼저가든 잘 이동하고 처음위치로 돌아오는 모습
이 코드 그대로 큐브가 하나일때도 잘 하나만 맞추고 돌아옴
이제 큐브가 하나일 경우와 두개일 경우에 잘 맞추고 돌아오는것을 완료 했으니
큐브가 세개일 경우를 해야한다.
엥 근데 이 코드 그대로 그냥 큐브 세개 만들고 해도 잘 모두 맞추고 돌아온다..? 개이득 ....??????
이게 오 ㅐ되징 ;; 암튼 비슷한거같다

어 .. 근데 속도를 높히니까 이상해짐
내가 원하는 느낌이 전혀 아님 ..
움짤로 보니까 별로 티가 안나는데 바로바로 이동해야하는데 좀 머물었다가 이동한다,
private IEnumerator CoFindAndMove()
{
yield return new WaitForSeconds(0.1f);
this.enemyGo = GameObject.FindGameObjectWithTag("Enemy");
if (enemyGo != null)
{
this.testMain.destPos = this.enemyGo.transform.position;
//Debug.Log(this.testMain.destPos);//계속 사라진애의 pos가 destpos임
//Debug.Log(this.enemyGo.transform.position);
this.Move(this.testMain.destPos);
}
else if (enemyGo == null)
{
this.Move(this.startPos);
}
}
코루틴을 돌릴때 0.2초 이후에 이동하도록 해놨기 때문에 그런거같다. 0.1초로 바꾸니 훨씬 나아졌다.