Skip to content

Instantly share code, notes, and snippets.

@DraconInteractive
Created August 21, 2022 09:08
Show Gist options
  • Save DraconInteractive/02b32e8ee54731b695e9463ed3be3bc5 to your computer and use it in GitHub Desktop.
Save DraconInteractive/02b32e8ee54731b695e9463ed3be3bc5 to your computer and use it in GitHub Desktop.
Basic logic for VR Bow mechanic
using System;
using System.Collections.Generic;
using UnityEngine;
using UnityEngine.Events;
public class BowTarget : MonoBehaviour
{
// Collection / context pattern
public static List<BowTarget> All = new List<BowTarget>();
// Rigidbody reference. Usually on target, experimenting with having it on attached objects
public Rigidbody rb;
// Vector difference between last frame and this frame. Used for aim assist prediction
public Vector3 Velocity;
// Same as arrow, time to live
public float destroyTTL;
// Toggle for whether to attempt to apply force on hit
public bool usePhysics;
// How much force to apply
public float hitForce;
// Prefab to be spawned on arrow hit
public GameObject hitFXPrefab;
// Scene functions for hit and destroy. Mainly for rapid prototyping, should be changed to delegates / events
public UnityEvent onHit;
public UnityEvent onDestroy;
// Reference to position on last frame
private Vector3 lastPos;
// Helper functions
// First is dot product comparing arrow forward and arrow direction to target (used to find best target)
public float Dot(Vector3 arrowPos, Vector3 arrowForward) => Vector3.Dot((transform.position - arrowPos).normalized, arrowForward);
// Second is finding the closest target to point. Used for ricochet
public static BowTarget ClosestTo(Vector3 point, List<BowTarget> previous = null)
{
if (previous == null)
{
previous = new List<BowTarget>();
}
float dist = Mathf.Infinity;
BowTarget output = null;
foreach (var target in All)
{
if (previous.Contains(target))
{
continue;
}
float d = Vector3.Distance(target.transform.position, point);
if (d < dist)
{
dist = d;
output = target;
}
}
return output;
}
// Enable / Disable for context control
private void OnEnable()
{
All.Add(this);
}
private void OnDisable()
{
All.Remove(this);
}
// Useful event
private void OnDestroy()
{
onDestroy?.Invoke();
}
// Update velocity and last pos
private void Update()
{
Velocity = transform.position - lastPos;
lastPos = transform.position;
}
// Useful for quickly populating the component when placed in scene.
// Can be overriding by dragging in a different rigidbody
private void OnValidate()
{
if (rb == null)
{
rb = GetComponent<Rigidbody>();
}
}
// Called by arrow on hit.
public virtual void Hit(Vector3 point, Vector3 impulse)
{
// Call scene event
onHit?.Invoke();
// Create FX prefab, point it in the direction the arrow came
if (hitFXPrefab != null)
{
Instantiate(hitFXPrefab, point, Quaternion.LookRotation(-impulse), this.transform);
}
// If should apply force, do so. Apply at position to get cool results
if (usePhysics && rb != null)
{
rb.AddForceAtPosition(impulse * hitForce, point);
}
// If TTL is 0, target will remain after hit. Else it will destroy after being hit.
if (destroyTTL != 0)
{
Destroy(this.gameObject, destroyTTL);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment