I have a virtual movement joystick that I'm using to control the movement of a player object which works fine, the code for this is below.
The problem I have is when I rotate the camera within game mode (or device) the direction is not adjusted according to the cameras rotation, exactly like is shown in this post here that I looked through to try and understand the problem.
I realise I need to rotate my movement around the forward direction the camera is facing which I tried to do with the bottom snippet of code however this yields really strange behavior when the player object moves incredibly fast and eventually unity becomes unresponsive, so something is being incorrectly multiplied I guess.
Could anybody point out where I'm going wrong please? ta !
Edit - Modified to potential answer
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using EasyJoystick;
public class NewJoystick : MonoBehaviour
{
[SerializeField] private float speed;
[SerializeField] private Joystick1 joystick;
[SerializeField] Camera MainCam;
private Rigidbody RB;
private Transform cameraTransform;
// Start is called before the first frame update
void Start()
{
cameraTransform = MainCam.transform;
}
void Awake()
{
RB = gameObject.GetComponent<Rigidbody>();
}
// Update is called once per frame
void Update()
{
float xMovement = joystick.Horizontal();
float zMovement = joystick.Vertical();
Vector3 inputDirection = new Vector3(xMovement, 0, zMovement);
//Get the camera horizontal rotation
Vector3 faceDirection = new Vector3(cameraTransform.forward.x, 0, cameraTransform.forward.z);
//Get the angle between world forward and camera
float cameraAngle = Vector3.SignedAngle(Vector3.forward, faceDirection, Vector3.up);
//Finally rotate the input direction horizontally by the cameraAngle
Vector3 moveDirection = Quaternion.Euler(0, cameraAngle, 0) * inputDirection;
RB.velocity = moveDirection * speed;
}
}
I tried this in the update loop -
var Direction = transform.position += new Vector3(xMovement, 0f,zMovement); //* speed * Time.deltaTime;
Vector3 newDir = MainCam.transform.TransformDirection(Direction);
transform.position += new Vector3(newDir.x, 0f,newDir.z) * speed * Time.deltaTime;
Well, here is a simple idea for how to solve it. At least this is how I did it, and it seems to work ever since.
First, you need to get the joystick input, like you did. Both axis input value should be between -1 and 1. This actually determines the direction itself, since the horizontal axis gives you the X coordinate of a vector and the vertical gives you the Y coordinate of that vector. You can visualize it easily:
Mind, that I just put up some random values there, but you get the idea.
Now, your problem is, that this angle you get from your raw input is
static in direction, meaning that it doesn't rely on the camera's face
direction. You can solve this problem by "locking it to the camera",
or in other words, rotate the input direction based on the camera
rotation. Here's a quick example:
//Get the input direction
float inputX = joystick.Horizontal();
float inputY = joystick.Vertical();
Vector3 inputDirection = new Vector3(inputX, 0, inputY);
//Get the camera horizontal rotation
Vector3 faceDirection = new Vector3(cameraTransform.forward.x, 0, cameraTransform.forward.z);
//Get the angle between world forward and camera
float cameraAngle = Vector3.SignedAngle(Vector3.forward, faceDirection, Vector3.up);
//Finally rotate the input direction horizontally by the cameraAngle
Vector3 moveDirection = Quaternion.Euler(0, cameraAngle, 0) * inputDirection;
IMPORTANT: The code above should be called in the Update cycle, since that is where you get the input information.
After this, you can work with moveDirection to move your player. (I suggest using physics for moving, instead of modifying its position)
Simple moving example:
public RigidBody rigidbody;
public Vector3 moveDirection;
public float moveSpeed = 5f;
void FixedUpdate()
{
rigidbody.velocity = moveDirection * moveSpeed;
}
Related
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.
I'm pretty new to Unity. I tried to create a script that the camera would follow the actor (with a little difference). Is there a way to improve the code? It works just fine. But I wonder if I did it the best way. I want to do it about as I wrote, so if you have any tips. Thank you
Maybe change Update to FixedUpdate ?
public GameObject player;
// Start is called before the first frame update
void Start()
{
player = GameObject.Find("Cube"); // The player
}
// Update is called once per frame
void Update()
{
transform.position = new Vector3(player.transform.position.x, player.transform.position.y + 5, player.transform.position.z - 10);
}
Making the camera following the player is quite straight forward.
Add this script to your main camera.
Drag the reference of the player object to the script and then you are done.
You can change the values in the Vector 3 depending on how far you want the camera to be from the player.
using UnityEngine;
public class Follow_player : MonoBehaviour {
public Transform player;
// Update is called once per frame
void Update () {
transform.position = player.transform.position + new Vector3(0, 1, -5);
}
}
Follows player in the back constantly when the player rotates, no parenting needed, with smoothing.
Piece of Knowledges:
Apparently, Quaternion * Vector3 is going to rotate the point of the Vector3 around the origin of the Vector3 by the angle of the Quaternion
The Lerp method in Vector3 and Quaternion stand for linear interpolation, where the first parameter gets closer to the second parameter by the amount of third parameter each frame.
using UnityEngine;
public class CameraFollow : MonoBehaviour
{
public Transform target;
public float smoothSpeed = 0.125f;
public Vector3 locationOffset;
public Vector3 rotationOffset;
void FixedUpdate()
{
Vector3 desiredPosition = target.position + target.rotation * locationOffset;
Vector3 smoothedPosition = Vector3.Lerp(transform.position, desiredPosition, smoothSpeed);
transform.position = smoothedPosition;
Quaternion desiredrotation = target.rotation * Quaternion.Euler(rotationOffset);
Quaternion smoothedrotation = Quaternion.Lerp(transform.rotation, desiredrotation, smoothSpeed);
transform.rotation = smoothedrotation;
}
}
This will always follow the player from the same direction, and if the player rotates it will still stay the same. This may be good for top-down or side-scrolling view, but the camera setup seems to be more fitting for 3rd person, in which case you'd want to rotate the camera when the player turns.
The easiest way to do this is actually not with code alone, simply make the camera a child of the player object, that way its position relative to the player will always stay the same!
If you do want to do it through code, you can change the code to be like this:
void Update()
{
Vector3 back = -player.transform.forward;
back.y = 0.5f; // this determines how high. Increase for higher view angle.
transform.position = player.transform.position - back * distance;
transform.forward = player.transform.position - transform.position;
}
You get the direction of the back of the player (opposite of transform's forward). Then you increase the height a little so the angle will be a bit from above like in your example. Last you set the camera's position to be the player's position and add the back direction multiplied by the distance. That will place the camera behind the player.
You also need to rotate the camera so it points at the player, and that's the last line - setting the camera's forward direction to point at the player.
Here is just another option. I always find it easier to have the variables which are populated in the inspector so you can adjust it and fine tune it as needed.
public GameObject player;
[SerializeField]
private float xAxis, yAxis, zAxis;
private void Update()
{
transform.position = new Vector3(player.transform.position.x + xAxis, player.transform.position.y + yAxis, player.transform.position.z + zAxis);
}
Hope my title summarises my problem. I have a rocket on a 2d game that only moves horizontally across the screen. I want it to rotate towards the players finger (the direction of movement), but cannot find a way to rotate the object without rotating the whole axis it moves on. I simply need it to seem like it has turned, but it should keep moving along the x. How can I go about this?
void Start () {
//scoreT = GetComponent<TextMeshProUGUI> ();
gameSpeed = 1;
score = 0;
Rigidbody2D rb2d = GetComponent<Rigidbody2D> ();
}
// Update is called once per frame
void Update () {
float MoveHorizontal = Input.GetAxis ("Horizontal");
Vector2 Movement = new Vector2 (MoveHorizontal, 0.0f);
rb2d.rotation = Quaternion.Euler (0.0f, 0, 0f, rb2d.velocity.x * -tilt);
transform.Translate (MoveHorizontal * speed, 0, 0);
One thing you can do is to modify rigidbody.rotation of your rocket rocket to make it tilt, when it moves, to one direction or to another. For example:
float tilt - 0.3f;
//In case you prefer the rotation in another axis you just need to modify the position of the rigidbody.velocity.x * -tilt
rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
Since you didn't add any code I am not sure how you are moving your rocket, so I will post a generic code you will need to adapt depending on your own project:
public class PlayerController : MonoBehaviour
{
public float speed;
//The tild factor
public float tilt;
//The limit within the spaceship can move
public Boundary boundary;
void FixedUpdate ()
{
//You will need to capture the screen touchs of the user for the inputs
float moveHorizontal = Input.GetAxis ("Horizontal");
float moveVertical = Input.GetAxis ("Vertical");
//Applying the movement to the GameObject
Vector3 movement = new Vector3 (moveHorizontal, 0.0f, moveVertical);
rigidbody.velocity = movement * speed;
//To ensure the GameObject doesnt move outside of the game boundary
rigidbody.position = new Vector3
(
Mathf.Clamp (rigidbody.position.x, boundary.xMin, boundary.xMax),
0.0f,
Mathf.Clamp (rigidbody.position.z, boundary.zMin, boundary.zMax)
);
//Here is where you apply the rotation
rigidbody.rotation = Quaternion.Euler (0.0f, 0.0f, rigidbody.velocity.x * -tilt);
}
}
As an aside, you are doing a space 2D game you may be interested in checking this tutorial:
https://unity3d.com/learn/tutorials/s/space-shooter-tutorial
What you want is to move your object in global/world space. As it seems the movement of your rocket is currently happening within local space. So when you rotate the rocket it's local coordinates are rotated as well. The world space coordinates are fixed and will never rotate when you change your rocket.
Here is another explanation at that.
You can also have a look at Transform.localPosition and Transform.position and see how your rocket behaves when using one or the other.
You could make the sprite/renderer a child of the GameObject that is your rocket.
Then you can freely rotate the sprite/renderer around without changing the rotation you move the parent GameObject.
This is a hacky solution but it achieves the desired result.
You must move your object relative to the world and rotate locally.
So use Transform.position to move your object and Transform.LocalRotation to rotate it.
Or you can put the part that must rotate as a children of the object that translate, and rotate the children.
I have been trying this for two days with no success. I cant figure out where I'm missing the point. All the missiles are moving towards the position of the target but not following it. The position remains fixed and all the newly created missiles come to this point instead of following the target.
Here is the code:
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class HomingMissile : MonoBehaviour
{
private GameObject target; //changed to private
private Rigidbody rb;
public float rotationSpeed;
public float speed;
Quaternion rotateToTarget;
Vector3 direction;
private void Start()
{
target = GameObject.FindGameObjectWithTag("Player"); //uncommented this
rb = GetComponent<Rigidbody>();
}
private void FixedUpdate()
{
//made some modifications
Vector3 direction = (target.transform.position - transform.position).normalized;
float angle = Mathf.Atan2(direction.x, direction.z) * Mathf.Rad2Deg;//interchanged x and z
Quaternion rotateToTarget = Quaternion.Euler(0, angle, 0);
transform.rotation = Quaternion.Slerp(transform.rotation, rotateToTarget, Time.deltaTime * rotationSpeed);
Vector3 deltaPosition = speed * direction * Time.deltaTime;
rb.MovePosition(transform.position + deltaPosition);
}
}
I selected the target(transform) using the inspector.
I'm using Unity and C# obviously you know that.
What Im trying to achieve is that the missile should follow the position of the target in real time. And i can add the destroy code for the missile myself.
Note :
Please don't tag this as a duplicate. It is not.
The game is 2D where Y is always constant. Vertical axis is X and Horizontal axis is X. The objects are 3D. That's why I can't use rigidbody2D.
EDIT:
Code edited. The missile follows the target and also points to the direction of motion. How to make the missile make a circular rotation when it needs to rotate?
Firstly, consider:
Not modifying a rigidbody.velocity directly, as it will result in unrealistic behaviour
Using FixedUpdate() instead of Update() when controlling rigidbodies
Use rigidbody.movePosition() and rigidbody.moveRotation() instead. Here's an example:
Vector3 dir = (target.transform.position - transform.position).normalized;
Vector3 deltaPosition = speed * dir * Time.deltaTime;
rb.MovePosition(transform.position + deltaPosition);
Try out rigidbody.MoveRotation() yourself for practice.
Finally, understand that there are many ways to implement homing for missiles. Here's one that is commonly used in real life.
Edit: I will not recommend using rb.addForce() because if u try it out u will realise it is too indeterministic.
So I am trying to create a kind of turrent like a tank. The upper section needs to look at my mouse. I have tried
GetComponent().ScreenToWorldPoint(Argument);
many times togheter with
Input.mousePosistion();
But I couldn't figure this out really. I am programing this in C# if any one could help or provide a simple script that works in a 3D setting that would be great!
My code so far:
using UnityEngine;
using System.Collections;
public class Laser : MonoBehaviour {
public float Speed;
public AudioClip LaserSFX;
private Transform Player;
public Vector3 mousePos;
public Camera Cam;
// Use this for initialization
void Start () {
GameObject player = GameObject.FindWithTag("Player");
Player = player.transform;
if(!GetComponent<AudioSource>().isPlaying)
GetComponent<AudioSource> ().PlayOneShot (LaserSFX);
}
// Update is called once per frame
void Update () {
//Code to look at mouse or rotate
//Code to move towards it
float dist = Vector3.Distance(Player.position, transform.position);
if (dist >= 500)
{
Destroy(gameObject);
}
}
}
With kind regards,
Misterk99
You have to convert the mouse position to the view position, this means the ScreenToWorldPoint should be provided to you by the camera you are using at that time, and since you already have the camera as cam:
Vector3 pos = Input.mousePosition;
pos = Cam.ScreenToWorldPoint(pos);
with that now you have to face the position, and since you are using topdown 3D i supose the camera is facing the -y position. if so, don't forget to set the y you want:
pos.y = (your value here, depending to the position you are giving to your objects);
Vectgor3 dir = this.position - pos;
float angle = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(angle, Vector3.up);
I figured out a solution while trying to find one for myself. The answer #Spike gave led me on the right track. However, I used the mouse's viewpoint position and converted the objects position to the viewport position. Here is the code in Javascript (what I am using) which should be easy enough to convert to C#.
My camera is facing the -Y direction, FYI.
#pragma strict
var cam: Camera;
function Start () {
}
function Update () {
var mousePos: Vector3 = Input.mousePosition;
var playerPos: Vector3 = cam.WorldToScreenPoint(transform.position);
var dir: Vector3 = mousePos - playerPos;
var angle: float = Mathf.Atan2(dir.y, dir.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.AngleAxis(-angle, Vector3.up);
}