Unity 3D 21.04.15.수업내용
어제 복습하던 것을 이어한다.
============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerMovement : MonoBehaviour
{
Animator animator;
AudioSource playerAudio;
Rigidbody playerRigid;
private float rotateSpeed = 1000f;
// Start is called before the first frame update
void Start()
{
this.animator = GetComponent<Animator>();
this.playerAudio = GetComponent<AudioSource>();
this.playerRigid = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (!GameManager.instance.isOver && GameManager.instance.isRotate)
{
Rotate();
Debug.LogFormat("Rotate Activated");
}
}
public void Rotate()
{
float turn = this.playerRigid.rotation.y * this.rotateSpeed * Time.deltaTime;
if(GameManager.instance.isRight && GameManager.instance.isLeft)
{
this.playerRigid.rotation = this.playerRigid.rotation;
}
if (GameManager.instance.isRight)
{
this.transform.Rotate(0, rotateSpeed * Time.deltaTime, 0f);
Debug.LogFormat("Right");
}
if (GameManager.instance.isLeft)
{
this.transform.Rotate(0, -rotateSpeed * Time.deltaTime, 0f);
Debug.LogFormat("Left");
}
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class GameManager : MonoBehaviour
{
public bool isRotate;
public bool isOver;
public bool isRight;
public bool isLeft;
GameObject player;
PlayerMovement playerMove;
public static GameManager instance
{
get
{
if (m_instance == null)
m_instance = FindObjectOfType<GameManager>();
return m_instance;
}
}
private static GameManager m_instance;
private void Awake()
{
if(instance != this)
{
Destroy(this.gameObject);
}
}
// Start is called before the first frame update
void Start()
{
this.player = GameObject.Find("Player");
this.playerMove = this.player.GetComponent<PlayerMovement>();
}
// Update is called once per frame
void Update()
{
}
public void RightRotate()
{
this.isRight = true;
this.playerMove.Rotate();
}
public void RightOff()
{
this.isRight = false;
}
public void LeftRotate()
{
this.isLeft = true;
this.playerMove.Rotate();
}
public void LeftOff()
{
this.isLeft = false;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class LRotateButton : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
EventTrigger eventTriggers = gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry_PointerDown = new EventTrigger.Entry();
entry_PointerDown.eventID = EventTriggerType.PointerDown;
entry_PointerDown.callback.AddListener((data) => {
GameManager.instance.isRotate = true;
GameManager.instance.isLeft = true;
Debug.LogFormat("LeftBtn Pressed");
});
eventTriggers.triggers.Add(entry_PointerDown);
EventTrigger.Entry entry_PointerUp = new EventTrigger.Entry();
entry_PointerUp.eventID = EventTriggerType.PointerUp;
entry_PointerUp.callback.AddListener((data) =>
{
GameManager.instance.isRotate = false;
GameManager.instance.isLeft = false;
Debug.LogFormat("LeftBtn Released");
});
eventTriggers.triggers.Add(entry_PointerUp);
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.UI;
public class UI : MonoBehaviour
{
public Button rotateButton;
public Text ammoText;
public Text scoreText;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.EventSystems;
public class RRotateButton : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
EventTrigger eventTriggers = gameObject.AddComponent<EventTrigger>();
EventTrigger.Entry entry_PointerDown = new EventTrigger.Entry();
entry_PointerDown.eventID = EventTriggerType.PointerDown;
entry_PointerDown.callback.AddListener((data) => {
GameManager.instance.isRotate = true;
GameManager.instance.isRight = true;
Debug.LogFormat("RightBtn Pressed");
});
eventTriggers.triggers.Add(entry_PointerDown);
EventTrigger.Entry entry_PointerUp = new EventTrigger.Entry();
entry_PointerUp.eventID = EventTriggerType.PointerUp;
entry_PointerUp.callback.AddListener((data) => {
GameManager.instance.isRotate = false;
GameManager.instance.isRight = false;
Debug.LogFormat("RightBtn Released");
});
eventTriggers.triggers.Add(entry_PointerUp);
}
// Update is called once per frame
void Update()
{
}
}
1.인게임에서 버튼이 안 눌렸는데 버튼에 들어가있는 Text가 ray를 받으면 button은 가려져서 인식을 못한다고 한다. Text의 RaycastTarget을 주의하자
2.PlayerMovement에서 Rotate을 this.playerRigid.rotate *= Quaternion.Euler(0, turn, 0); 이런식으로 했는데
this.transform.Ratate(0, turn*Time.deltaTime, 0)하니 깔끔하게 해결됐다.
============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShoot : MonoBehaviour
{
public Gun gun;
public Transform gunPivot;
public Transform leftHandMount;
public Transform rightHandMount;
Animator playerAnimator;
// Start is called before the first frame update
void Start()
{
this.playerAnimator = GetComponent<Animator>();
if (Input.GetMouseButtonDown(0))
{
Fire();
}
}
// Update is called once per frame
void Update()
{
}
private void Fire()
{
}
private void OnAnimatorIK(int layerIndex)
{
this.gunPivot.position = this.playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, this.leftHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, this.leftHandMount.rotation);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, this.rightHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, this.rightHandMount.rotation);
}
}
Gunpivot은 EmptyObject로 만들어서 대충 플레이어 손 위치쯤에 맞게 일단 조정해둔다.
어차피 코드로 IK를 붙여 위치를 맞출것이기 때문이다.
Gunpivot에 Gun을 넣어서 pivot내에서 gun의 위치를 갖게 만들고 IK코드로 GunPivot의 위치를 아바타 IK의 오른쪽 팔꿈치로 걸어둔 뒤에 그 아래에서 애니메이션의 각도와 위치를 정해주고
정해둔 위치와 각도들을 leftHandMount와 rightHandMount포지션, 로테이션에 각각 연결해준다
leftHandMount와 rightHandMount는 Gun 내의 위치값을 나타내는 빈 오브젝트 들이다.
또한 아바타의 IKPass버튼을 체크해주어야 하며 IK를 1로 만들어줘야 온전히 아바타의 애니메이션이 오브젝트와 연동되어 실행된다.
유니티 - 매뉴얼: 역운동학(Inverse Kinematics) (unity3d.com)
유니티 - 매뉴얼: 역운동학(Inverse Kinematics)
타겟 매칭(Target Matching) 루트 모션 - 작업 방법 역운동학(Inverse Kinematics) 대부분의 애니메이션은 스켈레톤에서 미리 정해진 값에 조인트를 변경하여 회전하는 것으로 실현됩니다. 자식 조인트의
docs.unity3d.com
============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public LineRenderer shootEffect;
public ParticleSystem muzzleEffect;
public ParticleSystem shellEjection;
public Transform firePosition;
float cycle = 0.8f;
float timeBetFire;
float lastTimeFire;
int magAmmo;
int magCapacity = 100;
int remainAmmo;
float attackRange;
bool isReload;
// Start is called before the first frame update
void Start()
{
}
void OnEnable()
{
this.magAmmo = this.magCapacity;
}
// Update is called once per frame
void Update()
{
}
public void Fire()
{
StartCoroutine(OnFire());
Debug.LogFormat("Gun.Fire() 작동함");
}
private IEnumerator OnFire()
{
while (true)
{
this.muzzleEffect.Play();
this.shellEjection.Play();
yield return new WaitForSeconds(this.cycle);
}
}
public void Shot()
{
/*this.shootEffect.SetPosition()*/
}
public bool Reload()
{
if (this.magAmmo < this.magCapacity || this.magAmmo <= 0 || this.remainAmmo > 0)
{
this.isReload = true;
}
return false;
}
public IEnumerator ReloadRoutine()
{
if (this.isReload)
{
yield return new WaitForSeconds(0.8f);
int ammoToFill = this.magCapacity - this.magAmmo;
if (this.remainAmmo < ammoToFill)
{
ammoToFill = this.remainAmmo;
this.remainAmmo -= ammoToFill;
}
this.magAmmo += ammoToFill;
}
}
public void OnDamage()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShoot : MonoBehaviour
{
public Gun gun;
public Transform gunPivot;
public Transform leftHandMount;
public Transform rightHandMount;
Animator playerAnimator;
// Start is called before the first frame update
void Start()
{
this.playerAnimator = GetComponent<Animator>();
}
private void FixedUpdate()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(0))
{
Fire();
Debug.LogFormat("PlayerShoot 작동함");
}
}
private void Fire()
{
this.gun.Fire();
Debug.LogFormat("PlayerShoot.Fire() 작동함");
}
public void Reload()
{
if (this.gun.Reload())
{
this.playerAnimator.SetTrigger("Reload");
}
}
private void OnAnimatorIK(int layerIndex)
{
this.gunPivot.position = this.playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, this.leftHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, this.leftHandMount.rotation);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, this.rightHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, this.rightHandMount.rotation);
}
}
가져온 소스로 총구에서 화염이 나오게 하고 탄피가 튀게 하는 파티클을 작동시켰다. Coroutine을 사용했는데, 중간에 이펙트가 안나오는 경우가 있어서 원인을 찾아봤더니 Coroutine메서드를 작성하고 StartCoroutine으로 불러오지 않아서 작동하지 않았던 것이었다.
============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public LineRenderer line;
public ParticleSystem muzzleEffect;
public ParticleSystem shellEjection;
public Transform fireTransform;
float cycle = 0.8f;
float timeBetFire;
float lastTimeFire;
int magAmmo;
int magCapacity = 100;
int remainAmmo;
float attackRange = 20f;
bool isReload;
// Start is called before the first frame update
void Start()
{
}
void OnEnable()
{
this.magAmmo = this.magCapacity;
}
private void Awake()
{
this.line.positionCount = 2;
}
// Update is called once per frame
void Update()
{
}
public void Fire()
{
StartCoroutine(ShotEffect());
Debug.LogFormat("Gun.Fire() 작동함");
}
private IEnumerator ShotEffect()
{
this.muzzleEffect.Play();
this.shellEjection.Play();
this.line.SetPosition(0, this.fireTransform.position);
var pos = this.fireTransform.position;
pos.z = this.fireTransform.forward.z * this.attackRange;
this.line.SetPosition(1, pos);
this.line.enabled = true;
yield return new WaitForSeconds(0.03f);
this.line.enabled = false;
}
public void Shot()
{
/*this.shootEffect.SetPosition()*/
}
public bool Reload()
{
if (this.magAmmo < this.magCapacity || this.magAmmo <= 0 || this.remainAmmo > 0)
{
this.isReload = true;
}
return false;
}
public IEnumerator ReloadRoutine()
{
if (this.isReload)
{
yield return new WaitForSeconds(0.8f);
int ammoToFill = this.magCapacity - this.magAmmo;
if (this.remainAmmo < ammoToFill)
{
ammoToFill = this.remainAmmo;
this.remainAmmo -= ammoToFill;
}
this.magAmmo += ammoToFill;
}
}
public void OnDamage()
{
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShoot : MonoBehaviour
{
public Gun gun;
public Transform gunPivot;
public Transform leftHandMount;
public Transform rightHandMount;
Animator playerAnimator;
// Start is called before the first frame update
void Start()
{
this.playerAnimator = GetComponent<Animator>();
}
private void FixedUpdate()
{
}
// Update is called once per frame
void Update()
{
if (Input.GetMouseButton(1))
{
Fire();
Debug.LogFormat("PlayerShoot 작동함");
}
}
private void Fire()
{
this.gun.Fire();
Debug.LogFormat("PlayerShoot.Fire() 작동함");
}
public void Reload()
{
if (this.gun.Reload())
{
this.playerAnimator.SetTrigger("Reload");
}
}
private void OnAnimatorIK(int layerIndex)
{
this.gunPivot.position = this.playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, this.leftHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, this.leftHandMount.rotation);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, this.rightHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, this.rightHandMount.rotation);
}
}
공격버튼이 좌클릭일때 버튼을 누르면 회전과 사격이 동시에 발생해서 우클릭으로 바꿨다.
총알의 LineRenderer가 총구에서 시작해서 자꾸 바닥으로 고꾸라지는 선을 그렸는데 그 원인이 var pos를 정할때 fireTransform의 localPosition을 가져와서 그런것이었다. 월드좌표를 받기위해 fireTransform.position으로 고쳐쓰니 정상적으로 작동했다.
++그리고 LineRenderer의 size는 배열의 크기를 나타낸다.
============================================================================
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Gun : MonoBehaviour
{
public UI ui;
public LineRenderer line;
public ParticleSystem muzzleEffect;
public ParticleSystem shellEjection;
public Transform fireTransform;
public int magAmmo;
float timeBetFire = 0.08f;
float lastTimeFire;
int magCapacity = 50;
int remainAmmo = 250;
float attackRange = 20f;
bool isReady = true;
bool isReloading = false;
// Start is called before the first frame update
void Start()
{
}
void OnEnable()
{
this.magAmmo = this.magCapacity;
}
private void Awake()
{
this.line.positionCount = 2;
}
// Update is called once per frame
void Update()
{
this.ui.ammoText.text = this.magAmmo + "/" + this.remainAmmo;
}
public void Fire()
{
if (this.magAmmo > 0)
{
if (Time.time > this.lastTimeFire + this.timeBetFire)
{
this.lastTimeFire = Time.time;
StartCoroutine(ShotEffect());
this.magAmmo--;
}
}
}
private IEnumerator ShotEffect()
{
this.muzzleEffect.Play();
this.shellEjection.Play();
this.line.SetPosition(0, this.fireTransform.position);
var pos = this.fireTransform.position;
pos.z = this.fireTransform.forward.z * this.attackRange;
this.line.SetPosition(1, pos);
this.line.enabled = true;
yield return new WaitForSeconds(0.03f);
this.line.enabled = false;
}
public void Shot()
{
/*this.shootEffect.SetPosition()*/
}
public bool IsReload()
{
if (this.magAmmo < this.magCapacity && this.remainAmmo > 0)
{
return true;
}
else
{
return false;
}
}
public void Reload()
{
this.isReady = false;
this.isReloading = true;
StartCoroutine(ReloadRoutine());
}
public IEnumerator ReloadRoutine()
{
int ammoToFill = this.magCapacity - this.magAmmo;
if (this.remainAmmo < ammoToFill)
{
ammoToFill = this.remainAmmo;
}
this.remainAmmo -= ammoToFill;
yield return new WaitForSeconds(0.8f);
this.magAmmo += ammoToFill;
this.isReady = true;
this.isReloading = false;
}
public int GetAmmo()
{
return this.magAmmo;
}
public int GetRemains()
{
return this.remainAmmo;
}
public void OnDamage()
{
}
public bool IsReloading()
{
return this.isReloading;
}
public bool IsReady()
{
return this.isReady;
}
}
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerShoot : MonoBehaviour
{
public Gun gun;
public Transform gunPivot;
public Transform leftHandMount;
public Transform rightHandMount;
Animator playerAnimator;
// Start is called before the first frame update
void Start()
{
this.playerAnimator = GetComponent<Animator>();
}
private void FixedUpdate()
{
if (this.gun.IsReady() && Input.GetMouseButton(1))
{
this.gun.Fire();
}
if (!this.gun.IsReloading() && Input.GetKeyDown(KeyCode.R))
{
Reload();
}
}
// Update is called once per frame
void Update()
{
}
public void Reload()
{
if (this.gun.IsReload())
{
this.playerAnimator.SetTrigger("Reload");
this.gun.Reload();
}
}
private void OnAnimatorIK(int layerIndex)
{
this.gunPivot.position = this.playerAnimator.GetIKHintPosition(AvatarIKHint.RightElbow);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.LeftHand, 1.0f);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.LeftHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.LeftHand, this.leftHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.LeftHand, this.leftHandMount.rotation);
this.playerAnimator.SetIKPositionWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKRotationWeight(AvatarIKGoal.RightHand, 1);
this.playerAnimator.SetIKPosition(AvatarIKGoal.RightHand, this.rightHandMount.position);
this.playerAnimator.SetIKRotation(AvatarIKGoal.RightHand, this.rightHandMount.rotation);
}
}
재장전이 중복되는 문제와 재장전 중에 발사가 가능한 문제가 있었는데 bool타입으로 장전중인 상태와 쏠 수 있는 상태를 추가하여 PlayerShoot에서 Gun의 상태에 따라 발사가 가능/불가능 하게 만들었고 장전또한 이미 장전중이면 다시 R을 눌러도 장전이 되지 않게 했다.
============================================================================
private IEnumerator ShotEffect()
{
this.muzzleEffect.Play();
this.shellEjection.Play();
this.line.SetPosition(0, this.fireTransform.position);
var pos = this.fireTransform.position;
pos.z = this.fireTransform.forward.z * this.attackRange;
pos.x = this.fireTransform.forward.x * this.attackRange;
this.line.SetPosition(1, pos);
Debug.Log(pos.z);
this.line.enabled = true;
yield return new WaitForSeconds(0.03f);
this.line.enabled = false;
}
캐릭터를 회전시키면처음 쏘던 방향으로만 총알이 나가는 문제를 해결했다.
원인은 x축값을 정해주지 않았기 때문에 z축으로만 경로가 바뀌어서 그랬다.
============================================================================
NavMesh까진 적용했는데 유닛들이 어떻게 NavMeshAgent를 이용하여 이동하는지 잘 모르겠다.
--미완성 기능들
-몬스터 피격
-캐릭터 피격
-점수
-유닛 이동
ㄴ네비게이션
-캐릭터 카메라(시네머신)