[4Idle - Gazzlers] Player 이동 기능 + 손 기능 합치기
1차적으로 R&D한 구현 내용을 GameScene을 생성해 기능을 통합시키려 한다.
-이 포스팅에서는 Player가 Vehicle을 타고 이동하는 기능과 손 기능을 합칠 것이다.
-먼저 TestHands 씬을 복사하여 네임스페이스가 없도록 스크립트를 모두 새로 생성하고, GameScene을 새로 생성해 똑같은 환경을 구성하여 미리 만들어둔 손 기능이 똑같이 동작하는지 확인하였다.
https://made-myblog.tistory.com/58
-namespace가 설정되지 않도록 스크립트들을 새로 생성해주었다.
-Rail.cs와 RailPoolManager.cs생성
=> Player의 경로 이동을 구현하기 위해 각각의 railPrefab에 경로를 지정한 뒤 그 경로를 player에게 전달하여 player가 이를 따라가도록 하였다.
=> railPrefab에는 Rail.cs가 부착되며, 이 스크립트를 통해 경로를 지정해준다.
=> Rail.cs에는 각각의 rail 종류를 알려주는 railType과 그들이 가지고 있는 경로에 대한 정보를 저장하는 routeTrans 배열이 있다.
=> GetRoute는 CamMove.cs가 player가 지나갈 경로를 얻기 위한 메서드이다.
=> player의 경로 갱신을 위해 MapController가 Rail을 설치할 때마다 TestMapMain에게 Rail 정보를 전달한다.
- Main은 camMove에게 rail에 저장되어있는 route 배열들을 전달한다.(118라인)
- CamMove.cs의 UpdateRoute는 경로를 전달받는 메서드이다.
=> 전달받은 경로가 queueRouteTrans에 enqueue되어 관리된다.
- Move는 player의 움직임을 관리하는 method로 player는 지정해놓은 경로를 향해 회전하면서 이동한다.
- 지정해놓은 경로의 position.z값이 player의 position.z값보다 클 경우(player가 경로를 지나칠 경우) queueRouteTrans.Dequeue를 하여 지나간 경로를 제외시킨다.
-StructurePoolManager.cs, SturctureManager.cs 생성
- Structure들을 저장해놓은 List들을 Queue로 관리하기 위해 qStructures를 선언하였다.
- Queue로 관리하는 이유는 앞선 코드들처럼 먼저 저장한 structure(시야에서 멀어진)를 제거하고 새로 생성하기 위함이다.
- InstallTree 메서드는 나무를 설치한다. tree가 rail을 따라 일정 간격으로 생성된다.
- offset은 position.z 간격(+ position.x 간격)을 xOffset은 LRail 혹은 RRail의 모양에 맞게끔 설치되기 위해 조정되어야 하는 position.x 간격을 의미한다.
- 따라서 switch문에서 rail의 type에 따라 xOffset의 값이 달라진다.
- listStructure는 qStructures에 enqueue하여 관리하기 위해 선언된 list로 listStructure 안에는 floor 간격마다 설치되는 Structure들이 저장되어 있다.
- for문은 tree를 설치하는 부분으로, 한 floor당 rail 양 옆으로 3개씩, 총 6개의 tree가 설치된다.
- leftTree와 rightTree가 설치될 위치는 앞서 지정해놓은 offset값과 설치된 rail의 위치를 토대로 계산되어진다.
-----------------------------------------------------------------------------------------------------------------------------------------------------------------
-PlayerCar, 즉 탈것의 자식으로 OVRCameraRig를 넣어준다.
-빈 오브젝트로 CamMove, Map Controller,Rail Pool Manager, StructurePoolManager, Structure Manager를 생성하고 각각의 스크립트를 붙여준다.
-StructurePoolManager에 들어가는 프리팹들은 Structure 스크립트를 부착해주어야한다.
- Player를 이동시키기 시작했더니 문제가 생겼다. 레이저는 제자리에서 생성되기 때문에 날아가 버리는 것처럼 보인다.
-레이저 빔 오브젝트의 위치가 고정되어서 생기는 문제였으므로 위치를 update에서 받도록 코드를 수정하였다.
-생성되는 시간을 0.3에서 0.2로 줄여주었다.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Structure : MonoBehaviour
{
public enum eType
{
Tree, Bench, Lamp, Clock, Sign, AdPole, ElecPole, FireHydrant, Architecture
}
public eType type;
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StructurePoolManager : MonoBehaviour
{
public static StructurePoolManager Instance;
[SerializeField] private GameObject[] arrStructurePrefabs;
private List<List<GameObject>> listStructures = new List<List<GameObject>>();
public int count = 10;
private void Awake()
{
if (Instance == null)
Instance = this;
}
//void Start()
//{
// this.InitStructure();
//}
public void InitStructure()
{
for (int i = 0; i < arrStructurePrefabs.Length; i++)
{
listStructures.Add(new List<GameObject>());
for (int j = 0; j < this.count; j++)
{
GameObject go = this.GenerateStructure(i);
go.transform.SetParent(this.transform);
go.SetActive(false);
}
}
}
private GameObject GenerateStructure(int idx)
{
GameObject go = Instantiate(this.arrStructurePrefabs[idx]);
listStructures[idx].Add(go);
return go;
}
public GameObject EnableStructure(int idx)
{
GameObject structure = null;
for (int i = 0; i < this.listStructures[idx].Count; i++)
{
GameObject go = this.listStructures[idx][i];
if (go.activeSelf == false)
{
go.SetActive(true);
go.transform.SetParent(null);
structure = go;
break;
}
}
if (structure == null)
{
structure = this.GenerateStructure(idx);
}
return structure;
}
public void DisableStructure(GameObject go)
{
go.transform.SetParent(this.transform);
go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
go.SetActive(false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class StructureManager : MonoBehaviour
{
private Queue<List<Structure>> qStructures = new Queue<List<Structure>>();
private Rail rail;
public void InstallStructure(Rail rail)
{
//Debug.LogFormat("railposition: {0}", rail.transform.position);
//Debug.LogFormat("<color=red>rail type: {0}</color>", rail.type);
List<Structure> listStructure = new List<Structure>();
this.rail = rail;
this.InstallTree(listStructure);
}
public void UninstallStructure()
{
List<Structure> list = qStructures.Dequeue();
for (int i = 0; i < list.Count; i++)
{
StructurePoolManager.Instance.DisableStructure(list[i].gameObject);
}
}
private void InstallTree(List<Structure> list)
{
float xOffset = 0f;
float offset = 4.8f;
switch (this.rail.type)
{
case Rail.eType.Left:
xOffset = 2f;
break;
case Rail.eType.Right:
xOffset = -2f;
break;
}
qStructures.Enqueue(list);
for (int i = 0; i < 3; i++)
{
GameObject leftTreeGo = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Tree);
leftTreeGo.transform.position = this.rail.gameObject.transform.position + Vector3.left * (offset + 1f - i * xOffset) + Vector3.back * offset * (i + 1);
list.Add(leftTreeGo.GetComponent<Structure>());
GameObject rightTreeGo = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Tree);
rightTreeGo.transform.position = this.rail.gameObject.transform.position + Vector3.right * (offset + 1f + i * xOffset) + Vector3.back * offset * (i + 1);
list.Add(rightTreeGo.GetComponent<Structure>());
}
}
private void InstallArchitecture(List<Structure> list)
{
int rand = Random.Range(0, 4);
if (rand >= 3) return;
float xOffset = 10f;
float offset = 8f;
GameObject go = StructurePoolManager.Instance.EnableStructure((int)Structure.eType.Architecture);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class RailPoolManager : MonoBehaviour
{
public static RailPoolManager Instance;
[SerializeField] private GameObject[] arrRailPrefabs;
private List<List<GameObject>> listRail = new List<List<GameObject>>();
private void Awake()
{
if (Instance == null)
Instance = this;
}
public void Init(int count)
{
this.GenerateRail(count);
}
private void GenerateRail(int count)
{
for (int i = 0; i < arrRailPrefabs.Length; i++)
{
listRail.Add(new List<GameObject>());
for (int j = 0; j < count; j++)
{
GameObject go = Instantiate(arrRailPrefabs[i], this.transform);
listRail[i].Add(go);
go.SetActive(false);
}
}
}
public GameObject EnableRail(int idx)
{
GameObject result = null;
for (int i = 0; i < listRail[idx].Count; i++)
{
GameObject go = listRail[idx][i];
if (go.activeSelf == false)
{
go.SetActive(true);
go.transform.SetParent(null);
result = go;
break;
}
}
return result;
}
public void DisableRail(GameObject go)
{
go.transform.SetParent(this.transform);
go.transform.SetPositionAndRotation(Vector3.zero, Quaternion.identity);
go.SetActive(false);
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Rail : MonoBehaviour
{
public enum eType
{
Left = 0, Middle = 1, Right = 2
}
private int value;
public int Value { get { return value; } }
public eType type;
[SerializeField] Transform[] arrRoute;
void Start ()
{
switch (type)
{
case eType.Left:
value = 4;
break;
case eType.Middle:
value = 0;
break;
case eType.Right:
value = -4;
break;
}
}
public Transform[] GetRoute()
{
return this.arrRoute;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MapController : MonoBehaviour
{
[SerializeField] private GameObject floorPrefab;
[SerializeField] private Transform playerTrans;
[SerializeField] private StructureManager structureManager;
private Queue<GameObject> queueFloor = new Queue<GameObject>();
private Queue<Rail> queueRail = new Queue<Rail>();
private Stack<Rail.eType> stackRail = new Stack<Rail.eType>();
private int maxStackSize = 3;
public float offset = 112f;
public float dis = 16f;
private int count;
private Transform lastFloorTrans;
private Vector3 railPos;
public System.Action<Vector3> onInformInitPos;
public System.Action<Rail> onInformRoute;
public void Init()
{
StructurePoolManager.Instance.InitStructure();
this.count = (int)(offset / dis * 2) + 1;
this.InitFloor();
this.InitRail();
}
void Update()
{
this.UpdateMap();
}
private void UpdateMap()
{
while (queueFloor.Count > 0 && queueFloor.Peek().transform.position.z > +playerTrans.position.z + this.offset + this.dis)
{
GameObject floorGo = queueFloor.Dequeue();
this.UnInstallRail();
Vector3 pos = lastFloorTrans.position + Vector3.forward * -this.dis;
floorGo.transform.position = pos;
railPos = pos + Vector3.right * this.CalculateRailOffset();
//Debug.LogFormat("<color=cyan>railPos: {0}</color>", railPos);
this.InstallRail();
queueFloor.Enqueue(floorGo);
lastFloorTrans = floorGo.transform;
}
}
private void InitFloor()
{
for (int i = 0; i < count; i++)
{
GameObject floorGo = Instantiate(floorPrefab);
floorGo.transform.position = playerTrans.position + Vector3.forward * (offset - i * this.dis);
queueFloor.Enqueue(floorGo);
if (i == count - 1)
lastFloorTrans = floorGo.transform;
}
}
private void InitRail()
{
RailPoolManager.Instance.Init(this.count);
for (int i = 0; i < this.count; i++)
{
float railOffset = this.CalculateRailOffset();
railPos = playerTrans.position + Vector3.forward * (offset - i * this.dis) + Vector3.right * railOffset;
if (railPos.z == 0)
this.onInformInitPos(railPos);
this.InstallRail();
}
}
private float CalculateRailOffset()
{
float railOffset = stackRail.Count;
if (railOffset > 0)
{
if (stackRail.Peek() == Rail.eType.Left)
{
railOffset *= 4;
}
else
{
railOffset *= -4;
}
}
return railOffset;
}
private void InstallRail()
{
int rand = Random.Range(0, 10);
if (rand < 2) //LRail
{
if (this.stackRail.Count == this.maxStackSize && this.stackRail.Peek() == Rail.eType.Left)
{
//Debug.Log(stackRail.Peek());
this.InstallRRail();
}
else
this.InstallLRail();
}
else if (rand < 4) //RRail
{
if (this.stackRail.Count == this.maxStackSize && this.stackRail.Peek() == Rail.eType.Right)
this.InstallLRail();
else
this.InstallRRail();
}
else //MRail
{
this.InstallMRail();
}
}
private void InstallLRail()
{
GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Left);
Rail rail = go.GetComponent<Rail>();
queueRail.Enqueue(rail);
go.transform.position = this.railPos;
//structure 생성
structureManager.InstallStructure(rail);
//루트 알려주기
this.onInformRoute(rail);
//stack에 저장
if (this.stackRail.Count == 0 || this.stackRail.Peek() == Rail.eType.Left)
{
stackRail.Push(Rail.eType.Left);
}
else if (this.stackRail.Peek() == Rail.eType.Right)
{
stackRail.Pop();
}
//Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
}
private void InstallMRail()
{
GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Middle);
Rail rail = go.GetComponent<Rail>();
queueRail.Enqueue(rail);
go.transform.position = this.railPos;
structureManager.InstallStructure(rail);
this.onInformRoute(rail);
//Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
}
private void InstallRRail()
{
GameObject go = RailPoolManager.Instance.EnableRail((int)Rail.eType.Right);
Rail rail = go.GetComponent<Rail>();
queueRail.Enqueue(rail);
go.transform.position = this.railPos;
structureManager.InstallStructure(rail);
this.onInformRoute(rail);
if (this.stackRail.Count == 0 || this.stackRail.Peek() == Rail.eType.Right)
{
stackRail.Push(Rail.eType.Right);
}
else if (this.stackRail.Peek() == Rail.eType.Left)
{
stackRail.Pop();
}
//Debug.LogFormat("<color=yellow>stack count:{0}</color>", stackRail.Count);
}
private void UnInstallRail()
{
Rail rail = queueRail.Dequeue();
RailPoolManager.Instance.DisableRail(rail.gameObject);
structureManager.UninstallStructure();
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CamMove : MonoBehaviour
{
[SerializeField] private GameObject player;
private Queue<Transform> queueRouteTrans = new Queue<Transform>();
private Transform curDestTrans;
private bool isReady;
public float rotSpeed = 1f;
public float moveSpeed = 1.5f;
// Update is called once per frame
public void Init(Vector3 pos)
{
this.player.transform.position = pos;
this.player.transform.rotation = Quaternion.Euler(Vector3.up * 180);
isReady = true;
}
void Update()
{
if (!isReady) return;
this.Move();
}
private void Move()
{
Vector3 destPos = this.queueRouteTrans.Peek().position;
while (destPos.z > this.player.transform.position.z)
{
Transform trans = this.queueRouteTrans.Dequeue();
//Debug.Log(trans.gameObject.name);
destPos = this.queueRouteTrans.Peek().position;
curDestTrans = this.queueRouteTrans.Peek();
}
//this.player.transform.LookAt(curDestTrans);
Vector3 dir = curDestTrans.position - this.player.transform.position;
this.player.transform.localRotation = Quaternion.Slerp(this.player.transform.rotation, Quaternion.LookRotation(dir), Time.deltaTime * this.rotSpeed);
this.player.transform.Translate(Vector3.forward * this.moveSpeed * Time.deltaTime);
}
public void UpdateRoute(Transform[] routes)
{
foreach (var route in routes)
{
queueRouteTrans.Enqueue(route);
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
using TMPro;
public class GameMain : MonoBehaviour
{
[SerializeField] EnemyGenerator enemyGen;
[SerializeField] Canvas worldCanvas;
[SerializeField] GameObject uiHPBarPrefab;
[SerializeField] GameObject uiDamageTextPrefab;
[SerializeField] RightHandController rightHand;
private GameObject hpBarUIGo;
private GameObject damageTextUIGo;
private EnemyGenerator enemyGenerator;
private string currGunType;
private int currGunDamage;
private List<EnemyController> enemyList = new List<EnemyController>();
private List<GameObject> enemyHpBarPools = new List<GameObject>();
private List<GameObject> activeHPBar = new List<GameObject>();
//----------------------------------SJY Map Main-----------------------------------------------------------
[SerializeField] private MapController mapController;
[SerializeField] private CamMove camMove;
private void Awake()
{
this.enemyGenerator = enemyGen;
//----------------------------------Generate Enemy-----------------------------------------------------------
this.enemyList.Add(this.enemyGenerator.Generate(GameEnums.eEnemyType.FrankenStein, new Vector3(-0.09f, -1.35f, 4.3f)));
this.enemyList.Add(this.enemyGenerator.Generate(GameEnums.eEnemyType.FrankenStein, new Vector3(2.41f, -1.35f, 4.3f)));
}
// Start is called before the first frame update
void Start()
{
DataManager.Instance.LoadPlayerDatas();
DataManager.Instance.LoadGunrDatas();
DataManager.Instance.LoadEnemyDatas();
List<PlayerData> playerData = DataManager.Instance.GetPlayerDatas();
List<GunData> gunData = DataManager.Instance.GetGunDatas();
List<EnemyData> enemyData = DataManager.Instance.GetEnemyDatas();
var playerGunId = playerData[0].currGunId;
// Debug.Log(playerGunId);
for (int i = 0; i < gunData.Count; i++)
{
if (playerGunId == gunData[i].gunId)
{//Find player's current gun's Data on GunData
Debug.Log(gunData[i].gunType);
this.currGunType = gunData[i].gunType;
this.currGunDamage = gunData[i].gunDamage;
//gunData[i].gunDamage;
}
}
//this.hpBarUIGo = Instantiate(this.uiHPBarPrefab,this.worldCanvas.transform);
this.enemyHpBarPool();
for(int i=0; i < this.enemyList.Count; i++)
{
this.activeHPBar.Add(this.CreateHpBar(this.enemyList[i].hpBarPoint.position));
// enemyList[i].gameObject.
for(int j = 0; j < enemyData.Count; j++)
{
if (this.enemyList[j].name == enemyData[j].enemyType)
{
//Debug.Log(enemyData[j].enemyType);
var slider = this.activeHPBar[i].GetComponent<Slider>();
slider.maxValue = enemyData[j].enemyHp;
Debug.Log(slider.maxValue);
slider.value = slider.maxValue;//reset slider value as full state
}
}
}
this.damageTextUIGo = Instantiate(this.uiDamageTextPrefab, this.worldCanvas.transform);
// this.hpBarUIGo.SetActive(false);
this.damageTextUIGo.SetActive(false);
this.rightHand.OnHitEnemy = (hitPos,hitObject) =>
{
// Debug.LogFormat("Hit Enemy! Point: {0}", hitPos);
StartCoroutine(this.CoShowDamageText(hitPos));
for (int i = 0; i < this.enemyList.Count; i++)
{
if (hitObject == this.enemyList[i].gameObject)
{
Debug.LogFormat("Object: {0} , Damage : {1}",i,currGunDamage);
this.activeHPBar[i].GetComponent<Slider>().value -= currGunDamage;
}
}
};
//----------------------------------SJY Map Main-----------------------------------------------------------
Vector3 camPos = Vector3.zero;
this.mapController.onInformInitPos = (pos) =>
{
camPos = pos;
};
this.mapController.onInformRoute = (rail) =>
{
this.camMove.UpdateRoute(rail.GetRoute());
};
this.mapController.Init();
this.camMove.Init(camPos);
}
private void enemyHpBarPool()
{
for(int i = 0; i < this.enemyList.Count; i++)
{
GameObject go = Instantiate(this.uiHPBarPrefab, this.worldCanvas.transform);
go.SetActive(false);
this.enemyHpBarPools.Add(go);
}
}
private GameObject GetEnemyHpBarInPool()
{
foreach(GameObject hpBar in enemyHpBarPools)
{
if(hpBar.activeSelf == false)
{
return hpBar;
}
}
return null;
}
private GameObject CreateHpBar(Vector3 position)
{
GameObject go = this.GetEnemyHpBarInPool();
go.transform.position = position;
go.SetActive(true);
return go;
}
private void UpdateEnemyHPBar(Vector3 worldPos)
{
//----------------if Canvas Render Mode is Overlay-------------------------------
//var screenPos = RectTransformUtility.WorldToScreenPoint(Camera.main, worldPos);
//Vector2 localPos;
//RectTransformUtility.ScreenPointToLocalPointInRectangle(
// (RectTransform)this.canvas.transform, screenPos, this.uiCam, out localPos);
//Debug.Log(localPos);
//this.hpBarUIGo.GetComponent<RectTransform>().localPosition = localPos;
//-------------------------------------------------------------------------------------
//---------------if world Position (VR always need world Pos)--------------------------
// this.hpBarUIGo.transform.position = worldPos;
//-------------------------------------------------------------------------------------
}
private IEnumerator CoShowDamageText(Vector3 UIPos)
{
this.damageTextUIGo.transform.position = UIPos;
this.damageTextUIGo.SetActive(true);
var text = this.damageTextUIGo.GetComponent<TextMeshProUGUI>();
text.text = string.Format("{0}",this.currGunDamage);
yield return new WaitForSeconds(0.3f);
this.damageTextUIGo.SetActive(false);
}
// Update is called once per frame
void Update()
{
for (int i = 0; i < this.enemyList.Count; i++)
{
// this.UpdateEnemyHPBar(this.enemyList[i].hpBarPoint.position);
this.activeHPBar[i].transform.position = this.enemyList[i].hpBarPoint.position;
}
}
}