first commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class EnemyTarget : MonoBehaviour
|
||||
{
|
||||
public float maxHealth = 100f;
|
||||
public float currentHealth;
|
||||
public Text healthText;
|
||||
public Renderer targetRenderer;
|
||||
|
||||
private Color originalColor = Color.white;
|
||||
private Collider targetCollider;
|
||||
private Coroutine flashRoutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
if (targetRenderer == null)
|
||||
targetRenderer = GetComponentInChildren<Renderer>();
|
||||
|
||||
targetCollider = GetComponent<Collider>();
|
||||
|
||||
if (targetRenderer != null)
|
||||
originalColor = targetRenderer.material.color;
|
||||
|
||||
ResetTarget();
|
||||
}
|
||||
|
||||
private void LateUpdate()
|
||||
{
|
||||
if (healthText != null && Camera.main != null)
|
||||
healthText.transform.rotation = Quaternion.LookRotation(healthText.transform.position - Camera.main.transform.position);
|
||||
}
|
||||
|
||||
public void TakeDamage(float amount)
|
||||
{
|
||||
if (currentHealth <= 0f)
|
||||
return;
|
||||
|
||||
// Targets can be reused in tests through ResetTarget without rebuilding the scene.
|
||||
currentHealth = Mathf.Max(0f, currentHealth - Mathf.Max(0f, amount));
|
||||
UpdateHealthText();
|
||||
|
||||
if (currentHealth <= 0f)
|
||||
{
|
||||
Die();
|
||||
return;
|
||||
}
|
||||
|
||||
if (flashRoutine != null)
|
||||
StopCoroutine(flashRoutine);
|
||||
|
||||
flashRoutine = StartCoroutine(FlashRoutine());
|
||||
}
|
||||
|
||||
public void ResetTarget()
|
||||
{
|
||||
currentHealth = maxHealth;
|
||||
|
||||
if (targetCollider != null)
|
||||
targetCollider.enabled = true;
|
||||
|
||||
if (targetRenderer != null)
|
||||
targetRenderer.material.color = originalColor;
|
||||
|
||||
UpdateHealthText();
|
||||
}
|
||||
|
||||
private void Die()
|
||||
{
|
||||
if (targetCollider != null)
|
||||
targetCollider.enabled = false;
|
||||
|
||||
if (targetRenderer != null)
|
||||
targetRenderer.material.color = Color.black;
|
||||
|
||||
UpdateHealthText();
|
||||
}
|
||||
|
||||
private IEnumerator FlashRoutine()
|
||||
{
|
||||
if (targetRenderer != null)
|
||||
targetRenderer.material.color = Color.yellow;
|
||||
|
||||
yield return new WaitForSeconds(0.08f);
|
||||
|
||||
if (targetRenderer != null && currentHealth > 0f)
|
||||
targetRenderer.material.color = originalColor;
|
||||
|
||||
flashRoutine = null;
|
||||
}
|
||||
|
||||
private void UpdateHealthText()
|
||||
{
|
||||
if (healthText != null)
|
||||
healthText.text = "HP: " + Mathf.CeilToInt(currentHealth) + " / " + Mathf.CeilToInt(maxHealth);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 91bdd1bdbdab9312fb7492c3e0712c7f
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,74 @@
|
||||
using UnityEngine;
|
||||
|
||||
[RequireComponent(typeof(CharacterController))]
|
||||
public class SimpleFPSController : MonoBehaviour
|
||||
{
|
||||
public Camera playerCamera;
|
||||
public float moveSpeed = 6f;
|
||||
public float mouseSensitivity = 2f;
|
||||
public float gravity = -20f;
|
||||
public float jumpHeight = 1.2f;
|
||||
public float recoilReturnSpeed = 18f;
|
||||
|
||||
private CharacterController characterController;
|
||||
private float verticalVelocity;
|
||||
private float pitch;
|
||||
private float recoilPitchOffset;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
characterController = GetComponent<CharacterController>();
|
||||
|
||||
if (playerCamera == null)
|
||||
playerCamera = GetComponentInChildren<Camera>();
|
||||
}
|
||||
|
||||
private void Start()
|
||||
{
|
||||
Cursor.lockState = CursorLockMode.Locked;
|
||||
Cursor.visible = false;
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
Look();
|
||||
Move();
|
||||
}
|
||||
|
||||
public void AddRecoil(float amount)
|
||||
{
|
||||
recoilPitchOffset -= Mathf.Clamp(amount, 0f, 8f);
|
||||
}
|
||||
|
||||
private void Look()
|
||||
{
|
||||
float mouseX = Input.GetAxis("Mouse X") * mouseSensitivity;
|
||||
float mouseY = Input.GetAxis("Mouse Y") * mouseSensitivity;
|
||||
|
||||
transform.Rotate(Vector3.up * mouseX);
|
||||
pitch = Mathf.Clamp(pitch - mouseY, -85f, 85f);
|
||||
recoilPitchOffset = Mathf.MoveTowards(recoilPitchOffset, 0f, recoilReturnSpeed * Time.deltaTime);
|
||||
|
||||
if (playerCamera != null)
|
||||
playerCamera.transform.localRotation = Quaternion.Euler(pitch + recoilPitchOffset, 0f, 0f);
|
||||
}
|
||||
|
||||
private void Move()
|
||||
{
|
||||
Vector3 input = new Vector3(Input.GetAxisRaw("Horizontal"), 0f, Input.GetAxisRaw("Vertical"));
|
||||
input = Vector3.ClampMagnitude(input, 1f);
|
||||
|
||||
Vector3 move = transform.TransformDirection(input) * moveSpeed;
|
||||
|
||||
if (characterController.isGrounded && verticalVelocity < 0f)
|
||||
verticalVelocity = -2f;
|
||||
|
||||
if (characterController.isGrounded && Input.GetKeyDown(KeyCode.Space))
|
||||
verticalVelocity = Mathf.Sqrt(jumpHeight * -2f * gravity);
|
||||
|
||||
verticalVelocity += gravity * Time.deltaTime;
|
||||
move.y = verticalVelocity;
|
||||
|
||||
characterController.Move(move * Time.deltaTime);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cffd232c30a413e2580e7585626facfb
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,307 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class Weapon : MonoBehaviour
|
||||
{
|
||||
public WeaponData data;
|
||||
public Text ammoText;
|
||||
public Text weaponNameText;
|
||||
public Text reloadText;
|
||||
public Transform firePoint;
|
||||
public Camera playerCamera;
|
||||
public WeaponHUD hud;
|
||||
|
||||
private int currentAmmo;
|
||||
private int currentReserve;
|
||||
private float nextFireTime;
|
||||
private bool isReloading;
|
||||
private Vector3 originalLocalPosition;
|
||||
private Quaternion originalCameraRotation;
|
||||
|
||||
private bool initialized;
|
||||
private AudioSource audioSource;
|
||||
private Coroutine reloadRoutine;
|
||||
private Coroutine recoilRoutine;
|
||||
private SimpleFPSController fpsController;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
audioSource = GetComponent<AudioSource>();
|
||||
originalLocalPosition = transform.localPosition;
|
||||
}
|
||||
|
||||
private void OnEnable()
|
||||
{
|
||||
if (Application.isPlaying)
|
||||
OnSelected();
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
bool wantsToShoot = data.automatic ? Input.GetButton("Fire1") : Input.GetButtonDown("Fire1");
|
||||
|
||||
if (wantsToShoot)
|
||||
TryShoot();
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.R))
|
||||
Reload();
|
||||
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void Initialize()
|
||||
{
|
||||
if (data == null || initialized)
|
||||
return;
|
||||
|
||||
currentAmmo = Mathf.Max(0, data.magazineSize);
|
||||
currentReserve = Mathf.Max(0, data.totalReserveAmmo);
|
||||
originalLocalPosition = transform.localPosition;
|
||||
|
||||
if (playerCamera != null)
|
||||
{
|
||||
originalCameraRotation = playerCamera.transform.localRotation;
|
||||
fpsController = playerCamera.GetComponentInParent<SimpleFPSController>();
|
||||
}
|
||||
|
||||
initialized = true;
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void TryShoot()
|
||||
{
|
||||
if (data == null)
|
||||
return;
|
||||
|
||||
// Shooting behavior is data-driven: automatic weapons use held Fire1, semi-auto weapons use one click.
|
||||
if (isReloading)
|
||||
return;
|
||||
|
||||
if (currentAmmo <= 0)
|
||||
{
|
||||
ShowMessage("No ammo. Press R to reload.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (Time.time < nextFireTime)
|
||||
return;
|
||||
|
||||
if (!CanShoot())
|
||||
return;
|
||||
|
||||
Shoot();
|
||||
}
|
||||
|
||||
public void Shoot()
|
||||
{
|
||||
if (data == null || playerCamera == null)
|
||||
return;
|
||||
|
||||
currentAmmo--;
|
||||
nextFireTime = Time.time + Mathf.Max(0.01f, data.fireRate);
|
||||
|
||||
PlaySound(data.shootSound);
|
||||
SpawnMuzzleFlash();
|
||||
|
||||
int pelletCount = Mathf.Max(1, data.pelletsPerShot);
|
||||
for (int i = 0; i < pelletCount; i++)
|
||||
FireRay();
|
||||
|
||||
ApplyRecoil();
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void Reload()
|
||||
{
|
||||
if (data == null || isReloading)
|
||||
return;
|
||||
|
||||
if (currentAmmo >= data.magazineSize)
|
||||
{
|
||||
ShowMessage("Magazine is full.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (currentReserve <= 0)
|
||||
{
|
||||
ShowMessage("No reserve ammo.");
|
||||
return;
|
||||
}
|
||||
|
||||
reloadRoutine = StartCoroutine(ReloadRoutine());
|
||||
}
|
||||
|
||||
public void UpdateUI()
|
||||
{
|
||||
if (data != null && weaponNameText != null)
|
||||
weaponNameText.text = "Weapon: " + data.weaponName;
|
||||
|
||||
if (ammoText != null)
|
||||
ammoText.text = "Ammo: " + currentAmmo + " / " + currentReserve;
|
||||
|
||||
if (reloadText != null)
|
||||
reloadText.text = isReloading ? "Reloading..." : "Ready";
|
||||
|
||||
if (hud != null)
|
||||
{
|
||||
hud.SetWeapon(data);
|
||||
hud.SetAmmo(currentAmmo, currentReserve);
|
||||
hud.SetReloading(isReloading);
|
||||
}
|
||||
}
|
||||
|
||||
public void OnSelected()
|
||||
{
|
||||
Initialize();
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
public void OnDeselected()
|
||||
{
|
||||
if (recoilRoutine != null)
|
||||
{
|
||||
StopCoroutine(recoilRoutine);
|
||||
recoilRoutine = null;
|
||||
}
|
||||
|
||||
transform.localPosition = originalLocalPosition;
|
||||
}
|
||||
|
||||
public bool CanShoot()
|
||||
{
|
||||
return data != null && !isReloading && currentAmmo > 0 && Time.time >= nextFireTime;
|
||||
}
|
||||
|
||||
public int GetCurrentAmmo()
|
||||
{
|
||||
return currentAmmo;
|
||||
}
|
||||
|
||||
public int GetCurrentReserve()
|
||||
{
|
||||
return currentReserve;
|
||||
}
|
||||
|
||||
public bool IsReloading()
|
||||
{
|
||||
return isReloading;
|
||||
}
|
||||
|
||||
private IEnumerator ReloadRoutine()
|
||||
{
|
||||
isReloading = true;
|
||||
UpdateUI();
|
||||
PlaySound(data.reloadSound);
|
||||
|
||||
yield return new WaitForSeconds(Mathf.Max(0f, data.reloadTime));
|
||||
|
||||
int neededAmmo = Mathf.Max(0, data.magazineSize - currentAmmo);
|
||||
int ammoToLoad = Mathf.Min(neededAmmo, currentReserve);
|
||||
|
||||
currentAmmo += ammoToLoad;
|
||||
currentReserve -= ammoToLoad;
|
||||
isReloading = false;
|
||||
reloadRoutine = null;
|
||||
UpdateUI();
|
||||
}
|
||||
|
||||
private void FireRay()
|
||||
{
|
||||
Vector3 origin = playerCamera.transform.position;
|
||||
|
||||
// Spread is applied around the camera forward vector, so all weapons use the same aiming source.
|
||||
Vector2 randomOffset = Random.insideUnitCircle * Mathf.Max(0f, data.spread);
|
||||
Vector3 direction = playerCamera.transform.forward +
|
||||
playerCamera.transform.right * randomOffset.x +
|
||||
playerCamera.transform.up * randomOffset.y;
|
||||
direction.Normalize();
|
||||
|
||||
Debug.DrawRay(origin, direction * data.range, Color.yellow, 1f);
|
||||
|
||||
if (Physics.Raycast(origin, direction, out RaycastHit hit, Mathf.Max(0.1f, data.range)))
|
||||
{
|
||||
EnemyTarget target = hit.collider.GetComponentInParent<EnemyTarget>();
|
||||
if (target != null)
|
||||
target.TakeDamage(data.damage);
|
||||
|
||||
SpawnHitEffect(hit);
|
||||
}
|
||||
}
|
||||
|
||||
private void ApplyRecoil()
|
||||
{
|
||||
// The FPS controller owns camera look; the weapon only feeds a temporary visual recoil offset.
|
||||
if (fpsController != null)
|
||||
fpsController.AddRecoil(data.recoilForce);
|
||||
else if (playerCamera != null)
|
||||
playerCamera.transform.localRotation = originalCameraRotation * Quaternion.Euler(-data.recoilForce, 0f, 0f);
|
||||
|
||||
if (recoilRoutine != null)
|
||||
StopCoroutine(recoilRoutine);
|
||||
|
||||
recoilRoutine = StartCoroutine(RecoilRoutine());
|
||||
}
|
||||
|
||||
private IEnumerator RecoilRoutine()
|
||||
{
|
||||
Vector3 recoilPosition = originalLocalPosition - Vector3.forward * Mathf.Clamp(data.recoilForce * 0.035f, 0.02f, 0.18f);
|
||||
float t = 0f;
|
||||
|
||||
while (t < 1f)
|
||||
{
|
||||
t += Time.deltaTime * 20f;
|
||||
transform.localPosition = Vector3.Lerp(transform.localPosition, recoilPosition, t);
|
||||
yield return null;
|
||||
}
|
||||
|
||||
t = 0f;
|
||||
while (t < 1f)
|
||||
{
|
||||
t += Time.deltaTime * 10f;
|
||||
transform.localPosition = Vector3.Lerp(transform.localPosition, originalLocalPosition, t);
|
||||
|
||||
if (fpsController == null && playerCamera != null)
|
||||
playerCamera.transform.localRotation = Quaternion.Slerp(playerCamera.transform.localRotation, originalCameraRotation, t);
|
||||
|
||||
yield return null;
|
||||
}
|
||||
|
||||
transform.localPosition = originalLocalPosition;
|
||||
recoilRoutine = null;
|
||||
}
|
||||
|
||||
private void SpawnMuzzleFlash()
|
||||
{
|
||||
if (data.muzzleFlashPrefab == null || firePoint == null)
|
||||
return;
|
||||
|
||||
GameObject flash = Instantiate(data.muzzleFlashPrefab, firePoint.position, firePoint.rotation, firePoint);
|
||||
Destroy(flash, 0.08f);
|
||||
}
|
||||
|
||||
private void SpawnHitEffect(RaycastHit hit)
|
||||
{
|
||||
if (data.hitEffectPrefab == null)
|
||||
return;
|
||||
|
||||
Quaternion rotation = Quaternion.LookRotation(hit.normal);
|
||||
GameObject effect = Instantiate(data.hitEffectPrefab, hit.point + hit.normal * 0.01f, rotation);
|
||||
Destroy(effect, 0.8f);
|
||||
}
|
||||
|
||||
private void PlaySound(AudioClip clip)
|
||||
{
|
||||
if (clip != null && audioSource != null)
|
||||
audioSource.PlayOneShot(clip);
|
||||
}
|
||||
|
||||
private void ShowMessage(string message)
|
||||
{
|
||||
if (hud != null)
|
||||
hud.ShowMessage(message);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c13135be72d369f4899a428acb107b04
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,23 @@
|
||||
using UnityEngine;
|
||||
|
||||
[CreateAssetMenu(fileName = "WeaponData", menuName = "WeaponSystem/WeaponData")]
|
||||
public class WeaponData : ScriptableObject
|
||||
{
|
||||
public string weaponName;
|
||||
public float damage;
|
||||
public float fireRate;
|
||||
public int magazineSize;
|
||||
public int totalReserveAmmo;
|
||||
public float spread;
|
||||
public float recoilForce;
|
||||
public float reloadTime;
|
||||
public int pelletsPerShot;
|
||||
public float range;
|
||||
public bool automatic;
|
||||
public GameObject muzzleFlashPrefab;
|
||||
public GameObject hitEffectPrefab;
|
||||
public AudioClip shootSound;
|
||||
public AudioClip reloadSound;
|
||||
public Color weaponColor;
|
||||
[TextArea(2, 5)] public string description;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 5413f03360d4e8dc6835d6046d00f28b
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,92 @@
|
||||
using System.Collections;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class WeaponHUD : MonoBehaviour
|
||||
{
|
||||
public Text labTitleText;
|
||||
public Text weaponNameText;
|
||||
public Text ammoText;
|
||||
public Text reloadText;
|
||||
public Text hintsText;
|
||||
public Text statsText;
|
||||
public Text messageText;
|
||||
public Text dataDrivenText;
|
||||
|
||||
private Coroutine messageRoutine;
|
||||
|
||||
private void Awake()
|
||||
{
|
||||
SetText(labTitleText, "Лабораторная работа №2 — Data-Driven система оружия");
|
||||
SetText(hintsText, "ЛКМ — стрелять\nR — перезарядка\n1/2/3 или колесо мыши — смена оружия\nWASD + мышь — движение");
|
||||
SetText(dataDrivenText, "Параметры оружия хранятся в ScriptableObject-ассетах в папке Assets/_Data. Для добавления нового оружия достаточно создать новый WeaponData и назначить его объекту Weapon.");
|
||||
SetReloading(false);
|
||||
}
|
||||
|
||||
public void SetWeapon(WeaponData data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
SetText(weaponNameText, "Weapon: None");
|
||||
UpdateWeaponStats(null);
|
||||
return;
|
||||
}
|
||||
|
||||
SetText(weaponNameText, "Weapon: " + data.weaponName);
|
||||
UpdateWeaponStats(data);
|
||||
}
|
||||
|
||||
public void SetAmmo(int currentAmmo, int reserve)
|
||||
{
|
||||
SetText(ammoText, "Ammo: " + currentAmmo + " / " + reserve);
|
||||
}
|
||||
|
||||
public void SetReloading(bool isReloading)
|
||||
{
|
||||
SetText(reloadText, isReloading ? "Reloading..." : "Ready");
|
||||
}
|
||||
|
||||
public void ShowMessage(string message)
|
||||
{
|
||||
if (messageText == null)
|
||||
return;
|
||||
|
||||
if (messageRoutine != null)
|
||||
StopCoroutine(messageRoutine);
|
||||
|
||||
messageRoutine = StartCoroutine(MessageRoutine(message));
|
||||
}
|
||||
|
||||
public void UpdateWeaponStats(WeaponData data)
|
||||
{
|
||||
if (data == null)
|
||||
{
|
||||
SetText(statsText, "Damage: -\nFire Rate: -\nSpread: -\nRecoil: -\nMagazine: -\nPellets: -");
|
||||
return;
|
||||
}
|
||||
|
||||
string stats =
|
||||
"Damage: " + data.damage.ToString("0.##") +
|
||||
"\nFire Rate: " + data.fireRate.ToString("0.##") + " sec" +
|
||||
"\nSpread: " + data.spread.ToString("0.###") +
|
||||
"\nRecoil: " + data.recoilForce.ToString("0.##") +
|
||||
"\nMagazine: " + data.magazineSize +
|
||||
"\nPellets: " + Mathf.Max(1, data.pelletsPerShot);
|
||||
|
||||
SetText(statsText, stats);
|
||||
}
|
||||
|
||||
private IEnumerator MessageRoutine(string message)
|
||||
{
|
||||
SetText(messageText, message);
|
||||
yield return new WaitForSeconds(2f);
|
||||
SetText(messageText, string.Empty);
|
||||
messageRoutine = null;
|
||||
}
|
||||
|
||||
private static void SetText(Text target, string value)
|
||||
{
|
||||
if (target != null)
|
||||
target.text = value;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f8b80e2f8ae09bd4ab5926760700b87a
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
@@ -0,0 +1,115 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
public class WeaponSwitcher : MonoBehaviour
|
||||
{
|
||||
public Weapon[] weapons;
|
||||
public int currentIndex;
|
||||
public Text weaponNameText;
|
||||
public WeaponHUD hud;
|
||||
|
||||
private void Start()
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0)
|
||||
return;
|
||||
|
||||
for (int i = 0; i < weapons.Length; i++)
|
||||
{
|
||||
if (weapons[i] == null)
|
||||
continue;
|
||||
|
||||
weapons[i].Initialize();
|
||||
weapons[i].gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
currentIndex = Mathf.Clamp(currentIndex, 0, weapons.Length - 1);
|
||||
SwitchTo(currentIndex);
|
||||
}
|
||||
|
||||
private void Update()
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0)
|
||||
return;
|
||||
|
||||
if (Input.GetKeyDown(KeyCode.Alpha1))
|
||||
SwitchTo(0);
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha2))
|
||||
SwitchTo(1);
|
||||
else if (Input.GetKeyDown(KeyCode.Alpha3))
|
||||
SwitchTo(2);
|
||||
|
||||
float scroll = Input.GetAxis("Mouse ScrollWheel");
|
||||
if (scroll > 0.01f)
|
||||
NextWeapon();
|
||||
else if (scroll < -0.01f)
|
||||
PreviousWeapon();
|
||||
}
|
||||
|
||||
public void SwitchTo(int index)
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0 || index < 0 || index >= weapons.Length)
|
||||
return;
|
||||
|
||||
// Reloading locks weapon switching to keep ammo state predictable.
|
||||
Weapon currentWeapon = GetCurrentWeapon();
|
||||
if (currentWeapon != null && currentWeapon.IsReloading())
|
||||
{
|
||||
if (hud != null)
|
||||
hud.ShowMessage("Cannot switch while reloading.");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < weapons.Length; i++)
|
||||
{
|
||||
if (weapons[i] == null)
|
||||
continue;
|
||||
|
||||
bool active = i == index;
|
||||
if (!active && weapons[i].gameObject.activeSelf)
|
||||
weapons[i].OnDeselected();
|
||||
|
||||
weapons[i].gameObject.SetActive(active);
|
||||
}
|
||||
|
||||
currentIndex = index;
|
||||
Weapon selected = GetCurrentWeapon();
|
||||
if (selected != null)
|
||||
{
|
||||
selected.OnSelected();
|
||||
|
||||
if (weaponNameText != null && selected.data != null)
|
||||
weaponNameText.text = "Weapon: " + selected.data.weaponName;
|
||||
|
||||
if (hud != null)
|
||||
hud.SetWeapon(selected.data);
|
||||
}
|
||||
}
|
||||
|
||||
public void NextWeapon()
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0)
|
||||
return;
|
||||
|
||||
SwitchTo((currentIndex + 1) % weapons.Length);
|
||||
}
|
||||
|
||||
public void PreviousWeapon()
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0)
|
||||
return;
|
||||
|
||||
int nextIndex = currentIndex - 1;
|
||||
if (nextIndex < 0)
|
||||
nextIndex = weapons.Length - 1;
|
||||
|
||||
SwitchTo(nextIndex);
|
||||
}
|
||||
|
||||
public Weapon GetCurrentWeapon()
|
||||
{
|
||||
if (weapons == null || weapons.Length == 0 || currentIndex < 0 || currentIndex >= weapons.Length)
|
||||
return null;
|
||||
|
||||
return weapons[currentIndex];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dfb51c15323d884dd814637ab1d51485
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
Reference in New Issue
Block a user