Skip to content

Instantly share code, notes, and snippets.

@LordNed
Last active April 13, 2022 21:17
Show Gist options
  • Save LordNed/c16715c1b7d9a496aa19 to your computer and use it in GitHub Desktop.
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.
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);
}
}
[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;
}
}
@iRook
Copy link

iRook commented Apr 13, 2022

Understood, this information does help. Appreciate the response, @LordNed. Cheers.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment