Created
August 5, 2019 09:07
-
-
Save qwe321qwe321qwe321/1c08b08cccc3fb8a1ef3ecba4c580181 to your computer and use it in GitHub Desktop.
A sticky platformer controller from the game PROJECT1ON in GMTKJam2019. https://itch.io/jam/gmtk-2019/rate/463690
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 System.Collections; | |
using System.Collections.Generic; | |
using UnityEngine; | |
using Clipping; | |
using VFX; | |
[RequireComponent(typeof(Rigidbody2D))] | |
[RequireComponent(typeof(StickyAnimationController))] | |
public class StickyController : MonoBehaviour { | |
private static StickyControoler s_Instance; | |
public static StickyControoler Instance { | |
get { | |
if (s_Instance) { | |
return s_Instance; | |
} | |
return null; | |
} | |
} | |
[Header("Misc")] | |
public bool canControl = true; | |
public bool usePhysics = true; | |
[Header("Basic")] | |
public float gravity; | |
public float moveSpeed; | |
public float airMoveSpeed; | |
public float colliderTestMinDist = 0.01f; | |
[Header("Dash")] | |
public float dashSpeed = 20f; | |
public float dashTime = 0.1f; | |
public GhostEffectPool ghostEffector; | |
public CreateCircularSpectrum audioSpectrum; | |
[Header("Grab")] | |
public float grabRadiusFactor = 0.1f; | |
public LayerMask grabLayerMask; | |
private StickyAnimationController m_animationController; | |
private Rigidbody2D m_Rigidbody; | |
private Collider2D m_Collider2D; | |
private Coroutine dashCorutine; | |
[SerializeField] | |
private bool isDashing; | |
[SerializeField] | |
private bool isSticked; | |
[SerializeField] | |
private bool canDash; | |
public bool IsDead { get; private set; } | |
public bool IsGoal { get; private set; } | |
private float inputX; | |
private float inputY; | |
private Vector2 inputDirection; | |
private Vector2 dashDirection; | |
private Vector2 colliderSize; | |
private float colliderRadius; | |
private float gravityVelocity; | |
[SerializeField] | |
private Vector2 moveVector; | |
private void Awake() | |
{ | |
if (s_Instance != null) { | |
Destroy(this.gameObject); | |
return; | |
} | |
s_Instance = this; | |
m_Rigidbody = GetComponent<Rigidbody2D>(); | |
m_Collider2D = GetComponent<Collider2D>(); | |
m_animationController = GetComponent<StickyAnimationController>(); | |
gravityVelocity = 0; | |
dashDirection = Vector2.zero; | |
moveVector = Vector2.zero; | |
colliderSize = m_Collider2D.bounds.size; | |
colliderRadius = Mathf.Min(colliderSize.x, colliderSize.y) / 2; | |
if (ghostEffector != null) { | |
ghostEffector.enabled = false; | |
} | |
} | |
private void FixedUpdate() | |
{ | |
if (!usePhysics) { return; } | |
if (IsDead || IsGoal) { return; } | |
// Consume moveVector. | |
Vector3 nextPos = transform.position + (Vector3)moveVector; | |
moveVector = Vector3.zero; | |
// Grab. | |
var overlaps = Physics2D.OverlapCircleAll(nextPos, colliderRadius * grabRadiusFactor, grabLayerMask); | |
if (overlaps != null && overlaps.Length > 0) | |
{ | |
float minDist = float.PositiveInfinity; | |
Vector3 nearestPos = transform.position; | |
Vector3 nearestDir = Vector3.zero; | |
Collider2D nearestCollider = null; | |
foreach(var overlap in overlaps) | |
{ | |
float dist; | |
Vector3 dir; | |
Vector2 pos = Grab(nextPos, overlap, out dist, out dir); | |
if (dist < minDist) | |
{ | |
minDist = dist; | |
nearestPos = pos; | |
nearestDir = dir; | |
nearestCollider = overlap; | |
} | |
} | |
// Trigger Collider. | |
if(nearestCollider != null) | |
{ | |
ClipObject clipObject = nearestCollider.gameObject.GetComponent<ClipObject>(); | |
if(clipObject != null && clipObject.IsClipped) | |
{ | |
StepOnClipObjAttr(clipObject.objAttr, nearestDir.normalized); | |
} | |
} | |
isSticked = true; | |
canDash = true; | |
// Stick position | |
if (nearestPos != transform.position) { | |
nextPos = nearestPos; | |
debug_NearestPos = nearestPos; | |
//transform.position = nearestPos; | |
} | |
} else { | |
isSticked = false; | |
} | |
m_Rigidbody.MovePosition(nextPos); | |
} | |
private void Update() | |
{ | |
if (!usePhysics) { return; } | |
if (IsDead || IsGoal) { return; } | |
if (canControl) { | |
inputX = (Input.GetKey(KeyCode.D) ? 1 : 0) - (Input.GetKey(KeyCode.A) ? 1 : 0); | |
inputY = (Input.GetKey(KeyCode.W) ? 1 : 0) - (Input.GetKey(KeyCode.S) ? 1 : 0); | |
inputDirection = new Vector2(inputX, inputY).normalized; | |
// Dash Trigger | |
if (canDash && Input.GetKeyDown(KeyCode.Space) && (inputX != 0 || inputY != 0)) { | |
canDash = false; | |
isSticked = false; | |
dashDirection = inputDirection; | |
if (dashCorutine != null) { StopCoroutine(dashCorutine); } | |
dashCorutine = StartCoroutine(DashTimer(dashTime)); | |
AudioMaster.Instance.PlaySound(SFX.Dash); | |
// VFX | |
BloomEffector.Bloom(0.5f); | |
CameraShaker.Shake(0.2f, 0.1f); | |
audioSpectrum.HypeTime(0.2f); | |
gravityVelocity = 0; // Reset | |
} | |
} | |
// Movement | |
if (isDashing) { // Dashing movement. | |
moveVector += dashDirection * dashSpeed * Time.deltaTime; | |
} else { | |
if (isSticked) { // Grounded movement. | |
moveVector += inputDirection * moveSpeed * Time.deltaTime; | |
} else { // Airbone movement. | |
moveVector += inputDirection * airMoveSpeed * Time.deltaTime; | |
} | |
// Gravity | |
if (!isSticked) { | |
gravityVelocity += gravity * Time.deltaTime; | |
} else { // Sticked without gravity. | |
gravityVelocity = 0; | |
} | |
moveVector += Vector2.down * gravityVelocity * Time.deltaTime; | |
} | |
} | |
public void ReleaseControl(bool keepOriginalVel) { | |
canControl = false; | |
if (!keepOriginalVel) { | |
inputDirection = Vector2.zero; | |
} | |
} | |
public void GainControl() { | |
canControl = true; | |
} | |
public void DisablePhysics(bool stopControl) { | |
if (stopControl) { | |
ReleaseControl(false); | |
} | |
usePhysics = false; | |
} | |
public void EnablePhysics() { | |
usePhysics = true; | |
} | |
IEnumerator DashTimer(float duration) | |
{ | |
ghostEffector.enabled = true; | |
isDashing = true; | |
yield return new WaitForSeconds(duration); | |
isDashing = false; | |
ghostEffector.enabled = false; | |
canDash = false; | |
dashCorutine = null; | |
} | |
Vector3 PointOnLine(Vector3 p0, Vector3 p1, Vector3 p) | |
{ | |
float l2 = Vector3.Distance(p0, p1); | |
if (l2 == 0.0f) | |
return p0; | |
Vector3 vec01 = p1 - p0; | |
float t = Mathf.Clamp01(Vector3.Dot(p - p0, vec01) / Vector3.Dot(vec01, vec01)); | |
Vector3 projection = p0 + t * vec01; | |
return projection; | |
} | |
float DistanceToLine(Vector3 p0, Vector3 p1, Vector3 p) | |
{ | |
Vector3 proj = PointOnLine(p0, p1, p); | |
return (proj - p).magnitude; | |
} | |
private Vector2 Grab(Vector3 position, Collider2D collider, out float minDistance, out Vector3 dir) | |
{ | |
MeshFilter mf = collider.gameObject.GetComponent<MeshFilter>(); | |
minDistance = float.PositiveInfinity; | |
dir = Vector3.zero; | |
if (mf != null) { | |
int intersectionCount = 0; | |
Vector3 nearestPoint = Vector3.zero; | |
Vector3 localPos = collider.gameObject.transform.InverseTransformPoint(position); | |
Mesh mesh = mf.mesh; | |
Vector3 last_p = mesh.vertices[mesh.vertices.Length - 2]; | |
for (int i = 0; i < mesh.vertices.Length-1; i++) { | |
Vector3 p0 = last_p; | |
Vector3 p1 = mesh.vertices[i]; | |
Vector3 p2 = PointOnLine(p0, p1, localPos); | |
float d0 = (p2 - localPos).magnitude; | |
if (d0 < minDistance) { | |
minDistance = d0; | |
nearestPoint = p2; | |
dir = (localPos - p2).normalized * (colliderRadius + colliderTestMinDist); | |
} | |
if ((p0.x - p1.x) != 0) { | |
float m = (p0.y - p1.y) / (p0.x - p1.x); | |
float b = p0.y - m * p0.x; | |
if ((p0.x <= localPos.x && p1.x > localPos.x || p1.x < localPos.x && p0.x >= localPos.x) && ((localPos.x * m + b) > localPos.y)) { | |
intersectionCount += 1; | |
} | |
} | |
last_p = p1; | |
} | |
if((intersectionCount & 1) > 0) { | |
dir *= -1; | |
} | |
return collider.gameObject.transform.TransformPoint(nearestPoint + dir); | |
} | |
return Vector2.zero; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment