Rotation of a single object when script is attached to multiple objects - c#

I have a game in Unity in which I have added three objects (Cube, Cylinder, Capsule). To these game objects, I have added a script to rotate them along an axis when they press the left click and x, y or z, rotating the object along the respective axis. But when I attempt rotate a single object, the other objects rotate too. How do I rotate each object without affecting the other objects.
if (Input.GetMouseButton(0) && Input.GetKey(KeyCode.Z))
{
transform.RotateAround(transform.position, transform.forward, Time.deltaTime * 90f);
}
if (Input.GetMouseButton(0) && Input.GetKey(KeyCode.X))
{
transform.RotateAround(transform.position, transform.right, Time.deltaTime * 90f);
}
if (Input.GetMouseButton(0) && Input.GetKey(KeyCode.Y))
{
transform.RotateAround(transform.position, transform.up, Time.deltaTime * 90f);
}

I assume what you are trying to achieve is
click on an object with the mouse Cursor and hold
then rotate this object using the according keys.
You actually need only one single script for this, not on every object but just one in your scene (like a central controller)
public class ObjectRotator : MonoBehaviour
{
[SerializeField] private Camera _camera;
//Optionally if you only want to hit objects on (a) certain layer(s)
//[SerializeField] private LayerMask layers;
private void Awake ()
{
if(!_camera) _camera = Camera.main;
}
private void Update ()
{
if(Input.GetMouseButton(0))
{
// Shoot a Raycast from the mouse position into your scene to check if you hit an object
var ray = _camera.ScreenPointToRay(Input.mousePosition);
if(Physics.Raycast(ray, out var hit)
// or if using the layer mask
//if(Physics.Raycast(ray, out var hit, float.positiveInfinity, layers)
{
if (Input.GetKey(KeyCode.Z))
{
// Instead of using RotateAround rather use Rotate which rotated around this object's
// pivot position anyway. By default it is in local space so also no need for transform.forward etc
hit.transform.Rotate(Vector3.forward * Time.deltaTime * 90f);
}
if (Input.GetKey(KeyCode.X))
{
hit.transform.Rotate(Vector3.right * Time.deltaTime * 90f);
}
if (Input.GetKey(KeyCode.Y))
{
hit.transform.Rotate(Vector3.up * Time.deltaTime * 90f);
}
}
}
}
}
NOTE: Requires your target objects to have colliders (and the selected layer(s))
As an alternative if you rather want to go for a solution with one script per object you could rather use e.g.
public class Rotation : MonoBehaviour
{
// This is called by Unity when the mouse is going down while hovering this object's collider
private void OnMouseDown ()
{
// Start the RotationRoutine
StartCoroutine (RotationRoutine());
}
// This is called when the mouse button goes up while hovering this object's collider
private void OnMouseUp()
{
// To simplify things just stop any routine started by this behavior
StopAllCoroutines ();
}
// Also stop when the mouse leaves this object's collider while still being pressed
private void OnMouseExit()
{
StopAllCoroutines ();
}
private void RotationRoutine()
{
// Whut?! No worries ;) This is fine in a Coroutine as soon as you "yield" somewhere inside
while(true)
{
if (Input.GetKey(KeyCode.Z))
{
transform.Rotate(Vector3.forward * Time.deltaTime * 90f);
}
if (Input.GetKey(KeyCode.X))
{
transform.Rotate(Vector3.right * Time.deltaTime * 90f);
}
if (Input.GetKey(KeyCode.Y))
{
transform.Rotate(Vector3.up * Time.deltaTime * 90f);
}
// Basically tells Unity to "pause" here, render this frame and
// continue from here in the next frame
yield return null;
}
}
}

Try three separate monobehaviour with input condition Input.GetKey(KeyCode.X), Input.GetKey(KeyCode.Y), Input.GetKey(KeyCode.Z) respectively:
if (Input.GetMouseButton(0) && Input.GetKey(KeyCode.X)) {
transform.RotateAround(transform.position, transform.forward, Time.deltaTime * 90f);
}
The transform applies to the transform of the gameObject the Monobehvaiour is attached to, so if the you have the script attached to many gameobjects, the logic applies to all of them.
You need to make 3 different Monobehaviours and handle the input logic respectively, or make a global Monobehaviour rotation handler, where you keep a reference for each of your gameObjects so that you can handle each of the transforms respectively.

Related

How can I lock the rotation of a player in Unity?

I'm currently making a 2D game as a beginner and I made a spinning platform. But when it's rotating the player's rotation (z-axis) also changes because it's a child of the platform. I need this when I use moving platforms. Now I want to lock the z-axis of the rotation of the player. I already tried it in 3 different ways, but none of them seems to be working. Does anybody know how to do this?
These are the three ways I tried:
// 1
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0);
// 2
PlayerTrans.transform.Rotate(
PlayerTrans.transform.rotation.x,
PlayerTrans.transform.rotation.y,
0,
Space.Self);
// 3
PlayerTrans.transform.localRotation = Quaternion.Euler(new Vector3(
PlayerTrans.transform.localEulerAngles.x,
PlayerTrans.transform.localEulerAngles.y,
0f));
and this is, what my code looks like for staying on the moving platforms. I used raycasting for this:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class Raycasting : MonoBehaviour
{
// Start is called before the first frame update
Transform PlayerTrans;
public float RayCastRange = 3;
void Start()
{
PlayerTrans = transform.parent;
}
// Update is called once per frame
void Update()
{
RaycastHit2D PlattformCheck = Physics2D.Raycast(transform.position, -Vector2.up, RayCastRange);
if (PlattformCheck.collider != null)
{
if (PlattformCheck.collider.gameObject.tag == "Platform")
{
PlayerTrans.transform.SetParent(PlattformCheck.collider.gameObject.transform);
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
else
{
PlayerTrans.transform.SetParent(null);
}
}
}
There are 2 ways that might help you:
Just freeze the rotation from the inspector:
you can use some LookAt function (there is one for 3D but you can look and find ones for 2D) and just look at the camera.
(if you cant find it let me know and I will add it)
You should raycast directly down and then apply velocities to both objects (un-child the player from the platforms). You could do something like this for the player:
public LayerMask mask; //set ‘mask’ to the mask of the
//platform in the Unity Editor.
public Vector3 velocity;
void Update()
{
RaycastHit hit;
if (Physics.Raycast(transform.position, -Vector3.up, out hit, 0.1f, mask))
//0.1f is the distance to the platform to be able to be moved by the platform.
{
velocity = hit.collider.gameObject.GetComponent<Platform>().velocity;
}
float h = Input.GetAxis(“Horizontal”);
//this one is for CharacterController:
cc.Move(velocity);
//this one is for rigidbody:
rb.velocity = velocity;
velocity = 0;
}
It takes the velocity from the ‘Platform’ script and moves the player based on it. Now we should add the platform script. Call it ‘Platform’.
public Vector3 velocity;
public Vector3 a; //a and b are the two points you want the platform to go between.
public Vector3 b;
public float period = 2f; //the amount of seconds per cycle.
float cycles;
void Update()
{
cycles = Time.time / period;
float amplitude = Mathf.Sin(Mathf.PI * 2f * cycles);
Vector3 location = Vector3.Lerp(a, b, amplitude);
velocity = transform.position - location;
transform.position = velocity;
}
This script interpolates between the point a and b, then it finds the velocity and applies it. The player takes this velocity and it moves the player. Comment if you found an error.

How to check whether a key is clicked Unity

I have a teleport orb in my game that you can hover over and click e to use to teleport to, however when I hold my e button down and hover over the orb it teleports me anyways, I want to make it so you have to hover over the orb and click e to teleport not so you can hold e and then hover over it. Any ideas on how to do this?
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class IsLookingAtTeleport : MonoBehaviour
{
public AudioSource teleportsound;
public Rigidbody rb;
void Start()
{
}
void FixedUpdate()
{
// Bit shift the index of the layer (8) to get a bit mask
int layerMask = 1 << 8;
// This would cast rays only against colliders in layer 8.
// But instead we want to collide against everything except layer 8. The ~ operator does this, it
//inverts a bitmask.
layerMask = ~layerMask;
RaycastHit hit;
// Does the ray intersect any objects excluding the player layer
if (Input.GetKey("e"))
{
if (Physics.Raycast(transform.position, transform.TransformDirection(Vector3.forward), out hit, Mathf.Infinity, layerMask))
{
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * hit.distance, Color.yellow);
Debug.Log("Did Hit");
if (hit.transform.tag == "TeleportOrb")
{
Debug.Log("Hit Teleportable Orb");
teleportsound.Play();
rb.transform.position = hit.transform.position;
Destroy(hit.transform.gameObject); // destroy the object hit
}
}
else
{
Debug.DrawRay(transform.position, transform.TransformDirection(Vector3.forward) * 1000, Color.white);
Debug.Log("Did not Hit");
}
}
}
}
Input.GetKey is fired every frame while the key stays pressed.
What you rather want to use is Input.GetKeyDown which is true only in the one frame when the button is actually pressed down.
Further note that instead of the stringed version I would always go for the ones using KeyCode as parameter.
And then make sure you do your user Input in Update not in FixedUpdate. While the later might work in some cases for getting continous inputs such as GetKey you might miss single event inputs.
And finally whenever a Rigibody is invoved you do not want to set anything through the Transform component.
// Rather configure the desired layers you want to hit here
// via the Inspector in Unity!
// actually way better than just including everything rather only
// select these layers that actually should be considered as teleport targets
// This way you wouldn't even need to check for Tags!
// just rather place all your teleport objects on a Teleport layer
// and only use a raycast on that layer
// see https://docs.unity3d.com/Manual/Layers.html
[serializeField] private LayerMask hitLayers;
private void Update()
{
if (Input.GetKeyDown(KeyCode.E))
{
// store the ray once
var ray = new Ray(rigibody.position, rigidbody.rotation * Vector3.forward);
if (Physics.Raycast(ray , out var hit, Mathf.Infinity, hitLayers))
{
Debug.DrawRay(ray.origin, ray.direction * hit.distance, Color.yellow);
Debug.Log("Did Hit");
// Rather use CompareTag instead of string based ==
if (hit.gameObject.CompareTag("TeleportOrb"))
{
Debug.Log("Hit Teleportable Orb");
teleportsound.Play();
rb.position = hit.transform.position;
// destroy the object hit
Destroy(hit.gameObject);
}
}
else
{
Debug.DrawRay(ray.origin, ray.direction * 1000, Color.white);
Debug.Log("Did not Hit");
}
}
}

Add Relative Force not in direction

Attempting to create a 2D game where force is added to a RigidBody2D according to the rotation of the object.
The object is a projectile. Objects spawn at their intended rotation but only move right along the X axis instead according to the rotation.
Anyone know where I'm going wrong?
// Start is called before the first frame update
void Start()
{
myRB = gameObject.GetComponent<Rigidbody2D>();
timer = Time.time + lifetime;
}
private void FixedUpdate()
{
myRB.AddRelativeForce(transform.right * thrust);
if(Time.time > timer)
{
Destroy(this.gameObject);
}
}
transform.right is already
The red axis of the transform in world space.
with the orientation applied.
You are passing that in again as a Vector relative to the local space of the object which is not what you want.
Either use in world space
myRB.AddForce(transform.right * thrust);
or in local space
myRB.AddRelativeForce(Vector3.right * thrust);

Mouse axes are based on position, not movement

Basically, when I have my cursor in the center of the screen, the camera doesn't rotate. Pretty standard. However, If I move my cursor to the left or right, It will start rotating. Still makes sense. However, When I stop moving the cursor, the rotation continues. I'm using an empty object and making the camera face in the same direction as that empty object. The problem is, the object continues to rotate. If I move the cursor back to the center of the screen, it stops rotating again. Something similar happens when I assign axes in the project settings to the right stick on an Xbox 1 controller. If I move the stick right, the camera begins to rotate, however, if I return the stick to the deadzone, it continues to rotate. If I then move the stick left, it will slow down the rotation, and eventually begin rotating the other direction. It doesn't happen with the vertical stick axis, though.
Here's my code with the mouse for the player and empty object rotation:
using System.Collections.Generic;
using UnityEngine;
public class Player : MonoBehaviour
{
public Transform PlayerT;
public Transform camT;
public Transform CamR;
public CharacterController controller;
public float speed;
public float CamRSpeed;
private float gravity;
private float HorizontalR;
private float VerticalR;
private Vector3 moveDirection;
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
HorizontalR = Input.GetAxis("Mouse X") * CamRSpeed * Time.deltaTime;
VerticalR = Input.GetAxis("Mouse Y") * CamRSpeed * Time.deltaTime;
CamR.Rotate(VerticalR, HorizontalR, 0f);
PlayerT.forward = CamR.forward;
PlayerT.eulerAngles = new Vector3(0f, PlayerT.eulerAngles.y, 0f);
moveDirection = (PlayerT.forward * Input.GetAxis("Vertical") * speed) + (PlayerT.right * Input.GetAxis("Horizontal") * speed);
controller.Move(moveDirection * Time.deltaTime);
}
}
//and for the camera (this is a separate script, I'm just not entirely sure how this site's formatting works):
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CameraScript : MonoBehaviour
{
public Transform cam;
public Transform player;
public Transform CamR;
private Vector3 offset;
// Start is called before the first frame update
void Start()
{
offset = CamR.position - cam.position;
}
// Update is called once per frame
void Update()
{
cam.eulerAngles = new Vector3(CamR.eulerAngles.x, CamR.eulerAngles.y, 0f);
cam.position = CamR.position - (CamR.rotation * offset);
}
}```
Well, as it turns out, it was simply because the object I was trying to rotate was a child of the player. I'm not entirely sure why it didn't work, but it does now.

Clamping an object axis in Vuforia AR camera

screenshot hereI want to clamp Y-axis on a cube. I can do it in Unity camera. But, it does not react correctly when I am using it in Vuforia camera.
My problem was that the cube follows the camera. I would like the cube to stay in its position and ignore the AR camera position. I sense it has something to do with WorldtoViewpoint but I cannot figure it out. Can you teach me how to do this please? thankyou
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class ClampMovementController : MonoBehaviour
{
// Start is called before the first frame update
void Start()
{
}
// Update is called once per frame
void Update()
{
Vector3 pos = transform.localPosition;
pos.y = Mathf.Clamp(transform.position.y, 0f, 0f);
transform.localPosition = pos;
}
}
This is my solution:
Actually its very simple. The INcorrect concept was my object attached to the AR camera, hence, object position is always moving related to camera position. Now. In order to make the object stays in its place. I need to get its localPosition. First. Store the localposition in Vector3 pos. And then do modification on Vector3 pos. At last, reassign the new value to the object localposition.
public class ClampMovementController : MonoBehaviour
{
public float currentPos;
public GameObject capsule;
void Update()
{
//store the value of object localPosition
Vector3 pos = capsule.transform.localPosition;
//modification on the value
pos.y = Mathf.Clamp(pos.y, currentPos, currentPos);
//rerassign the new value to the object localPosition
capsule.transform.localPosition = pos;
}
}
First of all your cube is moving with the camera because your image target is child of your ARCamera. Therefore, when you move the camera image target moves, then your cube moves as well. Make sure your ImageTarget has no parent.
I did not understand why you have to lock any movement in Y axis. I guess you are doing something wrong with lean touch when you move object. I have not used lean touch but i have achieved this with keyboard inputs. You can convert it to lean touch by modifying following script. Just add these line to your ImageTarget's DefaultTrackableEventHandler script:
//Variables for getting capsule and checking if ImageTarget is tracked
private bool isTracked = false;
private GameObject capsule;
Then create an Update method for getting input from user like this.
void Update()
{
if(isTracked)
{
if(Input.GetKey(KeyCode.W))
{
//using forward for moving object in z axis only.
//Also using local position since you need movement to be relative to image target
//Global forward can be very different depending on your World Center Mode
capsule.transform.localPosition += Vector3.forward * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.S))
{
capsule.transform.localPosition -= Vector3.forward * Time.deltaTime;
}
if (Input.GetKey(KeyCode.A))
{
//Using Vector3.left and right to be make sure movement is in X axis.
capsule.transform.localPosition += Vector3.left * Time.deltaTime;
}
else if (Input.GetKey(KeyCode.D))
{
capsule.transform.localPosition += Vector3.right * Time.deltaTime;
}
}
}
As you can see there is no movement in Y axis because i used forward, left and right vectors to make sure movement in in only X and Y axis.
Last you have to make sure isTracked is updated. In order to do that you have to add isTracked = false; in OnTrackingLost method and isTracked = true; in OnTrackingFound method. Good luck!

Categories