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.
Related
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.
I have a camera that orbits a point on the screen. Clicking and dragging your mouse left and right will orbit around the focus point and releasing the mouse will leave the camera at that angle.
I'm trying to create a function that will orbit the camera 180 degrees, smoothly. So for example, if the camera is behind the "player" and this function gets called, the camera will orbit to the front of the player over the course of X seconds and stop in the exact opposite position of where it started.
Here's the code I'm currently using for camera rotation based on mouse click and drag:
Quaternion camTurnAngle =
Quaternion.AngleAxis(Input.GetAxis("Mouse X") * rotationSpeed, Vector3.up);
this.cameraOffset = camTurnAngle * this.cameraOffset;
Vector3 newPos = this.currentFocusPoint + this.cameraOffset;
transform.position = Vector3.Slerp(transform.position, newPos, smoothFactor);
transform.LookAt(this.currentFocusPoint);
I am trying to replace this mouse-drag based camera rotate with a UI button (aka, a single function call) that will fully orbit the camera 180 degrees over a short period of time, and then it will rotate back if pressed again. Here's an MS paint drawing just in case my explanation is confusing:
I'm not sure how do to something like this. It sounds similar to using Vector3.Slerp, but I cannot find a solution that works. I am so far from a solution that I don't really have any sensible code to post. I'll just say that I've tried two methods so far:
1) I've tried using transform.RotateAround, where angle I pass in is rotateSpeed * Time.deltaTime. My issue is I don't know how to check for an end condition, and without one my camera will rotate forever.
2) I've tried messing with Quaternion.Slerp, but the results are seeing are not what I expected.
How can I achieve a smooth 180 degree camera orbit, that takes a predetermined amount of time to complete?
This is a textbook use for a coroutine. Basically, start a coroutine that keeps track of how much time is left in the orbit, then either updates cameraOffset based on the time that's left or Time.deltaTime, whatever is shorter.
private IEnumerator RotateCam(float rotateDuration)
{
float timeLeft = rotateDuration;
// possibly - disable control of camera here
while (timeLeft > 0)
{
yield return null;
float elapsedRotationTime = Mathf.Min(timeLeft, Time.deltaTime);
float angleThisFrame = 180f * elapsedRotationTime / rotateDuration;
Quaternion camTurnAngle = Quaternion.AngleAxis(angleThisFrame, Vector3.up);
this.cameraOffset = camTurnAngle * this.cameraOffset;
transform.position = this.currentFocusPoint + this.cameraOffset;
transform.LookAt(this.currentFocusPoint);
timeLeft -= Time.deltaTime;
}
// possibly - re-enable control of camera here
}
Then, when you want to begin the rotation:
// private Coroutine rotateCoroutine
this.rotateCoroutine = StartCoroutine(RotateCam(2f));
Im building a first person controller in unity and im trying to make it so the camera tracks mouse location so the player could look around but when i try and block the player from getting the camera upside down the camra starts to shake and it messes the rotation of the camera
// Get Axis
float mouseY = Input.GetAxis(mouseYInputName) * mouseSensitivity *
Time.deltaTime;
// Check if past limit
clampX += mouseY;
if (Mathf.Abs(clampX) < 90f) {
transform.Rotate(Vector3.left * mouseY);
}
else {
Vector3 eulerRotation = -transform.eulerAngles;
eulerRotation.x = (clampX > 90f ? 270f : 90f);
transform.eulerAngles = eulerRotation;
}
There's a few problems with your code.
First, you negative the rotation when assigning it to your vector (-transform.eulerAngles) but then just re-assign the modified vector to the transform. This will have the effect of flipping the Y and Z axis rotations every frame.
Additionally, the clamping itself looks a bit off. Right now, when the rotation is moved past 90°, it is snapped to 270° instantly, and when it's moved past -90°, it is snapped to 90° instead.
Another issue:
With your current code, if the player keeps moving the mouse after clamping has already started, the clampX variable is still being increased. Thus when the player starts moving the mouse in the other direction, they have to get the clampX value back into acceptable values again before the camera even starts moving.
Here's an approach to clamping that hopefully doesn't have those issues:
clampX += mouseY;
if (Mathf.Abs(clampX) < 90f) {
transform.Rotate(Vector3.right * mouseY);
}
else {
if (clampX > 90f) clampX = 90f;
else if (clampX < -90f) clampX = -90f;
Vector3 eulerRotation = transform.eulerAngles;
eulerRotation.x = clampX;
transform.eulerAngles = eulerRotation;
}
(I also replaced Vector3.left with Vector3.right, since that's positive X in Unity.)
I have designed an attack method for a player that is full functional, however I am new to AI and have no idea as to where I would begin when it comes to adapting this into a state for an FSM.
protected void UpdateAttackState()
{
// check for input
float rot = transform.localEulerAngles.y + rotationSpeed * Time.deltaTime * Input.GetAxis("Horizontal");
Vector3 fwd = transform.forward * moveSpeed * Time.deltaTime * Input.GetAxis("Vertical");
// Tank Chassis is rigidbody, use MoveRotation and MovePosition
GetComponent<Rigidbody>().MoveRotation(Quaternion.AngleAxis(rot, Vector3.up));
GetComponent<Rigidbody>().MovePosition(_rigidbody.position + fwd);
if (turret) {
Plane playerPlane = new Plane(Vector3.up, transform.position + new Vector3(0, 0, 0));
// Generate a ray from the cursor position
Ray RayCast = Camera.main.ScreenPointToRay(Input.mousePosition);
// Determine the point where the cursor ray intersects the plane.
float HitDist = 0;
// If the ray is parallel to the plane, Raycast will return false.
if (playerPlane.Raycast(RayCast, out HitDist))
{
// Get the point along the ray that hits the calculated distance.
Vector3 RayHitPoint = RayCast.GetPoint(HitDist);
Quaternion targetRotation = Quaternion.LookRotation(RayHitPoint - transform.position);
turret.transform.rotation = Quaternion.Slerp(turret.transform.rotation, targetRotation, Time.deltaTime* turretRotSpeed);
}
}
if(Input.GetButton("Fire1"))
{
if (elapsedTime >= shootRate)
{
//Reset the time
elapsedTime = 0.0f;
//Also Instantiate over the PhotonNetwork
if ((bulletSpawnPoint) & (bullet))
Instantiate(bullet, bulletSpawnPoint.transform.position, bulletSpawnPoint.transform.rotation);
}
}
// Update the time
elapsedTime += Time.deltaTime;
}
This is completely dependent on how you want your state machine to work. An example of this would be the AI trying to seek and destroy other players.
Seeking State: AI would move around the map with a detection zone (collider) that would change state on trigger enter. This would then save a position of the detection's generalized location which would be used to move and rotate the AI tank towards that position.
Firing State: AI enters a new detection zone (collider) within firing range, turret will move to aim at target. AI will also move around the map to keep firing range at a maximum while also keeping track of the last known position of the enemy if they happen to go out of range.
Detecting Fire: Player or other AI fires their weapon this could increase the detection radius resulting in a seeking state being called.
After those functions are figured out, place a switch or something similar within the Update() to allow everything within the AI to run alongside Unity's functions.
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