/// <summary> /// /// </summary> void Start() { // verify collider m_Collider = transform.GetComponent <Collider>(); if (m_Collider == null) { Debug.LogError("Error (" + this + ") This component requires a collider. Disabling self!"); this.enabled = false; return; } // find target damage handler if (TargetObject != null) { m_TargetDamageHandler = TargetObject.GetComponentInChildren <vp_DamageHandler>(); } else { m_TargetDamageHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_Collider); if (m_TargetDamageHandler != null) { TargetObject = m_TargetDamageHandler.gameObject; } } }
/// <summary> /// /// </summary> void OnTriggerEnter(Collider col) { // return if this is not a relevant object. TIP: this check can be expanded if (col.gameObject.layer == vp_Layer.Debris || col.gameObject.layer == vp_Layer.Pickup) return; // try to find a damagehandler on the target and abort on fail m_TargetDamageHandler = vp_DamageHandler.GetDamageHandlerOfCollider(col); if (m_TargetDamageHandler == null) return; // abort if target is already dead // NOTE: this deals with cases of multiple 'OnTriggerEnter' calls on contact if (m_TargetDamageHandler.CurrentHealth <= 0) return; // try to find a respawner on the target to see if it's currently OK to kill it m_TargetRespawner = vp_Respawner.GetByCollider(col); if (m_TargetRespawner != null) { // abort if target has respawned within one second before this call. // NOTE: this addresses a case where 'OnTriggerEnter' is called when // teleporting (respawning) away from the trigger, resulting in the // object getting insta-killed on respawn. it will only work if the // target gameobject has a vp_Respawner-derived component if (Time.time < m_TargetRespawner.LastRespawnTime + 1.0f) return; } m_TargetDamageHandler.Damage(new vp_DamageInfo(m_TargetDamageHandler.CurrentHealth, m_TargetDamageHandler.Transform, vp_DamageInfo.DamageType.KillZone)); }
/// <summary> /// attempts to do damage using a regular Unity-message, and / or more advanced /// UFPS format damage (whichever is supported by the bullet and target) /// </summary> protected virtual void TryDamage() { // send primitive damage as UnityMessage. this allows support for many third party // systems (simply use a 'void Damage(float)' method in target MonoBehaviours) if ((DamageMode == vp_DamageInfo.DamageMode.UnityMessage) || (DamageMode == vp_DamageInfo.DamageMode.Both)) { m_Hit.collider.SendMessage(DamageMethodName, Damage, SendMessageOptions.DontRequireReceiver); #if UNITY_EDITOR if (!m_DidWarnAboutBothMethodName && (DamageMethodName == "Damage") && (vp_DamageHandler.GetDamageHandlerOfCollider(m_Hit.collider) != null)) { Debug.LogWarning("Warning (" + this + ") Target object has a vp_DamageHandler. When damaging it with DamageMode: 'UnityMessage' or 'Both', you probably want to change 'DamageMethodName' to something other than 'Damage', or too much damage might be applied."); m_DidWarnAboutBothMethodName = true; } #endif } // send damage in UFPS format. this allows different damage types, and tracking damage source if ((DamageMode == vp_DamageInfo.DamageMode.DamageHandler) || (DamageMode == vp_DamageInfo.DamageMode.Both)) { m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_Hit.collider); if (m_TargetDHandler != null) { DoUFPSDamage(); } } }
// Update is called once per frame void Update() { timer += Time.deltaTime; vp_DamageHandler vp_DamageHandler = simplePlayer.GetComponent <vp_DamageHandler>(); score = vp_DamageHandler.deathCount; scoreLabel.text = score.ToString(); timeLabel.text = timer.ToString(); }
public void OnTriggerStay(Collider other) { vp_DamageHandler handler = other.GetComponent <vp_DamageHandler>(); if (handler) { handler.Damage(new vp_DamageInfo(DamagePerSecond * Time.deltaTime, transform)); } }
public override void OnAwake() { damageHandler = GetDefaultGameObject(targetGameObject.Value).GetComponent <vp_DamageHandler>(); if (damageHandler == null) { Debug.LogError("Error: Unable to find the vp_DamageHandler component on " + targetGameObject.Value); return; } prevHealth = damageHandler.CurrentHealth; }
/// <summary> /// retrieves, finds and caches target damagehandlers for more /// efficient fetching in the future /// </summary> public static vp_DamageHandler GetDamageHandlerOfCollider(Collider col) { // try to fetch a known damagehandler on this target if (!Instances.TryGetValue(col, out m_GetDamageHandlerOfColliderResult)) { // no damagehandler on record: see if there is one m_GetDamageHandlerOfColliderResult = col.transform.root.GetComponentInChildren <vp_DamageHandler>(); Instances.Add(col, m_GetDamageHandlerOfColliderResult); // add result to the dictionary (even if null) } return(m_GetDamageHandlerOfColliderResult); }
/// <summary> /// this method is called when any object is damaged locally in the /// master scene, transmitting its new health to all remote clients. /// it is typically initiated by vp_DamageHandler /// </summary> protected virtual void TransmitDamage(Transform targetTransform, Transform sourceTransform, float damage) { // NOTES: // 1) players (vp_PlayerDamageHandlers) will have health synced perfectly across // all machines at all times // 2) health of plain vp_DamageHandlers (props) is only kept in perfect sync if // 'SyncPropHealth' is true. however, when their health reaches zero on the // master and a 'TransmitKill' message occurs the prop in question will // always die immediately on all machines // 3) 'sourceTransform' is not used here, but needed for overrides that deal // with more complex gameplay (see example in 'vp_DMDamageCallbacks') // 4) 'damage' is assumed to have already been updated in the master scene // damage handler. it is not used here, but overrides can do a lot more with // it (see example in 'vp_DMDamageCallbacks'). // 5) 'damage' can be both positive and negative. a negative number will add health if (!PhotonNetwork.isMasterClient) { return; } int viewID = vp_MPMaster.GetViewIDOfTransform(targetTransform); if (viewID == 0) { return; } vp_DamageHandler d = GetDamageHandlerOfViewID(viewID); if (d == null) { return; } // abort if target already died (no health to update) if (d.CurrentHealth <= 0.0f) { return; } // abort if this is a prop and we're not supposed to sync prop health if (!SyncPropHealth && !(d is vp_PlayerDamageHandler)) { return; } photonView.RPC("ReceiveObjectHealth", PhotonTargets.Others, viewID, (float)d.CurrentHealth); // NOTE: cast to float required for Anti-Cheat Toolkit support }
/// <summary> /// /// </summary> void DoDamage() { m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_TargetCollider); if (m_TargetDHandler != null) { // target has a known damagehandler -> send damage in UFPS format. // this works with targets that have a vp_DamageHandler (or derived) component m_TargetDHandler.Damage(new vp_DamageInfo((DistanceModifier * Damage), Source, OriginalSource, vp_DamageInfo.DamageType.Explosion)); } else if (!RequireDamageHandler) { // target is known to have no damagehandler -> send damage the 'Unity way'. // this works with targets that have a custom script with the standard // method: "Damage(float damage)" m_TargetCollider.gameObject.BroadcastMessage(DamageMessageName, (DistanceModifier * Damage), SendMessageOptions.DontRequireReceiver); } }
protected virtual void ReceiveObjectHealth(int viewID, float health, PhotonMessageInfo info) { //vp_MPDebug.Log("GOT OBJECT HEALTH!"); if ((info.sender != PhotonNetwork.masterClient) || (info.sender.isLocal)) { return; } vp_DamageHandler d = GetDamageHandlerOfViewID(viewID); if (d == null) { return; } d.CurrentHealth = health; }
public virtual void ReceiveObjectKill(int viewId, PhotonMessageInfo info) { if (info.sender != PhotonNetwork.masterClient) { return; } vp_DamageHandler d = GetDamageHandlerOfViewID(viewId); if ((d == null) || (d is vp_PlayerDamageHandler)) { return; } // cache respawner before we deactivate the object, or 'GetRespawnerOfViewID' // won't be able to find the deactivated object later GetRespawnerOfViewID(viewId); d.Die(); }
void Start() { playerObject = GameObject.FindGameObjectWithTag("Player"); charAnim = gameObject.GetComponentInChildren <Animation>(); charAnim.animation["run"].wrapMode = WrapMode.Loop; //charAnim.animation["run"].wrapMode = WrapMode.Loop; controller = GetComponent <CharacterController>(); damageHandler = GetComponent <vp_DamageHandler>(); //heading = Random.Range(0, 360); transform.eulerAngles = new Vector3(0, Random.Range(0, 360), 0); nextPlayerTest = Time.time + testPlayerDelay; AIState = "Wander"; charAnim.CrossFade("run"); firing = false; //playAnimIdle(); }
/// <summary> /// caches and returns the damagehandler of the given photonview id. /// damagehandlers are stored in a dictionary that resets on level load /// </summary> public static vp_DamageHandler GetDamageHandlerOfViewID(int id) { vp_DamageHandler d = null; if (!m_DamageHandlersByViewID.TryGetValue(id, out d)) { PhotonView p = PhotonView.Find(id); if (p != null) { d = p.transform.GetComponent <vp_DamageHandler>(); if (d != null) { m_DamageHandlersByViewID.Add(id, d); } return(d); } // NOTE: we do not add null results, since photonviews come and go } return(d); }
void Start() { // hide frost and heat icon //tempHeatColor = heatIcon.color; //tempHeatColor.a = 0.0f; //heatIcon.color = tempHeatColor; damg = player.GetComponent <vp_FPPlayerDamageHandler>(); //Debug.Log (damg.GetType()); //hide frost icon on start tempFrostColor = frostIcon.color; tempFrostColor.a = 0.0f; frostIcon.color = tempFrostColor; tempHeatColor = heatIcon.color; currentFrostTemp = (int)THERMAL_LIMITS.MAX_TEMP; }
/// <summary> /// /// </summary> void OnTriggerEnter(Collider col) { // return if this is not a relevant object. TIP: this check can be expanded if (col.gameObject.layer == vp_Layer.Debris || col.gameObject.layer == vp_Layer.Pickup) { return; } // try to find a damagehandler on the target and abort on fail m_TargetDamageHandler = vp_DamageHandler.GetDamageHandlerOfCollider(col); if (m_TargetDamageHandler == null) { return; } // abort if target is already dead // NOTE: this deals with cases of multiple 'OnTriggerEnter' calls on contact if (m_TargetDamageHandler.CurrentHealth <= 0) { return; } // try to find a respawner on the target to see if it's currently OK to kill it m_TargetRespawner = vp_Respawner.GetByCollider(col); if (m_TargetRespawner != null) { // abort if target has respawned within one second before this call. // NOTE: this addresses a case where 'OnTriggerEnter' is called when // teleporting (respawning) away from the trigger, resulting in the // object getting insta-killed on respawn. it will only work if the // target gameobject has a vp_Respawner-derived component if (Time.time < m_TargetRespawner.LastRespawnTime + 1.0f) { return; } } m_TargetDamageHandler.Damage(new vp_DamageInfo(m_TargetDamageHandler.CurrentHealth, m_TargetDamageHandler.Transform, vp_DamageInfo.DamageType.KillZone)); }
/// <summary> /// /// </summary> static void CreateRespawnerForDamageHandler(vp_DamageHandler damageHandler) { if (damageHandler.gameObject.GetComponent <vp_Respawner>() || damageHandler.gameObject.GetComponent <vp_PlayerRespawner>()) { return; } vp_Respawner respawner = null; if (damageHandler is vp_FPPlayerDamageHandler) { respawner = damageHandler.gameObject.AddComponent <vp_PlayerRespawner>(); } else { respawner = damageHandler.gameObject.AddComponent <vp_Respawner>(); } if (respawner == null) { return; } if (damageHandler.MinRespawnTime != -99999.0f) { respawner.MinRespawnTime = damageHandler.MinRespawnTime; } if (damageHandler.MaxRespawnTime != -99999.0f) { respawner.MaxRespawnTime = damageHandler.MaxRespawnTime; } if (damageHandler.RespawnCheckRadius != -99999.0f) { respawner.ObstructionRadius = damageHandler.RespawnCheckRadius; } if (damageHandler.RespawnSound != null) { respawner.SpawnSound = damageHandler.RespawnSound; } }
/// <summary> /// retrieves, finds and caches target damagehandlers for more /// efficient fetching in the future /// </summary> public static vp_DamageHandler GetDamageHandlerOfCollider(Collider col) { m_GetDamageHandlerOfColliderResult = null; // try to fetch a known damagehandler on this target, if (!Instances.TryGetValue(col, out m_GetDamageHandlerOfColliderResult)) { // no damagehandler info on record for this collider: see if we can find // one on the transform or any of its ancestors (the lowest ancestor with // a damagehandler will be cached as belonging to this collider) Transform t = col.transform; while ((t != null) && (m_GetDamageHandlerOfColliderResult == null)) { m_GetDamageHandlerOfColliderResult = t.GetComponent <vp_DamageHandler>(); t = t.parent; } Instances.Add(col, m_GetDamageHandlerOfColliderResult); // add result to the dictionary (even if null) } return(m_GetDamageHandlerOfColliderResult); }
/// <summary> /// /// </summary> void Start() { // verify collider m_Collider = transform.GetComponent<Collider>(); if (m_Collider == null) { Debug.LogError("Error (" + this + ") This component requires a collider. Disabling self!"); this.enabled = false; return; } // find target damage handler if (TargetObject != null) m_TargetDamageHandler = TargetObject.GetComponentInChildren<vp_DamageHandler>(); else { m_TargetDamageHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_Collider); if(m_TargetDamageHandler != null) TargetObject = m_TargetDamageHandler.gameObject; } }
public override void SetValue(object value) { mValue = (vp_DamageHandler)value; }
/// <summary> /// /// </summary> static void CreateRespawnerForDamageHandler(vp_DamageHandler damageHandler) { if (damageHandler.gameObject.GetComponent<vp_Respawner>() || damageHandler.gameObject.GetComponent<vp_PlayerRespawner>()) return; vp_Respawner respawner = null; if(damageHandler is vp_FPPlayerDamageHandler) respawner = damageHandler.gameObject.AddComponent<vp_PlayerRespawner>(); else respawner = damageHandler.gameObject.AddComponent<vp_Respawner>(); if (respawner == null) return; if (damageHandler.MinRespawnTime != -99999.0f) respawner.MinRespawnTime = damageHandler.MinRespawnTime; if (damageHandler.MaxRespawnTime != -99999.0f) respawner.MaxRespawnTime = damageHandler.MaxRespawnTime; if (damageHandler.RespawnCheckRadius != -99999.0f) respawner.ObstructionRadius = damageHandler.RespawnCheckRadius; if (damageHandler.RespawnSound != null) respawner.SpawnSound = damageHandler.RespawnSound; }
/// <summary> /// retrieves, finds and caches target damagehandlers for more /// efficient fetching in the future /// </summary> public static vp_DamageHandler GetDamageHandlerOfCollider(Collider col) { // try to fetch a known damagehandler on this target if (!DamageHandlersByCollider.TryGetValue(col, out m_GetDamageHandlerOfColliderResult)) { // no damagehandler on record: see if there is one m_GetDamageHandlerOfColliderResult = col.transform.root.GetComponentInChildren<vp_DamageHandler>(); DamageHandlersByCollider.Add(col, m_GetDamageHandlerOfColliderResult); // add result to the dictionary (even if null) } return m_GetDamageHandlerOfColliderResult; }
/// <summary> /// spawns effects, applies forces and damage to close by objects /// and flags the explosion for removal /// </summary> void DoExplode() { m_HaveExploded = true; // spawn effects gameobjects foreach (GameObject fx in FXPrefabs) { if (fx != null) { #if UNITY_EDITOR Component[] c; c = fx.GetComponents<vp_Explosion>(); // OK from a performance perspective because it only occurs in editor if (c.Length > 0) Debug.LogError("Error: vp_Explosion->FXPrefab must not be a vp_Explosion (risk of infinite loop)."); else #endif vp_Utility.Instantiate(fx, Transform.position, Transform.rotation); } } // clear the list of affected objects in case this explosion has been pooled m_DHandlersHitByThisExplosion.Clear(); // apply shockwave to all rigidbodies and FPSPlayers within range, but // ignore small and walk-thru objects such as debris, triggers and water Collider[] colliders = Physics.OverlapSphere(Transform.position, Radius, vp_Layer.Mask.IgnoreWalkThru); foreach (Collider hit in colliders) { if (hit.gameObject.isStatic) continue; m_DistanceModifier = 0.0f; if ((hit != null) && (hit != this.collider)) { m_TargetCollider = hit; m_TargetTransform = hit.transform; // --- add camera shake --- AddUFPSCameraShake(); // --- abort if we have no line of sight to target --- if (TargetInCover()) continue; // --- try to add force --- m_TargetRigidbody = hit.rigidbody; if (m_TargetRigidbody != null) // target has a rigidbody: apply force using Unity physics AddRigidbodyForce(); else // target has no rigidbody. try and apply force using UFPS physics AddUFPSForce(); // --- try to add damage --- m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_TargetCollider); if (m_TargetDHandler != null) // this was a known damagehandler target: send the damage! DoUFPSDamage(DistanceModifier * Damage); else if (!RequireDamageHandler) // this target was known to have no damagehandler. send damage using unitymessage (if allowed) DoUnityDamage(DistanceModifier * Damage); //Debug.Log(m_TargetTransform.name + Time.time); // SNIPPET: to dump affected objects } } // play explosion sound Audio.clip = Sound; Audio.pitch = Random.Range(SoundMinPitch, SoundMaxPitch) * Time.timeScale; if (!Audio.playOnAwake) Audio.Play(); }
/// <summary> /// spawns effects, applies forces and damage to close by objects /// and flags the explosion for removal /// </summary> void DoExplode() { m_HaveExploded = true; // spawn effects gameobjects foreach (GameObject fx in FXPrefabs) { if (fx != null) { #if UNITY_EDITOR Component[] c; c = fx.GetComponents <vp_Explosion>(); // OK from a performance perspective because it only occurs in editor if (c.Length > 0) { Debug.LogError("Error: vp_Explosion->FXPrefab must not be a vp_Explosion (risk of infinite loop)."); } else #endif vp_Utility.Instantiate(fx, Transform.position, Transform.rotation); } } // clear the list of affected objects in case this explosion has been pooled m_DHandlersHitByThisExplosion.Clear(); // apply shockwave to all rigidbodies and FPSPlayers within range, but // ignore small and walk-thru objects such as debris, triggers and water Collider[] colliders = Physics.OverlapSphere(Transform.position, Radius, vp_Layer.Mask.IgnoreWalkThru); foreach (Collider hit in colliders) { if (hit.gameObject.isStatic) { continue; } m_DistanceModifier = 0.0f; if ((hit != null) && (hit != this.GetComponent <Collider>())) { m_TargetCollider = hit; m_TargetTransform = hit.transform; // --- add camera shake --- AddUFPSCameraShake(); // --- abort if we have no line of sight to target --- if (TargetInCover()) { continue; } // --- try to add force --- m_TargetRigidbody = hit.GetComponent <Rigidbody>(); if (m_TargetRigidbody != null) // target has a rigidbody: apply force using Unity physics { AddRigidbodyForce(); } else // target has no rigidbody. try and apply force using UFPS physics { AddUFPSForce(); } // --- try to add damage --- m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_TargetCollider); if (m_TargetDHandler != null) // this was a known damagehandler target: send the damage! { DoUFPSDamage(DistanceModifier * Damage); } else if (!RequireDamageHandler) // this target was known to have no damagehandler. send damage using unitymessage (if allowed) { DoUnityDamage(DistanceModifier * Damage); } //Debug.Log(m_TargetTransform.name + Time.time); // SNIPPET: to dump affected objects } } // play explosion sound Audio.clip = Sound; Audio.pitch = Random.Range(SoundMinPitch, SoundMaxPitch) * Time.timeScale; if (!Audio.playOnAwake) { Audio.Play(); } }
/// <summary> /// retrieves, finds and caches target damagehandlers for more /// efficient fetching in the future /// </summary> public static vp_DamageHandler GetDamageHandlerOfCollider(Collider col) { m_GetDamageHandlerOfColliderResult = null; // try to fetch a known damagehandler on this target, if (!Instances.TryGetValue(col, out m_GetDamageHandlerOfColliderResult)) { // no damagehandler info on record for this collider: see if we can find // one on the transform or any of its ancestors (the lowest ancestor with // a damagehandler will be cached as belonging to this collider) Transform t = col.transform; while ((t != null) && (m_GetDamageHandlerOfColliderResult == null)) { m_GetDamageHandlerOfColliderResult = t.GetComponent<vp_DamageHandler>(); t = t.parent; } Instances.Add(col, m_GetDamageHandlerOfColliderResult); // add result to the dictionary (even if null) } return m_GetDamageHandlerOfColliderResult; }
/// <summary> /// everything happens in the DoHit method. the script that /// spawns the bullet is responsible for setting its position /// and angle. after being instantiated, the bullet immediately /// raycasts ahead for its full range, then snaps itself to /// the surface of the first object hit. it then spawns a /// number of particle effects and plays a random impact sound. /// </summary> void DoHit() { Ray ray = new Ray(m_Transform.position, m_Transform.forward); // if this bullet was fired by the local player, don't allow it to hit the local player if ((m_Source != null) && (m_Source.gameObject.layer == vp_Layer.LocalPlayer)) LayerMask = vp_Layer.Mask.BulletBlockers; // raycast against all big, solid objects except the player itself // SNIPPET: using this instead may be useful in cases where bullets // fail to hit colliders (however likely at a performance cost) //if (Physics.Linecast(m_Transform.position, m_Transform.position + (m_Transform.forward * Range), out hit, LayerMask)) if (Physics.Raycast(ray, out m_Hit, Range, LayerMask)) { // NOTE: we can't bail out of this if-statement based on !collider.isTrigger, // because that would make bullets _disappear_ if they hit a trigger. to make a // trigger not interfere with bullets, put it in the layer: 'vp_Layer.Trigger' // (default: 27) // move this gameobject instance to the hit object Vector3 scale = m_Transform.localScale; // save scale to handle scaled parent objects m_Transform.parent = m_Hit.transform; m_Transform.localPosition = m_Hit.transform.InverseTransformPoint(m_Hit.point); m_Transform.rotation = Quaternion.LookRotation(m_Hit.normal); // face away from hit surface if (m_Hit.transform.lossyScale == Vector3.one) // if hit object has normal scale m_Transform.Rotate(Vector3.forward, Random.Range(0, 360), Space.Self); // spin randomly else { // rotated child objects will get skewed if the parent object has been // unevenly scaled in the editor, so on scaled objects we don't support // spin, and we need to unparent, rescale and reparent the decal. m_Transform.parent = null; m_Transform.localScale = scale; m_Transform.parent = m_Hit.transform; } // if hit object has physics, add the bullet force to it Rigidbody body = m_Hit.collider.attachedRigidbody; if (body != null && !body.isKinematic) body.AddForceAtPosition(((ray.direction * Force) / Time.timeScale) / vp_TimeUtility.AdjustedTimeScale, m_Hit.point); // spawn impact effect if (m_ImpactPrefab != null) vp_Utility.Instantiate(m_ImpactPrefab, m_Transform.position, m_Transform.rotation); // spawn dust effect if (m_DustPrefab != null) vp_Utility.Instantiate(m_DustPrefab, m_Transform.position, m_Transform.rotation); // spawn spark effect if (m_SparkPrefab != null) { if (Random.value < m_SparkFactor) vp_Utility.Instantiate(m_SparkPrefab, m_Transform.position, m_Transform.rotation); } // spawn debris particle fx if (m_DebrisPrefab != null) vp_Utility.Instantiate(m_DebrisPrefab, m_Transform.position, m_Transform.rotation); // play impact sound if (m_ImpactSounds.Count > 0) { m_Audio.pitch = Random.Range(SoundImpactPitch.x, SoundImpactPitch.y) * Time.timeScale; m_Audio.clip = m_ImpactSounds[(int)Random.Range(0, (m_ImpactSounds.Count))]; m_Audio.Stop(); m_Audio.Play(); } // do damage if possible m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_Hit.collider); // try to find a damage handler on the target if ((m_TargetDHandler != null) && (m_Source != null)) { // this was a known damagehandler target and we know the source: send UFPS damage! m_TargetDHandler.Damage(new vp_DamageInfo(Damage, m_Source, vp_DamageInfo.DamageType.Bullet)); } else if (!RequireDamageHandler) { // this target was known to have no damagehandler: send damage using unitymessage (if allowed) m_Hit.collider.SendMessage(DamageMethodName, Damage, SendMessageOptions.DontRequireReceiver); } // prevent adding decals to objects based on layer if ((m_Renderer != null) && NoDecalOnTheseLayers.Length > 0) { foreach (int layer in NoDecalOnTheseLayers) { if (m_Hit.transform.gameObject.layer != layer) continue; m_Renderer.enabled = false; TryDestroy(); return; } } // if bullet is visible (i.e. has a decal), queue it for deletion later if(m_Renderer != null) vp_DecalManager.Add(gameObject); else vp_Timer.In(1, TryDestroy); // we have no renderer, so destroy object in 1 sec } else vp_Utility.Destroy(gameObject); // hit nothing, so self destruct immediately }
/// <summary> /// everything happens in the DoHit method. the script that /// spawns the bullet is responsible for setting its position /// and angle. after being instantiated, the bullet immediately /// raycasts ahead for its full range, then snaps itself to /// the surface of the first object hit. it then spawns a /// number of particle effects and plays a random impact sound. /// </summary> void DoHit() { Ray ray = new Ray(m_Transform.position, m_Transform.forward); // if this bullet was fired by the local player, don't allow it to hit the local player if ((m_Source != null) && (m_Source.gameObject.layer == vp_Layer.LocalPlayer)) { LayerMask = vp_Layer.Mask.BulletBlockers; } // raycast against all big, solid objects except the player itself // SNIPPET: using this instead may be useful in cases where bullets // fail to hit colliders (however likely at a performance cost) //if (Physics.Linecast(m_Transform.position, m_Transform.position + (m_Transform.forward * Range), out hit, LayerMask)) if (Physics.Raycast(ray, out m_Hit, Range, LayerMask)) { // NOTE: we can't bail out of this if-statement based on !collider.isTrigger, // because that would make bullets _disappear_ if they hit a trigger. to make a // trigger not interfere with bullets, put it in the layer: 'vp_Layer.Trigger' // (default: 27) // move this gameobject instance to the hit object Vector3 scale = m_Transform.localScale; // save scale to handle scaled parent objects m_Transform.parent = m_Hit.transform; m_Transform.localPosition = m_Hit.transform.InverseTransformPoint(m_Hit.point); m_Transform.rotation = Quaternion.LookRotation(m_Hit.normal); // face away from hit surface if (m_Hit.transform.lossyScale == Vector3.one) // if hit object has normal scale { m_Transform.Rotate(Vector3.forward, Random.Range(0, 360), Space.Self); // spin randomly } else { // rotated child objects will get skewed if the parent object has been // unevenly scaled in the editor, so on scaled objects we don't support // spin, and we need to unparent, rescale and reparent the decal. m_Transform.parent = null; m_Transform.localScale = scale; m_Transform.parent = m_Hit.transform; } // if hit object has physics, add the bullet force to it Rigidbody body = m_Hit.collider.attachedRigidbody; if (body != null && !body.isKinematic) { body.AddForceAtPosition(((ray.direction * Force) / Time.timeScale) / vp_TimeUtility.AdjustedTimeScale, m_Hit.point); } // spawn impact effect if (m_ImpactPrefab != null) { vp_Utility.Instantiate(m_ImpactPrefab, m_Transform.position, m_Transform.rotation); } // spawn dust effect if (m_DustPrefab != null) { vp_Utility.Instantiate(m_DustPrefab, m_Transform.position, m_Transform.rotation); } // spawn spark effect if (m_SparkPrefab != null) { if (Random.value < m_SparkFactor) { vp_Utility.Instantiate(m_SparkPrefab, m_Transform.position, m_Transform.rotation); } } // spawn debris particle fx if (m_DebrisPrefab != null) { vp_Utility.Instantiate(m_DebrisPrefab, m_Transform.position, m_Transform.rotation); } // play impact sound if (m_ImpactSounds.Count > 0) { m_Audio.pitch = Random.Range(SoundImpactPitch.x, SoundImpactPitch.y) * Time.timeScale; m_Audio.clip = m_ImpactSounds[(int)Random.Range(0, (m_ImpactSounds.Count))]; m_Audio.Stop(); m_Audio.Play(); } // do damage if possible m_TargetDHandler = vp_DamageHandler.GetDamageHandlerOfCollider(m_Hit.collider); // try to find a damage handler on the target if ((m_TargetDHandler != null) && (m_Source != null)) { // this was a known damagehandler target and we know the source: send UFPS damage! m_TargetDHandler.Damage(new vp_DamageInfo(Damage, m_Source, vp_DamageInfo.DamageType.Bullet)); } else if (!RequireDamageHandler) { // this target was known to have no damagehandler: send damage using unitymessage (if allowed) m_Hit.collider.SendMessage(DamageMethodName, Damage, SendMessageOptions.DontRequireReceiver); } // prevent adding decals to objects based on layer if ((m_Renderer != null) && NoDecalOnTheseLayers.Length > 0) { foreach (int layer in NoDecalOnTheseLayers) { if (m_Hit.transform.gameObject.layer != layer) { continue; } m_Renderer.enabled = false; TryDestroy(); return; } } // if bullet is visible (i.e. has a decal), queue it for deletion later if (m_Renderer != null) { vp_DecalManager.Add(gameObject); } else { vp_Timer.In(1, TryDestroy); // we have no renderer, so destroy object in 1 sec } } else { vp_Utility.Destroy(gameObject); // hit nothing, so self destruct immediately } }