Adding a clamp to the rotation of a 2D object in Unity2D - c#

I have been working for a long time trying to get a clamp implemented on my 2D object in Unity2D. I have struggled with this issue for weeks now. I have a rocketship that moves upwards on it's own, and I'm trying to make it where you can't go further than 40 degrees rotating. Some details to note is that the ship rotates(x-axis of course) and moves toward the mouse(x-axis, doesn't go further up more then the current velocity) as a way to avoid obstacles which are coming soon. Here's my code for the ship:
public class MiniGameRocketShipController : MonoBehaviour
{
public Vector2 velocity = new Vector2(0f, 1500f);
private Vector2 directionX;
private Vector2 directionY;
public Rigidbody2D rb;
public new Camera camera;
public float moveSpeed = 100f;
public float rotation;
private void Start()
{
rotation = rb.rotation;
}
private void Update()
{
rb.AddForce(velocity * Time.deltaTime);
rb.velocity = new Vector2(directionX.x * moveSpeed, directionY.y * moveSpeed);
FaceMouse();
}
void FaceMouse()
{
if (rotation > 180)
{
rotation = 360;
}
rotation = Mathf.Clamp(rotation, -40f, 40f);
rb.rotation = rotation;
Vector3 mousePosition = Input.mousePosition;
mousePosition = camera.ScreenToWorldPoint(mousePosition);
directionX = (mousePosition - transform.position).normalized;
directionY = transform.position.normalized;
transform.up = directionX;
}
}

You can use Vector2.SignedAngle and Mathf.Clamp to do this. Explanation in comments:
// world direction to clamp to as the origin
Vector2 clampOriginDirection = Vector2.up;
// what local direction should we compare to the clamp origin
Vector2 clampLocalDirection = Vector2.up;
// how far can the local direction stray from the origin
float maxAbsoluteDeltaDegrees = 40f;
void ClampDirection2D()
{
// world direction of the clamped local
// could be `... = transform.up;` if you are ok hardcoding comparing local up
Vector2 currentWorldDirection = transform.TransformDirection(clampLocalDirection);
// how far is it from the origin?
float curSignedAngle = Vector2.SignedAngle(clampOriginDirection, currentWorldDirection);
// clamp that angular distance
float targetSignedAngle = Mathf.Clamp(curSignedAngle, -maxAbsoluteDeltaDegrees,
maxAbsoluteDeltaDegrees);
// How far is that from the current signed angle?
float targetDelta = targetSignedAngle - curSignedAngle;
// apply that delta to the rigidbody's rotation:
rb.rotation += targetDelta;
}

Related

How to track transform.position in real time?

I'm working on a little 2d game where you control a planet to dodge incoming asteroids. I'm implementing gravity in the following manner:
public class Gravity : MonoBehaviour
{
Rigidbody2D rb;
Vector2 lookDirection;
float lookAngle;
[Header ("Gravity")]
// Distance where gravity works
[Range(0.0f, 1000.0f)]
public float maxGravDist = 150.0f;
// Gravity force
[Range(0.0f, 1000.0f)]
public float maxGravity = 150.0f;
// Your planet
public GameObject planet;
void Start()
{
rb = GetComponent<Rigidbody2D>();
}
void Update()
{
// Distance to the planet
float dist = Vector3.Distance(planet.transform.position, transform.position);
// Gravity
Vector3 v = planet.transform.position - transform.position;
rb.AddForce(v.normalized * (1.0f - dist / maxGravDist) * maxGravity);
// Rotating to the planet
lookDirection = planet.transform.position - transform.position;
lookAngle = Mathf.Atan2(lookDirection.y, lookDirection.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, 0f, lookAngle);
}
}
The problem is that the asteroids are attracted to the initial spawn point of the planet (0,0), it doesn't update in real time with the movement of the planet. So if I move the planet to the corner of the screen, the asteroids are still attracted to the centre of it.
Is there a way to solve this?
Thank you very much and excuse any flagrant errors!
There are 2 ways to get to an object with speed:
get the object to the player and in each update just use the planet.transform.position
using look at first to rotate to the plant and then using vector3.forword as the direction of the movement.
The first solution doesn't work for you so you might want to try the second one.
any way, if your lookAt part doesn't work too you can use
Vector3 dir = target.position - transform.position;
float angle = Mathf.Atan2(dir.y,dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.forward);
or
Vector3 targetPos = target.transform.position;
Vector3 targetPosFlattened = new Vector3(targetPos.x, targetPos.y, 0);
transform.LookAt(targetPosFlattened);

Rotate rigidbody with moverotation in the direction of the camera

I want to use moverotation to rotate the object in the direction of the Main Camera, like a common third person shooter, but I don't know how to set the quaternion values or otherwise
`using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class movimento : MonoBehaviour
{
[SerializeField] float walk = 1;
[SerializeField] float run = 2;
Vector3 movement = Vector3.zero;
private Rigidbody rig;
// Start is called before the first frame update
void Start()
{
rig = GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
if (Input.GetKeyDown(KeyCode.LeftShift)) walk = (walk + run);
if (Input.GetKeyUp(KeyCode.LeftShift)) walk = (walk - run);
float Horizontal = Input.GetAxis("Horizontal");.
float Vertical = Input.GetAxis("Vertical");
movement = Camera.main.transform.forward * Vertical + Camera.main.transform.right * Horizontal;
float origMagnitude = movement.magnitude;
movement.y = 0f;
movement = movement.normalized * origMagnitude;
}
private void FixedUpdate ()
{
rig.MovePosition(rig.position + movement * walk * Time.fixedDeltaTime);
Quaternion rotation = Quaternion.Euler(???);
rig.MoveRotation(rig.rotation * rotation);
}
}`
i use a coroutine to do smooth rotation. I use Quaternion.LookRotation for the job.
so you indicate the position of object to look at and the duration of animation. Here you want to rotate face to the main camera
StartCoroutine(SmoothRotation(Camera.main.transform, 3f));
:
:
IEnumerator SmoothRotation(Transform target, float duration)
{
float currentDelta = 0;
var startrotation = transform.rotation;//use your rigisbody if you want here i use the gameobject
var LookPos = target.position - transform.position;
var finalrot = Quaternion.LookRotation(LookPos);
while (currentDelta <= 1f)
{
currentDelta += Time.deltaTime / duration;
transform.rotation = Quaternion.Lerp(startrotation, finalrot, currentDelta);//
yield return null;
}
transform.rotation = finalrot;
}
if you want to see (in scene when running) where your camera points just add this line of code in update():
Debug.DrawRay(Camera.main.transform.position, Camera.main.transform.TransformDirection(Vector3.forward) * 10f, Color.black);
if you want to point in same direction tha nthe Camera just change the line of finalrot in SmoothRotation Method:
var finalrot = Camera.main.transform.rotation;
you dont need to calculate the LookPos
for your problem of crazy rotation, i suggest you to reset rotation x and z
direction = hit.transform.position - transform.position;
Quaternion rotation = Quaternion.LookRotation(direction);
rotation.x = 0f;
rotation.z = 0f;
a tips to detect object what you want with the raycast inside spere : Physics.OverlapSphere: you could select what you want to cast when using the optional parameter layermask
private void DetectEnemy(Vector3 center, float radius)
{
var hitColliders = Physics.OverlapSphere(center, radius );
for (var i = 0; i < hitColliders.Length; i++)
{
print(hitColliders[i].name + "," + hitColliders[i].transform.position);
// collect information on the hits here
}
}
I created a raycast from the camera, and I would like to rotate the rigidbody to where the raycast is pointing but if i launch unity, it rotated wildly. What is the error?
Vector3 direction;
Vector3 rayDir = new Vector3(Screen.width/2,Screen.height/2);
void Update()
Ray ray = Camera.main.ScreenPointToRay(rayDir);
Debug.DrawRay(ray.origin, ray.direction * 10, Color.yellow);
RaycastHit hit = new RaycastHit ();
direction = hit.point - transform.position;
private void FixedUpdate ()
Quaternion rotation = Quaternion.LookRotation(direction);
rig.MoveRotation(rig.rotation * rotation);
Add a character controller component, this is what is for. See:
https://docs.unity3d.com/Manual/class-CharacterController.html

Camera always behind player in Unity3d

I'm struggling with this for quit some time now. I have GameObject, being a sphere, which is my player on a 3d Terrain. I have a Camera which is always on a fixed distance from the player, follows it where it goes with below script:
public GameObject player;
private Vector3 offset;
// Use this for initialization
void Start () {
offset = transform.position - player.transform.position;
}
void LateUpdate () {
transform.position = player.transform.position + offset;
}
So far so good. However what I actually want is that the camera rotates with the player, so it always looks into the direction where the sphere is moving, but always stays behind the player at the same fixed distance, so that the player is always visible in the camera view.
There are a lot of scripts available, but the problem with the onces I've seen so far is that the camera indeed rotate with the player, but because the player actually is a rolling sphere the camera view is rolling and turning as well.
The best script I found so far is below, but this one has the same problem as the other onces, the camera rolls with the player.
public Transform target;
public float distance = 3.0f;
public float height = 3.0f;
public float damping = 5.0f;
public bool smoothRotation = true;
public bool followBehind = true;
public float rotationDamping = 10.0f;
void Update () {
Vector3 wantedPosition;
if(followBehind)
wantedPosition = target.TransformPoint(0, height, -distance);
else
wantedPosition = target.TransformPoint(0, height, distance);
transform.position = Vector3.Lerp (transform.position, wantedPosition, Time.deltaTime * damping);
if (smoothRotation) {
Quaternion wantedRotation = Quaternion.LookRotation(target.position - transform.position, target.up);
//Quaternion ownRotation = Quaternion.RotateTowards;
transform.rotation = Quaternion.Slerp (transform.rotation, wantedRotation, Time.deltaTime * rotationDamping);
}
else transform.LookAt (target, target.up);
}
Can anyone help me with this please?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraFollow : MonoBehaviour {
public GameObject player;
public float cameraDistance = 10.0f;
// Use this for initialization
void Start () {
}
void LateUpdate ()
{
transform.position = player.transform.position - player.transform.forward * cameraDistance;
transform.LookAt (player.transform.position);
transform.position = new Vector3 (transform.position.x, transform.position.y + 5, transform.position.z);
}
}
My solution (based on #brennon-provencher answer) with smoothness and auto offset:
public class CameraFollow : MonoBehaviour
{
public GameObject target;
public float speed = 5;
Vector3 offset;
void Start()
{
offset = target.transform.position - transform.position;
}
void LateUpdate()
{
// Look
var newRotation = Quaternion.LookRotation(target.transform.position - transform.position);
transform.rotation = Quaternion.Slerp(transform.rotation, newRotation, speed * Time.deltaTime);
// Move
Vector3 newPosition = target.transform.position - target.transform.forward * offset.z - target.transform.up * offset.y;
transform.position = Vector3.Slerp(transform.position, newPosition, Time.deltaTime * speed);
}
}
You need to move your camera position based on sphere movement direction -
public GameObject player;
private Vector3 offset;
float distance;
Vector3 playerPrevPos, playerMoveDir;
// Use this for initialization
void Start () {
offset = transform.position - player.transform.position;
distance = offset.magnitude;
playerPrevPos = player.transform.position;
}
void LateUpdate () {
playerMoveDir = player.transform.position - playerPrevPos;
playerMoveDir.normalize();
transform.position = player.transform.position - playerMoveDir * distance;
transform.LookAt(player.transform.position);
playerPrevPos = player.transform.position;
}
Edit 2: To fix flickering camera, try this -
void LateUpdate () {
playerMoveDir = player.transform.position - playerPrevPos;
if (playerMoveDir != Vector3.zero)
{
playerMoveDir.normalize();
transform.position = player.transform.position - playerMoveDir * distance;
transform.position.y += 5f; // required height
transform.LookAt(player.transform.position);
playerPrevPos = player.transform.position;
}
}

Rotate Camera when touch unity 3d

Here it is. I have a human anatomy, and I would like to rotate the camera when I touch anywhere. but looking at the human anatomy. how can I do that :(
[https://www.youtube.com/watch?v=EfYeL2FYyyA&t=148s] this is what exactly I really wanted to please help me !
`
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/Mouse Orbit with zoom")]
public class MouseOrbitImproved : MonoBehaviour {
public Transform target;
public float distance = 5.0f;
public float xSpeed = 120.0f;
public float ySpeed = 120.0f;
public float yMinLimit = -20f;
public float yMaxLimit = 80f;
public float distanceMin = .5f;
public float distanceMax = 15f;
private Rigidbody rigidbody;
float x = 0.0f;
float y = 0.0f;
// Use this for initialization
void Start ()
{
Vector3 angles = transform.eulerAngles;
x = angles.y;
y = angles.x; rigidbody = GetComponent<Rigidbody>();
// Make the rigid body not change rotation
if (rigidbody != null)
{
rigidbody.freezeRotation = true;
}
}
void LateUpdate ()
{
if (target)
{
x += Input.GetAxis("Mouse X") * xSpeed * distance * 0.02f;
y -= Input.GetAxis("Mouse Y") * ySpeed * 0.02f;
y = ClampAngle(y, yMinLimit, yMaxLimit);
Quaternion rotation = Quaternion.Euler(y, x, 0);
distance = Mathf.Clamp(distance - Input.GetAxis("Mouse ScrollWheel")*5, distanceMin, distanceMax);
RaycastHit hit;
if (Physics.Linecast (target.position, transform.position, out hit))
{
distance -= hit.distance;
}
Vector3 negDistance = new Vector3(0.0f, 0.0f, -distance);
Vector3 position = rotation * negDistance + target.position;
transform.rotation = rotation;
transform.position = position;
}
}
public static float ClampAngle(float angle, float min, float max)
{
if (angle < -360F)
angle += 360F;
if (angle > 360F)
angle -= 360F;
return Mathf.Clamp(angle, min, max);
}}
You could use a trick instead of using too many "math" in this case.
There will be empty gameObjects in each of the element you want your camera to rotate around, lets call those "anchors"
When the element is selected, you simply make the camera child of the anchor.
And then if you just rotate the anchor, your camera will rotate because it is the child.
This will get the effect you want.
Here is a simple example from youtube, for rotating the object.
Here is another one from Unity answers.
Hope this helps! Cheers!

Unity C# use Transform.RotateAround with touch to move camera around target

So am looking at changing my Transform.RotateAround script for touch input. If I get rid of the "Input.GetAxis", the camera rotates on its own around the target. I cant seem to figure out how to format the code to get the position of the touch to a string to use with this. If anyone has any tips that would be awesome!
Here is the code:
using UnityEngine;
using System.Collections;
[AddComponentMenu("Camera-Control/Mouse Orbit with zoom2")]
public class MouseOrbitImproved2 : MonoBehaviour {
public float speed;
public Transform target;
public float rotateSpeed;
public Transform camera = Camera.main.transform;
public Vector3 camPosition;
public float camSpeed;
public float minDistance;
public float maxDistance;
//private Vector3 moveDirection = Vector3.zero;
//private Vector3 moveDirection = target.position;
public float perspectiveZoomSpeed = 0.5f; // The rate of change of the field of view in perspective mode.
public float orthoZoomSpeed = 0.5f; // The rate of change of the orthographic size in orthographic mode.
void start() {
//camPosition = camera.transform.position;
}
public void Update() {
if (Input.touchCount == 1) {
transform.LookAt(target);
Touch touchSwipe = Input.GetTouch(0);
string position = touchSwipe.deltaPosition;
transform.RotateAround(target.position, Vector3.up, Input.GetAxis(position)* speed);
transform.RotateAround(target.position, Vector3.forward, Input.GetAxis(position)* speed);
}
transform.LookAt(target);
/*
float scroll = Input.GetAxis("Mouse ScrollWheel");
if (scroll != 0)
{
// calculate new position first...
camPosition += transform.forward * scroll * camSpeed;
// then compare to the limits:
float distanceToTarget = Vector3.Distance(target.position, camPosition);
// you can clamp the movement to min and max distances:
if (distanceToTarget > maxDistance){ // clamp at maxDistance...
camPosition = target.position - maxDistance * transform.forward;
}
if (distanceToTarget < minDistance){ // or at minDistance
camPosition = target.position - minDistance * transform.forward;
}
// finally, update the actual camera position:
transform.position = camPosition;
// set camera position
}
*/
// If there are two touches on the device...
if (Input.touchCount == 2)
{
// Store both touches.
Touch touchZero = Input.GetTouch(0);
Touch touchOne = Input.GetTouch(1);
// Find the position in the previous frame of each touch.
Vector2 touchZeroPrevPos = touchZero.position - touchZero.deltaPosition;
Vector2 touchOnePrevPos = touchOne.position - touchOne.deltaPosition;
// Find the magnitude of the vector (the distance) between the touches in each frame.
float prevTouchDeltaMag = (touchZeroPrevPos - touchOnePrevPos).magnitude;
float touchDeltaMag = (touchZero.position - touchOne.position).magnitude;
// Find the difference in the distances between each frame.
float deltaMagnitudeDiff = prevTouchDeltaMag - touchDeltaMag;
// If the camera is orthographic...
if (Camera.main.orthographic)
{
// ... change the orthographic size based on the change in distance between the touches.
Camera.main.orthographicSize += deltaMagnitudeDiff * orthoZoomSpeed;
// Make sure the orthographic size never drops below zero.
Camera.main.orthographicSize = Mathf.Max(Camera.main.orthographicSize, 0.1f);
}
else
{
// Otherwise change the field of view based on the change in distance between the touches.
Camera.main.fieldOfView += deltaMagnitudeDiff * perspectiveZoomSpeed;
// Clamp the field of view to make sure it's between 0 and 180.
Camera.main.fieldOfView = Mathf.Clamp(Camera.main.fieldOfView, 0.1f, 179.9f);
}
}
}
public void ChangeToAxial (){
Camera.main.transform.position = new Vector3 (45.3f,234.5f,66.9f);
}
public void ChangeToSagright (){
Camera.main.transform.position = new Vector3 (28.13f,76.41f,222.68f);
}
public void ChangeToSagleft (){
Camera.main.transform.position = new Vector3 (49.36f,66.85f,-93.78f);
}
public void ChangeToCoronal () {
Camera.main.transform.position = new Vector3 (-71.6f,69.66f,66.29f);
}
}
I think it may be best if you look at this question and answer from the unity forum Unit 5 forum rotating camera with touch. It should work with the Input.GetTouch instead of the Input.GetAxis. Hope that helps. Be aware that script is in Javascript, but you shouldn't need the whole script.

Categories