How to completely prevent the player from going offscreen in Unity? - c#

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.

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?

Related

Configurable Joint: yDrive mode - brackeys multiplayer fps

As you may know, the mode for the configurable spring has been removed, and I've discovered this following a Brackeys tutorial on multiplayer fps (part 5). There was a comment under the video saying that you should ignore this as Brackeys sets it manually in the code later. However, when I did this, there was no errors, but the jump button did nothing (yes I checked the inputs)
My code is below:
using UnityEngine;
[RequireComponent(typeof(PlayerMotor))]
[RequireComponent(typeof(ConfigurableJoint))]
public class PlayerController : MonoBehaviour
{
private Vector3 _movHorizontal;
private Vector3 _movVertical;
private Vector3 _velocity;
[SerializeField]
private float speed = 5f;
[SerializeField]
private float lookSensitivity = 5f;
[SerializeField]
private float thrusterForce = 1000f;
[Header("Spring Settings:")]
[SerializeField]
private JointDriveMode jointMode = JointDriveMode.Position;
[SerializeField]
private float jointSpring = 20f;
[SerializeField]
private float joinMaxForce = 40f;
private PlayerMotor motor;
private ConfigurableJoint joint;
void Start ()
{
motor = GetComponent<PlayerMotor>();
joint = GetComponent<ConfigurableJoint>();
SetJointSettings(jointSpring);
}
void Update ()
{
//Calc movement velocity as Vector 3D
float _xMov = Input.GetAxisRaw("Horizontal");
float _zMov = Input.GetAxisRaw("Vertical");
_movHorizontal = transform.right * _xMov;
_movVertical = transform.forward * _zMov;
//Final movement vector
_velocity = (_movHorizontal + _movVertical).normalized * speed;
//Apply movement
motor.Move(_velocity);
//Calculate rotation as a 3d vector (turning around)
float _yrot = Input.GetAxisRaw("Mouse X");
Vector3 _rotation = new Vector3 (0f, _yrot, 0f) * lookSensitivity;
//Apply rotation
motor.Rotate(_rotation);
//Calculate rotation as a 3d vector (turning around)
float _xrot = Input.GetAxisRaw("Mouse Y");
Vector3 _cameraRotation = new Vector3 (_xrot, 0f, 0f) * lookSensitivity;
//Apply rotation
motor.RotateCamera(_cameraRotation);
//Calculate thruster force based on player input
Vector3 _thrusterForce = new Vector3 (0, 0, 0);
if (Input.GetButton("Jump"))
{
_thrusterForce = Vector3.up * thrusterForce;
SetJointSettings(0f);
} else
{
SetJointSettings(jointSpring);
}
//Apply thruster force
motor.ApplyThruster(_thrusterForce);
}
private void SetJointSettings(float _jointSpring)
{
joint.yDrive = new JointDrive
{
mode = jointMode,
positionSpring = _jointSpring,
maximumForce = joinMaxForce
};
}
}

Unity forward position = camera angle

Hi I am very new to Unity and only got basic knowledge. I created a player who can move and also a camera which follows the player and according to the mouseX and mouseY acis the camera rotates.
Now I want the camera rotation angle to be the forward position.
If the player press w he moves along x acis but if the camera angle change and the player press w it should move into this direction.
player.cs
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class DoublePlayer : MonoBehaviour
{
private CharacterController _controller;
public float _jumpHeight = 3f;
[SerializeField]
private float _moveSpeed = 5f;
[SerializeField]
private float _gravity = 9.81f;
private float _directionY;
void Start()
{
_controller = GetComponent<CharacterController>();
}
// Update is called once per frame
void Update()
{
float horizontalInput = Input.GetAxis("Horizontal");
float verticalInput = Input.GetAxis("Vertical");
Vector3 direction = new Vector3(horizontalInput, 0, verticalInput);
// set gravity
_directionY -= _gravity * Time.deltaTime;
direction.y = _directionY;
_controller.Move(direction * _moveSpeed * Time.deltaTime);
}
}
camera.cs
public class CameraController : MonoBehaviour
{
public float RotationSpeed = 1;
public Transform Target, Player;
float mouseX, mouseY;
void Start()
{
Cursor.visible = false;
Cursor.lockState = CursorLockMode.Locked;
}
// Update is called once per frame
void LateUpdate()
{
CamControl();
}
void CamControl()
{
mouseX += Input.GetAxis("Mouse X") * RotationSpeed;
mouseY += Input.GetAxis("Mouse Y") * RotationSpeed;
mouseY = Mathf.Clamp(mouseY, -35, 60);
transform.LookAt(Target);
Target.rotation = Quaternion.Euler(mouseY, mouseX, 0);
Player.rotation = Quaternion.Euler(0, mouseX, 0);
}
}
For Target and Player variable I selected the player object.
Do someone has a simple solution I can understand?
You could simply take the direction and rotate it along with the object that component is attached to using Quaternion * Vector3 operator
var rotatedDirection = transform.rotation * direction;
Or if you rather need the orientation of the other script attached to a different object then do e.g.
// link your camera or whatever shall be used for the orientation via the Inspector
[SerializeField] private Transform directionProvider;
and then
var rotatedDirection = directionProvider.rotation * direction;
Since you're getting the rotation directly from a Transform, you could use Transform.TransformDirection:
Vector3 rotatedDirection = transform.TransformDirection(direction);

How do I change my direction towards rotation angle at all times?

I have this script below and I want my player to always move towards the rotation angle, which doesn't happen. It only changes direction when I click.
My purpose is for the player to move at all times and towards the rotation which should be controlled by mouse position/mouse x axis (kind of like auto-run, but always change rotation based on mouse, not just move right or left).
I've tried about 10 different methods, nothing worked so far...
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class MovementController : MonoBehaviour
{
public float speed = 4;
public float rot = 0f;
public float rotSpeed = 80;
public float gravity = 8;
private Camera cam;
Vector3 moveDir = Vector3.zero;
CharacterController controller;
Animator anim;
// Start is called before the first frame update
void Start()
{
controller = GetComponent<CharacterController> ();
anim = GetComponent<Animator>();
}
// Update is called once per frame
void Update()
{
float horizontalSpeed = 8.0f;
//Get the mouse delta. This is not in the range -1...1
float h = horizontalSpeed * Input.GetAxis("Mouse X");
float z = horizontalSpeed * Input.GetAxis("Mouse Y");
transform.Rotate(0, h, 0);
//Move Input
if(controller.isGrounded){
if(Input.GetMouseButtonUp(1))
{
anim.SetInteger ("condition", 1);
moveDir = new Vector3 (0,0,1) * speed;
// moveDir *= speed;
moveDir = transform.TransformDirection(moveDir);
}
if(Input.GetMouseButtonDown(1))
{
anim.SetInteger("condition", 0);
moveDir = Vector3.zero;
}
}
moveDir.y -= gravity * Time.deltaTime;
controller.Move(moveDir * Time.deltaTime);
}
}
Transform.LookAt
Just get a cursor position and that's it
Vector3 direction = target.position - player.transform.position;
Quaternion finalPlayerRotation = Quaternion.LookRotation(direction);
player.transform.rotation = finalPlayerRotation;
This also works in some cases:
Vector3 direction = target.position - player.transform.position;
player.transform.right /*You may need to change the Right to upper, -upper, -Right depend on the player rotation and the target position*/ = direction;

How to fix Bullet Rotation in Unity? (2D)

So i'm having issues with a script I have, what it does is basically rotate the player graphics depending on where the mouse is aiming at. This works fine and as intended but my issue is that when I want to shoot on the opposite direction, that being the Left direction, it shoots the player instead of where it's aiming at.
I've decided to record a small video to show the problem.
https://streamable.com/02zqz
Here's the code to both the rotation and weapon script.
Rotating
public class Rotating : MonoBehaviour
{
public PlayerController player;
public float x;
public Vector3 flipArm;
void Start()
{
x = transform.localScale.x;
flipArm = transform.localScale;
player = GetComponentInParent<PlayerController>();
}
void Update()
{
Vector3 difference = Camera.main.ScreenToWorldPoint(Input.mousePosition ) - transform.position;
difference.Normalize();
float rotZ = Mathf.Atan2(difference.y, difference.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, rotZ + 0);
if(difference.x >= 0)
{
transform.rotation = Quaternion.Euler(0f, 0f, rotZ);
player.armRight = true;
}
else
{
transform.rotation = Quaternion.Euler(0f, 0f, rotZ+180);
player.armRight = false;
}
}
}
Weapon
public class Weapon : MonoBehaviour
{
public float shootDelay = 0;
public float damage = 1;
public LayerMask whatToHit;
public Transform firePoint;
public GameObject bullet;
// Start is called before the first frame update
void Start()
{
firePoint = transform.Find("FirePoint");
}
// Update is called once per frame
void Update()
{
shootDelay += Time.deltaTime;
if(shootDelay >= 0.1f)
{
if(Input.GetMouseButton(0))
{
shootDelay = 0;
Shot();
}
}
}
public void Shot()
{
Vector2 mousepos = new Vector2(Camera.main.ScreenToWorldPoint(Input.mousePosition).x, Camera.main.ScreenToWorldPoint(Input.mousePosition).y);
Vector2 firepointpos = new Vector2(firePoint.position.x, firePoint.position.y);
Instantiate(bullet, firepointpos, transform.rotation);
Debug.DrawLine(firepointpos, (mousepos - firepointpos) * 100, Color.cyan);
}
}
Try this:
transform.rotation = Quaternion.LookRotation ( end - start );
Also don't forget to check where the player is facing because you don't want to shoot from back. Using the above euler angles, one side is 90 and another 270 degrees in y component.

Rotate GameObject based on key presses and based on Terrain slope/curvature

I have code in C# in a game in Unity that needs to rotate the player based on the slope of the terrain but the RotateTowards function(calculates the slope and angle) doesn't allow the object to be rotated sideway to move it in different directions. If I take out the rotateTowards function, rotations sideways work. If I dont, the correct slope rotation works but player won'r rotate sideways when buttons are pressed.
How could I fix this so the player could rotate in both ways?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class PlayerController1 : MonoBehaviour
{
[System.Serializable]
public class MoveSettings
{
public float forwardVel = 10f; // walk speed
public float rotateVel = 100; // character rotation speed, character can walk 360 degree
public float jumpVel = 25f;
public LayerMask ground;
public Transform backLeft; // back left feet
public Transform backRight; // back right feet
public Transform frontLeft; // front left feet
public Transform frontRight; // front left feet
}
[System.Serializable]
public class PhysicsSettings
{
public float downAccel = 0.75f; // down speed when not grounded
}
public GameObject Model;
public GameObject Origin;
public MoveSettings moveSettings = new MoveSettings();
public PhysicsSettings physicsSettings = new PhysicsSettings();
private Vector3 velocity = Vector3.zero;
private Quaternion targetRotation;
private CharacterController cc;
private float forwardInput, turnInput, jumpInput = 0;
private RaycastHit lr;
private RaycastHit rr;
private RaycastHit lf;
private RaycastHit rf;
private Vector3 upDir;
private Animator Anim; // global private variable
private void Start()
{
Anim = GetComponent<Animator>(); // in the Start function
targetRotation = transform.rotation;
cc = GetComponent<CharacterController>();
}
public bool Grounded()
{
return cc.isGrounded;
}
private void FixedUpdate()
{
Run(); // calculate the velocity to be applied on character controller, stored in the velocity variable
Jump(); // code for jumping
GetInput(); // movement input keys
Turn(); // character movement direction input
cc.Move(transform.TransformDirection(velocity) * Time.deltaTime);
RotateTowardsGround();
}
private void GetInput()
{
Anim.SetFloat("vSpeed", forwardInput); // in the GetInput() function
Anim.SetFloat("Direction", 1f);
forwardInput = Input.GetAxis("Vertical");
turnInput = Input.GetAxis("Horizontal");
jumpInput = Input.GetAxisRaw("Jump");
}
private void Turn()
{
targetRotation *= Quaternion.AngleAxis(moveSettings.rotateVel * turnInput * Time.deltaTime, Vector3.up);
transform.rotation = targetRotation;
}
public void Jump()
{
if (jumpInput > 0 && Grounded())
{
velocity.y = moveSettings.jumpVel;
}
else if (jumpInput == 0 && Grounded())
{
velocity.y = 0;
}
else
{
velocity.y -= physicsSettings.downAccel;
}
}
private void Run()
{
velocity.z = moveSettings.forwardVel * forwardInput;
}
public void RotateTowardsGround()
{
// we have four feet
Physics.Raycast(moveSettings.backLeft.position + Vector3.up, Vector3.down, out lr);
Physics.Raycast(moveSettings.backRight.position + Vector3.up, Vector3.down, out rr);
Physics.Raycast(moveSettings.frontLeft.position + Vector3.up, Vector3.down, out lf);
Physics.Raycast(moveSettings.frontRight.position + Vector3.up, Vector3.down, out rf);
upDir = (Vector3.Cross(rr.point - Vector3.up, lr.point - Vector3.up) +
Vector3.Cross(lr.point - Vector3.up, lf.point - Vector3.up) +
Vector3.Cross(lf.point - Vector3.up, rf.point - Vector3.up) +
Vector3.Cross(rf.point - Vector3.up, rr.point - Vector3.up)
).normalized;
Debug.DrawRay(rr.point, Vector3.up);
Debug.DrawRay(lr.point, Vector3.up);
Debug.DrawRay(lf.point, Vector3.up);
Debug.DrawRay(rf.point, Vector3.up);
Model.transform.up = upDir;
}
}
The proper way to rotate object based on Terrain slope/curvature is to first throw raycast then obtain the returned RaycastHit.normal value and assign it your to the object's transform.up. It's better to use Lerp or Slerp to do this form smooth ration.
As for the position of the object, you can calculate that with Terrain.activeTerrain.SampleHeight as described in this post or you can use RaycastHit.point like you did in the code from your question.
Below is an example of what I described above. It is a minimal code to move/rotate object over a terrain. You can modify it to fit your four character legs scenario.
public class Hover : MonoBehaviour
{
public Transform objectToMove;
public float maxSpeed = 10f;
public float angleSpeed = 5f;
public float groundDistOffset = 2f;
private Vector3 toUpPos = Vector3.zero;
void Update()
{
float hInput = Input.GetAxis("Horizontal");
float vInput = Input.GetAxis("Vertical");
Vector3 objPos = objectToMove.position;
objPos += objectToMove.forward * vInput * maxSpeed * Time.deltaTime;
objPos += objectToMove.right * hInput * maxSpeed * Time.deltaTime;
RaycastHit hit;
if (Physics.Raycast(objectToMove.position, -Vector3.up, out hit))
{
//Get y position
objPos.y = (hit.point + Vector3.up * groundDistOffset).y;
//Get rotation
toUpPos = hit.normal;
}
//Assign position of the Object
objectToMove.position = objPos;
//Assign rotation/axis of the Object
objectToMove.up = Vector3.Slerp(objectToMove.up, toUpPos, angleSpeed * Time.deltaTime);
}
}

Categories

Resources