- Home /
Could not be given a straight answer that achieved what i was asking for. no further answers would be added because of the amount of current answers.
Directional force for Rigidbody's 'AddForce'
Hey there guys, I am having a bit of an issue with the directions of force being produced by a rigidbody. What I am trying to achieve is a realistic gun shooting mechanic. So how this works is, when you shoot, a bullet casing is instantiated, being shot out the side of the gun. Also a bullet gets instantiated and gets shot forward by the given force, this works great in theory and great as a prop, as can be seen in the below youtube clip.
https://www.youtube.com/watch?v=Lx9qW0pUzQI
but when the gun is attached to a FirstPersonCharacter both objects (bullet and casing) get forced in the same direction (example, the casings shoot out to the right hand side of the gun, but if i spin the character 180 degrees, they are now being shot out to the left side of the gun. so this added force is relative to the worlds direction, not the direction of its parent object (the gun)... how can this be achieved to use the direction of the parent (the gun) to send the force from and not the world's directions?
EDIT: My apologies, I had forgotten to add my piece of code, the gun handles all the shooting things, but once instantiated the objects take over from there own scripts.
Bullet Casing Script:
using UnityEngine;
public class ejectCase : MonoBehaviour {
float thrust = 250;
Rigidbody rig;
// Use this for initialization
void Start () {
rig = this.GetComponent<Rigidbody>();
rig.AddForce(transform.right * thrust);
}
// Update is called once per frame
void Update () {
}
}
Bullet Script:
using UnityEngine;
public class pistolBulletScript : MonoBehaviour {
float thrust = 1000;
Rigidbody rig;
private void OnTriggerEnter(Collider other)
{
if (other.CompareTag("testHit"))
{
Debug.Log("Bullet has hit Target, Health Removed from Target, Bullet Removed");
Destroy(this.gameObject);
}
}
// Use this for initialization
void Start () {
rig = this.GetComponent<Rigidbody>();
rig.AddForce(-transform.forward * thrust);
}
// Update is called once per frame
void Update () {
}
}
Gun Script:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class TestShoot : MonoBehaviour {
public bool canShoot;
public bool reloading;
public float bulletClip = 17;
float origClip;
float shotTimer;
float startRTimer;
float reloadTimer = 1.5f;
Animation anim;
public AudioClip shotFired;
public AudioClip reloadSound;
public AudioClip emptyClip;
AudioSource aud;
public Vector3 caseEjector;
public Transform casing;
public Transform casePos;
public Vector3 shootLoc;
public Transform bullet;
public Transform shootPos;
void Start () {
origClip = bulletClip;
startRTimer = reloadTimer;
aud = this.GetComponent<AudioSource>();
anim = this.GetComponent<Animation>();
canShoot = true;
reloading = false;
}
void Update () {
shotTimer += Time.deltaTime;
if(shotTimer <= 0.4f)
{
canShoot = false;
}
else
{
canShoot = true;
}
if (canShoot && bulletClip > 0)
{
if (Input.GetButtonDown("Shoot"))
{
anim.Play("APCFire");
aud.PlayOneShot(shotFired);
bulletClip -= 1;
shotTimer = 0;
caseEjector = casePos.transform.position;
shootLoc = shootPos.transform.position;
Instantiate(casing, caseEjector, Quaternion.Euler(0, 90, 180));
Instantiate(bullet, shootLoc, Quaternion.Euler(0, 90, 0));
}
}
if (bulletClip <= 0 && canShoot)
{
if (Input.GetButtonDown("Shoot"))
{
aud.PlayOneShot(emptyClip);
shotTimer = 0;
}
}
if (Input.GetButtonDown("Reload"))
{
aud.PlayOneShot(reloadSound);
canShoot = false;
reloading = true;
anim.Play("APCReload");
}
if (reloading)
{
reloadTimer -= Time.deltaTime;
if(reloadTimer <= 0)
{
bulletClip = origClip;
reloading = false;
canShoot = true;
reloadTimer = startRTimer;
}
}
}
}
PS. all the directions are jacked up due to forward not actually being the tip of the bullet ect. and for some reason all my buttons to make the text bolt and italic ect are missing, so i cannot click 'code' to add it correctly.
I have updated and added my gun script also, I'm getting a lot of mixed messages here, It's doing nothing but confusing the heck out of me.
Answer by aldonaletto · Jul 12, 2017 at 12:32 AM
AddForce takes world directions. You can use AddRelativeForce instead, which assumes the direction is relative to the Rigidbody's local space - for instance:
// shoot bullet in its forward direction with shotForce
bulletRbody.AddRelativeForce(Vector3.forward * shotForce);
// eject casing to its right with 45° elevation
casingRbody.AddRelativeForce(new Vector3(1,1,0).normalized * ejectForce);
EDITED:
Your code is ok, since it ejects the capsule in a direction relative to its transform. I suspect that the problem is the way you're instantiating the capsule. You should have an empty game object (let's call it "ejector") childed to the gun at the position where the capsule is supposed to be created and ejected, and use its position and rotation when instantiating the capsule - like this example (script attached to the object "ejector"):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class EjectCapsule : MonoBehaviour {
public GameObject capsule; // drag the capsule prefab here
void Update () {
if (Input.GetButtonDown ("Fire1")) {
// create the capsule at this position and rotation:
GameObject cap = Instantiate (capsule, transform.position, transform.rotation);
Destroy (cap, 2f); // capsule disappears after 2 seconds
}
}
}
Hey there, thank you for the speedy reply, i have added my code now, that may give you some insight as to what i currently have, i did however attempt both of your code suggestions, but with the casingRbody one i got an error of 'Vector3 cannot be used like a method'.
Right, i would also suggest to avoid that "ejectCase" script completely. All you need is an initial force impulse and maybe a delayed auto destruction. Both can easily be done from the instantiating point. Also i would suggest to apply a small additional force with AddForceAtPosition at the tip of the case. That will make it spin backwards. That's the usual motion the case will perform. Also add some tiny random elements to the force direction.
Though if you want to keep the script on the case (for whatever reason) you should remove the the Update method. An empty Update method just eats up performance, especially when you have many of those objects.
Applying the force from the instantiating script also has the advantage that you can more easily replace the "Instantiate" with an object pool. Relying on Start won't work with an object pool as Start is only called once.
If you declare the "case" prefab as "Rigidbody" you can easily add forces from the instantiating script:
public Rigidbody casePrefab;
public float ejectSpeed = 250f;
// [...]
// create "casePrefab" instance
Rigidbody case = Instantiate (casePrefab, transform.position, transform.rotation);
var force = transform.right * ejectSpeed;
case.AddForce(force, Force$$anonymous$$ode.Impulse);
var attackPoint = transform.position + transform.forward * 0.1f;
case.AddForceAtPosition(force * 0.1f, attackPoint, Force$$anonymous$$ode.Impulse);
Destroy (case.gameObject, 2f);
Answer by Milo_del_mal · Jul 12, 2017 at 12:21 AM
You can use the gun's transform.right/down, forward, or whatever.
alternatively you can create a gameobject that is the transform and use that transform's.direction (right, up, forward) to define the direction of the force.
I do not remember if the values are normalized. But that should work your issue.
I cannot give you a more complete answer without your code in gist, for instance.
Hey there, thank you for the speedy reply, i have now added my coding, i do apologise i had forgotten to add it in, but i will have an attempt at what you have provided. Thank you.