How do I unblock my camera while an object follows the cursor, stop 3x instantiation at the wrong position, and and make it decrease resources?

Hello, so for a little 2D project (it’s a bit of a city builder) I’m doing I have made a camera that can be moved with WASD and with the cursor on the edge of the screen, and that script worked perfectly.

So now I’m working on the actual builder part so I made a building and a script to place it. To place it I make an object follow the cursor, this object is red when you can’t place and green when you can (it work with a shader).

but for the moment it doesn’t work, whenever the object is in the scene it block the mouvment of the camera with the cursor (even when the object is not active), when I click to place the building it instantiate 3 of them at 0,0 (again, even when the object is not active) and doesn’t deactivate after, and it doesn’t decrease my resources. I’m at a loss and doesn’t know what to do enymore.

(please don’t mind the french in the code)

video of what is happening :


script of the camera :

public class 
MoveCam 
: MonoBehaviour
{
    [SerializeField] private float 
speed
;
    [SerializeField] private int 
screenEdge
;
    private Vector2 _moveInput;
    private Rigidbody2D _rb;

    
// Start is called once before the first execution of Update after the MonoBehaviour is created
    
void 
Start
()
    {
        _rb = GetComponent<Rigidbody2D>();
    }

    
// Update is called once per frame
    
void 
Update
()
    {
        _rb.linearVelocity = _moveInput.normalized * speed;
    }

    public void 
Move
(InputAction.CallbackContext ctx)
    {
        _moveInput = ctx.ReadValue<Vector2>();
    }
    
    public void 
EdgeMove
(InputAction.CallbackContext ctx)
    {
        if (ctx.ReadValue<Vector2>().y < screenEdge)
        {
            _moveInput.y = -1f;
            print("marche");
        }
        else if (ctx.ReadValue<Vector2>().y > Screen.height - screenEdge)
        {
            _moveInput.y = +1f;
            print("marche");
        }
        
        if (ctx.ReadValue<Vector2>().x < screenEdge)
        {
            _moveInput.x = -1f;
            print("marche");
        }
        else if (ctx.ReadValue<Vector2>().x > Screen.width - screenEdge)
        {
            _moveInput.x = +1f;
            print("marche");
        }

        if (ctx.ReadValue<Vector2>().y > screenEdge && ctx.ReadValue<Vector2>().y < Screen.height - screenEdge &&
            ctx.ReadValue<Vector2>().x > screenEdge && ctx.ReadValue<Vector2>().x < Screen.width - screenEdge)
        {
            _moveInput.x = 0f;
            _moveInput.y = 0f;
        }
    }
}

script of the placement object :

public class 
Placement 
: MonoBehaviour
{
    [SerializeField] private Batiment 
batiment
;
    [SerializeField] private bool 
plassable
;
    private Camera mainCam;
    private Collider2D collider;
    [SerializeField] private List<GameObject> 
bloking 
= new List<GameObject>();
    public Material 
placementMat
;
    private Transform position;
    private InputAction mousePos;
    
    public TypeMana 
costMana1
;
    public TypeMana 
costMana2
;
    public TypeMana 
costMana3
;
    public int 
manaAmount1 
= 0;
    public int 
manaAmount2 
= 0;
    public int 
manaAmount3 
= 0;

    public float 
playerMana1
;
    public float 
playerMana2
;
    public float 
playerMana3
;
    
    [SerializeField] private playerStat 
player
;

    private void 
Awake
()
    {
        player = GameObject.Find("player").GetComponent<playerStat>();
    }
    
    
// Start is called once before the first execution of Update after the MonoBehaviour is created
    
void 
Start
()
    {
        mainCam = Camera.main;
        collider = GetComponent<Collider2D>();
        position = GetComponent<Transform>();
        placementMat = GetComponent<Renderer>().material;
        mousePos = InputSystem.actions["mousePosBatiment"];
    }

    
// Update is called once per frame
    
void 
Update
()
    {
        FollowMousePosition();
        
        if (costMana1 != TypeMana.
None
)
        {
            playerMana1 = costMana1 switch
            {
                TypeMana.
AirMana 
=> player.air_mana,
                TypeMana.
EauMana 
=> player.eau_mana,
                TypeMana.
FeuMana 
=> player.feu_mana,
                TypeMana.
TerreMana 
=> player.terre_mana,
                TypeMana.
TempsMana 
=> player.temps_mana,
                TypeMana.
VideMana 
=> player.vide_mana,
                TypeMana.
EspaceMana 
=> player.espace_mana,
                TypeMana.
PlaceHolderMana 
=> player.placeHolder_mana
            };
        }
        
        if (costMana2 != TypeMana.
None
)
        {
            playerMana2 = costMana1 switch
            {
                TypeMana.
AirMana 
=> player.air_mana,
                TypeMana.
EauMana 
=> player.eau_mana,
                TypeMana.
FeuMana 
=> player.feu_mana,
                TypeMana.
TerreMana 
=> player.terre_mana,
                TypeMana.
TempsMana 
=> player.temps_mana,
                TypeMana.
VideMana 
=> player.vide_mana,
                TypeMana.
EspaceMana 
=> player.espace_mana,
                TypeMana.
PlaceHolderMana 
=> player.placeHolder_mana
            };
        }
        
        if (costMana3 != TypeMana.
None
)
        {
            playerMana3 = costMana1 switch
            {
                TypeMana.
AirMana 
=> player.air_mana,
                TypeMana.
EauMana 
=> player.eau_mana,
                TypeMana.
FeuMana 
=> player.feu_mana,
                TypeMana.
TerreMana 
=> player.terre_mana,
                TypeMana.
TempsMana 
=> player.temps_mana,
                TypeMana.
VideMana 
=> player.vide_mana,
                TypeMana.
EspaceMana 
=> player.espace_mana,
                TypeMana.
PlaceHolderMana 
=> player.placeHolder_mana
            };
        }
        
        if (bloking.Count != 0 && playerMana1 - manaAmount1 <= 0 
                               && playerMana2 - manaAmount2 <= 0 
                               && playerMana3 - manaAmount3 <= 0)
        {
            plassable = false;
            placementMat.SetColor("Color", Color.red);
        }
        else
        {
            plassable = true;
            placementMat.SetColor("Color", Color.green);
        }
    }

    private void 
OnTriggerEnter2D
(Collider2D collision)
    {
        print(collision.name);
        if (collision.CompareTag("Batiment"))
        {
            bloking.Add(collision.gameObject);
        }
    }

    private void 
OnTriggerExit2D
(Collider2D collision)
    {
        if (collision.CompareTag("Batiment"))
        {
            bloking.Remove(collision.gameObject);
        }
    }

    public void 
Place
(InputAction.CallbackContext ctx)
    {
        if (plassable)
        {
            Instantiate(batiment, position);
            Payment();
            gameObject.SetActive(false);
        }
    }

    private void Payment()
    {
        if (costMana1 != TypeMana.
None
)
        {
            switch (costMana1)
            {
                case TypeMana.
AirMana
:
                    player.air_mana -= manaAmount1;
                    break;
                case TypeMana.
EauMana
:
                    player.eau_mana -= manaAmount1;
                    break;
                case TypeMana.
FeuMana
:
                    player.feu_mana -= manaAmount1;
                    break;
                case TypeMana.
TerreMana
:
                    player.terre_mana -= manaAmount1;
                    break;
                case TypeMana.
TempsMana
:
                    player.temps_mana -= manaAmount1;
                    break;
                case TypeMana.
VideMana
:
                    player.vide_mana -= manaAmount1;
                    break;
                case TypeMana.
EspaceMana
:
                    player.espace_mana -= manaAmount1;
                    break;
                case TypeMana.
PlaceHolderMana
:
                    player.placeHolder_mana -= manaAmount1;
                    break;
            }
        }
        
        if (costMana2 != TypeMana.
None
)
        {
            switch (costMana2)
            {
                case TypeMana.
AirMana
:
                    player.air_mana -= manaAmount2;
                    break;
                case TypeMana.
EauMana
:
                    player.eau_mana -= manaAmount2;
                    break;
                case TypeMana.
FeuMana
:
                    player.feu_mana -= manaAmount2;
                    break;
                case TypeMana.
TerreMana
:
                    player.terre_mana -= manaAmount2;
                    break;
                case TypeMana.
TempsMana
:
                    player.temps_mana -= manaAmount2;
                    break;
                case TypeMana.
VideMana
:
                    player.vide_mana -= manaAmount2;
                    break;
                case TypeMana.
EspaceMana
:
                    player.espace_mana -= manaAmount2;
                    break;
                case TypeMana.
PlaceHolderMana
:
                    player.placeHolder_mana -= manaAmount2;
                    break;
            }
        }
        
        if (costMana3 != TypeMana.
None
)
        {
            switch (costMana3)
            {
                case TypeMana.
AirMana
:
                    player.air_mana -= manaAmount3;
                    break;
                case TypeMana.
EauMana
:
                    player.eau_mana -= manaAmount3;
                    break;
                case TypeMana.
FeuMana
:
                    player.feu_mana -= manaAmount3;
                    break;
                case TypeMana.
TerreMana
:
                    player.terre_mana -= manaAmount3;
                    break;
                case TypeMana.
TempsMana
:
                    player.temps_mana -= manaAmount3;
                    break;
                case TypeMana.
VideMana
:
                    player.vide_mana -= manaAmount3;
                    break;
                case TypeMana.
EspaceMana
:
                    player.espace_mana -= manaAmount3;
                    break;
                case TypeMana.
PlaceHolderMana
:
                    player.placeHolder_mana -= manaAmount3;
                    break;
            }
        }
    }

    private void FollowMousePosition()
    {
        transform.position = GetWorldPosition();
    }
    
    private Vector2 GetWorldPosition()
    {
        return mainCam.ScreenToWorldPoint(mousePos.ReadValue<Vector2>());
    }
}

my player input :

r/Unity2D - how do I unblock my camera while an object is following the cursor ? and how do I stop it from instantiating at the wrong position 3 time instead of 1 and without it decreasing the resources it use ?|414xauto

r/Unity2D - how do I unblock my camera while an object is following the cursor ? and how do I stop it from instantiating at the wrong position 3 time instead of 1 and without it decreasing the resources it use ?|750xauto

I’m really at the out of idea and in need of help, thank you in advance for your wisdom.

Okay first of all, for the sake of all programmers around the globe:

It belongs this way, and no other way:

public void Place(InputAction.CallbackContext ctx)

One line, not spread across lines. Your style makes all scripts extremely hard to read!
Worth noting: there’s usually a menu item in any IDE which, if you run it, it will reformat your code to accepted standards. I strongly advise to follow the accepted standards rather than invent your own because it confuses everyone else, while you’ll get accustomed to a code style that will be harder and harder to unlearn the more you lean into it. At the latest when you work with a team or even get a job as programmer that’ll clash.

That said, since you use Unity Events to receive input those callback methods run for started, performed and canceled events. Which is to say: in most cases three times.

Therefore you have to check if the event was actually performed before instantiating:

    public void Place(InputAction.CallbackContext ctx)
    {
        if (!ctx.performed)
            return;

        if (plassable)
        {
            Instantiate(batiment, position);
            Payment();
            gameObject.SetActive(false);
        }
    }

Now this code only runs once.


As to input handling in general, don’t repeat ReadValue (or anythig else) multiple times over. DRY principle! The repetitions are inefficient and make code bloat and thereby hard to read. This is the same method, it just reads the input vector ONCE into a variable and uses that henceforth. This even allows enough space to align the assignments if you like that.

public void EdgeMove(InputAction.CallbackContext ctx)
{
    var mousePos = ctx.ReadValue<Vector2>();

    if (mousePos.x < screenEdge)                      _moveInput.x = -1f;
    else if (mousePos.x > Screen.width - screenEdge)  _moveInput.x = 1f;
    else                                              _moveInput.x = 0f;

    if (mousePos.y < screenEdge)                      _moveInput.y = -1f;
    else if (mousePos.y > Screen.height - screenEdge) _moveInput.y = 1f;
    else                                              _moveInput.y = 0f;

    if (_moveInput != Vector2.zero)
    {
        print("marché"); // my french is rusty but the é is not silent ;)
    }
}

I reformatted the two script snippets:

MoveCam.cs
public class MoveCam : MonoBehaviour
{
    [SerializeField] private float speed;
    [SerializeField] private int screenEdge;
    private Vector2 _moveInput;
    private Rigidbody2D _rb;
    
    void Start()
    {
        _rb = GetComponent<Rigidbody2D>();
    }

    void Update()
    {
        _rb.linearVelocity = _moveInput.normalized * speed;
    }

    public void Move(InputAction.CallbackContext ctx)
    {
        _moveInput = ctx.ReadValue<Vector2>();
    }
    
    public void EdgeMove(InputAction.CallbackContext ctx)
    {
        if (ctx.ReadValue<Vector2>().y < screenEdge)
        {
            _moveInput.y = -1f;
            print("marche");
        }
        else if (ctx.ReadValue<Vector2>().y > Screen.height - screenEdge)
        {
            _moveInput.y = +1f;
            print("marche");
        }
        
        if (ctx.ReadValue<Vector2>().x < screenEdge)
        {
            _moveInput.x = -1f;
            print("marche");
        }
        else if (ctx.ReadValue<Vector2>().x > Screen.width - screenEdge)
        {
            _moveInput.x = +1f;
            print("marche");
        }

        if (ctx.ReadValue<Vector2>().y > screenEdge && ctx.ReadValue<Vector2>().y < Screen.height - screenEdge &&
            ctx.ReadValue<Vector2>().x > screenEdge && ctx.ReadValue<Vector2>().x < Screen.width - screenEdge)
        {
            _moveInput.x = 0f;
            _moveInput.y = 0f;
        }
    }
}
Placement.cs
public class Placement : MonoBehaviour
{
    [SerializeField] private Batiment batiment;
    [SerializeField] private bool plassable;
    private Camera mainCam;
    private Collider2D collider;
    [SerializeField] private List<GameObject> bloking = new List<GameObject>();
    public Material placementMat;
    private Transform position;
    private InputAction mousePos;
    
    public TypeMana costMana1;
    public TypeMana costMana2;
    public TypeMana costMana3;
    public int manaAmount1 = 0;
    public int manaAmount2 = 0;
    public int manaAmount3 = 0;

    public float playerMana1;
    public float playerMana2;
    public float playerMana3;
    
    [SerializeField] private playerStat player;

    private void Awake()
    {
        player = GameObject.Find("player").GetComponent<playerStat>();
    }
    
    void Start()
    {
        mainCam = Camera.main;
        collider = GetComponent<Collider2D>();
        position = GetComponent<Transform>();
        placementMat = GetComponent<Renderer>().material;
        mousePos = InputSystem.actions["mousePosBatiment"];
    }

    void Update()
    {
        FollowMousePosition();
        
        if (costMana1 != TypeMana.None)
        {
            playerMana1 = costMana1 switch
            {
                TypeMana.AirMana => player.air_mana,
                TypeMana.EauMana => player.eau_mana,
                TypeMana.FeuMana => player.feu_mana,
                TypeMana.TerreMana => player.terre_mana,
                TypeMana.TempsMana => player.temps_mana,
                TypeMana.VideMana => player.vide_mana,
                TypeMana.EspaceMana => player.espace_mana,
                TypeMana.PlaceHolderMana => player.placeHolder_mana
            };
        }
        
        if (costMana2 != TypeMana.None)
        {
            playerMana2 = costMana1 switch
            {
                TypeMana.AirMana => player.air_mana,
                TypeMana.EauMana => player.eau_mana,
                TypeMana.FeuMana => player.feu_mana,
                TypeMana.TerreMana => player.terre_mana,
                TypeMana.TempsMana => player.temps_mana,
                TypeMana.VideMana => player.vide_mana,
                TypeMana.EspaceMana => player.espace_mana,
                TypeMana.PlaceHolderMana => player.placeHolder_mana
            };
        }
        
        if (costMana3 != TypeMana.None)
        {
            playerMana3 = costMana1 switch
            {
                TypeMana.AirMana => player.air_mana,
                TypeMana.EauMana => player.eau_mana,
                TypeMana.FeuMana => player.feu_mana,
                TypeMana.TerreMana => player.terre_mana,
                TypeMana.TempsMana => player.temps_mana,
                TypeMana.VideMana => player.vide_mana,
                TypeMana.EspaceMana => player.espace_mana,
                TypeMana.PlaceHolderMana => player.placeHolder_mana
            };
        }
        
        if (bloking.Count != 0 && playerMana1 - manaAmount1 <= 0 
                               && playerMana2 - manaAmount2 <= 0 
                               && playerMana3 - manaAmount3 <= 0)
        {
            plassable = false;
            placementMat.SetColor("Color", Color.red);
        }
        else
        {
            plassable = true;
            placementMat.SetColor("Color", Color.green);
        }
    }

    private void OnTriggerEnter2D(Collider2D collision)
    {
        print(collision.name);
        if (collision.CompareTag("Batiment"))
        {
            bloking.Add(collision.gameObject);
        }
    }

    private void OnTriggerExit2D(Collider2D collision)
    {
        if (collision.CompareTag("Batiment"))
        {
            bloking.Remove(collision.gameObject);
        }
    }

    public void Place(InputAction.CallbackContext ctx)
    {
        if (plassable)
        {
            Instantiate(batiment, position);
            Payment();
            gameObject.SetActive(false);
        }
    }

    private void Payment()
    {
        if (costMana1 != TypeMana.None)
        {
            switch (costMana1)
            {
                case TypeMana.AirMana:
                    player.air_mana -= manaAmount1;
                    break;
                case TypeMana.EauMana:
                    player.eau_mana -= manaAmount1;
                    break;
                case TypeMana.FeuMana:
                    player.feu_mana -= manaAmount1;
                    break;
                case TypeMana.TerreMana:
                    player.terre_mana -= manaAmount1;
                    break;
                case TypeMana.TempsMana:
                    player.temps_mana -= manaAmount1;
                    break;
                case TypeMana.VideMana:
                    player.vide_mana -= manaAmount1;
                    break;
                case TypeMana.EspaceMana:
                    player.espace_mana -= manaAmount1;
                    break;
                case TypeMana.PlaceHolderMana:
                    player.placeHolder_mana -= manaAmount1;
                    break;
            }
        }
        
        if (costMana2 != TypeMana.None)
        {
            switch (costMana2)
            {
                case TypeMana.AirMana:
                    player.air_mana -= manaAmount2;
                    break;
                case TypeMana.EauMana:
                    player.eau_mana -= manaAmount2;
                    break;
                case TypeMana.FeuMana:
                    player.feu_mana -= manaAmount2;
                    break;
                case TypeMana.TerreMana:
                    player.terre_mana -= manaAmount2;
                    break;
                case TypeMana.TempsMana:
                    player.temps_mana -= manaAmount2;
                    break;
                case TypeMana.VideMana:
                    player.vide_mana -= manaAmount2;
                    break;
                case TypeMana.EspaceMana:
                    player.espace_mana -= manaAmount2;
                    break;
                case TypeMana.PlaceHolderMana:
                    player.placeHolder_mana -= manaAmount2;
                    break;
            }
        }
        
        if (costMana3 != TypeMana.None)
        {
            switch (costMana3)
            {
                case TypeMana.AirMana:
                    player.air_mana -= manaAmount3;
                    break;
                case TypeMana.EauMana:
                    player.eau_mana -= manaAmount3;
                    break;
                case TypeMana.FeuMana:
                    player.feu_mana -= manaAmount3;
                    break;
                case TypeMana.TerreMana:
                    player.terre_mana -= manaAmount3;
                    break;
                case TypeMana.TempsMana:
                    player.temps_mana -= manaAmount3;
                    break;
                case TypeMana.VideMana:
                    player.vide_mana -= manaAmount3;
                    break;
                case TypeMana.EspaceMana:
                    player.espace_mana -= manaAmount3;
                    break;
                case TypeMana.PlaceHolderMana:
                    player.placeHolder_mana -= manaAmount3;
                    break;
            }
        }
    }

    private void FollowMousePosition()
    {
        transform.position = GetWorldPosition();
    }
    
    private Vector2 GetWorldPosition()
    {
        return mainCam.ScreenToWorldPoint(mousePos.ReadValue<Vector2>());
    }
}

Not related to the problem, but I would probably do the edge scrolling like this:

EdgeMove
    public void EdgeMove(InputAction.CallbackContext ctx)
    {
        var s = new Vector2(Screen.width * 0.5f, Screen.height * 0.5f);
        var v = ctx.ReadValue<Vector2>() - s;
        var u = new Vector2(Mathf.Abs(v.x)-s.x+ScreenEdge, Mathf.Abs(v.y) -s.y+ScreenEdge) / ScreenEdge;
        v = new Vector2(Mathf.Sign(v.x)*Mathf.Max(u.x,0), Mathf.Sign(v.y) * Mathf.Max(u.y,0));
        _moveInput = v;
    }
}

This will ramp up the scrollspeed the closer you get to the edge (0 - 1).

“v” initially is the vector from the screen center to the mouse position

“u” is the absolute value of that vector but we subtract the “middle space”. That way any mouse
position inside the center area will yield a negative value and only positions in the ScreenEdge area will yield a positive value.

Finally we discard the negative values using Max by clamping it to 0 and we add back in the sign of our original v. The value will be between -1 and 0 for the bottom and left edge, it’s between 0 and 1 for the top and right edge and 0 for any position in the center.

Of course the ramping up would only work when you remove the “.normalized” from _moveInput in Update


Another little cleanup thing would be those switch expressions. You shouldn’t implement such switch statements that directly poke around in your player in a different class than the player. That mechanic should be part of the player and then implement a getter / setter that takes the TypeMana as argument. In those getter / setter you probably want to implement your cap so you can’t go below 0. The setter could return a bool to indicate if the setting was successful or not. I would probably create a separate nested “Mana” or “Stats” class that does all those things and has the actual stats values. That way you can also implement a C# indexer to access the different mana types. But that’s up to you

Anyways

in regards to your Problem, we don’t know how you hooked up your input actions and if they get disabled or overwritten at some point. The provided code does not indicate that those two scripts would interfere each other. Do you actually see the log messages?

Now that I had some time to actually look at your Placement script, I see one major problem. Namely this line:

 Instantiate(batiment, position);

which is misleading what it actually does. You named a field position which is actually your own Transform. So you do not pass a position here, but you pass a Transform which will be the parent of the newly instantiated object. So the new object will become a child of the gameobject your Placement script is on. So you never actually set a position and since you deactivate your gameobject afterwards it’s most likely not what you want. Instantiate has many overloads. The only variants that actually take a position always requires a position and rotation.

Note that you don’t use your “position” field anywhere else in that script. In your FollowMousePosition method you use transform directly. So I would highly recommend to remove that field as the name is highly misleading.

As to why you instantiate multiple times, that’s again something we can’t really debug from our side. We don’t even knowwhere that Placement script is attached to, if there are more than one Placement stript instances running at the same time or how / where the Place method is called from. It seems to be bound to a key or input. So use some Debug.Logs to figure out from where it was actually called.

Note that Debug.Log has an additional context parameter where you can pass any UnityEngine.Object reference that is bound to the log message. When you click on the log message in the console, Unity will “ping” that object in the hierarchy.

So add a

Debug.Log($"Place {gameObject.name}", gameObject);
inside your Place method. That should give you the name of the object that triggered this placement and you get that implicit reference. So when you see 3 objects getting instantiated, your Place method is most likely called 3 times. So you can inspect the 3 log messages to know who instantiated them.

ps: I just saw that your logic when you can actually place a building is wrong. You used && in your if statement but it should be ||. Your conditions are all “negative” conditions which should prevent placement. Currently you only prevent placement when there are blocking objects as well as you don’t have enough resources from every resource.

Another small possible issue is your concept of “None” when it comes to costs. If you allow some of your 3 costs to be TypeMana.None it may cause problems with your placement check as you check the amounts regardless. Also the conditions shouldn’t be <= as that means you always need 1 mana more than necessary. When the player has 20 and you require 20, subtracting them yields 0. So 0 or any positive value should be fine. Which means any value smaller than 0 would block the placement.