Hi there! I’m hoping that a bunch of smarter than me people can help me out here. I have a script (Yes I’ve used GPT codepilot but I don’t intend on releasing this unless I change it drastically). I first followed a tutorial (I do know a fair bit about C# and Unity’s libraries, but not enough to generate my own ideas) .
So in this script I essentially want the sword to be thrown from the empty game object its attached to (from where the camera is facing as reference) with a rotational spin that doesn’t look dumb. But I also want it to ricochet off walls without loosing speed. Sort of like that one scene in captain America or how it works in Fortnite but not so much locking onto enemies just sort of ricochet off of any collision, and then retract into the hand like Mjollnir.
I’m running into a few issues. (and this is mostly because I’ve been using GPY let’s be honest). Mostly in getting the ricochet to behave how I’m intending. The physics just don’t seem to work in my favour. Ive made the collider a sphere to see if that would help and adjusted the code to match, but im just not getting the result im looking for.
I also have a problem where the sword is not being held in the same position as i’ve placed it in the editor. My man’s is sorta gripping the blade. seems uncomfortable.
I’ll attach the script. Any advice / someone smarter than myself’s wisdom would be greatly appreciated! Thank you.
p.s. I know the code is horrible I do apologise for that its mostly AI generated.
using System.Collections;
using UnityEngine;
public class ThrowSword : MonoBehaviour
{
// Variables
public Transform throwPoint; // The point where the sword retracts to
public float throwPower = 20f; // Power applied to throw the sword
public float retractSpeed = 15f; // Speed at which the sword retracts
public float retractThreshold = 1f; // Distance at which the sword “catches” back to the throw point
public float spinSpeed = 10f; // Speed of spin when the sword is not held
public float fixedSpeed = 20f; // Fixed speed for the sword to maintain
public Camera playerCamera; // Reference to the player’s camera (ensure this is assigned)
private Rigidbody rb;
private SphereCollider sphereCollider; // Sphere collider for better bounce behavior
private bool isHeld = true; // Whether the sword is being held by the player
private bool isRetracting = false; // Whether the sword is in the process of retracting
private Vector3 randomSpin; // Random spin vector
// Initialize components
void Start()
{
// Get the Rigidbody component
rb = GetComponent<Rigidbody>();
rb.maxAngularVelocity = 50f; // Set high maximum angular velocity for faster spins
rb.drag = 0f; // Disable linear drag
rb.angularDrag = 0.05f; // Low angular drag to allow smooth spinning
// Add a sphere collider for predictable bouncing behavior
sphereCollider = gameObject.AddComponent<SphereCollider>();
sphereCollider.center = Vector3.zero; // Center the collider at the object's origin
sphereCollider.radius = 0.5f; // Set radius for the sphere collider (adjust based on the size of your sword)
// Start with the sword held
Catch();
}
// Update is called once per frame
void Update()
{
// Throw the sword if it's held and left mouse button is clicked
if (isHeld && Input.GetMouseButtonDown(0))
{
Throw();
}
// Retract the sword if it's not held and right mouse button is clicked
if (!isHeld && Input.GetMouseButtonDown(1))
{
StartRetracting();
}
// If the sword is not held and not retracting, generate a random spin
if (!isHeld && !isRetracting)
{
GenerateRandomSpin();
}
}
// Handle physics updates
void FixedUpdate()
{
if (isRetracting)
{
Retract();
}
// Apply random spin if the sword is not held
if (!isHeld && !isRetracting)
{
// Directly set angular velocity for spinning
rb.angularVelocity = randomSpin * spinSpeed;
}
}
// Generate random spin values for each axis
void GenerateRandomSpin()
{
randomSpin = new Vector3(
Random.Range(-1f, 1f), // Random value between -1 and 1 for x-axis
Random.Range(-1f, 1f), // Random value between -1 and 1 for y-axis
Random.Range(-1f, 1f) // Random value between -1 and 1 for z-axis
).normalized; // Normalize the vector to maintain uniform spin speed
}
// Throw the sword with physics force
void Throw()
{
isHeld = false;
isRetracting = false;
// Enable physics interactions
rb.isKinematic = false;
// Use the forward direction of the camera for throwing direction
Vector3 throwDirection = playerCamera.transform.forward;
rb.velocity = throwDirection * fixedSpeed;
// Detach the sword from the throw point (player)
transform.parent = null;
}
// Start the retraction process
void StartRetracting()
{
if (!isHeld)
{
isRetracting = true;
}
}
// Retract the sword back to the player
void Retract()
{
Vector3 directionToThrowPoint = throwPoint.position - transform.position;
float distanceToThrowPoint = directionToThrowPoint.magnitude;
// If the sword is close enough, catch it
if (distanceToThrowPoint < retractThreshold)
{
Catch();
}
else
{
// Move towards the throw point smoothly using Rigidbody.MovePosition
Vector3 retractDirection = directionToThrowPoint.normalized * retractSpeed * Time.fixedDeltaTime;
rb.MovePosition(rb.position + retractDirection);
}
}
// Catch the sword at the throw point
void Catch()
{
isHeld = true;
isRetracting = false;
// Disable physics while held
rb.isKinematic = true;
// Set sword's position and rotation to the throw point
transform.position = throwPoint.position;
transform.rotation = throwPoint.rotation;
transform.parent = throwPoint;
// Reset velocity and angular velocity to prevent weird behaviors
rb.velocity = Vector3.zero;
rb.angularVelocity = Vector3.zero;
}
// Handle sword collision with objects
private void OnCollisionEnter(Collision collision)
{
// If the sword is not being held and not retracting, handle ricochet
if (!isHeld && !isRetracting)
{
Vector3 collisionNormal = collision.contacts[0].normal; // Get the normal of the collision surface
Vector3 incomingVelocity = rb.velocity; // Get the current velocity of the sword
// Reflect the velocity along the collision surface normal
Vector3 reflectedVelocity = Vector3.Reflect(incomingVelocity, collisionNormal);
// Apply constant speed after ricochet, keep the sword's speed the same
Vector3 newVelocity = reflectedVelocity.normalized * fixedSpeed;
// Apply the new velocity to the sword
rb.velocity = newVelocity;
// Optional: Maintain a constant spin
rb.angularVelocity = randomSpin * spinSpeed;
}
}
}