Skip to content

Instantly share code, notes, and snippets.

@DraconInteractive
Created August 21, 2022 08:50
Show Gist options
  • Save DraconInteractive/c264172b3095a7f31a78c819c808de9d to your computer and use it in GitHub Desktop.
Save DraconInteractive/c264172b3095a7f31a78c819c808de9d to your computer and use it in GitHub Desktop.
Basic logic flow for VR Bow mechanic
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Arrow : MonoBehaviour
{
[Header("Main")]
public Transform tip;
public float movementSpeed;
public float ttl = 5;
public float dropRate;
[Header("Aim Assist")]
public float pathCorrectionBase;
float pathCorrection;
private float velocityCorrection;
[Space]
public GameObject[] activateOnLaunch;
protected Coroutine launchR;
protected bool launched;
protected Vector3 velocity;
protected BowTarget target;
// Called on 'proper' notch release from BowModule
public virtual void Launch(float power, BowTarget target, float correctionMult = 0, float velocityCorrectionMult = 0.1f)
{
// Apply slight starting rotation depending on aim assist. Makes further assistance less noticeable
if (target != null)
{
Quaternion targetRot = Quaternion.LookRotation((target.transform.position - transform.position).normalized, Vector3.up);
transform.rotation = Quaternion.Lerp(transform.rotation, targetRot, correctionMult);
}
// Setup values for assistance
if (correctionMult != 0)
{
pathCorrection = pathCorrectionBase * correctionMult;
velocityCorrection = velocityCorrectionMult;
}
// Release from nock, and set arrow speed
transform.SetParent(null);
movementSpeed *= power;
// Start controlling arrow, set state and turn on any useful objects
launchR = StartCoroutine(LaunchRoutine(target));
launched = true;
foreach (GameObject go in activateOnLaunch)
{
go.SetActive(true);
}
}
// Post-launch arrow control
protected virtual IEnumerator LaunchRoutine(BowTarget _target)
{
target = _target;
// ttl = time to live. Therefore: while (still alive)
while (ttl > 0)
{
ttl -= Time.deltaTime;
// Update last tip pos
Vector3 tipPos_last = transform.position;
// Increase drop rate. Gravity is accelleration so this replicates that
dropRate += dropRate * Time.deltaTime;
// Calculate base arrow movement vector
Vector3 delta = transform.forward * movementSpeed + Vector3.down * dropRate;
delta *= Time.deltaTime;
// Update velocity, apply movement
velocity = delta;
transform.position += delta;
// Update rotation to face velocity (needed for drop)
transform.rotation = Quaternion.LookRotation(delta.normalized, Vector3.up);
// If targetting, adjust rotation towards target
if (target != null)
{
// Use target velocity to get better prediction of required target position
Vector3 targetPos = target.transform.position + target.Velocity * velocityCorrection;
Vector3 targetDir = targetPos - transform.position;
transform.rotation = Quaternion.RotateTowards(transform.rotation, Quaternion.LookRotation(targetDir.normalized, Vector3.up), pathCorrection * Time.deltaTime);
}
// Check if arrow has hit anything this frame
DetectHit(tipPos_last);
yield return null;
}
// After time to live runs out, destroy arrow
Destroy(this.gameObject);
yield break;
}
// Check between last frame and this frame whether arrow hit something
protected virtual void DetectHit(Vector3 tipPos)
{
// Cast ray from arrow tip position in direction of velocity
Ray ray = new Ray(tipPos, velocity.normalized);
RaycastHit hit;
// If obstacle detected, try to get target component and forward to hit function
if (Physics.Raycast(ray, out hit, velocity.magnitude))
{
BowTarget t = hit.transform.GetComponent<BowTarget>();
BaseHit(hit, t);
}
}
protected virtual void BaseHit(RaycastHit hit, BowTarget t)
{
// Get vector from base of arrow / nock to tip of arrow
Vector3 tipDelta = tip.position - transform.position;
// Make sure the arrow actually launched before it hit anything
if (launched)
{
// Move arrow so that the tip is embedded in the hit point (looks cooler)
transform.position = hit.point - (tipDelta * 0.95f);
// If we hit a BowTarget, forward on the results
if (t != null)
{
t.Hit(tip.position, velocity);
}
// Stop the movement handling routine
StopCoroutine(launchR);
// Destroy this in 3 seconds. With no LaunchRoutine, TTL is disabled.
// This is nicer since it gives the player a chance to see the arrow properly in the target before it disappears
Destroy(this.gameObject, 3);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment