I have a character/rigidbody and 'she' can turn around. When I press Play in Unity, if I move forward/backward, that's fine, she moves forward/backward. It's a good start.
But then if I turn her left or right, then go forward/backward, she now moves sideways.
She is a rigidbody component set as a parent in the scene.
Surely it's not difficult to do, but I can't figure out how to set her rotation so that when she turns, she will move 'forward' when I press the button to move her forward! There are plenty of first-person-shooter games where you can turn and move 'forward' and the player goes in the correct direction.
My rotation script at the moment is this:
Vector3 EulerAngleVelocity;
public float rotateSpeed = 250.0f;
void Update() {
if (Input.GetKey(KeyCode.UpArrow) || Input.GetKey(KeyCode.DownArrow))
{
MoveVector = PoolInput();
Move();
}
if (Input.GetKey(KeyCode.RightArrow))
{
EulerAngleVelocity = new Vector3(0, rotateSpeed, 0);
Quaternion deltaRotation = Quaternion.Euler(EulerAngleVelocity * Time.deltaTime);
rigidbody.MoveRotation(rigidbody.rotation * deltaRotation);
}
}
private void Move()
{
rigidbody.AddForce((MoveVector * moveSpeed));
}
private Vector3 PoolInput()
{
Vector3 dir = Vector3.zero;
dir.x = joystick.Horizontal();
dir.z = joystick.Vertical();
if (dir.magnitude > 1)
dir.Normalize();
return dir;
}
You're moving your joystick and adding that direction in relation to the WORLD instead of in relation to your player. If you want to add force relative to the orientation of the RigidBody probably what you want to use is rigidBody.AddRelativeForce (documentation) instead of simply rigidBody.AddForce.
Your problem isn't your rotation code, it's your movement code. You're applying a motion in world-space, not local-space ('object'-space).
For example, if you're using Vector3.Forward, you will want to use transform.Forward instead.
Related
Im scratching my head over this one, really cant figure it out.
Im trying to implement the player movement from this example:
https://www.youtube.com/watch?v=LNidsMesxSE
Starts at minute 4:25 and ends at minute 5:20
This script tries translating all that to unity.So I started all the way over from scratch and just want to make a simple movement script.
You can plug this into any Unity version, throw the script onto and object with a CharacterController component, add a child object with a mesh that will tilt, the main object will rotate around its Y axis and move.
I do recommend using a simple T pose character or atleast a long capsule so you can better see what is happening than when you would be using a cube to test this.
The weird glitches im having is that the object randomly spasms out even though im always only adding extremely small rotations and movements every frame. And 95% of the time it doesnt happen, so I havent been able to pinpoint exactly what is causing this.
using System.Collections;
using System.Collections.Generic;
using UnityEngine;
public class CharacterMove : MonoBehaviour
{
public float speed = 200f;
public float rotationSpeed = 180f;
public float tiltFactor = 8f;
public bool normalizeVelocity = false;
CharacterController cc;
Vector3 velocity;
Transform armatureBody;
void Start()
{
cc = GetComponent<CharacterController>();
armatureBody = transform.GetChild(0);
}
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
}
void FixedUpdate()
{
if ((velocity != Vector3.zero))
{
var ccVelocity = cc.velocity; // CharacterController velocity. This might be the problem, but without it I cannot rotate the object towards the actual forward velocity.
Quaternion toRotation;
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.deltaTime);
if (Input.GetKey(KeyCode.S)) print("new toRotation: " + toRotation);
armatureBody.localEulerAngles = new Vector3(velocity.z * tiltFactor, 0, -velocity.x * tiltFactor); // Tilt Body towards velocity This causes the weirdest and most consistest twitching bugs when the tilt is backwards so the object is moving backwards, maybe because im using LookRotation to look backwards?
}
velocity = Quaternion.Euler(0, transform.eulerAngles.y, 0) * velocity; // forward is always forward
cc.SimpleMove(velocity * Time.deltaTime * speed);
}
}
This script does 4 things,
get the velocity from player input.
rotate the main Object(this script) towards the velocity
add tilt towards the velocity, to the child armatureBody, this is a t Pose character in my case.
move the main Object this script is on.
Any help would be appreciated.
I tried removing the vector3 != Zero check, I tried normalizing the vectors, and I tried using a different Quaternion rotation method, but all with the same faulty results.
Check in Update() if Input.forward > threshold if so rotate to velocity. Dont rotate to velocity when only going sideways or backwards, so we can now strafe sideways and walk backwards when standing still or run and rotate to our velocity with the AD keys when running.
void Update()
{
velocity = new Vector3(Input.GetAxis("Horizontal"), 0,
Input.GetAxis("Vertical"));
if (velocity.z > 0.2f)
{
forwardVelocityThreshold = true;
}
else forwardVelocityThreshold = false;
}
Then in our FixedUpdate() we check for that bool and apply the rotateToVelocity if true.
Adjust the following if statement:
if (ccVelocity != Vector3.zero && forwardVelocityThreshold)
{
toRotation = Quaternion.LookRotation(ccVelocity, Vector3.up);
transform.rotation = Quaternion.RotateTowards(transform.rotation, toRotation, rotationSpeed * Time.fixedDeltaTime);
}
Now it works as intended, I also changed the deltaTime to FixedDeltaTime as suggested by Voidsay in the comments, although I have heard conflicting things about this. Some say FixedUpdate always uses fixedDeltaTime even if you are using deltaTime, other say no, always use fixedDeltaTime in FixedUpdate.
My bat stays still in the air.
How can i check that the player, that is moving right below on x axis, comes from the left or right side relative to the bat?
public class Bat : MonoBehaviour
{
Player player;
void Start()
{
player = FindObjectOfType<Player>();
}
void Update()
{
if (transform.InverseTransformPoint(player.transform.position).x >= 0)
transform.rotation = new Quaternion(transform.rotation.x, 0f, transform.rotation.z, 0f);
else
transform.rotation = new Quaternion(transform.rotation.x, -180f, transform.rotation.z, 0f);
}
}
As you may notice i try to flip the the Bat so it actually looks at the player.
First, don't use new Quaternion(...); unless you absolutely, 100% know quaternions inside and out. For instance, having the w component be 0 results in zero effective rotation. Also, quaternions use unitless figures and not degrees for their components. See here for a nice visualization of what different quaternion values might look like, under Quaternion).
Second, your logic is weird because if the bat has zero (identity) rotation, and the player is on the left of the bat, then your logic tries to* flip the bat 180 degrees, so that the player is now on the right side of the bat.
Then, the next frame assuming the bat and player are still in the same positions, the logic says oh the player is on the right side, set the rotation to zero (identity) rotation, which is of course what it was in the first place. So basically, you would have the bat rotate 180 degrees again so that the player is once again on the left side of the bat.
And so, you could get into a situation where every frame, the bat would flip a complete 180. Definitely not desired.
* I'm assuming if you had used Quaternion.Euler(transform.eulerAngles.x, 0f, transform.eulerAngles.z) etc.
Instead of concerning with any of that, use Vector3.Cross to find the forward the bat should point so its right faces the player's position. Then, use Quaternion.LookRotation to set the rotation of the bat to point in that forward direction:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
If you have the player on a different z as the bat, this will cause the bat to rotate around the y axis accordingly, which is what you would want from a 3d game, and could be a neat effect for a 2d game if you want that kind of effect.
If you want it to ignore the z position of the player, you could zero out the z component of batRightDir...
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
batRightDir.z = 0; // ignore Z differences between bat and player
Vector3 batForwardDir = Vector3.Cross(batRightDir, Vector3.up);
if (batForwardDir.sqrMagnitude == Vector3.zero)
{
// player above or below bat. do nothing?
return;
}
transform.rotation = Quaternion.LookRotation(batForwardDir);
}
or it may be more intuitive to branch as you were previously:
void Update()
{
Vector3 batRightDir = player.transform.position - transform.position;
if (batRightDir.x > 0)
{
transform.rotation = Quaternion.identity;
}
else if (batRightDir.x < 0)
{
transform.rotation = Quaternion.LookRotation(Vector3.back);
}
else
{
// player above or below bat. do nothing?
}
}
You can get the access to his Rigidbody and than call it.
If Rigidbody.velocity.x > 0 he is moving to the right. If it's <0 it's moving to the left. All is related to X axis of course.
So my game is sort of like a 3d top down shooter, so I want my gun to shoot wherever the mouse is and it wont go to the mouse unless im shooting down. If you've seen brackyes game called ball wars, im sort of trying to replicate one like that but the projectile is not shooting the right way.
I got my script from blackthornprods ranged combat tutorial (which is for 2d so maybe thats the issue but I dont know how to solve it) :
public float speed;
public float lifeTime;
private void Start()
{
Invoke("DestoryProjectile", lifeTime);
}
private void Update()
{
transform.Translate(transform.up * speed * Time.deltaTime);
}
void DestroyProjectile()
{
Destroy(gameObject);
}
Appreciate anyone to try!
Here is my other script:
Camera mainCam;
public GameObject projectile;
public Transform shotPoint;
private float timeBtwShots;
public float startTimeBtwShots;
void Awake()
{
mainCam = Camera.main;
}
void Update()
{
float objectDepthFromCamera = Vector3.Dot(
transform.position - mainCam.transform.position,
mainCam.transform.forward);
Vector3 cursorWorldPosition = mainCam.ScreenToWorldPoint(Input.mousePosition
+ Vector3.forward * objectDepthFromCamera);
Vector3 localUpNeeded = Vector3.Cross(Vector3.forward,
cursorWorldPosition - transform.position);
transform.rotation = Quaternion.LookRotation(Vector3.forward, localUpNeeded);
if(timeBtwShots <= 0)
{
if (Input.GetMouseButtonDown(0))
{
Instantiate(projectile, shotPoint.position, transform.rotation);
timeBtwShots = startTimeBtwShots;
}
}
else
{
timeBtwShots -= Time.deltaTime;
}
}
Projectile not shooting direction of my weapon. Simple solution -
First instantiate or pool the instance of projectile.
Set rotation of projection from the rotation of weapon & set location to spawn point
Now fire, or whatever strategy you are using
Consider Global rotation, if you need help, tell me, I will edit and give a snippet of code.
This should work. If doesn't post all necessary code, I will give a better solution.
Here is sample github project I created, just for you. I opened Unity nearly after a year. Please check all the versions.
Must check :
firing in facing direction 💋
just instantiate at spawn point
just added some rotation
I think this should give you concept.
Press X for a rantom rotation
Press Space to shoot a projectile :lol:
The white cube shows that it always shoots at a constant direction
I just started with unity and followed thair 2D UFO example project.
In an effort to extend it, I came up with a quirky way of contolling my Player.
It always moves in circular paths and once I click a button, the circle's direction changes and the imaginary circle center is tanslated as shown in the picture below. That allows you to move in a figure 8 or S shape pattern and is quite fun.
However, once I figured out how to do this motion, the player object did not have any collision detection anymore.
In the original example the whole movemet handling is done within FixedUpdate(). I, however, use Update() since the former does not seem to work at all with my code (i.e. no movement at all).
This is my code for the movement so far:
public class ControlledRotation : MonoBehaviour
{
public float radius = 3f;
public float speed = 3f;
private float timeCounter = 0;
private float direction = 1f;
private Vector3 offset;
private Rigidbody2D rb2d;
void Start()
{
offset = transform.position;
}
void Update()
{
if (Input.GetKeyDown(KeyCode.RightArrow))
{
//change the offset from the circle center and the rotation direction on keypress
offset += new Vector3(Mathf.Cos(timeCounter), Mathf.Sin(timeCounter), 0) * radius * direction * 2;
direction *= -1;
}
timeCounter += Time.deltaTime * direction * speed;
transform.position = new Vector3(Mathf.Cos(timeCounter), Mathf.Sin(timeCounter)) * radius * direction + offset;
}
}
The Plyer object has a Rigidbody 2D and a Circle Collider 2D. The walles it should collide with have Box Collider 2D. Yet, the UFO can simply pass the walls.
I assume a probable cause in the fact that I simply change transform.position or since im using Update/FixedUpdate wrong.
If you happen to have any advice on how I can keep my chosen movement control mechanism and still be able to collide with objects, I'd highly appreciate it :)
Edit:
I feel I need to go with using the rigidbody and applying some force... but I haven't figured out how to reproduce this movement with forces and also forces seem to not be super crisp in response
When you need to move an object that has a Rigidbody, you need to move it using forces, you cannot do it with just transform.position, it ignores physics. that's why you cannot detect the collision.. I suggest you move it like that. I have some examples when I had to move a character that needed to interact with physics.
gameObject.GetComponent<Rigidbody>().velocity = Vector3.zero;
And this one is for moving it in certain directions:
//Keys for controlling the player (move and shoot) only when he's alive
if (Input.GetKey(KeyCode.UpArrow) && alive)
{
GetComponent<Rigidbody>().MovePosition(transform.position + Vector3.forward * Time.deltaTime * 4);
}
if (Input.GetKey(KeyCode.DownArrow) && alive)
{
GetComponent<Rigidbody>().MovePosition(transform.position + Vector3.back * Time.deltaTime * 4);
}
I hope it helps.. greetings :)
I already can make the player rotate around y- axis by the input of the axis that has been defined on the unity editor. But I want camera also rotating while the player rotates. I already made like below, but the camera not rotating, just the player. Anyone could help me?
using UnityEngine;
using System.Collections;
[RequireComponent(typeof(CharacterController))] // This script requires CharacterController attached to the game object where this script attached into
public class CheckPlayer : MonoBehaviour
{
private Vector3 moveDirection = Vector3.zero; // Define and set for the movement of the player is not moving by default
private float gravity = 20.0f, speed = 5.0f; // Define and set for the gravity, speed of the player
private void Update()
{
// Rotate the Player
transform.Rotate(0, Input.GetAxis("Rotate") * 60.0f * Time.deltaTime, 0);
Camera.main.transform.eulerAngles = new Vector3(0, Input.GetAxis("Rotate") * 60.0f * Time.deltaTime, 0);
// Get the CharacterController component
CharacterController controller = GetComponent<CharacterController>();
// If the character is on the ground
if (controller.isGrounded)
{
// Get the axis direction for the movement of the character from the Input in the editor
moveDirection = new Vector3(Input.GetAxis("Horizontal"), 0, Input.GetAxis("Vertical"));
// Player movement depends on the move direction
moveDirection = transform.TransformDirection(moveDirection);
// The player movement is depends on the player speed
moveDirection *= speed;
}
// How much for the time for player takes to hit the ground because of gravity
moveDirection.y -= gravity * Time.deltaTime;
// Move the character each second while pressing the key input defined in the editor
controller.Move(moveDirection * Time.deltaTime);
}
private void OnTriggerEnter(Collider col)
{
// If the game object colided with the certain tag
if (col.gameObject.tag == "Inn's Door")
{
// Load another level
GameManager.LoadLevel("Third Loading Scene");
}
// If the game object colided with the certain tag
else if (col.gameObject.tag == "Field's Door")
{
// Load another level
GameManager.LoadLevel("Fourth Loading Scene");
}
}
}
The code above I attached to the player and just refer the camera.
Thank you before
I think you're making this too complicated.
To simply have a camera follow an object while looking at that object, make the camera a child of the object. So the player would have a camera as a child object. The camera would be set up behind him, pointing at him.
This should give you the functionality you want but it doesn't look very good.
My suggestion is to look for the new sample assets (free to all unity3d users and created BY unity technologies) on the asset store. Inside they have a camera prefab which works rather well.
Good luck, I hope that helps.