//############################################################################################## // If the colliding object is the player, find matching ammo types and give the gun ammo. // If marked to, destroy this object on pickup //############################################################################################## private void OnTriggerEnter(Collider other) { if (other.tag == "Player") { GunComponent[] playerGuns = other.gameObject.GetComponents <GunComponent>(); bool gaveAmmo = false; foreach (var gun in playerGuns) { // Only pickup if the gun isn't maxed out already if (gun.currentGunData.ammoType == ammoType && gun.GetRemainingBoxAmmoCount() < gun.currentGunData.maxBoxAmmoCount) { gaveAmmo = true; gun.GiveAmmo(ammoType, pickupAmount); if (!giveAmmoToAllGuns) { break; } } } if (gaveAmmo) { SoundManagerComponent.PlaySound(ammoPickupSound, gameObject); if (destroyOnPickup) { Destroy(gameObject); } } } }
//############################################################################################## // If marked to, stop the sound on disable //############################################################################################## void OnDisable() { if (stopOnDisable && id != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.StopSound(id); } }
//############################################################################################## // If marked to, stop the sound on destroy //############################################################################################## void OnDestroy() { if (stopOnDestroy && id != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.StopSound(id); } }
//############################################################################################## // Do a simple random selection of the next bark. If it's the same one, decrement to get a // different one. Only do this if we have enough barks. Then play the sound, and record the // result. //############################################################################################## public void Bark() { if (barks.Length < 1) { return; } int newPickedBark = previousPickedBark; if (barks.Length != 1) { newPickedBark = Random.Range(0, barks.Length); if (newPickedBark == previousPickedBark) { newPickedBark = (newPickedBark + 1) % barks.Length; } } else { newPickedBark = 0; } SoundManagerComponent.PlaySound(barks[newPickedBark], gameObject); previousPickedBark = newPickedBark; }
//############################################################################################## // Play the sound with the given settings, on this gameObject, and cache the resulting id //############################################################################################## public void Play() { id = SoundManagerComponent.PlaySound( sound, gameObject ); }
//############################################################################################## // Used to reload the gun, whether manually in a subclass, or from running out of bullets //############################################################################################## protected void ReloadGun() { reloading = true; reloadTimer.Start(); // Play reloading sound if it exists if (currentGunData.reloadSound != null) { reloadSoundId = SoundManagerComponent.PlaySound(currentGunData.reloadSound, muzzleTransform.gameObject); } }
//############################################################################################## // If the colliding object is the player, heal them for the specified amount // If marked to, and only if the heal occured, destroy this object on pickup //############################################################################################## private void OnTriggerEnter(Collider other) { if (other.tag == "Player") { if (other.GetComponent <DamageableComponent>().Heal(healAmount)) { SoundManagerComponent.PlaySound(healthPickupSound, gameObject); if (destroyOnPickup) { Destroy(gameObject); } } } }
//############################################################################################## // When triggered, kick off playing the new sound (at very small volume) and kick off the fading // // This system uses TwoDimensional sound, because it's meant to feel omnipresent and ambient // without any particular directionality. //############################################################################################## private void OnTriggerEnter(Collider other) { // Ignore disabled components if (!enabled) { return; } if (other.tag == "Player") { fading = true; fadeTimer.Start(); fadingInSoundId = SoundManagerComponent.PlaySound(sound, gameObject); } }
//############################################################################################## // Get the player input and trigger the shooting, then play out the appropriate animation // based on the state of the gun. //############################################################################################## protected new void Update() { base.Update(); // Don't update animated gun if the player is dead if (damage.Dead()) { return; } // Either use getMouseButton if the gun is automatic, or getMouseButtonDown if not bool inputTriggerPulled = currentGunData.automaticAction ? Input.GetMouseButton(0) : Input.GetMouseButtonDown(0); if (inputTriggerPulled) { // If we are a manual reload and it's a progressive, interruptible load, do the interrupt if (reloading && currentGunData.manualReload && currentGunData.progressiveReloadInterruption) { reloading = false; // If playing a reload sound, try to stop it. if (reloadSoundId != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.StopSound(reloadSoundId); } } if (base.Shoot()) { player.AddGunRecoil(this); } } bool reloadInput = Input.GetKeyDown(KeyCode.R); // TODO make this a setting if (!reloading && reloadInput && currentGunData.useAmmo && currentGunData.manualReload && remainingMagazineAmmoCount < currentGunData.maxMagazineAmmoCount && remainingBoxAmmoCount != 0) { ReloadGun(); } bool zooming = Zooming(); gunAnimator.SetBool(idleAnimationName, !shooting && !reloading && !zooming); gunAnimator.SetBool(shootAnimationName, shooting && !zooming); gunAnimator.SetBool(reloadAnimationName, reloading && !zooming); gunAnimator.SetBool(scopeAnimationName, zooming); }
//############################################################################################## // If it's fading, drive the fade from the timer. Fade out the old sound and in the new. // When finished, stop the old, then set the shared current sound. //############################################################################################## void Update() { if (fading) { float p = fadeTimer.Parameterized(); if (currentAmbientSoundId != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.SetSoundVolume(currentAmbientSoundId, (1.0f - p)); } if (fadingInSoundId != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.SetSoundVolume(fadingInSoundId, p); } if (fadeTimer.Finished()) { fading = false; SoundManagerComponent.StopSound(currentAmbientSoundId); currentAmbientSoundId = fadingInSoundId; } } }
//############################################################################################## // Update the bullet, moving it along it's velocity, unless it collides with something. // If that something is a damageable, deal the damage to it. Either way, mark the bullet // for destruction next update. // This is done in Fixed Update so that the physics is properly synchronized for the bullet. //############################################################################################## void FixedUpdate() { // Prevents updating before firing information has been provided, since fixed update is // disjoint from regular unity update if (!fired) { return; } // The destruction is done 1 frame after being marked for kill so the bullet and effects // appear in the correct position visually for that last frame, before bullet is destroyed. if (shouldKill) { // Notify all delegates if (bulletDestroyedDelegates != null) { foreach (OnBulletDestroyed bulletDestroyedDelegate in bulletDestroyedDelegates) { bulletDestroyedDelegate(); } } // Destroy if not pooled, otherwise mark this bullet as freed if (poolIdentifier == null) { Destroy(gameObject); } else { PooledGameObjectManager.FreeInstanceToPool(poolIdentifier, gameObject); } return; } velocity += Physics.gravity * gravityModifier * Time.deltaTime * Time.deltaTime; Vector3 move = velocity * Time.deltaTime; float moveDist = move.magnitude; // Kill the bullet if it's gone too far if ((transform.position - startPosition).sqrMagnitude >= maxDistance * maxDistance) { shouldKill = true; } // See if move would hit anything, ignoring the 'no bullet collide' layer and triggers RaycastHit hit; if (Physics.Raycast(transform.position, move, out hit, moveDist, ~NO_BULLET_COLLIDE_LAYER, QueryTriggerInteraction.Ignore)) { if (hit.collider.gameObject != firer) { transform.position = hit.point; DamageableComponent damageable = hit.collider.gameObject.GetComponent <DamageableComponent>(); if (damageable == null) { DamageablePieceComponent damageablePiece = hit.collider.gameObject.GetComponent <DamageablePieceComponent>(); if (damageablePiece != null) { damageable = damageablePiece.GetDamageableComponent(); } } if (damageable != null) { // Never ricochet off a damageable collisionsRemaining = 0; damageable.DealDamage(damage, type, startPosition, firer); } else { // Don't spawn decals when hitting damageable if (optionalDecalObject != null && damageable == null) { GameObject decalInstance = GameObject.Instantiate(optionalDecalObject); // Add random offset to prevent z-fighting float randomOffset = (Random.value * BULLET_EFFECTS_OFFSET); decalInstance.transform.position = hit.point + (hit.normal * (BULLET_DECAL_OFFSET + randomOffset)); decalInstance.transform.rotation = Quaternion.LookRotation(hit.normal); } // TODO add impact effects lookup system for hit object if (optionalImpactEffects != null) { GameObject fx = GameObject.Instantiate(optionalImpactEffects); // Scoot fx back away from collision a little fx.transform.position = transform.position + (-move).normalized * BULLET_EFFECTS_OFFSET; } } // Play impact sound if needed if (impactSound != null) { SoundManagerComponent.PlaySound(impactSound, gameObject); } if (collisionsRemaining > 0) { collisionsRemaining--; if (Random.value <= collisionModeChance) { if (collisionMode == CollisionMode.Ricochet) { float velocityMagnitude = velocity.magnitude; velocity = Vector3.Reflect(velocity.normalized, hit.normal).normalized *velocityMagnitude; transform.position = hit.point + (velocity * 0.01f); } else if (collisionMode == CollisionMode.Pierce) { transform.position += move; } } else { collisionsRemaining = 0; } } if (collisionsRemaining <= 0) { shouldKill = true; } } } else { transform.position += move; } }
//############################################################################################## // Get the player input and trigger the shooting, then play out the appropriate animation // based on the state of the gun. //############################################################################################## protected new void Update() { base.Update(); if (tintWithNearestLight) { UpdateTintWithNearestLight(); } // Don't update the rest of the animated gun if the player is dead if (damage.Dead()) { return; } if (useGunBob) { UpdateGunBob(); } // Either use getMouseButton if the gun is automatic, or getMouseButtonDown if not bool inputTriggerPulled = currentGunData.automaticAction ? Input.GetMouseButton(0) : Input.GetMouseButtonDown(0); if (inputTriggerPulled) { // If we are a manual reload and it's a progressive, interruptible load, do the interrupt if (reloading && currentGunData.manualReload && currentGunData.progressiveReloadInterruption) { reloading = false; // If playing a reload sound, try to stop it. if (reloadSoundId != SoundManagerComponent.INVALID_SOUND) { SoundManagerComponent.StopSound(reloadSoundId); } } if (base.Shoot()) { currentFrame = 0; player.AddGunRecoil(this); if (reloading) { reloadingAnimationTimer.Start(); state = AnimatedGunState.Reloading; gunSpriteImage.sprite = reloadingSprites[0]; } else { firingAnimationTimer.Start(); state = AnimatedGunState.Shooting; gunSpriteImage.sprite = firingSprites[0]; } } } bool reloadInput = Input.GetKeyDown(KeyCode.R); // TODO make this a setting if (!reloading && reloadInput && currentGunData.useAmmo && currentGunData.manualReload && remainingMagazineAmmoCount < currentGunData.maxMagazineAmmoCount && remainingBoxAmmoCount != 0) { ReloadGun(); reloadingAnimationTimer.Start(); state = AnimatedGunState.Reloading; gunSpriteImage.sprite = reloadingSprites[0]; currentFrame = 0; } // Animating the Gun if (state == AnimatedGunState.Shooting) { if (firingAnimationTimer.Finished()) { firingAnimationTimer.Start(); currentFrame++; if (currentFrame >= firingSprites.Length) { state = AnimatedGunState.Idle; gunSpriteImage.sprite = idleSprite; } else { gunSpriteImage.sprite = firingSprites[currentFrame]; } } } else if (state == AnimatedGunState.Reloading) { if (reloadingAnimationTimer.Finished()) { reloadingAnimationTimer.Start(); currentFrame++; if (currentFrame >= reloadingSprites.Length || !reloading) { state = AnimatedGunState.Idle; gunSpriteImage.sprite = idleSprite; } else { gunSpriteImage.sprite = reloadingSprites[currentFrame]; } } } }
//############################################################################################## // Setup the instance, sources, records, and raycast directions. // Also error check the whole process. //############################################################################################## public void Awake() { instance = this; // Setup records, hinting at the capacity if (records == null) { soundIdIndex = 0; records = new List <SoundRecord>(); records.Capacity = SOUND_SOURCE_COUNT; } // Setup the sources from scratch, creating a new gameObject and adding the needed components // Then, place these newly created sources into the unusedSources if (usedSources == null) { usedSources = new List <AudioSource>(); usedSources.Capacity = SOUND_SOURCE_COUNT; transform.position = Vector3.zero; unusedSources = new Queue <AudioSource>(); for (int i = 0; i < SOUND_SOURCE_COUNT; ++i) { GameObject newObject = new GameObject(); newObject.AddComponent <AudioSource>(); newObject.AddComponent <AudioReverbFilter>(); newObject.GetComponent <AudioReverbFilter>().reverbPreset = AudioReverbPreset.Off; newObject.AddComponent <AudioLowPassFilter>(); newObject.GetComponent <AudioLowPassFilter>().cutoffFrequency = SoundConstants.LPF_CLEAR; newObject.gameObject.transform.parent = transform; #if UNITY_EDITOR newObject.gameObject.name = ("Sound " + i); #endif // UNITY_EDITOR AudioSource source = newObject.GetComponent <AudioSource>(); unusedSources.Enqueue(source); } } // Basically just hard-code the 6 cardinal directions into the array, so we can loop over // them later, when calculating reverb for each direction. if (raycastDirections == null) { raycastDirections = new Vector3[DIRECTIONS_COUNT]; raycastDirections[0] = Vector3.up; raycastDirections[1] = -Vector3.up; raycastDirections[2] = Vector3.right; raycastDirections[3] = -Vector3.right; raycastDirections[4] = Vector3.forward; raycastDirections[5] = -Vector3.forward; } // Check that the audio listener in game is set up correctly. This is necessary, so the // occlusion raycasts don't collide against the listener itself. AudioListener audioListener = FindObjectOfType(typeof(AudioListener)) as AudioListener; listener = audioListener.gameObject; if (1 << listener.layer != IGNORE_SOUND_RAYCAST_LAYER) { Logger.Error("Listener " + listener + " has incorrect layer " + (1 << listener.layer) + ", should be " + IGNORE_SOUND_RAYCAST_LAYER); } }
//############################################################################################## // Create the bullet(s) and send them shooting in the direction of muzzleTransform. Also spawn // effects if available. // The argument can be used to change the damage amount (usually called from subclasses to // modify damage) // return value indicates whether or not the gun actually fired. //############################################################################################## public bool Shoot(float damage) { if (gunTimer.Finished() && !reloading) { if (remainingMagazineAmmoCount == 0 && remainingBoxAmmoCount == 0) { return(BULLET_NOT_FIRED); } gunTimer.Start(); shooting = true; remainingMagazineAmmoCount--; if (remainingMagazineAmmoCount == 0 && remainingBoxAmmoCount > 0) { ReloadGun(); } // This is for shotgun-type weapons. It spawns several bullets in a random cone for (int i = 0; i < currentGunData.shots; ++i) { GameObject bulletInstance = null; if (currentGunData.usePooledBullets) { bulletInstance = PooledGameObjectManager.GetInstanceFromPool(currentGunData.poolIdentifier); } else { bulletInstance = GameObject.Instantiate(currentGunData.bulletPrefab); } BulletComponent bullet = bulletInstance.GetComponent <BulletComponent>(); if (currentGunData.usePooledBullets) { bullet.SetAsPooled(currentGunData.poolIdentifier); } if (bullet == null) { Logger.Error("Bullet Prefab " + currentGunData.bulletPrefab.name + " must have a bullet component"); return(BULLET_NOT_FIRED); } // Apply non-zero spread. This can be for shotgun scatter, // or for inaccurate, normal guns Quaternion spreadOffset = Quaternion.identity; if (currentGunData.spread > 0.0f) { spreadOffset = Quaternion.AngleAxis(Random.value * currentGunData.spread, Vector3.right); Quaternion rot = Quaternion.AngleAxis(Random.value * 360.0f, Vector3.forward); spreadOffset = rot * spreadOffset; } Vector3 bulletVelocity = Vector3.zero; bulletInstance.transform.position = muzzleTransform.position + muzzleTransform.TransformDirection(currentGunData.muzzleOffset); bulletInstance.transform.rotation = muzzleTransform.rotation * spreadOffset; bulletVelocity = bulletInstance.transform.forward * currentGunData.muzzleVelocity; // Add in player velocity if necessary if (player != null) { bulletVelocity += player.GetVelocity(); } // Notify the bullet it's been fired bullet.Fire(damage, currentGunData.damageType, bulletVelocity, gameObject); } // Spawn effects if available, outside the loop, so there's only ever one if (currentGunData.firingEffectsPrefab != null) { GameObject effectsInstance = GameObject.Instantiate(currentGunData.firingEffectsPrefab); effectsInstance.transform.parent = muzzleTransform; effectsInstance.transform.localPosition = currentGunData.firingEffectsOffset; } // If there's a casing particle, emit 1 if (optionalCasingParticle != null) { optionalCasingParticle.Emit(1); } // Play firing sound if it exists, outside the loop, so there's only ever one if (currentGunData.fireSound != null) { SoundManagerComponent.PlaySound(currentGunData.fireSound, muzzleTransform.gameObject); } return(BULLET_FIRED); } else { return(BULLET_NOT_FIRED); } }