Reputation: 157
I am really new to Unity3d. I'm creating a gallery shooting game and I have a gun object that only moves along the x axis. I want to limit the movement just to the edge of the screen. Once it hits the edge of the screen, I have it hard coded to move back into the screen a few pixels. But it looks rough since it flickers when it hits the edge of the screen. I've tried setting the transforms.position.x to equal the transform.position.x once it hits the edge of the screen but the gun object will continue to move off screen.
Currently this is the code:
using UnityEngine;
using System.Collections;
public class PlayerMovement : MonoBehaviour
{
public float maxStrafeSpeed = 0.01f; // max move speed, recommend a very low float, will move too fast if too high
public float maxMouseSpeed = 3.0f; // max speed the gun will rotate to where the mouse is pointing
private float h; // horizontal axis
// Use this for initialization
void Start ()
{
}
// Update is called once per frame
void Update ()
{
PlayerMovementStrafing ();
PlayerMovementMouseAim ();
}
void PlayerMovementStrafing()
{
// get input
h = Input.GetAxis ("Horizontal") * maxStrafeSpeed;
Vector3 pos = Camera.main.WorldToViewportPoint(transform.position);
if (pos.x < 0.0f)
{
transform.position = new Vector3(-0.6f, transform.position.y, transform.position.z);
Debug.Log("Left edge of view");
}
else if (pos.x > 1.0f)
{
transform.position = new Vector3(0.6f, transform.position.y, transform.position.z);
Debug.Log("Right edge of view");
}
this.transform.Translate (new Vector3 (h, 0.0f, 0.0f));
}
void PlayerMovementMouseAim ()
{
transform.Rotate(Vector3(Input.GetAxis("Mouse Y"), Input.GetAxis("Mouse X"), 0) * (Time.deltaTime * maxMouseSpeed));
}
}
Upvotes: 0
Views: 3929
Reputation: 2157
Your logic is pretty much there, you just made a subtle error in when you're moving.
The main issue is you're clamping before you move:
void PlayerMovementStrafing()
{
// Getting Input
h = ...
//Getting the viewport location for clamping
Camera.main.Worl...
//Clamping the position
if (pos.x < 0.0f) ...
else if (pos.x > 1.0f) ...
// Changes the position after we just clamped (so each turn clamping is one frame behind)
this.transform.Translate (new Vector3 (h, 0.0f, 0.0f));
}
But aside from that, you should apply clamping code like this in LateUpdate
. Update
should work if your game is very simple, but from the docs, "LateUpdate is called after all Update functions have been called.". The docs for order of execution also show that LateUpdate
is called after animations and physics each frame, and is the the last event before rendering.
Being the last event before rendering makes LateUpdate
is what make it so ideal here, I defined a method that gets called there, and shrunk the strafing method.
I also used ViewportToWorldPoint
to clamp the value instead of using a hardcoded value. With the hardcoded value, if the camera isn't in the exact location that you have it in now the clamping will fail (even if it's moved in the editor).
If the camera can never move and you're worried about performance, both ___to___
calls can be moved to Start
and their values cached but it's very likely to be a premature optimization at this point (and open you up to subtle bugs):
I also added a call to Time.delta
in the strafing method. Currently if the game isn't running at exactly 60fps, the movement will vary in speed, because Update
is only called once per frame. In other word your movement speed is X units per frame. Multiplying by Time.delta
(the time since the last frame) changes that to X units per second.
That means even if the FPS vary (e.g. on a slow machine), your movement speed won't. It also means you'll need to increase your speed values since Time.delta
will usually be less than 1.
As a "rule", if you're in Update and you're making a call that will result in movement over time multiply by Time.deltaTime
(so it includes the Translate
call [you already had them for the Rotate
call]). Note that in FixedUpdate
you can still multiply by Time.deltaTime
(which returns Time.fixedDeltaTime
in that method, but you don't have to, since it's not affected by FPS.
void PlayerMovementStrafing()
{
// get input
h = Input.GetAxis ("Horizontal") * maxStrafeSpeed;
transform.Translate(new Vector3 (h, 0.0f, 0.0f) * Time.delta); //Multiply by delta time in
}
void LateUpdate()
{
PlayerMovementClamping();
}
void PlayerMovementClamping()
{
var viewpointCoord = Camera.main.WorldToViewportPoint(transform.position);
if (viewpointCoord.x < 0.0f)
{
Debug.Log("Left edge of view");
viewpointCoord.x = 0.0f;
transform.position = Camera.main.ViewportToWorldPoint(viewpointCoord);
}
else if (viewpointCoord.x > 1.0f)
{
Debug.Log("Right edge of view");
viewpointCoord.x = 1.0f;
transform.position = Camera.main.ViewportToWorldPoint(viewpointCoord);
}
}
And a small note, if you aren't using the blocks for anything more than debugging you can shorten the clamping code with Mathf.Clamp01
:
void PlayerMovementClamping()
{
var viewpointCoord = Camera.main.WorldToViewportPoint(transform.position);
viewpointCoord.x = Mathf.Clamp01(viewpointCoord.x);
transform.position = Camera.main.ViewportToWorldPoint(viewpointCoord);
}
Upvotes: 1