Look Relative Movement in Unity using Cinemachine - c#

I'm relatively new to coding, having just started about a week ago, and I can't seem to figure out or find anything relating to look relative movement using a third person Cinemachine freelook camera.
What I'm trying to accomplish is a camera similar to Risk of Rain 2, or maybe Smite where you can orbit the player and look at the front of them while they're idle, but once you start moving the player rotates towards the direction the camera is looking, and stays facing that direction no matter how they're moving.
I have a basic scene set up with a Cinemachine freelook camera following a cube, but my problem is whenever I turn the camera then move, my character doesn't rotate and instead starts moving in the direction it's facing instead of where the camera's looking, then only starts rotating if I'm pressing a movement key, so the rotation gets disjointed from where the camera's looking, if that makes sense. I kind of frankensteined some code from Royal Skies' movement tutorial and someone else's rotation tutorial for a top-down game.
This is the code for a script applied to my player character;
public float speed = 4.5f;
public Vector3 deltaMove;
public float turnspeed = 500;
void Update()
{
deltaMove = new Vector3(Input.GetAxisRaw("Horizontal"), 0, Input.GetAxisRaw("Vertical")) * speed * Time.deltaTime; //movement code
transform.Translate(deltaMove);
float horizontal = Input.GetAxis("Mouse X");
if (Input.GetKey(KeyCode.W) || Input.GetKey(KeyCode.A) || Input.GetKey(KeyCode.S) || Input.GetKey(KeyCode.D) == true) //if wasd pressed, rotate
{
transform.Rotate(horizontal * turnspeed * Vector3.up * Time.deltaTime, Space.World); //the rotation code
}
}
I'd be willing to do anything to be honest, I've been trying to figure this out for a bit now.

Related

Instantiate a prefab pointing in the direction of another object

The Issue
I'm making a 2D Unity game where the main weapon of your character is a fireball gun. The idea is that a fireball will shoot out of the player's hand at the same angle the player's hand is pointing. I have 3 issues:
When I shoot the fireball, since the fireball is a RididBody, it pushes the player. This is because I've made the centre of the player's arm (the same place where the fireball shoots from) the point at which the arm rotates around the player (what is meant to be the shoulder);
To instantiate the fireball prefab on the arm, the only way I know how to do it is by using a piece of code which requires the arm to be a RigidBody. This means that the arm is affected by gravity and falls off the player on start unless I freeze the arm's y-axis movement, which means that when the player jumps, while the arm does not fall, it floats at the same y-position as where it started while moving along the x-axis; and
When the fireball is shot, the angle from which it is propelled after being shot is not the same angle as the angle of the player's arm.
Instantiating the Fireball
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var fireballTransform = Instantiate(fireballPrefab); //creates a new shot sprite
fireballTransform.position = new Vector3(transform.position.x + horizMultiplier, transform.position.y, transform.position.z);
fireballTransform.rotation = orientation;
fireballTransform.transform.Rotate(0, 0, transform.rotation.z);
}
if (Input.GetKeyDown(KeyCode.D)) // moves right
{
orientation = 0;
horizMultiplier = 0.08F;
}
if (Input.GetKeyDown(KeyCode.A)) // moves left
{
orientation = 180;
horizMultiplier = -0.08F;
}
This piece of code is located within the script applied to the player's arm. The movement of the arm works fine and the problem seems to be either within this piece of code or the code for my fireball (which I will put next). A few definitions:
pew is a sound effect played when the fireball is shot;
horizMultiplier is the distance from the arm's centre which I would like the fireball to instantiate (also dependant of if the player) is facing left or right); and
orientation is which direction the player is facing (left or right). The fireball is then instantiated facing that same direction.
Fireball Script
public Vector2 speed = new Vector2(); // x and y forces respectively
private Rigidbody2D rb; // shorthand
private float rotation;
void Start()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rotation = rb.rotation;
if (rotation == 0)
{
rb.AddForce(Vector3.right * speed.x); // propels right
}
if (rotation == 180)
{
rb.AddForce(Vector3.left * speed.x); // propels left
}
}
I believe this code is explanatory enough with comments (if not please comment and I'll address any question). I believe an issue could also be in this piece of code because of the lines: rb.AddForce(Vector3.right * speed.x); and rb.AddForce(Vector3.left * speed.x); as these add directional forces to the object. I don't know is this is objective direction (right or left no matter what direction the object the force is being applied to is facing) or if it's right or left in terms of the object-- say if an object was rotated 90 degrees clockwise and that object had a force applied so that it moves right making the object move downwards.
What I'm expecting to happen is the player's arm will turn so that when a fireball is fired it is fired in the direction the arm is facing. The arms turning mechanics are fine, it's just trying to properly instantiate the fireball. Can anyone help with any of the issues I've laid out?
How I would go about this:
Add an empty transform as child of the arm and move it to where the fireball should spawn. Also make sure the rotation of it is such that its forward vector (the local z-axis) points to where the fireball should go. To see this, you need to set "Local" left of the play button.
Spawn the fireball at the transform's position with its rotation.
Ignore collision between the arm's/player's collider and the fireball's collider with https://docs.unity3d.com/ScriptReference/Physics.IgnoreCollision.html. If necessary, you can enable the collision between the two colliders again after 1s or so.
Set the fireball rigidbody's velocity to speed * rigidbody.forward.
If you need help with the code, please post it so I can see what's going on.
Edit:
The arm definitely doesn't require a Rigidbody.
You can just use Instantiate(prefab, position, rotation) as shorthand.
Is this for a 2D game?
Also, I'm going to sleep now but I'll gladly try to help tomorrow.
Your Issues
1) You should be masking the fireball's layer not to collide with your player's layer.
You can find more info about this here: https://docs.unity3d.com/Manual/LayerBasedCollision.html
(note: make sure you're on the Physics2D panel, and not the Physics one, as that's for 3D)
2) There is a setting called gravityScale in RigidBody2D. You should set this to 0 if you don't want gravity to be affecting your object. More info: https://docs.unity3d.com/Manual/class-Rigidbody2D.html
Fireball Instantiate
if (Input.GetKeyDown(KeyCode.Space))
{
pew.Play();
var position = hand.transform.position + hand.transform.forward * horizMultiplier;
var rotation = hand.transform.rotation;
var fireball = Instantiate<Fireball>(fireballPrefab, position, rotation);
fireball.StartMoving();
}
Fireball Script
private Rigidbody2D rb; // shorthand
private float rotation;
public float Speed;
public void StartMoving()
{
rb = GetComponent<Rigidbody2D>(); // shorthand
rb.velocity = transform.forward * Speed;
}
void OnTriggerEnter(....) { .... }

Reflection of velocity no longer works after converting to 3D

I've been making a game based on Dragon Quest Heroes Rocket Slime's tank battle system for a while. First thing I need to get down is it's movement system where the player can stretch and sling themselves in a direction. When they hit a wall they should reflect off it realistically (eg: hitting it diagonally will reflect them diagonally)
My game was in 2D for the most part but figuring out pseudo-3d collision in a 2D space among other issues made it nearly impossible to continue so I moved it to fully 3d.
Everything worked well until I tried bouncing off a wall. If my player hits a wall going up, he goes down and vice versa. However, if my player hits a wall going left it tries to 'reflect' him left when it should reflect him right (and vice versa for hitting it while going right).
The way 'velocity' works in my game is that when stretching a Vector2 called pVelocity goes up based on how much they stretched. When they let go a 'endPos' is created based on currentPosition + pVelocity. The player will then move via Vector3.MoveTowards to that endPos at a constant speed. When hitting a wall I do this:
if (hit && foundHit.transform!=transform && curState != state.wallHit && foundHit.transform.tag!="Object")
{
//we've hit a wall while blasting
//now we make the player 'squish' up against the wall and bounce off!
Vector3 reflectedVelocity = Vector3.Reflect(new Vector3(pVelocity.x,0,pVelocity.y), foundHit.normal);
//playerAnimator.SetFloat("VelocityX", reflectedVelocity.x);
//playerAnimator.SetFloat("VelocityY", reflectedVelocity.y);
curState = state.wallHit;
playerSound.Play();
transform.position = foundHit.point;
playerAnimator.Play("Squish");
StartCoroutine(wallBounce(reflectedVelocity));
}
And in wallBounce, I do this:
IEnumerator wallBounce(Vector3 hitVelocity)
{
playerAnimator.enabled = true;
yield return new WaitForSeconds(.5f);
playerAnimator.Play("Walk blend tree");
pVelocity = new Vector2(hitVelocity.x,hitVelocity.z);
endPos = transform.position + (-transform.forward * pVelocity.magnitude);
curState = state.blasting;
Vector3 diff = endPos - transform.position;
diff.Normalize();
float rot_z = Mathf.Atan2(diff.y, diff.x) * Mathf.Rad2Deg;
transform.rotation = Quaternion.Euler(0f, rot_z - 90, 0f);
}
When slinging, the player is always facing in the direction they're going so I assume that the issue is somewhere inside wallBounce when I create the new endPos but I'm not really sure how to fix it.
Seems the issue was that my player wasn't looking in the direction of movement when hitting left or right. Adding this in the part before I call MoveTowards worked.
transform.LookAt(endPos);

Unity3D - Why doesn't my player rotate properly?

I'm building a top down shooter and so I have my camera above my player and the map. Here's the code I've written in the player controller script for movement:
public class playerMovement : MonoBehaviour {
public float speed;
private Camera mainCamera;
void Start () {
mainCamera = FindObjectOfType<Camera>();
}
// Update is called once per frame
void Update () {
// player movement
transform.Translate(speed * Input.GetAxis("Horizontal") * Time.deltaTime, 0f, speed * Input.GetAxis("Vertical") * Time.deltaTime);
// Camera Ray casting
Ray cameraRay = mainCamera.ScreenPointToRay(Input.mousePosition);
Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
float rayLength;
if (groundPlane.Raycast(cameraRay, out rayLength)) {
Vector3 look = cameraRay.GetPoint(rayLength);
Debug.DrawLine(cameraRay.origin, look, Color.red);
transform.LookAt(new Vector3(look.x, transform.position.y, look.z));
}
}
}
I want to be able to move the player using the WASD keys and also rotate following the direction on where the mouse is, however I don't want the rotation of the player to change the direction of the keys, I need the player to move forwards if the W key is pressed no matter which way the player is facing.
However for some reason my code makes the player move forwards depending on which way it is facing which I don't want.
How can I fix this?
The problem is that your transform.Translate call is in "self" space. Forward, backward, left, right are all relative to the direction the transform is facing. That is why your player is moving relative to the facing direction.
If you want to translate relative to "global" or "world" space, you have to add an additional parameter.
// player movement
transform.Translate(speed * Input.GetAxis("Horizontal") * Time.deltaTime,
0f,
speed * Input.GetAxis("Vertical") * Time.deltaTime,
Space.World);
Note the Space.World parameter at the end, to set the world coordinate system.
You can find more in the Unity docs here: https://docs.unity3d.com/ScriptReference/Transform.Translate.html
You need to look at the difference between local and global coordinate systems.
Right now your WASD keys are moving the player character according to global coordinates, and you want the WASD movement to be dependant on the player's orientation so you need to use a local coordinate system.
http://wiki.unity3d.com/index.php?title=Converting_Between_Coordinate_Systems

Rotate player with platform without parenting

I'm currently making a small platformer 3D game, but unfortunately I can't make the player to rotate properly when it is riding the platform, the thing here is that I don't want to make the player child of the platform, so far I've managed to make him move smoothly along with the platform, but the rotation is still going nowhere, here is the code I'm using for the rotation:
player.transform.rotation *= platform.rotation;
and here is the effect I got:
Rotation Error
not very nice :(
I guess the solution is something simple, some formula, but unfortunately I'm not very good with math :( So, thank you guys, I hope you can help me.
I'll show you a simple script example which makes a cube rotate by input while reacting to the rotation of the platform on which it stands:
using UnityEngine;
public class CubeRotation : MonoBehaviour {
public GameObject Platform;
Quaternion PreviousPlatformRotation;
public float rotationSpeed = 50;
private void Start() {
PreviousPlatformRotation = Platform.transform.rotation;
}
private void Update() {
//Rotate the cube by input
if (Input.GetKey(KeyCode.A)) {
transform.Rotate(Vector3.up, Time.deltaTime * rotationSpeed);
}
if (Input.GetKey(KeyCode.D)) {
transform.Rotate(Vector3.up, -Time.deltaTime * rotationSpeed);
}
//Adjust rotation due to platform rotating
if (Platform.transform.rotation != PreviousPlatformRotation) {
var platformRotatedBy = Platform.transform.rotation * Quaternion.Inverse(PreviousPlatformRotation);
transform.rotation *= platformRotatedBy;
PreviousPlatformRotation = Platform.transform.rotation;
}
}
}
The logic of the adjustment to the platform rotation is this:
Get at start the rotation quaternion of the platform (in your case, get it when the cube object climbs on the platform)
With A and D rotate the cube normally around the local Y axis.
Afterwards check if the platform's rotation has changed, if yes:
3.a Get how much the platform rotated since the previous frame, with the operation Actual rotation * Inverse(Previous Rotation); this operation it's akin to a difference between two quaternions
3.b Add that quaternion to the cube's rotation with the *= operator
3.c Set the platform's previous rotation value to the new one.
That's pretty much it.

Unity C# joystick rotation rest

I am making a game in C# in unity where the player uses an Xbox 360 controller to control a character, I can rotate the player easily like this using the right joystick:
if(Input.GetAxis("RightJoystickX")!=0 && Input.GetAxis("RightJoystickY")!=0)
{
float horizontal = Input.GetAxis("RightJoystickX") * Time.deltaTime;
float vertical = Input.GetAxis("RightJoystickY") * Time.deltaTime;
float angle = Mathf.Atan2(vertical, horizontal) * Mathf.Rad2Deg;
characterController.transform.eulerAngles = new Vector3(0, newAngle, 0);
}
however, every time I release the joystick and then move it again, it immediately jumps to that new position and doesn't add the rotation to the previous rotation. This is a problem as the player can only move forwards in the direction the character is rotation, i need to be able to add rotations to the previous state without jumping to a new rotation.

Categories