Hi Xander here…
For our game Endless Elevator, which is in development, we have a bad guy who is a knife thrower. I know nasty. In keeping with the blocky style of the characters in the game only his throwing arm moves and the rest of him is rigid. We decided to use the Unity inbuilt Hinge Joint and “spring” feature to simulate the throwing action. It turned out to be really easy to implement but hard to control perfectly. This is the story of how it all hangs together.
This guy below with the creepy eyes and beard is our knife throwing guy. You can see the top level empty Game Object called KnifeSpy_Package and two child objects (one for his body mesh the other for his arm which is separate). You can just make out the orange arrow of the Hinge Joint near his shoulder but more of that below.
This is his arm object. He’s holding a knife now… but soon we are going to teach him how to throw it (kinda).
You can see the Hinge Joint attached to the arm object here as a red arc around the Z axis. The arc of the Hinge Joint has been limited to just the angle that he needs to raise the arm and bring it back down in a throwing action.
Here is what the Hinge Joint looks like in the Editor. That Connected Body is the main figure of the character. The arm mesh that this script is attached to also has a Rigidbody component and must be set to “Use Gravity” for the Spring to work.
You can see where we set the limits for the arm axis in the bottom of the object there. We access the Use Motor boolean from our script to turn that feature on and off. When it’s on a spring winds up the arm to it’s firing position and when he throws that spring is released shooting his arm back down in a throwing arc.
This is what our script looks like in the editor:
The Target Angle is the height of the arm as it raises to throw. When the Player is in range and we are facing him if the arm has been raised above the target angle we can throw the knife. We can use this to tweak the throw. You can see in the example below our arm doesn’t really come up high enough so we can use this setting to fix what it looks like on the fly.
The ‘X’ is exposed in the editor to help with that process so that we can understand what’s going on with that setting without having to click around in the editor to see it against the arm transform.
The Knife Prefab is the knife mesh and the Knife Transform is an empty game object used to define the spot where we instantiate the Knife Prefab.
The Knife is instantiated with some Force and there are booleans to control if we can shoot and if the wait between shoots has completed.
This is what the script looks like as code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class KnifeHinge : MonoBehaviour {
private HingeJoint hingeKnife;
public Vector3 targetAngle;
public float x; // Debug in Editor
public GameObject knifePrefab;
public GameObject knifeTransform;
public float force;
public bool canShoot;
public bool waitDone;
private Transform playerTransform;
public Transform parentTransform;
// Use this for initialization
void Start () {
hingeKnife = GetComponent<HingeJoint>();
playerTransform = GameObject.FindWithTag("Player").transform;
}
// Update is called once per frame
void Update () {
if (Mathf.Abs(playerTransform.transform.position.y - (parentTransform.transform.position.y)) < 1) // If the player is on the same level as you
{
if (Mathf.Abs(playerTransform.transform.position.x - parentTransform.transform.position.x) < 8) // If he is within 16 units from you
{
var lookPos = playerTransform.position;
var rotation = Quaternion.LookRotation(lookPos);
parentTransform.transform.LookAt(lookPos);
if (waitDone)
{
canShoot = true; // Can shoot is only true if you are looking at the Player (on the same level and 16 units away)
}
}
}
}
void FixedUpdate()
{
x = hingeKnife.transform.rotation.eulerAngles.x; // this is for debugging the angle of the arm and hinge in the editor easily
if (canShoot)
{
if (hingeKnife.transform.rotation.eulerAngles.x > targetAngle.x && hingeKnife.transform.rotation.eulerAngles.x < (targetAngle.x + 10f)) // this is set to 295 it goes up to 297 (If your arm is all the way up)
{
hingeKnife.useMotor = false;
var theKnife = (GameObject)Instantiate(knifePrefab, knifeTransform.transform.position, knifeTransform.transform.rotation);
theKnife.GetComponent<Rigidbody>().AddForce(-force, 0, 0, ForceMode.Impulse);
Destroy(theKnife, 2.0f);
canShoot = false;
waitDone = false;
StartCoroutine(WaitAround(2f)); // shoot every 2 seconds
}
}
}
private IEnumerator WaitAround(float waitTime)
{
yield return new WaitForSeconds(waitTime);
waitDone = true;
hingeKnife.useMotor = true;
}
}
This is what the whole thing looks like put together:
As I mentioned above there is a bit of tweaking to get it to look right – but this post is about the process of putting everything together and how the components work to achieve the effect.
This is what it looks like after the tweaking:
I hope you found this interesting enough – if you did and want to read more about this stuff I did a post a few weeks back about how we do the guns in this game Endless Elevator:
http://localhost/2019/03/15/unity-how-to-do-guns/
Enjoy.