Last active
April 13, 2022 21:17
-
-
Save LordNed/c16715c1b7d9a496aa19 to your computer and use it in GitHub Desktop.
A 2D grass that sways and springs back when you walk through it or land on it. Place onto a 2D quad.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
using UnityEngine; | |
using System.Collections; | |
[RequireComponent(typeof(BoxCollider2D))] | |
public class ReactiveGrassSway : MonoBehaviour | |
{ | |
[SerializeField] private float m_bendForceOnExit = -0.1f; | |
[SerializeField] private bool m_windIsEnabled; | |
[SerializeField] private float m_baseWindForce = 0f; | |
[SerializeField] private float m_windPeriod = 0f; | |
[SerializeField] private float m_windOffset; | |
[SerializeField] private float m_windForceMultiplier = 0f; | |
[SerializeField] private float m_bendFactor =0.5f; | |
private bool m_isBending; | |
private bool m_isRebounding; | |
private float m_exitOffset; | |
private float m_enterOffset; | |
private float m_colliderHalfWidth; | |
[SerializeField] private Spring m_spring= new Spring(); | |
private Mesh m_meshCache; | |
private Transform m_transformCache; | |
private Collider2D m_colliderCache; | |
private void Awake() | |
{ | |
m_colliderCache = collider2D; | |
m_colliderCache.isTrigger = true; | |
m_colliderHalfWidth = collider2D.bounds.size.x / 2f; | |
m_transformCache = transform; | |
m_meshCache = GetComponent<MeshFilter>().mesh; | |
} | |
private void OnDestroy() | |
{ | |
Destroy(m_meshCache); | |
} | |
private void OnTriggerEnter2D(Collider2D col) | |
{ | |
CharacterMovement charMovement = col.GetComponent<CharacterMovement>(); | |
if(charMovement != null) | |
{ | |
m_enterOffset = col.transform.position.x - m_transformCache.position.x; | |
if (charMovement.Velocity.y < -3f) | |
{ | |
// Apply a force in the proper direction based on which half of the grass we landed on. | |
if (col.transform.position.x < m_transformCache.position.x) | |
m_spring.ApplyAdditiveForce(-m_bendForceOnExit); | |
else | |
m_spring.ApplyAdditiveForce(m_bendForceOnExit); | |
m_isRebounding = true; | |
} | |
} | |
} | |
private void OnTriggerStay2D(Collider2D col) | |
{ | |
if(col.GetComponent<Character>() != null) | |
{ | |
float offset = col.transform.position.x - m_transformCache.position.x; | |
if(m_isBending || Mathf.Sign(m_enterOffset) != Mathf.Sign(offset)) | |
{ | |
m_isRebounding =false; | |
m_isBending = true; | |
// Figure out how far we have moved into the trigger and then map the offset to a [-1, 1] range. | |
float radius = m_colliderHalfWidth + m_colliderCache.bounds.size.x * 0.5f; | |
m_exitOffset = Map(offset, -radius, radius, -1f, 1f); | |
// Apply a slow drag back to center | |
m_exitOffset *= 0.9f; | |
SetHorizontalOffset(m_exitOffset); | |
} | |
} | |
} | |
private void OnTriggerExit2D(Collider2D col) | |
{ | |
if (col.GetComponent<Character>() != null) | |
{ | |
if (m_isBending) | |
{ | |
// Apply a force in the opposite direction of the way that we're currently bending | |
m_spring.ApplyForceStartingAtPosition(m_bendForceOnExit * Mathf.Sign(m_exitOffset), m_exitOffset); | |
} | |
m_isBending = false; | |
m_isRebounding = true; | |
} | |
} | |
private void FixedUpdate() | |
{ | |
if (m_windIsEnabled && !m_isBending) | |
{ | |
var windForce = m_baseWindForce + Mathf.Pow(Mathf.Sin(Time.time * m_windPeriod + m_windOffset) * 0.7f + 0.05f, 4) * 0.05f * m_windForceMultiplier; | |
m_spring.ApplyAdditiveForce(windForce); | |
// Only simulate if we're not rebounding, as that overwritten below. | |
if (!m_isRebounding) | |
{ | |
SetHorizontalOffset(m_spring.Simulate()); | |
} | |
} | |
if (m_isRebounding) | |
{ | |
SetHorizontalOffset(m_spring.Simulate()); | |
// Apply spring forces until its acceleration dies off. | |
if (Mathf.Abs(m_spring.Velocity) < 0.000005f) | |
{ | |
// Reset the grass to zero and stop rebounding. | |
SetHorizontalOffset(0f); | |
m_isRebounding = false; | |
} | |
} | |
} | |
private void SetHorizontalOffset(float offset) | |
{ | |
//MaterialPropertyBlock dest = new MaterialPropertyBlock(); | |
//GetComponent<SpriteRenderer>().GetPropertyBlock(dest); | |
//dest.SetFloat("_FlashAmount", offset); | |
//GetComponent<SpriteRenderer>().SetPropertyBlock(dest); | |
var verts = m_meshCache.vertices; | |
verts[1].x = 0.5f + offset * m_bendFactor / m_transformCache.localScale.x; | |
verts[3].x = -0.5f + offset * m_bendFactor / m_transformCache.localScale.x; | |
m_meshCache.vertices = verts; | |
} | |
public static float Map(float value, float leftMin, float leftMax, float rightMin, float rightMax) | |
{ | |
return rightMin + (value - leftMin) * (rightMax - rightMin) / (leftMax - leftMin); | |
} | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[System.Serializable] | |
public class Spring | |
{ | |
public float Dampening = 0.1f; | |
public float Tension = 0.05f; | |
public float BaseHeight; //get; private set;? | |
public float Velocity; // get; private set;? | |
public float CurrentHeight; // get; private set;? | |
public void ApplyAdditiveForce(float force) | |
{ | |
Velocity += force; | |
} | |
public void ApplyForceStartingAtPosition(float force, float position) | |
{ | |
CurrentHeight = position; | |
Velocity = force; | |
} | |
public float Simulate() | |
{ | |
float heightOffset = BaseHeight - CurrentHeight; | |
Velocity += Tension * heightOffset - Velocity * Dampening; | |
CurrentHeight += Velocity; | |
return CurrentHeight; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Understood, this information does help. Appreciate the response, @LordNed. Cheers.