while(Input.GetMouseButton(1)) bugged?

Hi there, I want to create a archery game.
You know - the typical stuff! Aim with archer, load up power, shoot.

To load up the power I tried this:

while(Input.GetMouseButton(1))

Sadly it crashed my unity. I debugged it with outcommenting the rest to this line.
Is it maye a common issue? O.o

It’s called here (= Power()):

void Update () {

mousex = Input.mousePosition.x;
mousey = Input.mousePosition.y;

if(Input.GetMouseButtonDown(1) && isAiming == false)
{isAiming = true; Power();}

So the method should be called just once and the while statement should be triggerd as long as i press the mouse key. Or do I miss something?

yup, you’re trying to put a loop into a single frame and expecting it to do something over time.

Update is called once per frame, it is already a loop.

1 Like

Please use Code Tags when you post code,

what do you want to do with the mouse exactly? move an object?

I do? I just do the if statement every frame…checking if the method is allready running (isAiming). By setting the bool to true it should be executed only once as far as I know.

@Rob21894 I want to use the mouse to charge up the bow. In the end a kind of energy bar filling up and getting low again.
So the Player has to do the right timing.

→ Need to check every frame for the mouseclick → charge up and down the energy bar (endless till mouse is released)–> get a strength number at mouse relase

The problem is several things.

While() is dangerous in Unity. It freeze all other code execution for most scripts.

Update() is running once per Frame and each instance is checking if the mouse is held down.

I’d need to see more code. The above isn’t right.

You probably want to us Input.GetMouseButtonDown(1) for the first frame the user pressed it. And Input.GetMouseButtonUp(1) for the first frame they released it.

Initialization/Start/Update

public class Archery : MonoBehaviour {
    float mousex;
    float mousey;
   
    float aimx;
    float aimy;
   
    float shootx;
    float shooty;
   
    float positionx;
    float positiony;
   
    float power = 0;
   
    public GameObject arrow;
    bool isShooting = false;
    bool isAiming = false;
   
    bool morepower = true;
    bool lesspower = false;
   
    public Vector2 shootingVec;
   
    Animator Anim;
   
   
   
    // Use this for initialization
    void Start () {
        Anim = GetComponent<Animator>();
        positionx = this.transform.position.x;
        positiony = this.transform.position.y;
       
        mousex = Input.mousePosition.x;
        mousey = Input.mousePosition.y;
    }
   
    // Update is called once per frame
    void Update () {
   
        mousex = Input.mousePosition.x;
        mousey = Input.mousePosition.y;
       
        if(Input.GetMouseButtonDown(1) && isAiming == false)
        {isAiming = true; Power();}
           
           
           
        if(Input.GetMouseButtonUp(1))
        {        if(isShooting == false)
            {
                aimx = mousex;
                aimy = mousey;
           
           
               
                ShootingVector();
                Shoot();
               
            }   
        }
        Animationhandler();
        if(Input.GetKey(KeyCode.Q))
        {Reload();}
    }

Method

void Power(){
       
//        if(Input.GetMouseButton(1))
//        Debug.Log("Pressed");
        isAiming = true;
        while(Input.GetMouseButton(1)== true)
        {   
            while(morepower==true)
            {
       
                if(Input.GetMouseButtonUp(1))
                    {break;}
                if(power >= 100)
                    {morepower = false; lesspower = true;}
                power += 1f;
                Debug.Log(power);
           
            }
   
   
            while(lesspower == true)
            {
               
                if(Input.GetMouseButtonUp(1))
                {break;}
                if(power <= 0)
                    {morepower = true; lesspower = false;}
                power -= 1f;
                Debug.Log(power);
            }       
       
        }
    }

This should be all relevant code, like I said the freeze is definetly caused by the quoted line:
while(Input.GetMouseButton(1))

@Lloyd_RedironLabs : So there are issues with the while? Sadly expected that …how can I do it otherwise?
How can I check for the mouse release/press just the frame its done? O.o
Expected I would have to check it every frame…like I do with keyboardinputs 2.

Still new to unity - sounds like I missed something important there?

1 Like

The problem with those loops is that it’s impossible for it to stop. If Input.GetMouseButton(1) is true, then Input.GetMouseButtonUp(1) can’t be true, so you never hit the breaks. You can’t hold down the mouse button and release it at the same time.

The 2-player tank tutorial actually does something very similar to what you’re trying to do. You should check it out.

No worries, in Unity, just know you almost never want to use a While loop. While is dangerous, should only be used in Co-routines or when you are certain you want to halt execution, and have escape procedures.

I’d probably do something like this:

using System;
using UnityEngine;

public class Archery : MonoBehaviour
{
    /// <summary>
    /// Define some states for the archer
    /// </summary>
    public enum ArcherState
    {
        Rest,
        Reload,
        Fire,
        Aim,
        None
    }

    // What prefabs do we have?
    public GameObject ArrowPrefab;

    // What state are we in?
    private ArcherState currentState = ArcherState.Rest;
    private ArcherState lastState = ArcherState.None;

    private Animator animamatorReference;

    private float firingPower = 0f;
    public int arrows = 10;
    // Setup our scene
    void Start()
    {
        animamatorReference = GetComponent<Animator>();
    }

    // Update is called once per frame
    void Update()
    {
        if (Input.GetMouseButtonDown(1))
            currentState = ArcherState.Aim;

        if (Input.GetMouseButtonUp(1))
            currentState = ArcherState.Fire;

        if (Input.GetKey(KeyCode.Q))
            currentState = ArcherState.Reload;

        if (currentState != lastState)
        {
            // the state has changed, time to change our animation
            AnimateArcher();
            if (currentState != lastState)
                Debug.Log(name + "::Upddate > Archer animation state changed to " + currentState.ToString());
        }

        // Execute our aiming, firing, reload, etc
        RunActions();

        // Update our reference, so we can determine if a state is "held" or if a a state is "new".
        lastState = currentState;
    }


    void RunActions()
    {
        // What state is the character currently in?
        switch (currentState)
        {
            case ArcherState.Aim:
                // Execute every time
                IncreasePower();
                break;
            case ArcherState.Reload:
                // Only execute once
                if (currentState != lastState)
                    Reload();
                // Then back to resting
                currentState = ArcherState.Rest;
                break;
            case ArcherState.Fire:
                // Only execute once
                if (currentState != lastState)
                    Fire();
                // Then back to resting
                currentState = ArcherState.Rest;
                break;
        }
    }


    void IncreasePower()
    {
        float minPower = 10f;
        float maxPower = 100f;
        firingPower = Mathf.Clamp(firingPower += 1f, minPower, maxPower);
    }

    void Fire()
    {
        // Ammo?
        if (arrows == 0)
            return;

        // Fire prefab
        var fireArrow = GameObject.Instantiate(ArrowPrefab);
        if (fireArrow != null)
        {
            fireArrow.transform.rotation = transform.rotation;
            fireArrow.transform.position = transform.position;

            // Get or add a rigid body for phyiscs
            var fireArrowRigid = fireArrow.GetComponent<Rigidbody>();
            if (fireArrowRigid == null)
                fireArrowRigid = fireArrow.AddComponent<Rigidbody>();

            // then apply rigidbody or whatever with a direction of transform.position.forward against the firing power;
            fireArrowRigid.useGravity = true;
            fireArrowRigid.velocity = fireArrow.transform.forward * firingPower;
            firingPower = 0f;
        }

        // Use up ammo
        arrows -= 1;
    }

    void Reload()
    {
        // Reload
        arrows = 10;
    }

    void AnimateArcher()
    {
        // Animate based on the characters state
    }
}

That uses a mini state-machine for the archer, and will lob objects at a distance based on the power.

1 Like

On second thought, just download this. It is a mini unity project using the script above. It should help get you started.

1 Like

You did that just now? O.o
Thanks a lot … I ll check out your script and try to get mine working accordingly.

1 Like

I’ve been using Unity a long time, so the coding side comes really easy for me :slight_smile:

1 Like

That was really easy to read and understand - and as far as I know thats the most important thing to be a really good programmer. Thanks for your work! :slight_smile:

The Math.Clampf will help a lot! So I don’t have to struggle with booleans for max and min and with the state machine I can say goodbye to the while. I ll try to keep the machine in my memory.

Is the structur done by your comments a usual blueprint you are following? Or just best fitting in this case? :slight_smile:

Just for this case…
Usually I would split building/continuous actions and one-shot actions. But because this one was easy to keep them coupled it just made sense.

If you get stuck just holler :slight_smile:

But this is representative of a lot of one off objects in my game worlds.