Make my bow shoot? Help please :)

Hello,
I would like to make my bow shoot an arrow which i already have positions on the bow.
I need this arrow to travel forward until it hits something and disappear.
However, the catch is that i need to make it so after 2 seconds of hitting ‘Fire1’ it will reload with another arrow?

I already have an if getbuttondown fire1 statemtn setup.
It also has to be the actual game object that shoots cause i have the bow and arrow at an angle at the left side of the screen. I want the arrow to go from it’s location straight to the crosshair and continue moving until it hits something. But i need a copy of the arrow to reappear even if the other arrow isn’t destroyed

Help appreciated thanks!

I’m a little confused as to what you’re seeking help on, the arrow flight or the reloading. It seems like your main question is how to get the same arrow that has just been fired to reappear two seconds after you fire the arrow.

The best way I can think of is to create a prefab of the arrow and then instantiate the prefab as a GameObject each time you reload the bow. I’m going to leave out information about creating prefabs and the necessary animations for reloading the bow as I’m sure you already know how to do that.

If you don’t have a timer class, I suggest you create one as timers are a very common thread in game design. Unless you plan on doing everything through IEnumerator coroutines which can be a pain to manage once you have a lot of them to worry about.

There are several steps you need to take to make this process work properly.

  1. Make a prefab of the arrow you are firing.
  2. Create an animation for when the player reloads the bow with another arrow.
  3. Create a public function that will instantiate the prefab at the desired position & rotation.
  4. Attach a keyframe event on the frame of the reload animation that brings the arrow onto the bow.
  5. Have the keyframe execute the public function you created for prefab instantiation.

Once you are done with steps 1 and 2, you need to create a function on your bow script that looks like this:

 public void OnReloadArrow()
    {
        //The following variables are here for example only. Ideally you'd want to set these
        //as global variables at the start of your bow script.
        // * Global as in global to the scope of the scirpt, not global to the entire solution.

        //This would be your prefab so you'd want to direct this to the prefab you created for the
        //new arrow.
        GameObject fabArrow = (GameObject)Resources.Load("Prefabs/Objects/Ammo/StandardArrow");
        //This is the reference to the actual instantiated gameObject.
        GameObject gArrow;
        //You said at some point in your post that you had created all the necessary anchors and positions
        //on the bow, so I assume you have variables for these somewhere
        Vector3 pt_pos_arrow = Vector3.zero;//This is the position of the arrow when on the bow in ready position
        Quaternion pt_rot_arrow = Quaternion.identity; //This is the rotation of the arrow when on the bow in ready position.

        //Create the new arrow and assign it to gArrow
        //NOTE: This assumes that on firing the bow, the arrow is no longer tracked by this script.
        //If that isn't the case, you'll want separate GameObject references for the arrow in flight
        //and the arrow in the bow.
         gArrow = (GameObject)Instantiate(fabArrow, pt_pos_arrow, pt_rot_arrow);

    }

It’s important to note that this function has to be attached to a monobehavior script that is attached to the GameObject of the Bow that has the actual Animation component, otherwise the function won’t show up on the keyframe event list.

Now that you have your function and the keyframe event that points to it, all that’s left to do is to create your timer variable to handle when the reload animation gets played.

void Update()
    {


        if(JustFired)
        {

            //while the timer is greater than 0, decrease it by increments of deltaTime
            if(ReloadTimer > 0)
            {
                ReloadTimer -= Time.deltaTime;
            }

            //If the timer gets below zero, set it to zero
            if(ReloadTimer < 0)
            {
                ReloadTimer = 0;
            }

            //Once the timer has officially reached 0
            if(ReloadTimer == 0)
            {
                animation.Play["ReloadBow"];
                JustFired = false;
            }
        }



    }

There are two variables not mentioned in there, because again they need to be global to the scope of the script.

bool JustFired; //This is a simple boolean to track whether or not the player just fired the bow
float ReloadTimer; //Simple float to track the amount of time passed between firing and reloading

JustFired should be set to true right when the player fires the arrow. Additionally, ReloadTimer should be set to 2.0f at the same time.

I’m sure that’s a lot to take in, but I hope it helps you out somewhat.

-McMayhem

1 Like

Thanks so much! But my bow wont even shoot! I got my whole game figured out but i am having difficulty even making
my bow shoot an arrow. I need my bow to shoot at the cross hair at the center and hit and enemy if crosshair is on the enemy. But I think i couldv’e worded this part better. I want it so that when i shoot the arrow gets destroyed when it hits something. But a new arrow is reloaded in after 2 seconds and you can shoot again. So even if the other one is still moving i can shoot again. I’m very new to unity and everything is done except the attacking system. Thanks!

Ahhh okay. Then there’s just a few more things I’d need clarified to help you further.

  1. Do you have animations that you are using for the firing/reloading of the bow? Are you using animations at all?
  2. Do you want your arrow to have physics? That is to say, are you looking for the arrow to be affected by gravity, or just continuously move on a single forward vector?
  3. Are you also looking for help on how to aim, or are you just looking for how to launch the arrow?

For the destruction of the arrow upon hitting a target, that’s super simple. You can just use Destroy(arrow); to get rid of the arrow once it’s reached its destination.

As for the actual movement, a very common (but restricted) procedure is to keep the arrow facing its target, and translate it forward at the speed you desire.

So let’s say we have a few variables set up:
float arrowSpeed = 30; //This will be the speed at which the arrow travels when in flight
GameObject gArrow; //This is the actual arrow that has been fired and is moving
Vector3 target; //This is the target position that the arrow is trying to reach

With those variables in mind, your arrow travel code would look something like this (remember this has to be called from the Update() thread)

public void ArrowFlight()
{
     //First we get the direction of the arrow's forward vector
     //to the target position.
     Vector3 tDir = gArrow.transform.position - target;

     //Now we use a Quaternion function to get the
     //rotation based on the direction
     Quaternion rot = Quaternion.LookRotation(tDir);

    //And finally, set the arrow's rotation to the one we just
    // created.
     gArrow.transform.rotation = rot;

     //Get the distance from the arrow to the target
     float dist = Vector3.Distance(transform.position, target);


     if(dist <= 0.1f)
     {
           //This will destroy the arrow when it is within .1 units
           //of the target location. You can set this to whatever
           //distance you're comfortable with.
           GameObject.Destroy(gArrow);

     }
     else
     {
          //If not, then we just keep moving forward
          gArrow.transform.Translate(Vector3.forward * (arrowSpeed * Time.deltaTime);
     }
}

That code won’t work if you’re looking for a physics approach. Based on your answers to the first three questions, I can make a better function for you that will do exactly what you want.

-McMayhem

No, i am not using animations as of now but i think i have the capability to edit the code if i need to.
Uhm, it would be nice for it to be affected by gravity and then just have it so when it hits the ground or an enemy it destroys. However one set distance would be fine and if the enemy is out of range just destory.
I already have a crosshair set up, however i do not know how to make the arrow fly to the crosshair and then straight to the enemy. Is there a system we could do that would basically say if the crosshair is on the enemy launch to the enemy. Because idk what i want to do with it because the actual bow and the arrow is on the left side of the screen tilted inwards. Meanwhile, the crosshair is in the center and an arrow that shoots to the middle and then makes a 45 degree turn towards the enemy may look a bit weird haha. Any solution to that?

And it would be even better to have the arrow destroyed if it hits a tree, the ground, an enemy or anything on the map that isn’t the air :slight_smile:

Just let me know if you have an idea. :slight_smile:

@McMayhem , we are having a similar issue and would like to find a physics based solution for the movement of the arrow. So far, it has spawned a number of heated discussions between me and my programmer so any clues you can give us would be helpful.

Is there an asset on the store that might help? He is a good programmer but has never done this before and I think is off in the wrong direction.

Thank you.

The real question when it comes to physics based solutions is, how fast do you want the arrow to go? The answer to that will dramatically change how you approach this. For my project, we are using ballistic weapons, so the speed of the projectile had to be very high. The problem with using high speed physics objects is that the percent of collision detection error scales up inversely with projectile speed.

You’ll find that forces > 65 are going to pass through most objects regardless of the type of collider they are using. This is due to the fact that the update for physics needs to be less frequent than your usual update loop to ensure optimized performance (this isn’t unity specific, every game engine runs into this issue).

There is a very nice way to handle physics of high speed objects using raycasts. The results are often more accurate and in some cases more CPU friendly than the physics solution.

So if you would like, I can post a simple script that will do the raycast physics. But if you aren’t dealing with projectiles that travel at high speeds, the built-in physics system should do just fine and I can show you how to go that route as well.

I think 40 would be fine for me :slight_smile:

We are not using high speed projectiles so lower is good for us too. :slight_smile: Thank you. You may have saved a relationship. lol

Things can often times get tense on teams, particularly when facing an issue of how to plan implementation of features, so I understand that 100%.

I’m going to drum something up really quick using a new project. I’ll post the step-by-step here for you guys, but if you also want the project file, just let me know and I’ll send it to you.

Should have something in about 15 minutes.

1 Like

Alright, I’ve tested this out myself and it works just fine. Fun note: The 65 force threshold I gave earlier was incorrect it’s more like 78 – 95 that misses on collision. I haven’t used this for projectiles since Unity 3, so it’s clear UT have made some very nice improvements here.

Let’s start with the setup. (Remember, let me know if you want the project file so you can test it out yourself.)

Scripts:

BowHandler.cs (This will be attached to the bow you are shooting the arrow from)

ArrowHandler.cs (This will be attached to the arrow that is being shot)

In your Scene:

BowWeapon [GameObject] [BowHandler]

Pt_arrow [GameObject]

Arrow [GameObject] [ArrowHandler] (make this use the same position/rotation as pt_arrow)

Prefabs:

Arrow.prefab (this is the prefab of the arrow that’s in the scene. It will get instantiated where the old one was after firing)

Bow Handler

using UnityEngine;
using System.Collections;

public class BowHandler : MonoBehaviour {

    public GameObject fabArrow; //Reference to the arrow prefab
    public GameObject gArrow; //Reference to the initial arrow. Note: You need to have an arrow on the bow already at start.

    public Transform pt_arrow; //Point on the bow to create the new arrow after firing.


    public float ArrowForce; //Force to be applied to the arrow when firing


    // Update is called once per frame
    void Update () {
   
        //Simple input code to get a keypress. Change this to whatever key you like
        if(Input.GetKeyDown(KeyCode.LeftControl))
        {
            FireArrow();
        }

    }


    public void FireArrow()
    {
        //Anytime you use a public variable, or any variable really, you should
        //always make sure the variable has been set before attempting to use it.
        //This will avoid infuriating errors. Alternatively, you may want to send
        //some information to the output log if it is null so that you can see what
        //happened later.

        if(fabArrow != null && gArrow != null)
        {
          
            //Get the rigidbody component of the arrow.
            Rigidbody rArrow = gArrow.GetComponent<Rigidbody>();

            if (rArrow != null)
            {
                //Setting isKinematic to false will allow physics to be applied to
                //the arrow.
                rArrow.isKinematic = false;

                //Grab a reference to the arrow handler.
                ArrowHandler aHand = gArrow.GetComponent<ArrowHandler>();

                if(aHand != null)
                {
                    //Set the arrow handler to active since we are now firing
                    aHand.IsActive = true;
                }

                //This command will add velocity to a rigidbody using a vector
                //For our situation, we want the arrow to fly forward, so we multiply
                //the arrow's forward facing vector times the force amount.
                rArrow.AddForce(pt_arrow.forward * ArrowForce, ForceMode.VelocityChange);

                //ForceMode.Impulse is just one of several force modes. You might want to play around with those
                //to see if you can get better results with different options.

                //Now we recreate a new arrow on the old arrow spot. This should be placed in your reload function
                // if you have one.
                gArrow = (GameObject)Instantiate(fabArrow, pt_arrow.position, pt_arrow.rotation);
            }
            else
            {
                Debug.Log("Bow Error! There is no rigidbody attached to the arrow object! Sent from: " + gArrow.name);
            }
        }
        else
        {
            //If it is null, then we need to have a little chat with ourselves.
            Debug.Log("Bow Error! You are trying to instantiate an object without a reference! Sent from: " + gameObject.name);
            //I usually like to toss in the name of the object that throws the error just so I have a more focused search area
            // when trying to identify a problem.
        }


    }
}

Arrow Handler

using UnityEngine;
using System.Collections;

public class ArrowHandler : MonoBehaviour {


    public float Lifetime; //The float amount of seconds this arrow should remain in play before being destroyed
    public float DestroyDelay; //The delay after this object hits something before destroying it

    private TimerVar _lifeTimer; //If the arrow doesn't hit anything we need to destroy it after a time to save performance.
    private TimerVar _deathTimer; //Timer for when to destroy if the arrow hits another object while in flight.
    private TimerVar _colTimer; //We need this small timer to turn on the box collider for this object after firing.
    private bool _hitSomething; //Has this arrow hit an object? Used to destroy the arrow after a time.
    public bool IsActive; //Has this arrow been fired? Used to start lifetime calculations.


    // Use this for initialization
    void Start () {
        //Set up our timers.
        _lifeTimer = new TimerVar(Lifetime, false);
        _deathTimer = new TimerVar(DestroyDelay, false);
   
    }
   
    // Update is called once per frame
    void Update () {

        //Don't do any calculations until after the arrow has been fired.
        if (!IsActive)
            return;

        if(_hitSomething)
        {
            //Has this arrow hit something?
            //If yes, start the death routine
            if(_deathTimer.TimerDone)
            {
                //Check to see if the destroy delay is done.
                //If yes, destroy the object.

                Destroy(gameObject);
            }
            else
            {
                //If not, keep counting down.
                _deathTimer.TimerUpdate();
            }
        }
        else
        {
            //If not, keep checking the lifetime info
            if(_lifeTimer.TimerDone)
            {
                //Has this object exceeded it's allotted life time?
                //If yes, we destroy it.
                Destroy(gameObject);
            }
            else
            {
                //If no, we keep counting down.
                _lifeTimer.TimerUpdate();
            }
        }

   
    }

    void OnCollisionEnter(Collision col)
    {
        //This function must be on the gameObject that has the box collider attached to it.
        _hitSomething = true;
    }
}

I’ve tested this with force range from 10 – 70 and it works just fine. You can adjust the arrow speed from the bow and you can adjust the lifetime/deathtime of the arrow from the arrow in the inspector.

One last thing. You’ll notice I have a variable in the ArrowHandler called TimerVar.cs. This is the timer I talked about in my first post. My version is probably a little overkill for this scenario, but it does give you a lot of control for use later if you need it. (Better coders, feel free to let me know if this is poorly optimized. I did write it several years ago)

using UnityEngine;
using System.Collections;

//*********************************************************************************//
// Class: TimerVar
// Created By: Chuck (McMayhem) McMackin
//*********************************************************************************//
// The Timer Var class is used as a simple timer for anyting that requires timed
// activation or intervals. This is used to replace the general means of timers which
// can cause convoluted code and poor management. The timer var must be updated in the
// update function of any script it is attached to. For this reason, it can only be
// placed in MonoBehavior-based scripts.
//*********************************************************************************//

[System.Serializable]
public class TimerVar
{

    public float TimerSet;
    public bool TimerDone;
    public bool Looping;
    public float TimerSpeed;
    public float CurTime;
    public float Percent;

    private float _timer;

    public TimerVar(float tSet, bool loop)
    {
        TimerSet = tSet;
        Looping = loop;
        TimerDone = false;
        _timer = TimerSet;
    }

    public TimerVar(float tSet, float tSpeed, bool loop)
    {
        TimerSet = tSet;
        TimerSpeed = tSpeed;
        Looping = loop;
        TimerDone = false;
        _timer = TimerSet;
    }


    public void TimerUpdate()
    {
        if (_timer > 0)
        {
            if (TimerSpeed > 0)
            {
                _timer -= Time.deltaTime * TimerSpeed;
            }
            else
            {
                _timer -= Time.deltaTime;
            }
        }

        if (_timer < 0)
        {
            _timer = 0;
        }

        CurTime = _timer;
        Percent = (CurTime / TimerSet) * 100;

        if (_timer == 0)
        {
            TimerDone = true;
            if (Looping)
            {
                Reset();
            }
        }

    }

    public void Reset()
    {
        Percent = 0;
        TimerDone = false;
        _timer = TimerSet;
    }

    public void Reset(float num)
    {
        TimerSet = num;
        TimerDone = false;
        _timer = TimerSet;
    }

    public void Reset(float num, float speed)
    {
        TimerDone = false;
        _timer = num;
        TimerSpeed = speed;

    }
}

Hope this helps. Let me know if you need more clarification.

1 Like

Thank you! We will test it this weekend and let you know. :slight_smile:

I love the Unity community!

1 Like

Thanks so much!
What is g and pt arrow?

After careful consideration, I realized this probably isn’t something that can be fully explained in a forum post. With that in mind, I created a nifty little unity package called BowTutorial and threw it up on my website downloads page. If you want to grab it:

Bow Tutorial Package

Some things I forgot to mention

  • You need to create new layers for both the Arrow and the Bow and make it so that Bow !->* Arrow and that Arrow !-> Arrow. This will make it so they do not collide with one another while you are shooting the arrow.
  • You should have a physics material for the arrow that way you can change it’s bounciness when hitting other objects
    • This is just my shorthand for X does not interact with Y. In this case it means Bow does not interact with Arrow and Arrow does not interact with Arrow.
2 Likes

Wow, very nice!!

We have it working for the most part, just a few glitches, but will look at your tutorial before asking more questions. Thank you!

I don’t, i am still confused on what pt_Arrow and g arrow is?
After that we should as well :slight_smile:

And also how do i do this layer thing?
As i said, I’m new to unity and am having trouble understanding :slight_smile:

gArrow is the GameObject of the arrow that is currently loaded onto the bow. In the tutorial package I posted, you can see the green arrow object on the white bow object, that green arrow object is gArrow;

pt_arrow is a Transform point on your bow weapon object that you use to place the new arrow when the old one has fired. This is also shown in the tutorial package.

I’ve commented out just about everything in the scripts pretty rigorously, but if you need specific help with anything let me know.