1

For the entire day, I been trying to find a good solution to completely stop the player from going offscreen without hard coding.

I have this script called player controller and all it does so far is allow the player to move along the x-axis. It also has an additional function that clamps the player's movement in the x-axis. Here it is.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public const float MAX_SPEED = 5.0f;

    // Update is called once per frame
    void Update()
    {
        transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
        clampPlayerMovement();
    }

    void clampPlayerMovement()
    {
        Vector3 pos = Camera.main.WorldToViewportPoint(transform.position);
        pos.x = Mathf.Clamp01(pos.x);
        transform.position = Camera.main.ViewportToWorldPoint(pos);
    }
}

The problem with this script is that it doesn't completely stops the player from going offscreen(half of the player's body still goes offscreen).

So this is what I tried next.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public const float MAX_SPEED = 5.0f;

    private float xLeft;
    private float xRight;

    void Start()
    {
        float pivotX = GetComponent<SpriteRenderer>().sprite.pivot.x;
        float pixelsPerunit = GetComponent<SpriteRenderer>().sprite.pixelsPerUnit;
        float textureWidth = GetComponent<SpriteRenderer>().sprite.texture.width;

        //Units on the left from the sprite's pivot.
        xLeft = pivotX / pixelsPerunit;

        //Units on the right from the sprite's pivot.
        xRight = (textureWidth - pivotX) / pixelsPerunit;
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
        clampPlayersMovement();
    }


    void clampPlayersMovement()
    {
        Vector3 pos = transform.position;
        Vector3 posMin = transform.position;
        Vector3 posMax = transform.position;

        posMin.x = posMin.x - xLeft;
        posMax.x = posMax.x + xRight;

        pos = Camera.main.WorldToViewportPoint(pos);
        posMin = Camera.main.WorldToViewportPoint(posMin);
        posMax = Camera.main.WorldToViewportPoint(posMax);

        pos.x = Mathf.Clamp(pos.x, posMin.x, posMax.x);
        transform.position = Camera.main.ViewportToWorldPoint(pos);
    }
}

Unfortunately, this code is no good. In fact, it is even worse because it does not stop the player from going offscreen at all.

So at this point I'm stuck between a rock and a hard place. Any suggestions would be vastly appreciated.

Luis Averhoff
  • 887
  • 2
  • 11
  • 22
  • You are clamping the center of your sprite to the edge of your sprite, not the edges of your sprite to the edges of your screen in your 2nd example. Look in to clamping posMin and posMax to between 0 and 1 instead of clamping pos and maybe you can figure out the correct math (too sleepy to figure it out right now so not posting an answer) – Scott Chamberlain Mar 15 '17 at 06:05
  • @ScottChamberlain So what you are saying is to clamp both posMin and posMax with Mathf.clamp01 and after that do `pos.x = Mathf.Clamp(pos.x, posMin.x, posMax.x);`? Because I still have to clamp pos no matter what since that vector is what gets assigned to transform.position. – Luis Averhoff Mar 15 '17 at 14:50
  • No, I mean you need to do two clamps after each one updating pos after removing the offset. After work today I will come back and write up an answer with the basic structure. – Scott Chamberlain Mar 15 '17 at 15:20
  • @ScottChamberlain Ok and what do you mean by removing the offset? Like once I clamp both posMin and posMax do I then need to do `posMin.x = posMin.x + xLeft and posMax.x = posMax.x - xRight`? Because that is the only offseting I see there. – Luis Averhoff Mar 15 '17 at 15:34
  • @ScottChamberlain Dont worry I found an answer to my dilemma. – Luis Averhoff Mar 15 '17 at 16:08

1 Answers1

4

After long searching I finally found an answer.

using UnityEngine;

public class PlayerController : MonoBehaviour
{
    public const float MAX_SPEED = 5.0f;

    private float halfPlayerSizeX;

    void Start()
    {
        halfPlayerSizeX = GetComponent<SpriteRenderer>().bounds.size.x / 2;
    }

    // Update is called once per frame
    void Update()
    {
        transform.Translate(Input.GetAxis("Horizontal") * MAX_SPEED * Time.deltaTime, 0.0f, 0.0f);
        clampPlayerMovement();
    }

    void clampPlayerMovement()
    {
        Vector3 position = transform.position;

        float distance = transform.position.z - Camera.main.transform.position.z;

        float leftBorder = Camera.main.ViewportToWorldPoint(new Vector3(0, 0, distance)).x + halfPlayerSizeX;
        float rightBorder = Camera.main.ViewportToWorldPoint(new Vector3(1, 0, distance)).x - halfPlayerSizeX;

        position.x = Mathf.Clamp(position.x, leftBorder, rightBorder);
        transform.position = position;
    }
}

The only thing that I don't get is why do I need to subtract the z position from both the gameobject and the camera? why not the x position?

Luis Averhoff
  • 887
  • 2
  • 11
  • 22