/// <summary> /// returns the surface type of a single material and no texture regions, /// by raycast hit point /// </summary> protected static vp_SurfaceType GetSimpleSurface(RaycastHit hit) { if (hit.collider == null) { return(null); } vp_SurfaceType s = null; if (!m_SurfacesTypesByCollider.TryGetValue(hit.collider, out s)) { if (!HasMultiMaterial(hit.collider)) { Texture tex = GetMainTexture(hit); if ((tex != null) && (!IsUVTexture(tex))) { s = GetNonUVSurface(tex); } } // associate final result to this collider from now on, whether null or not m_SurfacesTypesByCollider.Add(hit.collider, s); } return(s); }
/// <summary> /// composes a dictionary of all the terrain textures in the level /// and their associated surface types /// </summary> protected void InitTerrainSurfaces() { List <Texture> levelTerrainTextures = GetLevelTerrainTextures(); if (levelTerrainTextures == null) { return; } foreach (Texture tex in levelTerrainTextures) { // see if this terrain texture has been added to the surface manager as a NON-UV texture vp_SurfaceType surfaceType = GetNonUVSurface(tex); // if not, complain with a warning and skip it if (surfaceType == null) { if (IsUVTexture(tex)) { Debug.LogWarning("Warning (SurfaceManager) Terrain texture '" + tex.name + "' has an UV region in the Surface Manager. UV regions do not work with terrain textures. Ground with this texture will fallback to the default surface type."); } else { Debug.LogWarning("Warning (SurfaceManager) Terrain texture '" + tex.name + "' has not been added to the Surface Manager. Ground with this texture will fallback to the default surface type."); } continue; } // if so, add this surface type to the terrain surface dictionary m_SurfacesByTerrainTexture.Add(tex, surfaceType); } }
public static void CreateVpSurfaceType() { vp_SurfaceType asset = (vp_SurfaceType)vp_EditorUtility.CreateAsset("UFPS/Base/Content/Surfaces/SurfaceTypes", typeof(vp_SurfaceType)); if (asset != null) { asset.Init(); } }
/// <summary> /// returns the surface type at a raycast hit point. detects surfaces by /// surface identifier, texture / material, atlas texture and terrain /// </summary> public static vp_SurfaceType GetSurfaceType(RaycastHit hit) { // TIP: if you have problems getting the correct footstep- or projectile // FX to spawn on an object, uncomment all the debug lines in this method // to see what 'GetSurfaceType' detects (if anything) vp_SurfaceType s = null; // --- surface identifier --- // detect objects with a surface identifier vp_SurfaceIdentifier i = GetSurfaceIdentifier(hit); if (i != null) { s = i.SurfaceType; //Debug.Log("hit a surface identifier '" + s .name + "' @ " + Time.time); if (s != null) { return(s); } } // --- simple surface --- // detect objects with a single material and no texture regions s = GetSimpleSurface(hit); if (s != null) { //Debug.Log("hit a single material object with surface '" + s.name + "' @ " + Time.time); return(s); } // --- complex surface --- // detect objects with texture regions (atlases) and / or multiple materials s = GetComplexSurface(hit); if (s != null) { //Debug.Log("hit a multi-material (or uv region fallback) object with surface '" + s.name + "' @ " + Time.time); return(s); } // --- terrain surface --- // check the terrain for a surface if all of the above failed s = GetTerrainSurface(hit); if (s != null) { //Debug.Log("hit a terrain object with surface '" + s.name + "' @ " + Time.time); return(s); } // --- no surface --- // failed to find a surface based on the raycast hit //Debug.Log("failed to find a surface on object '"+hit.transform.gameObject.name+"'"); return(null); }
/// <summary> /// spawns the footprint effect resulting from the 'impactEvent' + 'surface'. /// 'footprintDirection' should be the forward vector of the foot. if /// 'footprintFlip' is true, the decal's X-scale will be inverted. if /// 'footprintVerifyGroundContact' is true (not recommended) the decal /// system will perform four extra raycasts per footstep (!) to verify /// ground contact /// </summary> public static void SpawnFootprintEffect(RaycastHit hit, vp_SurfaceType surface, vp_ImpactEvent impactEvent, Vector3 footprintDirection, bool footprintFlip, bool footprintVerifyGroundContact = false, AudioSource audioSource = null, bool allowDecal = true) { // set footprint flags on the vp_SurfaceEffect system before spawning vp_SurfaceEffect.FootprintDirection = footprintDirection; vp_SurfaceEffect.FootprintFlip = footprintFlip; vp_SurfaceEffect.FootprintVerifyGroundContact = footprintVerifyGroundContact; SpawnEffect(hit, impactEvent, surface, audioSource, allowDecal); vp_SurfaceEffect.FootprintDirection = vp_SurfaceEffect.NO_DIRECTION; vp_SurfaceEffect.FootprintVerifyGroundContact = false; }
/// <summary> /// tries to play the sound related to this object and 'surfaceType'. /// if none found, reverts to fallbacks /// </summary> public bool TryPlaySound(vp_SurfaceType surfaceType) { if (surfaceType == null) { return(false); } if (Audio2 == null) { return(false); } // don't allow audiosources in 'Logarithmic Rolloff' mode to be audible // beyond their max distance (Unity bug?) if (Vector3.Distance(transform.position, UnityEngine.Camera.main.transform.position) > Audio2.maxDistance) { return(false); } if (!vp_Utility.IsActive(gameObject)) { return(false); } bool playedSound = false; int fallback = -1; for (int v = CollisionSounds.Count - 1; v > -1; v--) { if (surfaceType == CollisionSounds[v].SurfaceType) { DoPlaySound(CollisionSounds[v].Sound); playedSound = true; } else if (m_HaveSurfaceManager && (CollisionSounds[v].SurfaceType == vp_SurfaceManager.Instance.Fallbacks.SurfaceType)) { fallback = v; } } if (!playedSound && (fallback >= 0)) { DoPlaySound(CollisionSounds[fallback].Sound); } return(true); }
/// <summary> /// returns the surface effect stored in 'surfaceType' under 'impact' /// (or null if no match) /// </summary> protected static vp_SurfaceEffect GetPrimaryEffect(vp_SurfaceType surfaceType, vp_ImpactEvent impact) { Dictionary <vp_ImpactEvent, vp_SurfaceEffect> impacts = GetImpactFXDictionary(surfaceType); if (impacts == null) { return(null); } if (impacts.Count == 0) { return(null); } vp_SurfaceEffect fx; impacts.TryGetValue(impact, out fx); return(fx); }
/// <summary> /// spawns the effect resulting from 'impactEvent' + 'surface'. if 'surface' /// is null, derives it from the raycast hit /// </summary> public static bool SpawnEffect(RaycastHit hit, vp_ImpactEvent impact, vp_SurfaceType surface = null, AudioSource audioSource = null, bool allowDecal = true) { // 'surface' is optional. if we don't already know it we try to derive it from the RaycastHit if (surface == null) { surface = GetSurfaceType(hit); // if this returns null we will rely on fallbacks } if (allowDecal) { // a surface identifier can always force 'allowdecal' to false allowDecal = AllowsDecals(hit.collider); // test against stretched decals on non-uniform objects if (allowDecal && // if decal is allowed ... (vp_DecalManager.Instance != null) && // ... and we have a decalmanager ... !vp_DecalManager.Instance.AllowStretchedDecals && // ... which is concered about stretching ... !hit.transform.gameObject.isStatic) // ... then unless the target is static ... (decals don't get childed to static objects so there won't be stretching) { // ... then only allow decal in case the object has uniform scale! // (but use an epsilon of '0.00001' in case there is a slight but insignificant scale difference) allowDecal = vp_MathUtility.IsUniform(hit.transform.localScale, 0.00001f); } } vp_SurfaceEffect fx = GetResultingEffect(impact, surface, ref allowDecal); if (fx == null) { return(false); } if (allowDecal) { fx.SpawnWithDecal(hit, audioSource); } else { fx.Spawn(hit, audioSource); } return(true); }
/// <summary> /// retrieves the dictionary of impact effects stored in a certain surfacetype object /// </summary> protected static Dictionary <vp_ImpactEvent, vp_SurfaceEffect> GetImpactFXDictionary(vp_SurfaceType surface) { if (surface == null) { return(null); } //Debug.Log("surface : " + surface); Dictionary <vp_ImpactEvent, vp_SurfaceEffect> dict; if (!m_ImpactFXBySurface.TryGetValue(surface, out dict)) { Dictionary <vp_ImpactEvent, vp_SurfaceEffect> impactFX = new Dictionary <vp_ImpactEvent, vp_SurfaceEffect>(); for (int v = 0; v < surface.ImpactFX.Count; v++) { if (surface.ImpactFX[v].ImpactEvent == null) { continue; } if (impactFX.ContainsKey(surface.ImpactFX[v].ImpactEvent)) { Debug.LogWarning("Warning (vp_SurfaceManager) Surface Type '" + surface + "' has more than one '" + surface.ImpactFX[v].ImpactEvent + "' added. Only the first one will be used."); continue; } impactFX.Add(surface.ImpactFX[v].ImpactEvent, surface.ImpactFX[v].SurfaceEffect); } //Debug.Log("impactFX: " + impactFX); m_ImpactFXBySurface.Add(surface, impactFX); dict = impactFX; } return(dict); }
/// <summary> /// arrives at the most suitable surface effect based on one combo /// of surfaceType + impactEvent (or predefined fallbacks) plus the /// merged 'allowDecal' settings (from the calling method, any surface /// identifiers and the effect itself) /// </summary> protected static vp_SurfaceEffect GetResultingEffect(vp_ImpactEvent impact, vp_SurfaceType surface, ref bool allowDecals) { m_UsingFallbackImpact = false; m_UsingFallbackSurface = false; // if no IMPACT EVENT was provided - attempt to use fallback impact if ((impact == null) && (Instance != null)) { impact = Instance.Fallbacks.ImpactEvent; m_UsingFallbackImpact = true; } if (impact == null) { return(null); } // if no SURFACE TYPE could be found - attempt to use fallback surface if ((surface == null) && (Instance != null)) { surface = Instance.Fallbacks.SurfaceType; m_UsingFallbackSurface = true; } if (surface == null) { return(null); } if (surface.ImpactFX == null) { return(null); } if (surface.ImpactFX.Count == 0) { return(null); } vp_SurfaceEffect fx = GetPrimaryEffect(surface, impact); // if fx is null here and we are not using a fallback surface, it means the // level surface did not contain our impact event: so use fallback surface if ((fx == null) && !m_UsingFallbackSurface && (Instance != null)) { surface = Instance.Fallbacks.SurfaceType; fx = GetPrimaryEffect(surface, impact); } // if fx is null here, the detected surface does not recognize the impact // event, so try again with the SurfaceManager's fallback impact event // (this can solve cases where the surface is the fallback surface and the // impact type is a new, unknown impact type that has not been assigned to // anything) if (fx == null) { fx = GetPrimaryEffect(surface, Instance.Fallbacks.ImpactEvent); } // if fx is null here, we have nothing to work with: abort if (fx == null) { return(null); } // we have an effect! determine if it can be spawned with a decal // (global fallbacks are allowed to override 'allowDecals') if (allowDecals && (fx.Decal.m_Prefabs.Count > 0)) { if (m_UsingFallbackSurface || m_UsingFallbackImpact) { allowDecals = Instance.Fallbacks.AllowDecals; } } else { allowDecals = false; // spawn with sounds & objects only } return(fx); }
/// <summary> /// /// </summary> public virtual void DoTextureFallbacksFoldout() { GUILayout.BeginHorizontal(); GUILayout.Space(10); m_TextureFallbacksFoldout = EditorGUILayout.Foldout(m_TextureFallbacksFoldout, "Texture Fallbacks"); GUILayout.Space(10); GUILayout.EndHorizontal(); if (m_TextureFallbacksFoldout) { GUILayout.Space(0); if (m_Component.ObjectSurfaces != null) { for (int i = 0; i < m_Component.ObjectSurfaces.Count; i++) { vp_SurfaceManager.ObjectSurface surface = m_Component.ObjectSurfaces[i]; if (surface.UVTextures.Count == 0) { vp_SurfaceManager.UVTexture texture = new vp_SurfaceManager.UVTexture(true); surface.UVTextures.Add(texture); } GUILayout.BeginHorizontal(); GUILayout.Space(20); surface.Foldout = EditorGUILayout.Foldout(surface.Foldout, surface.Name); if (GUILayout.Button("Remove", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(50), GUILayout.MaxWidth(50), GUILayout.MinHeight(15))) { m_Component.ObjectSurfaces.RemoveAt(i); i--; } GUI.backgroundColor = Color.white; GUILayout.Space(20); GUILayout.EndHorizontal(); GUILayout.Space(5); if (surface.Foldout) { GUILayout.BeginHorizontal(); GUILayout.Space(35); vp_SurfaceType s = surface.SurfaceType; surface.SurfaceType = (vp_SurfaceType)EditorGUILayout.ObjectField("SurfaceType", surface.SurfaceType, typeof(vp_SurfaceType), false); if ((s != surface.SurfaceType)) { for (int ii = 0; ii < m_Component.ObjectSurfaces.Count; ii++) { if (i != ii) { if (m_Component.ObjectSurfaces[ii].SurfaceType == m_Component.ObjectSurfaces[i].SurfaceType) { EditorUtility.DisplayDialog("Ooops!", "The SurfaceType '" + surface.SurfaceType.name + "' has already been added. Please choose a different SurfaceType.", "OK"); surface.SurfaceType = s; } } } } if ((surface.SurfaceType != null) && (s != surface.SurfaceType)) { surface.Name = surface.SurfaceType.name; } if ((surface.SurfaceType != null) && GUILayout.Button("X", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(12), GUILayout.MaxWidth(12), GUILayout.MinHeight(12))) { surface.SurfaceType = null; } GUILayout.Space(20); GUILayout.EndHorizontal(); if (surface.SurfaceType == null) { surface.Name = "Please assign a SurfaceType"; } GUILayout.BeginHorizontal(); GUILayout.Space(38); if (surface.TexturesFoldout) { surface.TexturesFoldout = EditorGUILayout.Foldout(surface.TexturesFoldout, "Textures", HeaderStyleSelected); } else { surface.TexturesFoldout = EditorGUILayout.Foldout(surface.TexturesFoldout, "Textures"); } GUILayout.EndHorizontal(); if (surface.TexturesFoldout) { if (surface.UVTextures != null) { if (surface.UVTextures.Count > 0) { int counter = 0; for (int x = 0; x < surface.UVTextures.Count; x++) { if (counter == 0) { GUILayout.BeginHorizontal(GUILayout.MinHeight(100)); GUILayout.Space(50); } GUILayout.BeginVertical(GUILayout.MinHeight(90)); GUILayout.Space(12); // moves everything vertically GUILayout.BeginHorizontal(GUILayout.MaxWidth(75), GUILayout.MinWidth(75)); // --- uv editor closed --- GUILayout.Space(8); // left margin for lower buttons if (!surface.UVTextures[x].ShowUV) { GUILayout.FlexibleSpace(); if (GUILayout.Button("X", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(12), GUILayout.MaxWidth(12), GUILayout.MinHeight(12))) { if (((x > 0) || ((x == 0) && (surface.UVTextures[0].Texture != null) || (surface.UVTextures.Count > 1)))) { if (surface.UVTextures[x].Texture != null) { surface.UVTextures[x].Texture = null; vp_SurfaceManager.Instance.Reset(); } else { surface.UVTextures.RemoveAt(x); x = Mathf.Max(0, x - 1); GUI.FocusControl(null); } } //m_Component.SetDirty(true); } GUILayout.Space(-4); // right margin for upper buttons } // --- uv editor open --- GUILayout.EndHorizontal(); if (surface.UVTextures[x].Texture != null) { GUILayout.Space(44); } else { GUILayout.Space(63); } GUILayout.BeginHorizontal(GUILayout.MaxWidth(75), GUILayout.MinWidth(75)); GUILayout.Space(4); // left margin for upper buttons if (surface.UVTextures[x].Texture == null) { surface.UVTextures[x].ShowUV = false; } else if (!surface.UVTextures[x].ShowUV) { if (GUILayout.Button(("UV" + (surface.UVTextures[x].UV != m_Component.DefaultUV ? " *" : "")), vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(25), GUILayout.MaxWidth(25), GUILayout.MinHeight(13))) { surface.UVTextures[x].ShowUV = true; } } GUILayout.FlexibleSpace(); GUILayout.Space(-4); // right margin for upper buttons GUILayout.EndHorizontal(); if (!surface.UVTextures[x].ShowUV) { GUILayout.Space(-63); } else { GUILayout.Space(-25); } GUILayout.Space(-17); // moves texture + upper buttons vertically if (!surface.UVTextures[x].ShowUV) { surface.UVTextures[x].Texture = (Texture)EditorGUILayout.ObjectField(surface.UVTextures[x].Texture, typeof(Texture), false, GUILayout.MinWidth(75), GUILayout.MaxWidth(75), GUILayout.MinHeight(75), GUILayout.MaxHeight((surface.UVTextures[x].ShowUV ? 75 : 75))); } if (surface.UVTextures[x].ShowUV) { Rect v = surface.UVTextures[x].UV; GUILayout.BeginVertical(GUILayout.MaxWidth(75), (GUILayout.MinWidth(75))); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); GUI.SetNextControlName("reset"); if (GUILayout.Button("Reset UV", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinHeight(15), GUILayout.MaxWidth(75))) { surface.UVTextures[x].UV = m_Component.DefaultUV; // focus the reset button to get rid of possible textfield focus // or the textfields won't update properly when resetting GUI.FocusControl("reset"); GUI.FocusControl(null); // unfocus or button highlight may go weird } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.Space(4); GUILayout.BeginHorizontal(GUILayout.MaxWidth(75), GUILayout.MinWidth(75)); surface.UVTextures[x].UV = EditorGUILayout.RectField(surface.UVTextures[x].UV, GUILayout.MinWidth(95), GUILayout.MaxWidth(95)); if (v != surface.UVTextures[x].UV) { surface.UVTextures[x].UV = new Rect( Mathf.Clamp(surface.UVTextures[x].UV.xMin, 0, 1), Mathf.Clamp(surface.UVTextures[x].UV.yMin, 0, 1), Mathf.Clamp(surface.UVTextures[x].UV.width, 0, 1), Mathf.Clamp(surface.UVTextures[x].UV.height, 0, 1) ); } GUILayout.EndHorizontal(); GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Show Texture", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(75), GUILayout.MaxWidth(75), GUILayout.MinHeight(15))) { surface.UVTextures[x].ShowUV = !surface.UVTextures[x].ShowUV; } GUILayout.FlexibleSpace(); GUILayout.EndHorizontal(); GUILayout.EndVertical(); } GUILayout.Space(-80); // top margin for upper buttons GUILayout.BeginHorizontal(GUILayout.MaxWidth(75), GUILayout.MinWidth(75)); GUILayout.Space(8); // left margin for upper buttons // --- uv 2 --- if (!surface.UVTextures[x].ShowUV) { if ((x == 0) && ((surface.UVTextures[0].Texture == null) && (surface.UVTextures.Count == 1))) { GUI.color = Color.clear; } GUILayout.FlexibleSpace(); GUILayout.Button("X", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(12), GUILayout.MaxWidth(12), GUILayout.MinHeight(12)); GUILayout.Space(-4); // right margin for lower buttons GUI.color = Color.white; } GUI.backgroundColor = Color.white; GUILayout.EndHorizontal(); GUILayout.Space(43); GUILayout.BeginHorizontal(GUILayout.MaxWidth(75), GUILayout.MinWidth(75)); GUILayout.Space(4); // left margin for upper buttons if (surface.UVTextures[x].Texture == null) { surface.UVTextures[x].ShowUV = false; } else if (!surface.UVTextures[x].ShowUV) { GUILayout.Button(("UV" + (surface.UVTextures[x].UV != m_Component.DefaultUV ? " *" : "")), vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(25), GUILayout.MaxWidth(25), GUILayout.MinHeight(13)); } GUILayout.FlexibleSpace(); if (!surface.UVTextures[x].ShowUV) { GUILayout.Button("Select", vp_EditorGUIUtility.SmallButtonStyle, GUILayout.MinWidth(35), GUILayout.MaxWidth(35), GUILayout.MinHeight(13)); } GUILayout.Space(-4); // right margin for upper buttons GUILayout.EndHorizontal(); GUILayout.EndVertical(); counter++; if (counter == 4 || x == surface.UVTextures.Count - 1) { GUILayout.Space(20); GUILayout.EndHorizontal(); counter = 0; } } } } GUILayout.BeginHorizontal(); GUILayout.FlexibleSpace(); if (GUILayout.Button("Add Texture Slot", GUILayout.MinWidth(120), GUILayout.MaxWidth(120))) { vp_SurfaceManager.UVTexture texture = new vp_SurfaceManager.UVTexture(true); surface.UVTextures.Add(texture); } GUILayout.Space(20); GUI.backgroundColor = Color.white; GUILayout.EndHorizontal(); } vp_EditorGUIUtility.Separator(); GUILayout.Space(5); } } } if (m_Component.m_ShowHelp && (m_Component.ObjectSurfaces.Count == 0)) { GUILayout.BeginHorizontal(); GUILayout.Space(50); EditorGUILayout.HelpBox("There are no Texture Groups. Click the \"Add Texture Groups\" button to add one.", MessageType.Info); GUILayout.Space(20); GUILayout.EndHorizontal(); } GUILayout.Space(8); GUILayout.BeginHorizontal(); GUILayout.Space(10); if (GUILayout.Button("Add Texture Group", GUILayout.MinWidth(150), GUILayout.MinHeight(25))) { vp_SurfaceManager.ObjectSurface surface = new vp_SurfaceManager.ObjectSurface(); m_Component.ObjectSurfaces.Add(surface); } GUI.backgroundColor = Color.white; GUILayout.Space(20); GUILayout.EndHorizontal(); GUILayout.Space(10); if (m_Component.m_ShowHelp) { GUILayout.BeginHorizontal(); GUILayout.Space(10); GUI.enabled = false; EditorGUILayout.HelpBox("• To create a new SurfaceType fallback for a texture set, click 'Add Texture Group' and assign the vp_SurfaceType. Then, add all the textures you want associated with that particular surface type.\n\n• You can click the 'UV' button to restrict the surface inside a texture. Note that if you want to have several surfaces inside a single texture, you need to add the texture once for every UV region.\n", MessageType.None); GUI.enabled = true; GUILayout.Space(20); GUILayout.EndHorizontal(); } vp_EditorGUIUtility.Separator(); } }
/// <summary> /// places a footstep effect on the ground using the foot, raycast /// hit, footprint flip, audiosource and other settings assigned by the /// previously executed methods. also, updates next allowed footstep /// time for the timed interval mode, and yaw for the 'IsRotating' /// property /// </summary> protected virtual void DoStep() { // mute is a debug feature (for tweaking footstep ranges in multiplayer) if (m_Mute) { return; } // --- play effects using the surface system --- // first retrieve the surface type, so see if it supports footprints. if (FootstepImpactEvent != null) { vp_SurfaceType surface = vp_SurfaceManager.GetSurfaceType(m_Hit); //Debug.Log("surface: " + surface); if (!IsMoving || ((surface == null) || !surface.CanHaveFootprints())) { // non-footrint surface: place a simple effect supporting just audio and particles vp_SurfaceManager.SpawnEffect(m_Hit, FootstepImpactEvent, surface, m_CurrentAudioSource, false); // NOTES: // 1) if surface is null, a fallback surface will be used // 2) checking for 'IsMoving' prevents spawning hundreds of footprint decals under // a player who may be just standing still mouselooking } else { // footprint surface: place the effect with direction, movement and footprint flip info vp_SurfaceManager.SpawnFootprintEffect(m_Hit, surface, FootstepImpactEvent, m_CurrentFoot.transform.forward, m_FlipFootprint, VerifyGroundContact, m_CurrentAudioSource); } } // --- update misc variables for next frame --- // for timed interval mode if (Mode == (int)FootstepMode.FixedTimeInterval) { if (!IsMoving && IsRotating) { m_NextAllowedFootstepTime = Time.time + (TimeInterval * Random.Range(1.0f, 2.0f)); // make foosteps a bit irregular standing still and rotating } else { m_NextAllowedFootstepTime = Time.time + (TimeInterval / Mathf.Max(0.2f, Player.InputMoveVector.Get().magnitude)); // dividing by input move vector allows slowing down the footsteps using an analog controller } } // for 'IsRotating' property m_PrevYaw = transform.eulerAngles.y; // if debugging with the pause feature, pause the editor now #if UNITY_EDITOR if (PauseOnEveryStep) { UnityEditor.EditorApplication.isPaused = true; Debug.Log("Debug (" + this + ") " + (m_FlipFootprint ? " Right " : " Left ") + " footstep triggered @ " + Time.time); } #endif }